# istio_spire-quickstart.yaml
---
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