コンテナを使用してNode.jsエージェントをインストール
Node.jsエージェントのコンテナへのインストールは、インストールがコンテナ内で行われることを除いて、基本的に標準のインストール手順と同じです。ベストプラクティスに従うには、環境変数を使用してContrastの認証情報を設定する必要があります。
Node.jsエージェントをコンテナにインストールする場合、環境変数を使用するのが最も安全な方法です。コンテナはQAや本番システムから移行されることが多いため、コンテナ定義に認証情報をハードコーディングしないことをお勧めします。
インストールを行う前に
本項では、Dockerを例として、コンテナ化されたアプリケーションにNode.jsエージェントをインストールするための一般的な手順について説明します。
コンテナや関連ソフトウェアの仕組みを基本的に理解している必要があります。実際の手順は、ご利用の環境に合わせて調整してください。
エージェントをインストール
以下のいずれかの方法で、Nodejsエージェントをインストールします。
アプリケーションの開発時にエージェントを追加 (推奨)
この方法の場合、アプリケーションの
package.json
でエージェントを取り込みます。次のコマンドを使用して、パイプラインやコンテナイメージにエージェントを追加します。
npm install @contrast/agent
Dockerfileにエージェントを追加
アプリケーションのイメージを別々に管理したい場合(Contrastエージェントを使用する場合と使用しない場合)は、コンテナのビルド時にエージェントを追加します。
次のコマンドを使用して、既存のDockerfile、またはアプリケーションのイメージをベースイメージとして使用する新規のDockerfileにエージェントを追加します。
npm install @contrast/agent
エージェントを設定
Node.jsエージェントをDockerなどのコンテナにデプロイしたアプリケーションで設定する場合は、以下の手順に従ってください(そうでない場合は、通常のNode.jsエージェントの設定を参照してください)。Node.jエージェントの設定には、設定した値が有効になる優先順位があります。
エージェントトークンを使用 する場合
この変数はbase64でエンコードされたJSONオブジェクトで、url、api_key、service_key、user_nameの構成設定が含まれます。これらの値を、この1つの変数で設定できます。
エージェントトークンを使用します。
CONTRAST__API__TOKEN
環境変数を使用して、アプリケーション固有の設定値を指定します。これらは、Dockerfile内でENV命令を定義するか、Docker runコマンドの実行時に
-e
オプションで指定することができます。アプリケーション固有の値を設定するためによく使用される環境変数の一覧を参照して下さい。また、Contrastエージェント設定エディタで変数の全一覧を参照することもできます。view a full list of variables.例えば、次のコマンドを使用して、コンテナを構築することができます。
docker build -t my-app-image
そして、コンテナの実行時には次のコマンドを使用します。
docker run -p 3000:3000 --name my-app-instance \ -e "CONTRAST__API__TOKEN=your-api-token" \ My-app-image
クラウドプロバイダの使用時に環境変数を設定するプロセスでは、一般的には、シークレットマネージャを使用し、それらのシークレットの値を環境変数にリンクすることになります。
従来の設定を使用する場合
エージェントの設定で、従来の設定とエージェントトークンの両方を参照している場合(環境変数またはYAMLファイル内)、従来の設定が優先されます。エージェントトークンの値のみを使用するには、従来の設定への参照を削除してください。
5.15.0より前のバージョンのNode.jsエージェントを使用している場合、以下の変数が必要です。
export CONTRAST__API__URL export CONTRAST__API__API_KEY export CONTRAST__API__SERVICE_KEY export CONTRAST__API__USER_NAME
環境変数を使用して、アプリケーション固有の設定値を指定します。これらは、Dockerfile内でENV命令を定義するか、Docker runコマンドの実行時に
-e
オプションで指定することができます。アプリケーション固有の値を設定するためによく使用される環境変数の一覧を参照して下さい。また、Contrastエージェント設定エディタで変数の全一覧を参照することもできます。例えば、次のコマンドを使用して、コンテナを構築することができます。
docker build -t my-app-image
そして、コンテナの実行時には次のコマンドを使用します。
docker run -p 3000:3000 --name my-app-instance \ -e "CONTRAST__API__URL=your-ts-url" \ -e "CONTRAST__API__API_KEY=your-api-key" \ -e "CONTRAST__API__SERVICE_KEY=your-service-key" \ -e "CONTRAST__API__USER_NAME=your-user-name" \ My-app-image
クラウドプロバイダの使用時に環境変数を設定するプロセスでは、一般的には、シークレットマネージャを使用し、それらのシークレットの値を環境変数にリンクすることになります。
実行して確認
Node.js エージェントのリライタCLI を使用する場合は、エージェントのリライタCLIの説明を参照して下さい。
アプリケーションを起動する際に、Contrastエージェントを先にロードしておく必要があります。通常は、これはDockerfileのCMD文で行いますが、
package.json
に定義したnpmスクリプトも使用できます。例えば、通常アプリケーションを次のように起動する場合:
CMD [“node”, “app”]
次のコマンドを使用すると、Contrastを有効にしてアプリケーションを実行できます。
CMD [“node”, “--import”, “@contrast/agent”, “app”]
エージェントが起動すると、エージェントの認証キーを使用してContrastサーバへの接続を試みます。
ヒント
エージェントの認証情報を保護するために、Docker secretを利用して、デプロイ時に環境変数として渡すこともできます。例:
docker run -e CONTRAST__API_ -e CONTRAST__API__API_KEY=<value> -e CONTRAST__API__SERVICE_KEY=<value> -e CONTRAST__API__USER_NAME=<value> -e CONTRAST__SERVER__ENVIRONMENT=<value> image_with_contrast
コンテナのログのアクティビティをチェックして、Contrastが実行されていることを確認します。
例えば、ログアクティビティは以下のようになります。
2025-01-02 10:54:46 {"level":30,"time":1735833286319,"tid":0,"pid":1,"hostname":"5d9ee61ac84f","name":"contrast","msg":"Starting @contrast/agent 2.16.8--------------------------------------v5.23.0"} 20202025-0701-20T1902 10:0554:14.407Z INFO 46 {"level":30,"time":1735833286319,"tid":0,"pid":1,"hostname":"5d9ee61ac84f","name":"contrast-service","config": BUILD {"progname_errors":[],"enable": "true","api":{"enable":"true","url":"https://teamserver-darpa-agents.contsec.com/Contrast Service", "api_key":"contrast-redacted-api.api_key","service_key":"contrast-redacted-api.service_key","user_name":"agent_b47d608e-1971-4560-961c-a55bd4111b61@Alexstestorg","token":"contrast-redacted-api.token","proxy":{"enable":"false","url":"undefined"}},"agent":{"stack_trace_limit":"10","stack_trace_filters":"agent-,@contrast,node-agent","diagnostics":{"enable":"true","report_path":"/juice-shop"},"route_coverage":{"enable":"true"},"reporters":{"file":"undefined"},"heap_dump":{"enable":"false","path":"contrast_heap_dumps","delay_ms":"10000","window_ms":"10000","count":"5"},"polling":{"app_activity_ms":"30000","app_settings_ms":"30000","app_update_ms":"30000","server_settings_ms":"30000"},"logger":{"path":"/juice-shop/contrast.log","level":"info","append":"true","stdout":"true"},"security_logger":{"path":"/juice-shop/security.log","level":"error","stdout":"false","syslog":{"enable":"false","ip":"127.0.0.1","port":"514","facility":"19","severity_exploited":"alert","severity_blocked":"notice","severity_blocked_perimiter":"notice","severity_probed":"warning","severity_suspicious":"warning"}},"node":{"app_root":"/juice-shop","cmd_ignore_list":"","exclusive_entrypoint":"undefined","rewrite":{"enable":"true","cache":{"enable":"true","path":"/juice-shop/.contrast"}},"source_maps":{"enable":"true"},"library_usage":{"reporting":{"enable":"true","interval_ms":"100"}},"metrics":{"enable":"true","warn_ms":"5000"},"npm_path":"npm"}},"inventory":{"analyze_libraries":"true","gather_metadata_via":"undefined"},"assess":{"enable":"false","probabilistic_sampling":{"enable":"false","base_probability":"0.1","route_monitor":{"enable":"true","ttl_ms":"1800000"}},"tags":"undefined","stacktraces":"ALL","max_context_source_events":"150","max_propagation_events":"500","safe_positives":{"enable":"false"},"trust_custom_validators":"false"},"protect":{"enable":"false","probe_analysis":{"enable":"true"},"rules":{"disabled_rules":"","cmd-injection":{"mode":"off"},"cmd-injection-command-backdoors":{"mode":"off"},"cmd-injection-semantic-chained-commands":{"mode":"off"},"cmd-injection-semantic-dangerous-paths":{"mode":"off"},"crypto-bad-mac":{"mode":"off"},"crypto-bad-ciphers":{"mode":"off"},"crypto-weak-randomness":{"mode":"off"},"method-tampering":{"mode":"off"},"nosql-injection":{"mode":"off"},"nosql-injection-mongo":{"mode":"off"},"path-traversal":{"mode":"off"},"path-traversal-semantic-file-security-bypass":{"mode":"off"},"reflected-xss":{"mode":"off"},"sql-injection":{"mode":"off"},"ssjs-injection":{"mode":"off"},"ssrf":{"mode":"off"},"unsafe-code-execution":{"mode":"off"},"unsafe-file-upload":{"mode":"off"},"untrusted-deserialization":{"mode":"off"},"xxe":{"mode":"off"},"unvalidated-redirect":{"mode":"off"}}},"application":{"name":"mw-juice-slim","path":"/","group":"undefined","code":"undefined","version": "2.8.1undefined","tags":"undefined", "buildTimemetadata":"undefined","session_id":"undefined","session_metadata":"undefined"},"server":{"name":"5d9ee61ac84f","type":"Node.js v20.15.0","environment":"undefined","tags":"undefined","version":"undefined","discover_cloud_resource":"true"}},"msg":"Agent configuration"} 20202025-0701-20T1902 10:0554:14.407Z INFO Building timer for orphan request cleanup46 {"prognamelevel":30,"time":1735833286328,"tid": 0,"Contrast Servicepid":1, "cleanupMshostname":"5d9ee61ac84f","name":"contrast","strategy":1,"msg":"updating assess 5000sampler"} 20202025-0701-20T1902 10:0554:14.408Z INFO Building timer for orphan app cleanup48 {"prognamelevel": "Contrast Service"30, "time":1735833287906,"tid":0,"pid":1,"hostname":"5d9ee61ac84f","name":"contrast","msg":"Received new log 5000level: debug from server-features"} 20202025-0701-20T1902 10:0554:14.450Z INFO Creating New Application Server48 {"prognamelevel": "Contrast Service"30, "uuidtime": "96299b72-f867-4354-b9c9-1eb23511cb8a"1735833287907, "serverNametid": "bc1bd6e5cd3a"0, "clientId": "1", "pid": 1,"hostname":"5d9ee61ac84f","name":"contrast","msg":"assess sampling disabled"} 20202025-0701-20T1902 10:0554:14.450Z WARN Failed to initialize secure client, falling back to insecure client48 {"prognamelevel":30,"time":1735833287907,"tid":0,"pid":1,"hostname":"5d9ee61ac84f","name":"contrast","enabledRules": ["Contrast Servicebot-blocker","cmd-injection","cmd-injection-command-backdoors","cmd-injection-semantic-chained-commands","cmd-injection-semantic-dangerous-paths","crypto-bad-mac","crypto-bad-ciphers","crypto-weak-randomness","ip-denylist","method-tampering","nosql-injection","nosql-injection-mongo","path-traversal","path-traversal-semantic-file-security-bypass","reflected-xss","sql-injection","ssjs-injection","ssrf","unsafe-code-execution","unsafe-file-upload","untrusted-deserialization","virtual-patch","xxe","unvalidated-redirect","autocomplete-missing","cache-controls-missing","clickjacking-control-missing","parameter-pollution","csp-header-missing","csp-header-insecure","hsts-header-missing","x-powered-by-header","xcontenttype-header-missing","xxssprotection-header-disabled","httponly","secure-flag-missing"],"msg":"Assess policy enabled rules updated"} 20202025-0701-20T1902 10:0554:15.473Z INFO setting new server features for context48 {"prognamelevel":30,"time":1735833287907,"tid":0,"pid":1,"hostname":"5d9ee61ac84f","name":"contrast","policy":{"exclusions":{"url":[],"querystring":[],"header":[],"body":[],"cookie":[],"parameter":[]},"bot-blocker":"block_at_perimeter","cmd-injection":"monitor","cmd-injection-command-backdoors":"monitor","cmd-injection-semantic-chained-commands":"monitor","cmd-injection-semantic-dangerous-paths":"monitor","crypto-bad-mac":"off","crypto-bad-ciphers":"off","crypto-weak-randomness":"off","ip-denylist":"off","method-tampering":"monitor","nosql-injection":"monitor","nosql-injection-mongo":"monitor","path-traversal":"monitor","path-traversal-semantic-file-security-bypass":"monitor","reflected-xss":"monitor","sql-injection": "Contrast Servicemonitor","ssjs-injection":"monitor", "uuidssrf": "96299b72off","unsafe-f867code-4354execution":"off","unsafe-b9c9file-1eb23511cb8aupload":"off", "serverNameuntrusted-deserialization": "bc1bd6e5cd3amonitor","virtual-patch":"off","xxe":"monitor","unvalidated-redirect":"off","rulesMask":510},"msg":"Protect policy updated"} 20202025-0701-20T1902 10:0554:15.474Z ERROR Error setting up CEF syslog53 {"prognamelevel":30,"time":1735833293571,"tid":2,"pid":1,"hostname":"5d9ee61ac84f","name": "Contrast Servicecontrast", "errmsg":"Received new "open /juice-shop/security.loglog level: permission denieddebug from server-features"} 2020-07-20T19:05:15.475Z INFO starting event scanner {"progname": "Contrast Service", "report": {}} ....2020-07-20T19:05:15.486Z INFO Creating new application {"progname": "Contrast Service", "uuid": "96299b72-f867-4354-b9c9-1eb23511cb8a", "serverName": "bc1bd6e5cd3a", "appName": "juiceshop-guide", "language": "Node", "clientId": "1", "pid": 1} 2020-07-20T19:05:15.486Z INFO AppCreate: creating and initializing new application {"progname": "Contrast Service", "uuid": "96299b72-f867-4354-b9c9-1eb23511cb8a", "server_name": "bc1bd6e5cd3a", "app_name": "juiceshop-guide", "app_lang": "Node", "client_id": "1", "pid": 1} 2020-07-20T19:05:15.921Z INFO setting new application settings {"progname": "Contrast Service", "uuid": "96299b72-f867-4354-b9c9-1eb23511cb8a", "serverName": "bc1bd6e5cd3a", "appName": "juiceshop-guide", "language": "Node"} 2020-07-20T19:05:15.922Z INFO Setting session id on app context: {"progname": "Contrast Service", "uuid": "96299b72-f867-4354-b9c9-1eb23511cb8a", "clientid": "1", "appname": "juiceshop-guide", "applang": "Node", "apppath": "/juice-shop/package.json", "sessionid": "cd0b271e66974162bf5fcca8b32e37b1"} Entering main at /juice-shop/appinfo: All dependencies in ./package.json are satisfied (OK)...
注記
また、Dockerイメージの作成時にエージェントをインストールしたり、distrolessのNode.jsコンテナを使用してコンテナにインストールすることも可能です。
関連項目
Contrastサポートポータル:KubernetesでのNode.jsエージェント
Contrastサポートポータル:AWS FargateとContrastエージェント