--- apiVersion: v1 kind: Namespace metadata: name: spire --- apiVersion: storage.k8s.io/v1 kind: CSIDriver metadata: name: "csi.spiffe.io" spec: # Only ephemeral, inline volumes are supported. There is no need for a # controller to provision and attach volumes. attachRequired: false # Request the pod information which the CSI driver uses to verify that an # ephemeral mount was requested. podInfoOnMount: true # Don't change ownership on the contents of the mount since the Workload API # Unix Domain Socket is typically open to all (i.e. 0777). fsGroupPolicy: None # Declare support for ephemeral volumes only. volumeLifecycleModes: - Ephemeral --- apiVersion: v1 kind: ServiceAccount metadata: name: spire-server namespace: spire --- # ConfigMap for spire-agent bootstrapping. apiVersion: v1 kind: ConfigMap metadata: name: spire-bundle namespace: spire --- # ClusterRole to allow spire-server to query k8s API server. kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: spire-server-cluster-role rules: # allow TokenReview requests (to verify service account tokens for PSAT # attestation) - apiGroups: ["authentication.k8s.io"] resources: ["tokenreviews"] verbs: ["get", "create"] - apiGroups: [""] resources: ["nodes"] verbs: ["get"] --- # Binds above cluster role to spire-server service account. kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: spire-server-cluster-role-binding subjects: - kind: ServiceAccount name: spire-server namespace: spire roleRef: kind: ClusterRole name: spire-server-cluster-role apiGroup: rbac.authorization.k8s.io --- # Role for the SPIRE server. kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: namespace: spire name: spire-server-role rules: # allow "get" access to pods (to resolve selectors for PSAT attestation) - apiGroups: [""] resources: ["pods"] verbs: ["get"] # allow access to "get" and "patch" the spire-bundle ConfigMap (for SPIRE # agent bootstrapping, see the spire-bundle ConfigMap) - apiGroups: [""] resources: ["configmaps"] resourceNames: ["spire-bundle"] verbs: ["get", "patch"] --- # RoleBinding granting the spire-server-role to the SPIRE server # service account. kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: spire-server-role-binding namespace: spire subjects: - kind: ServiceAccount name: spire-server namespace: spire roleRef: kind: Role name: spire-server-role apiGroup: rbac.authorization.k8s.io --- # ClusterRules for the SPIRE Controller Manager. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: manager-role rules: - apiGroups: [""] resources: ["namespaces"] verbs: ["get", "list", "watch"] - apiGroups: ["admissionregistration.k8s.io"] resources: ["validatingwebhookconfigurations"] verbs: ["get", "list", "patch", "watch"] - apiGroups: [""] resources: ["nodes"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["pods"] verbs: ["get", "list", "watch"] - apiGroups: ["spire.spiffe.io"] resources: ["clusterfederatedtrustdomains"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["spire.spiffe.io"] resources: ["clusterfederatedtrustdomains/finalizers"] verbs: ["update"] - apiGroups: ["spire.spiffe.io"] resources: ["clusterfederatedtrustdomains/status"] verbs: ["get", "patch", "update"] - apiGroups: ["spire.spiffe.io"] resources: ["clusterspiffeids"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["spire.spiffe.io"] resources: ["clusterspiffeids/finalizers"] verbs: ["update"] - apiGroups: ["spire.spiffe.io"] resources: ["clusterspiffeids/status"] verbs: ["get", "patch", "update"] --- # Binds manager-role cluster role to spire-server service account. apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: manager-role-binding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: manager-role subjects: - kind: ServiceAccount name: spire-server namespace: spire --- # Permissions for the SPIRE server to do leader election. apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: name: leader-election-role namespace: spire rules: - apiGroups: [""] resources: ["configmaps"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: ["coordination.k8s.io"] resources: ["leases"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] - apiGroups: [""] resources: ["events"] verbs: ["create", "patch"] --- # Binds leader-election-role to spire-server service account. apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: leader-election-role-binding namespace: spire roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: leader-election-role subjects: - kind: ServiceAccount name: spire-server namespace: spire --- # ConfigMap containing the SPIRE server configuration. apiVersion: v1 kind: ConfigMap metadata: name: spire-server namespace: spire data: server.conf: | server { bind_address = "0.0.0.0" bind_port = "8081" trust_domain = "example.org" data_dir = "/run/spire/server/data" log_level = "DEBUG" federation { bundle_endpoint { address = "0.0.0.0" port = 8443 } } } plugins { DataStore "sql" { plugin_data { database_type = "sqlite3" connection_string = "/run/spire/server/data/datastore.sqlite3" } } NodeAttestor "k8s_psat" { plugin_data { clusters = { # NOTE: Change this to your cluster name "demo-cluster" = { service_account_allow_list = ["spire:spire-agent"] } } } } KeyManager "disk" { plugin_data { keys_path = "/run/spire/server/data/keys.json" } } Notifier "k8sbundle" { plugin_data { namespace = "spire" } } } health_checks { listener_enabled = true bind_address = "0.0.0.0" bind_port = "8080" live_path = "/live" ready_path = "/ready" } --- # Configuration for the SPIRE Controller Manager. apiVersion: v1 kind: ConfigMap metadata: name: spire-controller-manager-config namespace: spire data: spire-controller-manager-config.yaml: | apiVersion: spire.spiffe.io/v1alpha1 kind: ControllerManagerConfig metrics: bindAddress: 127.0.0.1:8082 healthProbe: bindAddress: 127.0.0.1:8083 leaderElection: leaderElect: true resourceName: 98c9c988.spiffe.io resourceNamespace: spire clusterName: demo-cluster trustDomain: example.org ignoreNamespaces: - kube-system - kube-public - spire - local-path-storage --- # SPIRE Server Deployment. apiVersion: apps/v1 kind: Deployment metadata: name: spire-server namespace: spire labels: app: spire-server spec: replicas: 1 selector: matchLabels: app: spire-server template: metadata: namespace: spire labels: app: spire-server spec: serviceAccountName: spire-server shareProcessNamespace: true containers: - name: spire-server image: ghcr.io/spiffe/spire-server:1.5.4 imagePullPolicy: IfNotPresent args: - -config - /run/spire/server/config/server.conf livenessProbe: httpGet: path: /live port: 8080 failureThreshold: 2 initialDelaySeconds: 15 periodSeconds: 60 timeoutSeconds: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 5 ports: - containerPort: 8081 volumeMounts: - name: spire-config mountPath: /run/spire/server/config readOnly: true - name: spire-server-socket mountPath: /tmp/spire-server/private readOnly: false - name: spire-controller-manager image: ghcr.io/spiffe/spire-controller-manager:0.2.3 imagePullPolicy: IfNotPresent args: - "--config=spire-controller-manager-config.yaml" ports: - containerPort: 9443 volumeMounts: - name: spire-server-socket mountPath: /spire-server readOnly: true - name: spire-controller-manager-config mountPath: /spire-controller-manager-config.yaml subPath: spire-controller-manager-config.yaml volumes: - name: spire-config configMap: name: spire-server - name: spire-server-socket emptyDir: {} - name: spire-controller-manager-config configMap: name: spire-controller-manager-config --- # Service definition for SPIRE server defining the gRPC port. 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 --- # Service definition for SPIRE server bundle endpoint. apiVersion: v1 kind: Service metadata: name: spire-server-bundle-endpoint namespace: spire spec: type: NodePort ports: - name: tcp-api port: 8443 protocol: TCP selector: app: spire-server --- # Service definition for SPIRE controller manager webhook. apiVersion: v1 kind: Service metadata: name: spire-controller-manager-webhook-service namespace: spire spec: ports: - name: tcp port: 443 protocol: TCP targetPort: 9443 selector: app: spire-server --- # ClusterFederatedTrustDomains CRD. apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.8.0 creationTimestamp: null name: clusterfederatedtrustdomains.spire.spiffe.io spec: group: spire.spiffe.io names: kind: ClusterFederatedTrustDomain listKind: ClusterFederatedTrustDomainList plural: clusterfederatedtrustdomains singular: clusterfederatedtrustdomain scope: Cluster versions: - additionalPrinterColumns: - jsonPath: .spec.trustDomain name: Trust Domain type: string - jsonPath: .spec.bundleEndpointURL name: Endpoint URL type: string name: v1alpha1 schema: openAPIV3Schema: description: ClusterFederatedTrustDomain is the Schema for the clusterfederatedtrustdomains API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: ClusterFederatedTrustDomainSpec defines the desired state of ClusterFederatedTrustDomain properties: bundleEndpointProfile: description: BundleEndpointProfile is the profile for the bundle endpoint. properties: endpointSPIFFEID: description: EndpointSPIFFEID is the SPIFFE ID of the bundle endpoint. It is required for the "https_spiffe" profile. type: string type: description: Type is the type of the bundle endpoint profile. enum: - https_spiffe - https_web type: string required: - type type: object bundleEndpointURL: description: BundleEndpointURL is the URL of the bundle endpoint. It must be an HTTPS URL and cannot contain userinfo (i.e. username/password). type: string trustDomain: description: TrustDomain is the name of the trust domain to federate with (e.g. example.org) pattern: '[a-z0-9._-]{1,255}' type: string trustDomainBundle: description: TrustDomainBundle is the contents of the bundle for the referenced trust domain. This field is optional when the resource is created. type: string required: - bundleEndpointProfile - bundleEndpointURL - trustDomain type: object status: description: ClusterFederatedTrustDomainStatus defines the observed state of ClusterFederatedTrustDomain type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] --- # ClusterSPIFFEID CRD. apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.8.0 creationTimestamp: null name: clusterspiffeids.spire.spiffe.io spec: group: spire.spiffe.io names: kind: ClusterSPIFFEID listKind: ClusterSPIFFEIDList plural: clusterspiffeids singular: clusterspiffeid scope: Cluster versions: - name: v1alpha1 schema: openAPIV3Schema: description: ClusterSPIFFEID is the Schema for the clusterspiffeids API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' type: string kind: description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' type: string metadata: type: object spec: description: ClusterSPIFFEIDSpec defines the desired state of ClusterSPIFFEID properties: admin: description: Admin indicates whether or not the SVID can be used to access the SPIRE administrative APIs. Extra care should be taken to only apply this SPIFFE ID to admin workloads. type: boolean dnsNameTemplates: description: DNSNameTemplate represents templates for extra DNS names that are applicable to SVIDs minted for this ClusterSPIFFEID. The node and pod spec are made available to the template under .NodeSpec, .PodSpec respectively. items: type: string type: array downstream: description: Downstream indicates that the entry describes a downstream SPIRE server. type: boolean federatesWith: description: FederatesWith is a list of trust domain names that workloads that obtain this SPIFFE ID will federate with. items: type: string type: array namespaceSelector: description: NamespaceSelector selects the namespaces that are targeted by this CRD. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array required: - key - operator type: object type: array matchLabels: additionalProperties: type: string description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object podSelector: description: PodSelector selects the pods that are targeted by this CRD. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: description: A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values. properties: key: description: key is the label key that the selector applies to. type: string operator: description: operator represents a key's relationship to a set of values. Valid operators are In, NotIn, Exists and DoesNotExist. type: string values: description: values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch. items: type: string type: array required: - key - operator type: object type: array matchLabels: additionalProperties: type: string description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is "key", the operator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object spiffeIDTemplate: description: SPIFFEID is the SPIFFE ID template. The node and pod spec are made available to the template under .NodeSpec, .PodSpec respectively. type: string ttl: description: TTL indicates an upper-bound time-to-live for SVIDs minted for this ClusterSPIFFEID. If unset, a default will be chosen. type: string workloadSelectorTemplates: description: WorkloadSelectorTemplates are templates to produce arbitrary workload selectors that apply to a given workload before it will receive this SPIFFE ID. The rendered value is interpreted by SPIRE and are of the form type:value, where the value may, and often does, contain semicolons, .e.g., k8s:container-image:docker/hello-world The node and pod spec are made available to the template under .NodeSpec, .PodSpec respectively. items: type: string type: array required: - spiffeIDTemplate type: object status: description: ClusterSPIFFEIDStatus defines the observed state of ClusterSPIFFEID properties: stats: description: Stats produced by the last entry reconciliation run properties: entriesMasked: description: How many entries were masked by entries for other ClusterSPIFFEIDs. This happens when one or more ClusterSPIFFEIDs produce an entry for the same pod with the same set of workload selectors. type: integer entriesToSet: description: How many entries are to be set for this ClusterSPIFFEID. In nominal conditions, this should reflect the number of pods selected, but not always if there were problems encountered rendering an entry for the pod (RenderFailures) or entries are masked (EntriesMasked). type: integer entryFailures: description: How many entries were unable to be set due to failures to create or update the entries via the SPIRE Server API. type: integer namespacesIgnored: description: How many (selected) namespaces were ignored (based on configuration). type: integer namespacesSelected: description: How many namespaces were selected. type: integer podEntryRenderFailures: description: How many failures were encountered rendering an entry selected pods. This could be due to either a bad template in the ClusterSPIFFEID or Pod metadata that when applied to the template did not produce valid entry values. type: integer podsSelected: description: How many pods were selected out of the namespaces. type: integer type: object type: object type: object served: true storage: true subresources: status: {} status: acceptedNames: kind: "" plural: "" conditions: [] storedVersions: [] --- # ValidatingWebhookConfiguration for validating ClusterSPIFFEID and # ClusterFederatedTrustDomain custom resources. apiVersion: admissionregistration.k8s.io/v1 kind: ValidatingWebhookConfiguration metadata: name: spire-controller-manager-webhook webhooks: - admissionReviewVersions: ["v1"] clientConfig: service: name: spire-controller-manager-webhook-service namespace: spire path: /validate-spire-spiffe-io-v1alpha1-clusterfederatedtrustdomain failurePolicy: Fail name: vclusterfederatedtrustdomain.kb.io rules: - apiGroups: ["spire.spiffe.io"] apiVersions: ["v1alpha1"] operations: ["CREATE", "UPDATE"] resources: ["clusterfederatedtrustdomains"] sideEffects: None - admissionReviewVersions: ["v1"] clientConfig: service: name: spire-controller-manager-webhook-service namespace: spire path: /validate-spire-spiffe-io-v1alpha1-clusterspiffeid failurePolicy: Fail name: vclusterspiffeid.kb.io rules: - apiGroups: ["spire.spiffe.io"] apiVersions: ["v1alpha1"] operations: ["CREATE", "UPDATE"] resources: ["clusterspiffeids"] sideEffects: None --- apiVersion: v1 kind: ServiceAccount metadata: name: spire-agent namespace: spire --- # Required cluster role to allow spire-agent to query k8s API server. kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: spire-agent-cluster-role rules: - apiGroups: [""] resources: ["pods","nodes","nodes/proxy"] verbs: ["get"] --- # Binds above cluster role to spire-agent service account. kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: spire-agent-cluster-role-binding subjects: - kind: ServiceAccount name: spire-agent namespace: spire roleRef: kind: ClusterRole name: spire-agent-cluster-role apiGroup: rbac.authorization.k8s.io --- # ConfigMap for the SPIRE agent featuring: # 1) PSAT node attestation # 2) K8S Workload Attestation over the secure kubelet port 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/secrets/workload-spiffe-uds/socket" trust_bundle_path = "/run/spire/bundle/bundle.crt" trust_domain = "example.org" } plugins { NodeAttestor "k8s_psat" { plugin_data { # NOTE: Change this to your cluster name cluster = "demo-cluster" } } KeyManager "memory" { plugin_data { } } WorkloadAttestor "k8s" { plugin_data { # Defaults to the secure kubelet port by default. # Minikube does not have a cert in the cluster CA bundle that # can authenticate the kubelet cert, so skip validation. skip_kubelet_verification = true # We need to set disable_container_selectors = true if we make holdApplicationUntilProxyStarts = true in istio # see https://istio.io/latest/docs/reference/config/istio.mesh.v1alpha1/#ProxyConfig # If true, container selectors are not produced. # This can be used to produce pod selectors when the workload pod is known # but the workload container is not ready at the time of attestation. # disable_container_selectors = true } } WorkloadAttestor "unix" { plugin_data { } } } --- # SPIRE Agent DaemonSet. 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 containers: - name: spire-agent image: ghcr.io/spiffe/spire-agent:1.5.4 imagePullPolicy: IfNotPresent args: ["-config", "/run/spire/config/agent.conf"] volumeMounts: - name: spire-config mountPath: /run/spire/config readOnly: true - name: spire-bundle mountPath: /run/spire/bundle readOnly: true - name: spire-agent-socket-dir mountPath: /run/secrets/workload-spiffe-uds - name: spire-token mountPath: /var/run/secrets/tokens # This is the container which runs the SPIFFE CSI driver. - name: spiffe-csi-driver image: ghcr.io/spiffe/spiffe-csi-driver:0.2.0 imagePullPolicy: IfNotPresent args: [ "-workload-api-socket-dir", "/spire-agent-socket", "-csi-socket-path", "/spiffe-csi/csi.sock", ] env: # The CSI driver needs a unique node ID. The node name can be # used for this purpose. - name: MY_NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName volumeMounts: # The volume containing the SPIRE agent socket. The SPIFFE CSI # driver will mount this directory into containers. - mountPath: /spire-agent-socket name: spire-agent-socket-dir readOnly: true # The volume that will contain the CSI driver socket shared # with the kubelet and the driver registrar. - mountPath: /spiffe-csi name: spiffe-csi-socket-dir # The volume containing mount points for containers. - mountPath: /var/lib/kubelet/pods mountPropagation: Bidirectional name: mountpoint-dir securityContext: privileged: true # This container runs the CSI Node Driver Registrar which takes care # of all the little details required to register a CSI driver with # the kubelet. - name: node-driver-registrar image: registry.k8s.io/sig-storage/csi-node-driver-registrar:v2.4.0 imagePullPolicy: IfNotPresent args: [ "-csi-address", "/spiffe-csi/csi.sock", "-kubelet-registration-path", "/var/lib/kubelet/plugins/csi.spiffe.io/csi.sock", ] volumeMounts: # The registrar needs access to the SPIFFE CSI driver socket - mountPath: /spiffe-csi name: spiffe-csi-socket-dir # The registrar needs access to the Kubelet plugin registration # directory - name: kubelet-plugin-registration-dir mountPath: /registration volumes: - name: spire-config configMap: name: spire-agent - name: spire-bundle configMap: name: spire-bundle - name: spire-token projected: sources: - serviceAccountToken: path: spire-agent expirationSeconds: 7200 audience: spire-server # This volume is used to share the workload api socket between the # CSI driver and SPIRE agent - name: spire-agent-socket-dir emptyDir: {} # This volume is where the socket for kubelet->driver communication lives - name: spiffe-csi-socket-dir hostPath: path: /var/lib/kubelet/plugins/csi.spiffe.io type: DirectoryOrCreate # This volume is where the SPIFFE CSI driver mounts volumes - name: mountpoint-dir hostPath: path: /var/lib/kubelet/pods type: Directory # This volume is where the node-driver-registrar registers the plugin # with kubelet - name: kubelet-plugin-registration-dir hostPath: path: /var/lib/kubelet/plugins_registry type: Directory