はじめに
以前に投稿した SPIRE の Node/Workload Attestation を AWS 上で体感する の Kubernetes バージョンの記事となります。
今回は Kubernetes 上で SPIRE Server と SPIRE Agent を稼働させて、K8s Worker Nodes が Node Attestation を終えた後に、アプリケーションコンテナが Workload Attestation を行い、自身の SVID を取得するまでの流れを確認していきたいと思います。
今回紹介するフローは以下となります。
- Node Attestation を許可する条件をホワイトリストで定義して SPIRE Server を起動する
- K8s Worker Nodes で SPIRE Agent を起動して Service Account ベースで Node Attestation を行う
- Node Attestation に成功した K8s Worker Nodes 上でアプリケーションコンテナ(Pod)を起動する
- アプリケーションコンテナの Namespace、Service Account、Podラベル などを使って Workload Attestation を行う
- SPIRE Agent から Workload Attestation に成功したアプリケーションコンテナに SVID が配られる
それでは実際にやっていきます。
環境
今回は minikube
で作成した Kubernetes v1.13.3
のクラスタを利用していきます。
SPIRE のバージョンは 0.8.0
を利用していきます。
SPIRE Server/Agent 用の Namespace 作成
SPIRE Server/Agent 用に spire
という Namespace を作成します。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Namespace
metadata:
name: spire
EOF
Namespace が作成できたか確認します。
$ kubectl get namespaces spire
NAME STATUS AGE
spire Active 9s
SPIRE Server の起動
まずは SPIRE Server を起動していきます。
Service Account の作成
SPIRE Server 用に spire-server
という Service Account を作成します。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: spire-server
namespace: spire
EOF
Service Account が作成できたか確認します。
$ kubectl get serviceaccounts -n spire spire-server
NAME SECRETS AGE
spire-server 1 19s
Secret の作成
SPIRE Server の起動に必要な CA証明書の鍵 を Secret として作成します。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Secret
metadata:
name: spire-server
namespace: spire
type: Opaque
data:
bootstrap.key: LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1JR2tBZ0VCQkRBZzJMYnVsWHpRWDFORisyRGkwUkt6TVdmRUdpb0JoaC9mRnB4N3lPRXFrYS8vVHBhZVUzTzUKUUpSWlhkV0hLdWFnQndZRks0RUVBQ0toWkFOaUFBUmFNSDZkSVpMRWhpTE9kdnpqRzdsWVlObVB6U2N2dGJWegpmTi9qeGFITFNacnRqdVlJRXJOOUNTdUFPQzRqaVBSbjdUKzBNZit2eUMwNjBzdXNpbTR6QlllaDdpOXRVRVcxCjdXK1BwZTNwWjRUeVZmQndLOHV6K1p5YTgrcFVyMk09Ci0tLS0tRU5EIEVDIFBSSVZBVEUgS0VZLS0tLS0K
EOF
bootstrap.key
の実体は以下となります。
$ echo -n 'LS0tLS1CRUdJTiBFQyBQUklWQVRFIEtFWS0tLS0tCk1JR2tBZ0VCQkRBZzJMYnVsWHpRWDFORisyRGkwUkt6TVdmRUdpb0JoaC9mRnB4N3lPRXFrYS8vVHBhZVUzTzUKUUpSWlhkV0hLdWFnQndZRks0RUVBQ0toWkFOaUFBUmFNSDZkSVpMRWhpTE9kdnpqRzdsWVlObVB6U2N2dGJWegpmTi9qeGFITFNacnRqdVlJRXJOOUNTdUFPQzRqaVBSbjdUKzBNZit2eUMwNjBzdXNpbTR6QlllaDdpOXRVRVcxCjdXK1BwZTNwWjRUeVZmQndLOHV6K1p5YTgrcFVyMk09Ci0tLS0tRU5EIEVDIFBSSVZBVEUgS0VZLS0tLS0K' | base64 -D
-----BEGIN EC PRIVATE KEY-----
MIGkAgEBBDAg2LbulXzQX1NF+2Di0RKzMWfEGioBhh/fFpx7yOEqka//TpaeU3O5
QJRZXdWHKuagBwYFK4EEACKhZANiAARaMH6dIZLEhiLOdvzjG7lYYNmPzScvtbVz
fN/jxaHLSZrtjuYIErN9CSuAOC4jiPRn7T+0Mf+vyC060susim4zBYeh7i9tUEW1
7W+Ppe3pZ4TyVfBwK8uz+Zya8+pUr2M=
-----END EC PRIVATE KEY-----
Secret が作成できたか確認します。
$ kubectl get secrets -n spire spire-server
NAME TYPE DATA AGE
spire-server Opaque 1 16s
ConfigMap の作成
SPIRE Server の設定ファイルと、起動に必要な CA証明書 を ConfigMap として作成する前に、Kubernetes で Node Attestation をするのに必要な設定を解説していきます。
以下が設定ファイルとなります。
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "example.org"
data_dir = "/run/spire/data"
log_level = "DEBUG"
upstream_bundle = true
svid_ttl = "1h"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/run/spire/data/datastore.sqlite3"
}
}
NodeAttestor "k8s_sat" {
plugin_data {
clusters = {
"demo-cluster" = {
service_account_key_file = "/run/k8s-certs/sa.pub"
service_account_whitelist = ["spire:spire-agent"]
}
}
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "disk" {
plugin_data {
keys_path = "/run/spire/data/keys.json"
}
}
UpstreamCA "disk" {
plugin_data {
ttl = "1h"
key_file_path = "/run/spire/secrets/bootstrap.key"
cert_file_path = "/run/spire/config/bootstrap.crt"
}
}
}
Kubernetes で Node Attestation をするためには、NodeAttestor Plugin に以下のどちらかを指定する必要があります。どちらもホワイトリストな Service Account ベースでの認証となります。
今回は k8s_sat
を利用して spire-agent
という Service Account で SPIRE Agent が起動している Node に認証を許可する設定で進めていきます。
それでは ConfigMap を作成していきます。
なお bootstrap.crt
は Secret で追加した bootstrap.key
と対となる CA証明書 です。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: spire-server
namespace: spire
data:
server.conf: |
server {
bind_address = "0.0.0.0"
bind_port = "8081"
registration_uds_path = "/tmp/spire-registration.sock"
trust_domain = "example.org"
data_dir = "/run/spire/data"
log_level = "DEBUG"
upstream_bundle = true
svid_ttl = "1h"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/run/spire/data/datastore.sqlite3"
}
}
NodeAttestor "k8s_sat" {
plugin_data {
clusters = {
# NOTE: Change this to your cluster name
"demo-cluster" = {
service_account_key_file = "/run/k8s-certs/sa.pub"
service_account_whitelist = ["spire:spire-agent"]
}
}
}
}
NodeResolver "noop" {
plugin_data {}
}
KeyManager "disk" {
plugin_data {
keys_path = "/run/spire/data/keys.json"
}
}
UpstreamCA "disk" {
plugin_data {
ttl = "1h"
key_file_path = "/run/spire/secrets/bootstrap.key"
cert_file_path = "/run/spire/config/bootstrap.crt"
}
}
}
bootstrap.crt: |
-----BEGIN CERTIFICATE-----
MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYT
AlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkz
MzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0C
AQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7m
CBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXw
cCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3Bp
ZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZ
IbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDF
D7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc=
-----END CERTIFICATE-----
EOF
ConfigMap が作成できたか確認します。
$ kubectl get configmaps -n spire spire-server
NAME DATA AGE
spire-server 2 22s
StatefulSet の作成
SPIRE Server を StatefulSet で起動する前に Volume と Mount 先を整理しておきます。
Volume | 種別 | 概要 | Mount先 |
---|---|---|---|
spire-config | ConfigMap | SPIRE Server の設定ファイル(server.conf)と起動に必要な CA証明書(bootstrap.crt)を渡す | /run/spire/config |
spire-secrets | Secret | 起動に必要な CA証明書の鍵(bootstrap.key)を渡す | /run/spire/secrets |
spire-data | Persistent Volume | DataStore Plugin で管理されるデータや KeyManager Plugin で管理される鍵を永続化するために渡す | /run/spire/data |
k8s-sa-cert | hostPath | NodeAttestor "k8s_sat" で Service Account を検証するための鍵を渡す | /run/k8s-certs/sa.pub |
それでは StatefulSet を作成していきます。
$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: spire-server
namespace: spire
labels:
app: spire-server
spec:
replicas: 1
selector:
matchLabels:
app: spire-server
serviceName: spire-server
template:
metadata:
namespace: spire
labels:
app: spire-server
spec:
serviceAccountName: spire-server
containers:
- name: spire-server
image: gcr.io/spiffe-io/spire-server:0.8.0
args:
- -config
- /run/spire/config/server.conf
ports:
- containerPort: 8081
volumeMounts:
- name: spire-config
mountPath: /run/spire/config
readOnly: true
- name: spire-secrets
mountPath: /run/spire/secrets
readOnly: true
- name: spire-data
mountPath: /run/spire/data
readOnly: false
- name: k8s-sa-cert
mountPath: /run/k8s-certs/sa.pub
readOnly: true
livenessProbe:
exec:
command:
- /opt/spire/bin/spire-server
- healthcheck
failureThreshold: 2
initialDelaySeconds: 15
periodSeconds: 60
timeoutSeconds: 3
volumes:
- name: spire-config
configMap:
name: spire-server
- name: spire-secrets
secret:
secretName: spire-server
- name: k8s-sa-cert
hostPath:
# NOTE: Change to the kube-apiserver's service account certificate
path: /var/lib/minikube/certs/sa.pub
type: File
volumeClaimTemplates:
- metadata:
name: spire-data
namespace: spire
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
EOF
StatefulSet が作成できたか確認します。
$ kubectl get statefulsets -n spire spire-server
NAME READY AGE
spire-server 1/1 14s
ログを確認すると SPIRE Server が正常に起動したことがわかります。
$ kubectl get pods -n spire spire-server-0
NAME READY STATUS RESTARTS AGE
spire-server-0 1/1 Running 0 36s
$ kubectl logs -n spire spire-server-0
time="2019-07-16T06:57:54Z" level=warning msg="Current umask 0022 is too permissive; setting umask 0027."
time="2019-07-16T06:57:54Z" level=info msg="data directory: \"/run/spire/data\""
time="2019-07-16T06:57:54Z" level=info msg="Opening SQL database" dbtype=sqlite3 subsystem_name=builtin.sql
time="2019-07-16T06:57:54Z" level=info msg="initializing database." subsystem_name=builtin.sql
time="2019-07-16T06:57:54Z" level=info msg="Plugin loaded." built-in=true name=sql services="[]" subsystem_name=catalog type=DataStore
time="2019-07-16T06:57:54Z" level=info msg="Plugin loaded." built-in=true name=k8s_sat services="[]" subsystem_name=catalog type=NodeAttestor
time="2019-07-16T06:57:54Z" level=info msg="Plugin loaded." built-in=true name=noop services="[]" subsystem_name=catalog type=NodeResolver
time="2019-07-16T06:57:54Z" level=info msg="Plugin loaded." built-in=true name=disk services="[]" subsystem_name=catalog type=KeyManager
time="2019-07-16T06:57:54Z" level=info msg="Plugin loaded." built-in=true name=disk services="[]" subsystem_name=catalog type=UpstreamCA
time="2019-07-16T06:57:54Z" level=info msg="plugins started"
time="2019-07-16T06:57:54Z" level=debug msg="Loading journal" path=/run/spire/data/journal.pem subsystem_name=ca_manager
time="2019-07-16T06:57:54Z" level=info msg="Journal loaded" jwt_keys=0 subsystem_name=ca_manager x509cas=0
time="2019-07-16T06:57:54Z" level=debug msg="Preparing X509 CA" slot=A subsystem_name=ca_manager
time="2019-07-16T06:57:54Z" level=info msg="X509 CA prepared" issued_at="2019-07-16T06:57:54Z" not_after="2019-07-16T07:57:54Z" self_signed=false slot=A subsystem_name=ca_manager upstream_bundle=true
time="2019-07-16T06:57:54Z" level=info msg="X509 CA activated" issued_at="2019-07-16T06:57:54Z" not_after="2019-07-16T07:57:54Z" slot=A subsystem_name=ca_manager
time="2019-07-16T06:57:54Z" level=debug msg="Preparing JWT key" slot=A subsystem_name=ca_manager
time="2019-07-16T06:57:54Z" level=info msg="JWT key prepared" issued_at="2019-07-16T06:57:54Z" not_after="2019-07-17T06:57:54Z" slot=A subsystem_name=ca_manager
time="2019-07-16T06:57:54Z" level=info msg="JWT key activated" issued_at="2019-07-16T06:57:54Z" not_after="2019-07-17T06:57:54Z" slot=A subsystem_name=ca_manager
time="2019-07-16T06:57:54Z" level=debug msg="Rotating server SVID" subsystem_name=svid_rotator
time="2019-07-16T06:57:54Z" level=debug msg="Signed X509 SVID" expires_at="2019-07-16T07:57:54Z" spiffe_id="spiffe://example.org/spire/server" subsystem_name=ca
time="2019-07-16T06:57:54Z" level=debug msg="Initializing API endpoints" subsystem_name=endpoints
time="2019-07-16T06:57:54Z" level=info msg="Starting UDS server /tmp/spire-registration.sock" subsystem_name=endpoints
time="2019-07-16T06:57:54Z" level=info msg="Starting TCP server on [::]:8081" subsystem_name=endpoints
Service の作成
SPIRE Agent が SPIRE Server にアクセスできるように Service を作成します。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: Service
metadata:
name: spire-server
namespace: spire
spec:
type: NodePort
ports:
- name: grpc
port: 8081
targetPort: 8081
protocol: TCP
selector:
app: spire-server
EOF
Service が作成できたか確認します。
$ kubectl get svc -n spire spire-server
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
spire-server NodePort 10.96.201.29 <none> 8081:31146/TCP 4s
以上で、SPIRE Server の起動は完了です。
SPIRE Agent の起動(Node Attestation)
次に SPIRE Agent を起動していきます。
SPIRE Agent の起動に成功する = Node Attestation 成功 となります。
Service Account の作成
SPIRE Agent 用に spire-agent
という Service Account を作成します。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: spire-agent
namespace: spire
EOF
Service Account が作成できたか確認します。
$ kubectl get sa -n spire spire-agent
NAME SECRETS AGE
spire-agent 1 10s
ConfigMap の作成
SPIRE Agent の設定ファイルと、起動に必要な CA証明書 を ConfigMap として作成する前に、Kubernetes で Workload Attestation をするのに必要な設定を解説していきます。
以下が設定ファイルとなります。
agent {
data_dir = "/run/spire"
log_level = "DEBUG"
server_address = "spire-server"
server_port = "8081"
socket_path = "/run/spire/sockets/agent.sock"
trust_bundle_path = "/run/spire/config/bootstrap.crt"
trust_domain = "example.org"
}
plugins {
NodeAttestor "k8s_sat" {
plugin_data {
# NOTE: Change this to your cluster name
cluster = "demo-cluster"
}
}
KeyManager "memory" {
plugin_data {
}
}
WorkloadAttestor "k8s" {
plugin_data {
kubelet_read_only_port = "10255"
}
}
WorkloadAttestor "unix" {
plugin_data {
}
}
}
Kubernetes で Workload Attestation をするためには、WorkloadAttestor Plugin に Agent plugin: WorkloadAttestor "k8s" を指定する必要があります。こちらの Plugin を使うことで Namespace、Service Account、Podラベルなど K8s Resource に関連する情報でアプリケーションコンテナの認証を制御することが可能になります。
それでは ConfigMap を作成していきます。
なお bootstrap.crt
は SPIRE Server に適用したものと同じ CA証明書 です。
$ cat << EOF | kubectl apply -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: spire-agent
namespace: spire
data:
agent.conf: |
agent {
data_dir = "/run/spire"
log_level = "DEBUG"
server_address = "spire-server"
server_port = "8081"
socket_path = "/run/spire/sockets/agent.sock"
trust_bundle_path = "/run/spire/config/bootstrap.crt"
trust_domain = "example.org"
}
plugins {
NodeAttestor "k8s_sat" {
plugin_data {
# NOTE: Change this to your cluster name
cluster = "demo-cluster"
}
}
KeyManager "memory" {
plugin_data {
}
}
WorkloadAttestor "k8s" {
plugin_data {
kubelet_read_only_port = "10255"
}
}
WorkloadAttestor "unix" {
plugin_data {
}
}
}
bootstrap.crt: |
-----BEGIN CERTIFICATE-----
MIIBzDCCAVOgAwIBAgIJAJM4DhRH0vmuMAoGCCqGSM49BAMEMB4xCzAJBgNVBAYT
AlVTMQ8wDQYDVQQKDAZTUElGRkUwHhcNMTgwNTEzMTkzMzQ3WhcNMjMwNTEyMTkz
MzQ3WjAeMQswCQYDVQQGEwJVUzEPMA0GA1UECgwGU1BJRkZFMHYwEAYHKoZIzj0C
AQYFK4EEACIDYgAEWjB+nSGSxIYiznb84xu5WGDZj80nL7W1c3zf48Why0ma7Y7m
CBKzfQkrgDguI4j0Z+0/tDH/r8gtOtLLrIpuMwWHoe4vbVBFte1vj6Xt6WeE8lXw
cCvLs/mcmvPqVK9jo10wWzAdBgNVHQ4EFgQUh6XzV6LwNazA+GTEVOdu07o5yOgw
DwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwGQYDVR0RBBIwEIYOc3Bp
ZmZlOi8vbG9jYWwwCgYIKoZIzj0EAwQDZwAwZAIwE4Me13qMC9i6Fkx0h26y09QZ
IbuRqA9puLg9AeeAAyo5tBzRl1YL0KNEp02VKSYJAjBdeJvqjJ9wW55OGj1JQwDF
D7kWeEB6oMlwPbI/5hEY3azJi16I0uN1JSYTSWGSqWc=
-----END CERTIFICATE-----
EOF
ConfigMap が作成できたか確認します。
$ kubectl get configmaps -n spire spire-agent
NAME DATA AGE
spire-agent 2 13s
DaemonSet の作成
SPIRE Agent を DaemonSet で起動する前に Volume と Mount 先を整理しておきます。
Volume | 種別 | 概要 | Mount先 |
---|---|---|---|
spire-config | ConfigMap | SPIRE Agent の設定ファイル(agent.conf)と起動に必要な CA証明書(bootstrap.crt)を渡す | /run/spire/config |
spire-agent-socket | hostPath | Unix Domain Socket で LISTEN される Workload API にアプリケーションコンテナがアクセスできるように hostPath で共有できる状態にしておく | /run/spire/sockets |
それでは DaemonSet を作成していきます。
$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: spire-agent
namespace: spire
labels:
app: spire-agent
spec:
selector:
matchLabels:
app: spire-agent
template:
metadata:
namespace: spire
labels:
app: spire-agent
spec:
hostPID: true
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
serviceAccountName: spire-agent
initContainers:
- name: init
# This is a small image with wait-for-it, choose whatever image
# you prefer that waits for a service to be up. This image is built
# from https://github.com/lqhl/wait-for-it
image: gcr.io/spiffe-io/wait-for-it
args: ["-t", "30", "spire-server:8081"]
containers:
- name: spire-agent
image: gcr.io/spiffe-io/spire-agent:0.8.0
args: ["-config", "/run/spire/config/agent.conf"]
volumeMounts:
- name: spire-config
mountPath: /run/spire/config
readOnly: true
- name: spire-agent-socket
mountPath: /run/spire/sockets
readOnly: false
livenessProbe:
exec:
command:
- /opt/spire/bin/spire-agent healthcheck -socketPath /run/spire/sockets/agent.sock
failureThreshold: 2
initialDelaySeconds: 15
periodSeconds: 60
timeoutSeconds: 3
volumes:
- name: spire-config
configMap:
name: spire-agent
- name: spire-agent-socket
hostPath:
path: /run/spire/sockets
type: DirectoryOrCreate
EOF
DaemonSet が作成できたか確認します。
$ kubectl get daemonsets -n spire spire-agent
NAME DESIRED CURRENT READY UP-TO-DATE AVAILABLE NODE SELECTOR AGE
spire-agent 1 1 1 1 1 <none> 11s
ログを確認すると Node Attestation に成功して SPIRE Agent が正常に起動したことがわかります。
$ kubectl get pods -n spire spire-agent-ts8jj
NAME READY STATUS RESTARTS AGE
spire-agent-ts8jj 1/1 Running 0 30s
$ kubectl logs -n spire spire-agent-ts8jj
time="2019-07-16T07:07:53Z" level=warning msg="Current umask 0022 is too permissive; setting umask 0027."
time="2019-07-16T07:07:53Z" level=info msg="data directory: \"/run/spire\""
time="2019-07-16T07:07:53Z" level=info msg="Plugin loaded." built-in=true name=k8s services="[]" subsystem_name=catalog type=WorkloadAttestor
time="2019-07-16T07:07:53Z" level=info msg="Plugin loaded." built-in=true name=unix services="[]" subsystem_name=catalog type=WorkloadAttestor
time="2019-07-16T07:07:53Z" level=info msg="Plugin loaded." built-in=true name=k8s_sat services="[]" subsystem_name=catalog type=NodeAttestor
time="2019-07-16T07:07:53Z" level=info msg="Plugin loaded." built-in=true name=memory services="[]" subsystem_name=catalog type=KeyManager
time="2019-07-16T07:07:53Z" level=debug msg="No pre-existing agent SVID found. Will perform node attestation" subsystem_name=attestor
time="2019-07-16T07:07:53Z" level=info msg="Starting workload API" subsystem_name=endpoints
以下のコマンドで SPIRE Server に Attested Node(Node Attestation に成功した Node)一覧を問い合わせることも可能です。
$ kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server agent list
以上で、SPIRE Agent の起動は完了です。
アプリケーションコンテナを起動して SVID 取得(Workload Attestation)
最後にアプリケーションコンテナ(Workload)を起動して SVID の取得を行っていきます。
SVID の取得に成功する = Workload Attestation 成功 となります。
アプリケーションコンテナの起動
SVID を取得するための Workload API Client 機能を持つコンテナを起動します。
※ Workload API Client としては SPIRE Agent が手っ取り早いのでエンドポイントを上書きして利用
$ cat << EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
name: client
labels:
app: client
spec:
selector:
matchLabels:
app: client
template:
metadata:
labels:
app: client
spec:
hostPID: true
hostNetwork: true
dnsPolicy: ClusterFirstWithHostNet
containers:
- name: client
image: gcr.io/spiffe-io/spire-agent:0.8.0
command: ["sleep"]
args: ["1000000000"]
volumeMounts:
- name: spire-agent-socket
mountPath: /run/spire/sockets
readOnly: true
volumes:
- name: spire-agent-socket
hostPath:
path: /run/spire/sockets
type: Directory
EOF
Registration Entry の作成
アプリケーションコンテナが SVID を取得するためには、対象の Workload を SPIRE Server に登録する必要があります。Workload の実行を許可する Node(今回で言う K8s Worker Nodes)と、どのような Workload(今回で言うアプリケーションコンテナ)の実行を許可するかの条件を登録していきます。
まずは K8s Worker Nodes を spiffe://example.org/ns/spire/sa/spire-agent
(Namespace が spire
で Service Account が spire-agent
なので適当に命名)という SPIFFE ID でグルーピングするためのエントリを登録します。
なお、NodeAttestor "k8s_sat" では Node Attestation 完了時に クラスタ名 と Namespace と Service Account の情報が Selector として自動登録される(Node Resolver の役割も担っている)ので、それらを使用しています。
$ kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/spire/sa/spire-agent \
-parentID spiffe://example.org/spire/server \
-selector k8s_sat:cluster:demo-cluster \
-selector k8s_sat:agent_ns:spire \
-selector k8s_sat:agent_sa:spire-agent \
-node
そして、アプリケーションコンテナ(Workload)の情報を登録します。
以下は、Node Attestation が完了した Node(K8s Worker Nodes)で、Namespace が default
かつ Service Account が default
の K8s Resource を Workload として登録するエントリとなります。
$ kubectl exec -n spire spire-server-0 -- /opt/spire/bin/spire-server entry create \
-spiffeID spiffe://example.org/ns/default/sa/default \
-parentID spiffe://example.org/ns/spire/sa/spire-agent \
-selector k8s:ns:default \
-selector k8s:sa:default
以上で Workload Attestation の準備が整いました。
SVID の取得
準備が整ったのでアプリケーションコンテナから SVID の取得を行います。
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
client-99f959d5-98zcw 1/1 Running 0 13s
$ kubectl exec client-99f959d5-98zcw -- /opt/spire/bin/spire-agent api fetch -socketPath /run/spire/sockets/agent.sock
Received 1 bundle after 11.272788ms
SPIFFE ID: spiffe://example.org/ns/default/sa/default
SVID Valid After: 2019-07-16 07:28:05 +0000 UTC
SVID Valid Until: 2019-07-16 07:57:54 +0000 UTC
Intermediate #1 Valid After: 2019-07-16 06:57:44 +0000 UTC
Intermediate #1 Valid Until: 2019-07-16 07:57:54 +0000 UTC
CA #1 Valid After: 2018-05-13 19:33:47 +0000 UTC
CA #1 Valid Until: 2023-05-12 19:33:47 +0000 UTC
SVID の取得を行うことが出来ました。
後片付け
以下のコマンドで後片付けが可能です。
$ kubectl delete deployment client
$ kubectl delete namespace spire
まとめ
今回は Kubernetes 上で SPIRE Server と SPIRE Agent を稼働させて、K8s Worker Nodes が Node Attestation を終えた後に、アプリケーションコンテナが Workload Attestation を行い、自身の SVID を取得するまでの流れを紹介しました。
Kubernetes 上のアプリケーションコンテナに SVID が配れるようになると、SPIFFE ID で統一された ID を使った Service Mesh の実現などが可能になります。SPIRE 0.8.0 から Envoy の SDS API サポートの機能も盛り込まれているので、次回はその機能を使った検証なども行えたらと思います。
おしまい。