etcd を開けたらパスワードが平文で出てきた
目標
クラスターで唯一の真実の保存先であるetcdを直接開き、Secretが平文で保存されていることを自分の目で確認します。 そして、RBACが守るのはapiserverを通るAPI経路だけであり、etcdに直接接続すればその境界を 迂回できるという事実を、対比しながら観察します。
なぜ重要なのか
Kubernetesのすべてのオブジェクトは、最終的にetcdに保存されます。Secretのdataの値がbase64で見えるため、まるで
隠されているように感じますが、base64はエンコードであって暗号化ではありません。保存時の暗号化(encryption at rest)を
有効にしなければ、etcdに到達できる人は誰でもパスワードの原文をそのまま読めます。
一方でRBACは強力ですが、その統制が適用されるのはapiserverを通るリクエストだけです。ノードやetcdのデータ ディレクトリ、あるいは認証が設定されていないetcdのポートに直接アクセスする経路には、RBACは介入できません。 そのため、etcd自体をTLS・相互認証・保存時の暗号化で別途守る必要があります。このラボは「なぜそれらが 必要なのか」を、防御がないときの姿で先に見せます。
ステップ
- ネームスペース
kcsa-etcdを作成します。 - Secret
db-cred(password)とConfigMapapp-cfg(mode)を作成します。 - 証明書なしでetcdのエンドポイントに接続し、状態をファイルに残します。
- etcdからSecretを直接読み、パスワードの原文をファイルに取り出します。
- その値が暗号化ではなく平文であることを判定して記録します。
- ConfigMapだけを読める最小権限のServiceAccountで、RBACの境界を確認します。
- etcdを守る3つの統制(client TLS・peer TLS・保存時の暗号化)を整理します。
- 観察結果を
/root/kcsa-etcd/report.txtに帳簿として残します。
参考
- このラボのetcdは学習用のため、TLSも認証もありません。本番クラスターでは、絶対にこのまま放置してはいけません。
kubectl auth can-i ... --as=system:serviceaccount:<ns>:<sa>で、RBACの判定を直接確認できます(プレースホルダーはネームスペース名とServiceAccount名です)。- 公式ドキュメント: Secret・ 保存データの暗号化・ クラスターコンポーネント。
観察対象のネームスペースを開く
ネームスペースkcsa-etcdを作成してください。この中のSecretを、etcdから直接のぞき込みます。
ネームスペースはkubectl create namespaceで作成します。何度実行しても安全にするには、--dry-run=client -o yaml | kubectl apply -f -を使います。
パスワードを1つクラスターに預ける
ネームスペースkcsa-etcdにgenericなSecretdb-credを作成し、リテラルpassword=Pl4inEtcd!を格納してください。同じネームスペースにConfigMapapp-cfgを作成し、キーmode=prodを格納します。
kubectl create secret generic <이름> --from-literal=키=값でSecretを、kubectl create configmap <이름> --from-literal=키=값でConfigMapを作成します(プレースホルダーは名前、キー、値です)。Secretのdataの値はbase64でエンコードされて保存されます(暗号化ではありません)。
証明書なしでetcdにそのまま届く
etcdctlでetcdエンドポイントの状態を表として取り出し、/root/kcsa-etcd/etcd-status.txtに保存してください。クライアント証明書なしで127.0.0.1:2379に接続できることを確認してください。
ETCDCTL_API=3 etcdctl --endpoints=127.0.0.1:2379 endpoint status -w tableで状態を確認します。このクラスターのetcdにはTLSも認証もかかっていないため、証明書のオプションなしですぐに応答します。出力をファイルにリダイレクトしてください。
保存先の底からパスワードを拾う
etcdに保存されたdb-credSecretを直接読み、その中のパスワード文字列だけを/root/kcsa-etcd/leaked.txtに書いてください(値のみ、余分な改行なし)。ファイルの内容は、実際のパスワードと正確に一致している必要があります。
Secretはetcdのキー/registry/secrets/<네임스페이스>/<이름>の下にあります(プレースホルダーはネームスペース名とSecret名です)。etcdctl get <키>の結果にはバイナリが混ざっているので、grep -aでテキストだけを絞り込みます(プレースホルダーはキーです)。値が何なのかは、自分で見つけ出す必要があります。Secretのdataをbase64デコードすれば原文がわかり、その文字列がetcdの原本にもそのまま入っていることを確認してください。
これは暗号化ではないと判定する
ディスク(etcd)に置かれたその値が暗号化されているかを判定してください。暗号化されていなければ、/root/kcsa-etcd/encrypted.txtに1単語noだけを書きます。
base64はエンコードであって暗号化ではありません。etcdの原本でパスワードの原文がgrepでそのまま見つかるなら、保存時の暗号化(encryption at rest)が無効になっているということです。判定結果を小文字の1単語で書いてください。
RBACはAPI経路だけを守る
ネームスペースkcsa-etcdにServiceAccountapponlyとRolecfg-only(ConfigMapに対するgetとlistのみ)を作成し、このRoleをapponlyにバインドしてください。すると、apponlyはConfigMapは読めてもSecretは読めないはずです。
kubectl create role <이름> --verb=get --verb=list --resource=configmapsで最小権限のRoleを、kubectl create rolebindingでServiceAccountにバインドします(プレースホルダーは名前です)。kubectl auth can-i <동사> <자원> -n <ns> --as=system:serviceaccount:<ns>:<sa>で、実際の判定を確認できます(プレースホルダーは動詞、リソース、ネームスペース、ServiceAccount名です)。
etcdを守る3つを書く
読み物を根拠に、etcdを保護する3つの統制を/root/kcsa-etcd/mitigations.txtに正確に3行で書いてください。client-tls=required、peer-tls=required、encryption-at-rest=requiredです。
etcdのセキュリティは3つの軸です。クライアント↔etcd間のTLS・相互認証、etcdノード間(peer)のTLS、そしてapiserverの保存時の暗号化(EncryptionConfiguration)です。3つのキーをすべてrequiredで書いてください。キー名とスペルは、指示文のとおりに合わせる必要があります。
何を観察したかを帳簿として残す
/root/kcsa-etcd/report.txtに、正確に4行を書いてください。etcd-auth=none、secret-on-disk=plaintext、rbac-blocks-apponly=yes、etcd-bypasses-rbac=yesです。値は、前のステップで観察した実際の状態と一致している必要があります。
採点ツールはこの4行を実際の状態と照合します。etcdが認証なしで接続できたか(none)、Secretが平文で置かれていたか(plaintext)、apponlyがSecretを読めないか(yes)、そしてetcdへの直接アクセスがそのRBACの境界を迂回するか(yes)を、前のステップの結果で埋めてください。