# Dev ContainersでのDevice Protection

Device Protectionを[Feature](https://containers.dev/implementors/features/)としてDev Containerに追加すると、どの`devcontainer.json`からでも、カスタムDockerfileを用意せずに任意のベースイメージへ追加できます。

まず[コンテナでのDevice Protection](https://help.aikidogov.us/docs/docs-ja/aikido-device-protection/deploying-aikido-endpoint/device-protection-mdm-guides/linux/device-protection-in-containers)をお読みください。このFeatureは同じパッケージを同じ`--container`モードでインストールするため、エントリーポイント、Aikido CA変数、トークンのルールはすべて同じように機能します。

## Featureを追加する

プロジェクト内に、たとえば`.devcontainer/features/device-protection/`として以下を作成します。

{% code title=".devcontainer/features/device-protection/devcontainer-feature.json" overflow="wrap" %}
```json
{
  "id": "device-protection",
  "version": "1.0.0",
  "name": "Aikido Device Protection",
  "description": "Installs Aikido Device Protection in --container mode so package manager traffic inside the container is inspected for malware.",
  "entrypoint": "aikido-endpoint-protection-container-entrypoint"
}
```
{% endcode %}

`entrypoint`は、イメージ本来のエントリーポイントを置き換えるのではなく、それと並行してエージェントのスーパーバイザーを実行します。

{% code title=".devcontainer/features/device-protection/install.sh" overflow="wrap" %}
```bash
#!/usr/bin/env bash
set -euo pipefail

case "$(uname -m)" in
  x86_64) ARCH=amd64 ;;
  aarch64) ARCH=arm64 ;;
  *) echo "Aikido Device Protection: unsupported architecture $(uname -m)" >&2; exit 1 ;;
esac

if command -v apt-get >/dev/null 2>&1; then
  apt-get update
  apt-get install -y --no-install-recommends curl ca-certificates
  curl -fLO "https://github.com/AikidoSec/safechain-internals/releases/latest/download/EndpointProtection-${ARCH}.deb"
  AIKIDO_CONTAINER=1 AIKIDO_CI_CD=1 apt-get install -y --no-install-recommends "./EndpointProtection-${ARCH}.deb"
  rm -f "EndpointProtection-${ARCH}.deb"
  rm -rf /var/lib/apt/lists/*
elif command -v dnf >/dev/null 2>&1; then
  . /etc/os-release
  EL="el${VERSION_ID%%.*}"
  dnf install -y --setopt=install_weak_deps=False curl ca-certificates
  curl -fLO "https://github.com/AikidoSec/safechain-internals/releases/latest/download/EndpointProtection-${ARCH}.${EL}.rpm"
  AIKIDO_CONTAINER=1 AIKIDO_CI_CD=1 dnf install -y --setopt=install_weak_deps=False "./EndpointProtection-${ARCH}.${EL}.rpm"
  rm -f "EndpointProtection-${ARCH}.${EL}.rpm"
else
  echo "Aikido Device Protection: no apt-get or dnf found, unsupported base image" >&2
  exit 1
fi
```
{% endcode %}

これは「コンテナでのDevice Protection」にあるパッケージファイル方式なので、イメージにトークンが組み込まれることはありません。ベースイメージのパッケージマネージャーとアーキテクチャに合った`.deb`または`.rpm`を選択します。これらは手動でのLinuxインストールで使用するものと同じファイルです。

{% hint style="warning" %}
`token`をFeatureのオプションとして追加しないでください。オプションは、イメージビルド中にインストールスクリプトが読み込む`devcontainer-features.env`ファイルに書き込まれるため、その方法で渡したトークンはビルドレイヤーに残ってしまう可能性があります。代わりに、以下の`containerEnv`を使って、コンテナ起動時に`AIKIDO_TOKEN`を渡してください。
{% endhint %}

## devcontainer.jsonで設定する

{% code title=".devcontainer/devcontainer.json" overflow="wrap" %}
```json
{
  "name": "my-app",
  "image": "mcr.microsoft.com/devcontainers/javascript-node:20",
  "features": {
    "./features/device-protection": {}
  },
  "containerEnv": {
    "AIKIDO_TOKEN": "${localEnv:AIKIDO_TOKEN}"
  },
  "postCreateCommand": "echo 'for f in /etc/profile.d/aikido-*.sh; do [ -e \"$f\" ] && . \"$f\"; done' >> ~/.bashrc"
}
```
{% endcode %}

* `features`は、`devcontainer.json`からの相対的なローカルパスでFeatureを参照します。
* `containerEnv`は、コンテナの起動時にトークンを渡します。Dev Containerを開く前に、シェル（またはCodespacesのシークレット）で`AIKIDO_TOKEN`を設定してください。
* `postCreateCommand`はCAの読み込み処理を`~/.bashrc`に追記します。これにより、後からコンテナ内で開いたターミナル（VS Codeの統合ターミナル、Codespaces）がAikido CAを読み込み、インストールが`SELF_SIGNED_CERT_IN_CHAIN`で失敗することを防ぎます。

Dev Containerのデフォルトシェルがbashではなくzshの場合は、代わりに`~/.zshrc`に追記してください。

{% tabs %}
{% tab title="bash" %}
```json
"postCreateCommand": "echo 'for f in /etc/profile.d/aikido-*.sh; do [ -e \"$f\" ] && . \"$f\"; done' >> ~/.bashrc"
```
{% endtab %}

{% tab title="zsh" %}
```json
"postCreateCommand": "echo 'for f in /etc/profile.d/aikido-*.sh; do [ -e \"$f\" ] && . \"$f\"; done' >> ~/.zshrc"
```
{% endtab %}
{% endtabs %}

他の理由ですでにカスタム`Dockerfile`をビルドしている場合でも、`features`はその上に適用されます。エージェントのインストールや`ENTRYPOINT`の行をその`Dockerfile`に自分で追加する必要はなくなり、Featureがそれを行います。

各リポジトリにフォルダーをコピーして含める代わりに、複数のリポジトリでFeatureを再利用するには、`devcontainer features publish`を使ってチームが管理するレジストリに一度公開し、パスではなく`ghcr.io/<your-org>/devcontainer-features/device-protection:1`として参照します。publishコマンドについては[Dev Containers CLIのドキュメント](https://containers.dev/implementors/features-distribution/)を参照してください。

## GitHub Codespacesのシークレット

Codespacesでは、`AIKIDO_TOKEN`をコミットするのではなく、リポジトリまたは組織のCodespacesシークレットとして追加してください。`${localEnv:AIKIDO_TOKEN}`は、ローカルのシェル変数と同じように、環境変数として公開されたシークレットを読み込みます。

## 動作を確認する

Dev Containerで新しいターミナルを開き（`postCreateCommand`の変更を反映させるため、新規のものを開きます）、以下を実行します。

{% code overflow="wrap" %}
```bash
curl -sS -o /dev/null -w "%{http_code}\n" --max-time 15 https://registry.npmjs.org/safe-chain-test/-/safe-chain-test-0.0.1-security.tgz
aikidoctl version
```
{% endcode %}

`curl`は`403`を出力します。これはエージェントがマルウェアのテストパッケージを拒否したことを示します。`aikidoctl version`は、インストールされているバージョンとエージェントの状態を表示します。

## トラブルシューティング

エントリーポイントとインストール手順は他のLinuxコンテナと同じなので、まず「コンテナでのDevice Protection」のトラブルシューティング表を確認してください。Dev Containers固有の追加事項が2つあります。

| 問題                                                                                                                          | 解決策                                                                                                                                                                 |
| --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 統合ターミナルでパッケージのインストールが`SELF_SIGNED_CERT_IN_CHAIN`で失敗するが、`postCreateCommand`経由で実行すると成功する | `postCreateCommand`が完了する前にターミナルが開かれていたか、追記先とは別のシェルを使用しています。新しいターミナルを開き、`~/.bashrc`（または`~/.zshrc`）に`aikido-*.sh`の読み込み行があるか確認してください |
| `no apt-get or dnf found, unsupported base image`                                                                                 | ベースイメージが、Featureがまだ対応していないパッケージマネージャー（たとえばAlpineの`apk`）を使用しています。そのための分岐を追加するか、「コンテナでのDevice Protection」に示されている手順でエージェントを手動でインストールしてください |
