Kubernetesの基本
Reading time: 28 minutes
Kubernetesの基本
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
このページの元の著者は Jorge (彼の元の投稿を こちらで読む)
アーキテクチャと基本
Kubernetesは何をするのか?
- コンテナエンジンでコンテナを実行できる。
- スケジュールによりコンテナのミッションを効率的に行う。
- コンテナを生存させる。
- コンテナ間の通信を可能にする。
- デプロイメント技術を許可する。
- 情報のボリュームを処理する。
アーキテクチャ
- ノード: ポッドまたはポッドを持つオペレーティングシステム。
- ポッド: コンテナまたは複数のコンテナを包むラッパー。ポッドは通常、1つのアプリケーションのみを含むべきである(通常、ポッドは1つのコンテナを実行する)。ポッドはKubernetesが実行しているコンテナ技術を抽象化する方法である。
- サービス: 各ポッドはノードの内部範囲から1つの内部IPアドレスを持つ。しかし、サービスを介しても公開されることがある。サービスにもIPアドレスがあり、その目的はポッド間の通信を維持することである。したがって、1つのポッドが死んだ場合、新しい置き換え(異なる内部IPを持つ)がサービスの同じIPでアクセス可能になる。内部または外部として構成できる。サービスはまた、2つのポッドが同じサービスに接続されているときにロードバランサーとして機能する。
サービスが作成されると、kubectl get endpoints
を実行して各サービスのエンドポイントを見つけることができる。 - Kubelet: プライマリノードエージェント。ノードとkubectl間の通信を確立するコンポーネントであり、ポッドのみを実行できる(APIサーバーを介して)。KubeletはKubernetesによって作成されていないコンテナを管理しない。
- Kube-proxy: apiserverとノード間の通信(サービス)を担当するサービス。ノードのためのIPtablesが基本である。経験豊富なユーザーは、他のベンダーからの他のkube-proxiesをインストールすることができる。
- サイドカーコンテナ: サイドカーコンテナは、ポッド内のメインコンテナと一緒に実行されるべきコンテナである。このサイドカーパターンは、現在のコンテナの機能を変更することなく拡張し強化する。現在、私たちはアプリケーションがどこでも実行できるようにすべての依存関係をラップするためにコンテナ技術を使用していることを知っている。コンテナは1つのことだけを行い、そのことを非常にうまく行う。
- マスタープロセス:
- Api Server: ユーザーとポッドがマスタープロセスと通信するための方法である。認証されたリクエストのみが許可されるべきである。
- スケジューラ: スケジューリングは、ポッドがノードにマッチすることを確認することを指す。Kubeletがそれらを実行できるようにする。どのノードがより多くのリソースを利用可能かを決定するための十分な知性を持ち、新しいポッドをそれに割り当てる。スケジューラは新しいポッドを開始するのではなく、ノード内で実行されているKubeletプロセスと通信し、新しいポッドを起動する。
- Kube Controller manager: レプリカセットやデプロイメントなどのリソースをチェックし、例えば、正しい数のポッドやノードが実行されているかを確認する。ポッドが欠けている場合、新しいポッドを開始するためにスケジューラと通信する。APIへのレプリケーション、トークン、およびアカウントサービスを制御する。
- etcd: データストレージ、永続的、一貫性があり、分散型。Kubernetesのデータベースであり、クラスターの完全な状態を保持するキー-バリューストレージ(各変更はここにログされる)。スケジューラやコントローラーマネージャーなどのコンポーネントは、どの変更が発生したかを知るためにこのデータに依存する(ノードの利用可能なリソース、実行中のポッドの数...)。
- Cloud controller manager: フロー制御とアプリケーションのための特定のコントローラーである。つまり、AWSやOpenStackにクラスターがある場合。
ノードが複数(複数のポッドを実行)される可能性があるため、Apiサーバーへのアクセスが負荷分散され、etcdが同期される複数のマスタープロセスも存在する可能性がある。
ボリューム:
ポッドがデータを作成し、そのポッドが消えるときに失われるべきでない場合、それは物理ボリュームに保存されるべきである。Kubernetesはデータを永続化するためにポッドにボリュームをアタッチすることを許可する。ボリュームはローカルマシンまたはリモートストレージに存在する可能性がある。異なる物理ノードでポッドを実行している場合、すべてのポッドがアクセスできるようにリモートストレージを使用するべきである。
その他の構成:
- ConfigMap: サービスにアクセスするためのURLを構成できる。ポッドはここからデータを取得して、他のサービス(ポッド)と通信する方法を知る。これは資格情報を保存するための推奨場所ではないことに注意!
- Secret: パスワード、APIキーなどの秘密データをB64でエンコードして保存する場所である。ポッドは必要な資格情報を使用するためにこのデータにアクセスできる。
- Deployments: これはKubernetesによって実行されるコンポーネントが示される場所である。ユーザーは通常ポッドと直接作業しない。ポッドはReplicaSets(複製された同じポッドの数)で抽象化され、デプロイメントを介して実行される。デプロイメントはステートレスアプリケーションのためのものであることに注意。デプロイメントの最小構成は、名前と実行するイメージである。
- StatefulSet: このコンポーネントは、データベースのようなアプリケーションのために特に設計されており、同じストレージにアクセスする必要がある。
- Ingress: これはアプリケーションをURLで公開するために使用される構成である。これは外部サービスを使用しても行うことができるが、アプリケーションを公開するための正しい方法であることに注意。
- Ingressを実装する場合、Ingress Controllersを作成する必要がある。Ingress Controllerは、リクエストを受け取り、それをチェックし、サービスに負荷分散するエンドポイントとなるポッドである。Ingress Controllerは構成されたIngressルールに基づいてリクエストを送信する。Ingressルールは、異なるパスや異なる内部Kubernetesサービスへのサブドメインを指すことができることに注意。
- より良いセキュリティプラクティスは、Kubernetesクラスターのどの部分も公開しないように、エントリーポイントとしてクラウドロードバランサーまたはプロキシサーバーを使用することである。
- どのIngressルールにも一致しないリクエストが受信されると、Ingress Controllerはそれを「デフォルトバックエンド」に送る。
describe
コマンドを使用してIngress Controllerのこのパラメータのアドレスを取得できる。 minikube addons enable ingress
PKIインフラストラクチャ - 証明書機関CA:
- CAはクラスター内のすべての証明書の信頼されたルートである。
- コンポーネントが互いに検証できるようにする。
- すべてのクラスター証明書はCAによって署名される。
- etcdは独自の証明書を持つ。
- タイプ:
- apiserver cert.
- kubelet cert.
- scheduler cert.
基本的なアクション
Minikube
Minikubeは、完全なKubernetes環境をデプロイすることなく、Kubernetesでいくつかのクイックテストを実行するために使用できる。マスターとノードプロセスを1台のマシンで実行する。Minikubeはノードを実行するためにVirtualBoxを使用する。インストール方法は こちら を参照。
$ minikube start
😄 minikube v1.19.0 on Ubuntu 20.04
✨ Automatically selected the virtualbox driver. Other choices: none, ssh
💿 Downloading VM boot image ...
> minikube-v1.19.0.iso.sha256: 65 B / 65 B [-------------] 100.00% ? p/s 0s
> minikube-v1.19.0.iso: 244.49 MiB / 244.49 MiB 100.00% 1.78 MiB p/s 2m17.
👍 Starting control plane node minikube in cluster minikube
💾 Downloading Kubernetes v1.20.2 preload ...
> preloaded-images-k8s-v10-v1...: 491.71 MiB / 491.71 MiB 100.00% 2.59 MiB
🔥 Creating virtualbox VM (CPUs=2, Memory=3900MB, Disk=20000MB) ...
🐳 Preparing Kubernetes v1.20.2 on Docker 20.10.4 ...
▪ Generating certificates and keys ...
▪ Booting up control plane ...
▪ Configuring RBAC rules ...
🔎 Verifying Kubernetes components...
▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟 Enabled addons: storage-provisioner, default-storageclass
🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by defaul
$ minikube status
host: Running
kubelet: Running
apiserver: Running
kubeconfig: Configured
---- ONCE YOU HAVE A K8 SERVICE RUNNING WITH AN EXTERNAL SERVICE -----
$ minikube service mongo-express-service
(This will open your browser to access the service exposed port)
$ minikube delete
🔥 Deleting "minikube" in virtualbox ...
💀 Removed all traces of the "minikube" cluster
Kubectlの基本
Kubectl
はKubernetesクラスター用のコマンドラインツールです。これは、Kubernetes内でアクションを実行したりデータを要求したりするために、マスタープロセスのAPIサーバーと通信します。
kubectl version #Get client and server version
kubectl get pod
kubectl get services
kubectl get deployment
kubectl get replicaset
kubectl get secret
kubectl get all
kubectl get ingress
kubectl get endpoints
#kubectl create deployment <deployment-name> --image=<docker image>
kubectl create deployment nginx-deployment --image=nginx
#Access the configuration of the deployment and modify it
#kubectl edit deployment <deployment-name>
kubectl edit deployment nginx-deployment
#Get the logs of the pod for debbugging (the output of the docker container running)
#kubectl logs <replicaset-id/pod-id>
kubectl logs nginx-deployment-84cd76b964
#kubectl describe pod <pod-id>
kubectl describe pod mongo-depl-5fd6b7d4b4-kkt9q
#kubectl exec -it <pod-id> -- bash
kubectl exec -it mongo-depl-5fd6b7d4b4-kkt9q -- bash
#kubectl describe service <service-name>
kubectl describe service mongodb-service
#kubectl delete deployment <deployment-name>
kubectl delete deployment mongo-depl
#Deploy from config file
kubectl apply -f deployment.yml
Minikube ダッシュボード
ダッシュボードを使用すると、minikube が何を実行しているかをより簡単に確認できます。アクセスするための URL は次の場所にあります:
minikube dashboard --url
🔌 Enabling dashboard ...
▪ Using image kubernetesui/dashboard:v2.3.1
▪ Using image kubernetesui/metrics-scraper:v1.0.7
🤔 Verifying dashboard health ...
🚀 Launching proxy ...
🤔 Verifying proxy health ...
http://127.0.0.1:50034/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/
YAML構成ファイルの例
各構成ファイルには3つの部分があります: metadata、specification(起動する必要があるもの)、status(望ましい状態)。
デプロイメント構成ファイルの仕様の中には、実行するイメージを定義する新しい構成構造で定義されたテンプレートがあります:
同じ構成ファイルで宣言されたDeployment + Serviceの例( こちらから)
サービスは通常1つのデプロイメントに関連しているため、同じ構成ファイルで両方を宣言することが可能です(この構成で宣言されたサービスは内部からのみアクセス可能です):
apiVersion: apps/v1
kind: Deployment
metadata:
name: mongodb-deployment
labels:
app: mongodb
spec:
replicas: 1
selector:
matchLabels:
app: mongodb
template:
metadata:
labels:
app: mongodb
spec:
containers:
- name: mongodb
image: mongo
ports:
- containerPort: 27017
env:
- name: MONGO_INITDB_ROOT_USERNAME
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-username
- name: MONGO_INITDB_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mongodb-secret
key: mongo-root-password
---
apiVersion: v1
kind: Service
metadata:
name: mongodb-service
spec:
selector:
app: mongodb
ports:
- protocol: TCP
port: 27017
targetPort: 27017
外部サービス構成の例
このサービスは外部からアクセス可能です(nodePort
と type: LoadBlancer
属性を確認してください):
---
apiVersion: v1
kind: Service
metadata:
name: mongo-express-service
spec:
selector:
app: mongo-express
type: LoadBalancer
ports:
- protocol: TCP
port: 8081
targetPort: 8081
nodePort: 30000
note
これはテストに役立ちますが、本番環境では内部サービスのみを持ち、アプリケーションを公開するためのIngressを持つべきです。
Ingress構成ファイルの例
これにより、アプリケーションがhttp://dashboard.com
で公開されます。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
spec:
rules:
- host: dashboard.com
http:
paths:
- backend:
serviceName: kubernetes-dashboard
servicePort: 80
シークレット構成ファイルの例
パスワードがB64でエンコードされていることに注意してください(これは安全ではありません!)
apiVersion: v1
kind: Secret
metadata:
name: mongodb-secret
type: Opaque
data:
mongo-root-username: dXNlcm5hbWU=
mongo-root-password: cGFzc3dvcmQ=
ConfigMapの例
ConfigMapは、ポッドに与えられる設定であり、ポッドが他のサービスをどのように見つけてアクセスするかを知るためのものです。この場合、各ポッドは、mongodb-service
という名前が、通信できるポッドのアドレスであることを知っています(このポッドはmongodbを実行します):
apiVersion: v1
kind: ConfigMap
metadata:
name: mongodb-configmap
data:
database_url: mongodb-service
その後、deployment config内で、このアドレスは次のように指定でき、ポッドのenv内にロードされます:
[...]
spec:
[...]
template:
[...]
spec:
containers:
- name: mongo-express
image: mongo-express
ports:
- containerPort: 8081
env:
- name: ME_CONFIG_MONGODB_SERVER
valueFrom:
configMapKeyRef:
name: mongodb-configmap
key: database_url
[...]
ボリューム設定の例
さまざまなストレージ構成のyamlファイルの例は、https://gitlab.com/nanuchi/youtube-tutorial-series/-/tree/master/kubernetes-volumesで見つけることができます。
ボリュームは名前空間内には存在しないことに注意してください
名前空間
Kubernetesは、同じ物理クラスターにバックアップされた複数の仮想クラスターをサポートしています。これらの仮想クラスターは名前空間と呼ばれます。これは、多くのユーザーが複数のチームやプロジェクトに分散している環境での使用を意図しています。数人から十数人のユーザーがいるクラスターでは、名前空間を作成したり考えたりする必要はありません。Kubernetesにデプロイされたアプリケーションの各部分をより良く制御し、整理するために名前空間を使用し始めるべきです。
名前空間は名前のスコープを提供します。リソースの名前は名前空間内で一意である必要がありますが、名前空間間では一意である必要はありません。名前空間は互いにネストすることはできず、各Kubernetes リソースは1つの 名前空間のみに存在できます。
minikubeを使用している場合、デフォルトで4つの名前空間があります:
kubectl get namespace
NAME STATUS AGE
default Active 1d
kube-node-lease Active 1d
kube-public Active 1d
kube-system Active 1d
- kube-system: ユーザーが使用するためのものではなく、触れるべきではありません。マスターとkubectlプロセス用です。
- kube-public: 公開アクセス可能なデータ。クラスター情報を含むconfigmapが含まれています。
- kube-node-lease: ノードの可用性を決定します。
- default: ユーザーがリソースを作成するために使用する名前空間です。
#Create namespace
kubectl create namespace my-namespace
note
注意すべきは、ほとんどのKubernetesリソース(例:ポッド、サービス、レプリケーションコントローラーなど)は、いくつかのネームスペースに存在することです。しかし、ネームスペースリソースやノード、persistentVolumesなどの低レベルリソースはネームスペースに存在しません。どのKubernetesリソースがネームスペースにあり、どれがないかを確認するには:
kubectl api-resources --namespaced=true #ネームスペース内
kubectl api-resources --namespaced=false #ネームスペース外
そのコンテキスト内のすべての後続のkubectlコマンドに対してネームスペースを保存できます。
kubectl config set-context --current --namespace=<insert-namespace-name-here>
Helm
HelmはKubernetesのパッケージマネージャーです。YAMLファイルをパッケージ化し、公開およびプライベートリポジトリで配布することを可能にします。これらのパッケージはHelm Chartsと呼ばれます。
helm search <keyword>
Helmは、変数を使用して設定ファイルを生成するテンプレートエンジンでもあります。
Kubernetesシークレット
Secretは、パスワード、トークン、またはキーなどの機密データを含むオブジェクトです。このような情報は、Pod仕様やイメージに配置される可能性があります。ユーザーはSecretsを作成でき、システムもSecretsを作成します。Secretオブジェクトの名前は有効なDNSサブドメイン名でなければなりません。ここで公式ドキュメントを読むことができます。
Secretsは以下のようなものです:
- API、SSHキー。
- OAuthトークン。
- 資格情報、パスワード(プレーンテキストまたはb64 + 暗号化)。
- 情報やコメント。
- データベース接続コード、文字列… 。
Kubernetesには異なるタイプのシークレットがあります。
ビルトインタイプ | 使用法 |
---|---|
Opaque | 任意のユーザー定義データ(デフォルト) |
kubernetes.io/service-account-token | サービスアカウントトークン |
kubernetes.io/dockercfg | シリアライズされた~/.dockercfgファイル |
kubernetes.io/dockerconfigjson | シリアライズされた~/.docker/config.jsonファイル |
kubernetes.io/basic-auth | ベーシック認証のための資格情報 |
kubernetes.io/ssh-auth | SSH認証のための資格情報 |
kubernetes.io/tls | TLSクライアントまたはサーバーのためのデータ |
bootstrap.kubernetes.io/token | ブートストラップトークンデータ |
note
Opaqueタイプはデフォルトであり、ユーザーによって定義された典型的なキーと値のペアです。
シークレットの動作:
以下の設定ファイルは、mysecret
というシークレットを定義し、2つのキーと値のペアusername: YWRtaW4=
とpassword: MWYyZDFlMmU2N2Rm
を持っています。また、mysecret
で定義されたusername
とpassword
が環境変数SECRET_USERNAME
__ と __ SECRET_PASSWOR
に公開されるsecretpod
というpodも定義しています。さらに、mysecret
内のusername
シークレットをパス/etc/foo/my-group/my-username
に0640
の権限でマウントします。
apiVersion: v1
kind: Secret
metadata:
name: mysecret
type: Opaque
data:
username: YWRtaW4=
password: MWYyZDFlMmU2N2Rm
---
apiVersion: v1
kind: Pod
metadata:
name: secretpod
spec:
containers:
- name: secretpod
image: nginx
env:
- name: SECRET_USERNAME
valueFrom:
secretKeyRef:
name: mysecret
key: username
- name: SECRET_PASSWORD
valueFrom:
secretKeyRef:
name: mysecret
key: password
volumeMounts:
- name: foo
mountPath: "/etc/foo"
restartPolicy: Never
volumes:
- name: foo
secret:
secretName: mysecret
items:
- key: username
path: my-group/my-username
mode: 0640
kubectl apply -f <secretpod.yaml>
kubectl get pods #Wait until the pod secretpod is running
kubectl exec -it secretpod -- bash
env | grep SECRET && cat /etc/foo/my-group/my-username && echo
Secrets in etcd
etcdは、すべてのクラスターデータのKubernetesバックストアとして使用される、一貫性があり高可用性のキー-バリューストアです。etcdに保存されている秘密にアクセスしてみましょう:
cat /etc/kubernetes/manifests/kube-apiserver.yaml | grep etcd
証明書、キー、URLがファイルシステムにどこにあるかを見ることができます。それを取得すると、etcdに接続できるようになります。
#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] health
ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] health
通信が確立されると、秘密を取得できるようになります:
#ETCDCTL_API=3 etcdctl --cert <path to client.crt> --key <path to client.ket> --cacert <path to CA.cert> endpoint=[<ip:port>] get <path/to/secret>
ETCDCTL_API=3 etcdctl --cert /etc/kubernetes/pki/apiserver-etcd-client.crt --key /etc/kubernetes/pki/apiserver-etcd-client.key --cacert /etc/kubernetes/pki/etcd/etcd/ca.cert endpoint=[127.0.0.1:1234] get /registry/secrets/default/secret_02
ETCDへの暗号化の追加
デフォルトでは、すべてのシークレットはプレーンテキストでetcd内に保存されますが、暗号化レイヤーを適用しない限りそうなります。以下の例はhttps://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/に基づいています。
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources:
- secrets
providers:
- aescbc:
keys:
- name: key1
secret: cjjPMcWpTPKhAdieVtd+KhG4NN+N6e3NmBPMXJvbfrY= #Any random key
- identity: {}
その後、作成した設定ファイルの場所を指すように kube-apiserver
の --encryption-provider-config
フラグを設定する必要があります。 /etc/kubernetes/manifest/kube-apiserver.yaml
を修正し、次の行を追加できます:
containers:
- command:
- kube-apiserver
- --encriyption-provider-config=/etc/kubernetes/etcd/<configFile.yaml>
ボリュームマウントの中をスクロールダウンしてください:
- mountPath: /etc/kubernetes/etcd
name: etcd
readOnly: true
ボリュームマウントの中でhostPathまでスクロールします:
- hostPath:
path: /etc/kubernetes/etcd
type: DirectoryOrCreate
name: etcd
データが暗号化されていることの確認
データはetcdに書き込まれるときに暗号化されます。kube-apiserver
を再起動した後、新しく作成または更新されたシークレットは、保存される際に暗号化されるべきです。確認するには、etcdctl
コマンドラインプログラムを使用してシークレットの内容を取得できます。
default
名前空間にsecret1
という新しいシークレットを作成します:
kubectl create secret generic secret1 -n default --from-literal=mykey=mydata
- etcdctlコマンドラインを使用して、そのシークレットをetcdから読み取ります:
ETCDCTL_API=3 etcdctl get /registry/secrets/default/secret1 [...] | hexdump -C
ここで[...]
はetcdサーバーに接続するための追加の引数でなければなりません。
- 保存されたシークレットが
k8s:enc:aescbc:v1:
で始まることを確認します。これはaescbc
プロバイダーが結果のデータを暗号化したことを示します。 - APIを介して取得したときにシークレットが正しく復号化されていることを確認します:
kubectl describe secret secret1 -n default
はmykey: bXlkYXRh
と一致するべきです。mydataはエンコードされているため、シークレットを完全にデコードするにはシークレットのデコードを確認してください。
シークレットは書き込み時に暗号化されるため、シークレットの更新を行うとその内容が暗号化されます:
kubectl get secrets --all-namespaces -o json | kubectl replace -f -
最終的なヒント:
- FSに秘密を保持しないようにし、他の場所から取得してください。
- あなたの秘密にさらなる保護を追加するためにhttps://www.vaultproject.io/をチェックしてください。
- https://kubernetes.io/docs/concepts/configuration/secret/#risks
- https://docs.cyberark.com/Product-Doc/OnlineHelp/AAM-DAP/11.2/en/Content/Integrations/Kubernetes_deployApplicationsConjur-k8s-Secrets.htm
参考文献
kubesectips v1 | sickrov.github.io
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。