---
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: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  annotations:
    controller-gen.kubebuilder.io/version: v0.2.4
  name: spiffeids.spiffeid.spiffe.io
spec:
  group: spiffeid.spiffe.io
  names:
    kind: SpiffeID
    listKind: SpiffeIDList
    plural: spiffeids
    singular: spiffeid
  scope: Namespaced
  versions:
  - name: v1beta1
    served: true
    storage: true
    subresources:
      status: {}
    schema:
      openAPIV3Schema:
        description: SpiffeID is the Schema for the spiffeid 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: SpiffeIDSpec defines the desired state of SpiffeID
            properties:
              dnsNames:
                items:
                  type: string
                type: array
              federatesWith:
                items:
                  type: string
                type: array
              parentId:
                type: string
              downstream:
                type: boolean
              selector:
                properties:
                  arbitrary:
                    description: Arbitrary selectors
                    items:
                      type: string
                    type: array
                  containerImage:
                    description: Container image to match for this spiffe ID
                    type: string
                  containerName:
                    description: Container name to match for this spiffe ID
                    type: string
                  namespace:
                    description: Namespace to match for this spiffe ID
                    type: string
                  nodeName:
                    description: Node name to match for this spiffe ID
                    type: string
                  podLabel:
                    additionalProperties:
                      type: string
                    description: Pod label name/value to match for this spiffe ID
                    type: object
                  podName:
                    description: Pod name to match for this spiffe ID
                    type: string
                  podUid:
                    description: Pod UID to match for this spiffe ID
                    type: string
                  serviceAccount:
                    description: ServiceAccount to match for this spiffe ID
                    type: string
                  cluster:
                    description: The k8s_psat cluster name
                    type: string
                  agent_node_uid:
                    description: UID of the node
                    type: string
                type: object
              spiffeId:
                type: string
            required:
            - parentId
            - selector
            - spiffeId
            type: object
          status:
            description: SpiffeIDStatus defines the observed state of SpiffeID
            properties:
              entryId:
                description: 'INSERT ADDITIONAL STATUS FIELD - define observed state
                  of cluster Important: Run "make" to regenerate code after modifying
                  this file'
                type: string
            type: object
        type: object
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: k8s-workload-registrar-role-binding
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: k8s-workload-registrar-role
subjects:
- kind: ServiceAccount
  name: spire-server
  namespace: spire
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: k8s-workload-registrar-role
rules:
- apiGroups: ["authentication.k8s.io"]
  resources: ["tokenreviews"]
  verbs: ["get", "create"]
- apiGroups: [""]
  resources: ["endpoints", "nodes", "pods"]
  verbs: ["get", "list", "watch"]
- apiGroups: ["spiffeid.spiffe.io"]
  resources: ["spiffeids"]
  verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
- apiGroups: ["spiffeid.spiffe.io"]
  resources: ["spiffeids/status"]
  verbs: ["get", "patch", "update"]
- apiGroups: ["admissionregistration.k8s.io"]
  resources: ["validatingwebhookconfigurations"]
  verbs: ["get", "list", "update", "watch"]
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: k8s-workload-registrar
  namespace: spire
data:
  k8s-workload-registrar.conf: |
    trust_domain = "example.org"
    server_socket_path = "/run/spire/sockets/server.sock"
    cluster = "demo-cluster"
    mode = "crd"
    metrics_bind_addr = "0"
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: spire-server
  namespace: spire
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: trust-bundle
  namespace: spire
