Skip to content

Instantly share code, notes, and snippets.

@rootsongjc
Last active December 26, 2022 08:20
Show Gist options
  • Save rootsongjc/5dac0518cc432cbf844114faca74aa40 to your computer and use it in GitHub Desktop.
Save rootsongjc/5dac0518cc432cbf844114faca74aa40 to your computer and use it in GitHub Desktop.
SPIRE + Cert Manager + Istio Integration
# Copyright Istio Authors
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
##################################################################################################
# This file defines the services, service accounts, and deployments for the Bookinfo sample.
#
# To apply all 4 Bookinfo services, their corresponding service accounts, and deployments:
#
# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
#
# Alternatively, you can deploy any resource separately:
#
# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l service=reviews # reviews Service
# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l account=reviews # reviews ServiceAccount
# kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml -l app=reviews,version=v3 # reviews-v3 Deployment
##################################################################################################
##################################################################################################
# Details service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
name: details
labels:
app: details
service: details
spec:
ports:
- port: 9080
name: http
selector:
app: details
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-details
labels:
account: details
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: details-v1
labels:
app: details
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: details
version: v1
template:
metadata:
labels:
app: details
version: v1
annotations:
inject.istio.io/templates: "sidecar,spire"
spec:
serviceAccountName: bookinfo-details
containers:
- name: details
image: docker.io/istio/examples-bookinfo-details-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
---
##################################################################################################
# Ratings service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
name: ratings
labels:
app: ratings
service: ratings
spec:
ports:
- port: 9080
name: http
selector:
app: ratings
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-ratings
labels:
account: ratings
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ratings-v1
labels:
app: ratings
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: ratings
version: v1
template:
metadata:
labels:
app: ratings
version: v1
annotations:
inject.istio.io/templates: "sidecar,spire"
spec:
serviceAccountName: bookinfo-ratings
containers:
- name: ratings
image: docker.io/istio/examples-bookinfo-ratings-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
securityContext:
runAsUser: 1000
---
##################################################################################################
# Reviews service
##################################################################################################
apiVersion: v1
kind: Service
metadata:
name: reviews
labels:
app: reviews
service: reviews
spec:
ports:
- port: 9080
name: http
selector:
app: reviews
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-reviews
labels:
account: reviews
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v1
labels:
app: reviews
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v1
template:
metadata:
labels:
app: reviews
version: v1
annotations:
inject.istio.io/templates: "sidecar,spire"
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v1:1.17.0
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v2
labels:
app: reviews
version: v2
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v2
template:
metadata:
labels:
app: reviews
version: v2
annotations:
inject.istio.io/templates: "sidecar,spire"
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v2:1.17.0
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: reviews-v3
labels:
app: reviews
version: v3
spec:
replicas: 1
selector:
matchLabels:
app: reviews
version: v3
template:
metadata:
labels:
app: reviews
version: v3
annotations:
inject.istio.io/templates: "sidecar,spire"
spec:
serviceAccountName: bookinfo-reviews
containers:
- name: reviews
image: docker.io/istio/examples-bookinfo-reviews-v3:1.17.0
imagePullPolicy: IfNotPresent
env:
- name: LOG_DIR
value: "/tmp/logs"
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
- name: wlp-output
mountPath: /opt/ibm/wlp/output
securityContext:
runAsUser: 1000
volumes:
- name: wlp-output
emptyDir: {}
- name: tmp
emptyDir: {}
---
##################################################################################################
# Productpage services
##################################################################################################
apiVersion: v1
kind: Service
metadata:
name: productpage
labels:
app: productpage
service: productpage
spec:
ports:
- port: 9080
name: http
selector:
app: productpage
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: bookinfo-productpage
labels:
account: productpage
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: productpage-v1
labels:
app: productpage
version: v1
spec:
replicas: 1
selector:
matchLabels:
app: productpage
version: v1
template:
metadata:
labels:
app: productpage
version: v1
annotations:
inject.istio.io/templates: "sidecar,spire"
spec:
serviceAccountName: bookinfo-productpage
containers:
- name: productpage
image: docker.io/istio/examples-bookinfo-productpage-v1:1.17.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 9080
volumeMounts:
- name: tmp
mountPath: /tmp
securityContext:
runAsUser: 1000
volumes:
- name: tmp
emptyDir: {}
---
---
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
# manage SPIRE CA
# 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"]
- apiGroups: ["cert-manager.io"]
resources: ["certificates","certificaterequests","issuers"]
verbs: ["*"]
---
# 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"
}
}
UpstreamAuthority "cert-manager" {
plugin_data {
issuer_name = "selfsigned-ca"
issuer_kind = "ClusterIssuer"
issuer_group = "cert-manager.io"
namespace = "cert-manager"
}
}
}
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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment