LoginSignup
2
2

More than 3 years have passed since last update.

SPIRE の Node/Workload Attestation を Kubernetes で体感する

Last updated at Posted at 2019-07-16

はじめに

以前に投稿した SPIRE の Node/Workload Attestation を AWS 上で体感する の Kubernetes バージョンの記事となります。

今回は Kubernetes 上で SPIRE Server と SPIRE Agent を稼働させて、K8s Worker Nodes が Node Attestation を終えた後に、アプリケーションコンテナが Workload Attestation を行い、自身の SVID を取得するまでの流れを確認していきたいと思います。

今回紹介するフローは以下となります。

  1. Node Attestation を許可する条件をホワイトリストで定義して SPIRE Server を起動する
  2. K8s Worker Nodes で SPIRE Agent を起動して Service Account ベースで Node Attestation を行う
  3. Node Attestation に成功した K8s Worker Nodes 上でアプリケーションコンテナ(Pod)を起動する
  4. アプリケーションコンテナの Namespace、Service Account、Podラベル などを使って Workload Attestation を行う
  5. 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 サポートの機能も盛り込まれているので、次回はその機能を使った検証なども行えたらと思います。

おしまい。

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2