シークレットを適切に管理する
- データベース接続文字列
- 外部APIを呼び出すための認証キー
- データの暗号鍵
- 各種証明書
これらの情報が外部に漏洩することで、データベースへの不正アクセスによる情報流出やAPIの不正利用、サーバの乗っ取りなどビジネスに大きな損失が生じるリスクがあります。
例えば、フロントアプリがバックエンドのデータベースサーバにアクセスするときに使用するシークレットをDockerfileのCOPY命令を使ってコンテナに追加し、その後RUN命令で削除するDockerfileを作成したとします。このやり方であればファイルを削除しているので、一見安全そうにも思えます。
FROM gcr.io/distroless/static
COPY secret /root/bad-secret
RUN rm -f /root/bad-secret
しかし、DockerイメージはDockerfileの命令ごとにレイヤごとにファイルシステムの差分を計算し、イメージレイヤを作成します。そのためdocker saveコマンドでイメージをアーカイブとして出力することで削除したはずのファイルが参照できてしまうため注意してください。
docker build -t bad-image .
docker save bad-image | tar xv
tar xvf xxxxxxxxxxxx/layer.tar
cat root/bad-secret
なお、DockerfileのCOPY命令とADD命令はどちらもローカルマシンからコンテナにファイルを追加する命令ですが、ADD命令はリモートのファイルがダウンロードできるという違いがあります。そのため、ADD命令ではインターネット上から悪意のあるファイルがコンテナイメージに追加されるリスクがあります。いずれにせよ、コンテナイメージ内でシークレットを管理するのではなく、外部から参照できるような実装にしておくことが重要です。
Kubernetesでコンテナアプリケーションを動かす場合、機密情報はSecretsリソースを使って管理します。KubernetesのSecretsリソースは以下の組込み型を提供します。
タイプ | 利用例 |
---|---|
Opaque | ユーザーのデータ |
kubernetes.io/service-account-token | Service Account トークン |
kubernetes.io/dockercfg | ~/.dockercfg |
kubernetes.io/dockerconfigjson | ~/.docker/config.json |
kubernetes.io/basic-auth | Basic認証のためのクレデンシャル |
kubernetes.io/ssh-auth | SSH認証のためのクレデンシャル |
kubernetes.io/tls | TLSクライアント/サーバのデータ |
bootstrap.kubernetes.io/token | Bootstrapトークン |
例えば、ユーザー名とパスワードを設定したSecretsリソースを作成するには次のマニュフェストを作成します。その際、「username」と「password」は元の値をBase64エンコードした値を設定します。
apiVersion: v1
kind: Secret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
PodからSecretsリソースをファイルとして利用するには、次のマニュフェストを作成します。ファイルのパーミッションはデフォルトで「0644」になります。「.spec.volumes[].secret.defaultMode」でSecretボリューム全体のデフォルトモードを指定し、必要に応じてキー単位で上書きもできます。
apiVersion: v1
kind: Pod
...
spec:
containers:
- name: mypod
image: xxxx
volumeMounts:
- name: foo
mountPath: "/etc/foo"
readOnly: true
volumes:
- name: foo
secret:
secretName: mysecret
defaultMode: 0400
また、PodでSecretsを環境変数として利用するときは、次のようなマニュフェストを作成します。
apiVersion: v1
kind: Pod
metadata:
name: secret-env-pod
spec:
containers:
- name: mycontainer
image: redis
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
restartPolicy: Never
ここで1つ注意しておきたいのは、Secretsマニュフェストで指定した「username」と「password」は一見意味のない値に見えますが、これは元の値をBase64エンコードしただけであるため、簡単にデータを読み出すことができるという点です。
AzureにはAzure Key Vault という秘匿情報を扱うためのマネージドサービスがあります。Key Vault に格納した秘匿情報は暗号化して保存され、秘匿情報に対するアクセス許可を細やかに設定できます。格納された秘匿情報は Azure によって業界標準のアルゴリズムとキーの長さを使用して自動的に保護されます。そのため、保管時の暗号化について開発者が意識する必要はありません。
AKSはシークレット ストアCSI ドライバと呼ばれるしくみを通じ、PodからAzure Key Vault上の秘匿情報にアクセスできます。
Azure Kubernetes ServiceとKey Vaultの詳細については、第6回の「Kubernetes クラスタの保護」を参照してください。