---
# ClusterRole to allow spire-server node attestor to query Token Review API
# and to be able to push certificate bundles to a configmap
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: spire-server-trust-role
rules:
- apiGroups: ["authentication.k8s.io"]
  resources: ["tokenreviews"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["patch", "get", "list"]

---
# Binds above cluster role to spire-server service account
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: spire-server-trust-role-binding
subjects:
- kind: ServiceAccount
  name: spire-server
  namespace: spire
roleRef:
  kind: ClusterRole
  name: spire-server-trust-role
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: spire-server
  namespace: spire
data:
  server.conf: |
    server {
      bind_address = "0.0.0.0"
      bind_port = "8081"
      socket_path = "/run/spire/sockets/server.sock"
      trust_domain = "example.org"
      data_dir = "/run/spire/data"
      log_level = "DEBUG"
      ca_key_type = "rsa-2048"
      default_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_psat" {
        plugin_data {
          clusters = {
            # NOTE: Change this to your cluster name
            "demo-cluster" = {
              use_token_review_api_validation = true
              service_account_allow_list = ["spire:spire-agent"]
            }
          }
        }
      }
      KeyManager "disk" {
        plugin_data {
          keys_path = "/run/spire/data/keys.json"
        }
      }
      Notifier "k8sbundle" {
        plugin_data {
          namespace = "spire"
          config_map = "trust-bundle"
          config_map_key = "root-cert.pem"
        }
      }
    }
    health_checks {
      listener_enabled = true
      bind_address = "0.0.0.0"
      bind_port = "8080"
      live_path = "/live"
      ready_path = "/ready"
    }
---
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
      shareProcessNamespace: true
      containers:
        - name: spire-server
          image: gcr.io/spiffe-io/spire-server:1.2.0
          args:
            - -config
            - /run/spire/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/config
              readOnly: true
            - name: spire-data
              mountPath: /run/spire/data
              readOnly: false
            - name: spire-registration-socket
              mountPath: /run/spire/sockets
              readOnly: false
        - name: k8s-workload-registrar
          image: gcr.io/spiffe-io/k8s-workload-registrar:1.2.0
          args:
            - -config
            - /run/spire/config/k8s-workload-registrar.conf
          ports:
            - containerPort: 9443
              name: webhook
              protocol: TCP
          volumeMounts:
            - mountPath: /run/spire/config
              name: k8s-workload-registrar-config
              readOnly: true
            - name: spire-registration-socket
              mountPath: /run/spire/sockets
              readOnly: true
      volumes:
        - name: spire-config
          configMap:
            name: spire-server
        - name: k8s-workload-registrar-config
          configMap:
            name: k8s-workload-registrar
        - name: spire-registration-socket
          hostPath:
            path: /run/spire/server-sockets
            type: DirectoryOrCreate
  volumeClaimTemplates:
    - metadata:
        name: spire-data
        namespace: spire
      spec:
        accessModes:
          - ReadWriteOnce
        resources:
          requests:
            storage: 1Gi
---
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
---
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
---
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/root-cert.pem"
      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
        }
      }
      WorkloadAttestor "unix" {
          plugin_data {
          }
      }
    }
    health_checks {
      listener_enabled = true
      bind_address = "0.0.0.0"
      bind_port = "8080"
      live_path = "/live"
      ready_path = "/ready"
    }
---
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:1.2.0
          args: ["-config", "/run/spire/config/agent.conf"]
          volumeMounts:
            - name: spire-config
              mountPath: /run/spire/config
              readOnly: true
            - name: spire-bundle
              mountPath: /run/spire/bundle
            - name: spire-agent-socket-dir
              mountPath: /run/secrets/workload-spiffe-uds
            - name: spire-token
              mountPath: /var/run/secrets/tokens
          livenessProbe:
            httpGet:
              path: /live
              port: 8080
            failureThreshold: 2
            initialDelaySeconds: 15
            periodSeconds: 60
            timeoutSeconds: 3
          readinessProbe:
            httpGet:
              path: /ready
              port: 8080
            initialDelaySeconds: 5
            periodSeconds: 5
        # 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: [
              "-node-id", "CSI_NODE",
              "-workload-api-socket-dir", "/spire-agent-socket",
              "-csi-socket-path", "/spiffe-csi/csi.sock",
          ]
          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: trust-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
          hostPath:
            path: /run/spire/socket-dir
            type: DirectoryOrCreate
        # 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
