Skip to content

Commit

Permalink
Implement kubernetes mutating admission controller for tratteria side…
Browse files Browse the repository at this point in the history
…car injection
  • Loading branch information
kchiranjewee63 committed Jun 4, 2024
1 parent 6a93b54 commit 1adf929
Show file tree
Hide file tree
Showing 41 changed files with 1,261 additions and 187 deletions.
8 changes: 8 additions & 0 deletions kubernetes/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cd spire
chmod +x deploy.sh
./deploy.sh
cd ..

cd tratd
chmod +x deploy.sh
./deploy.sh
8 changes: 8 additions & 0 deletions kubernetes/destroy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
cd tratd
chmod +x destroy.sh
./destroy.sh
cd ..

cd spire
chmod +x destroy.sh
./destroy.sh
5 changes: 5 additions & 0 deletions kubernetes/spire/agent/agent-account.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: spire-agent
namespace: spire
24 changes: 24 additions & 0 deletions kubernetes/spire/agent/agent-cluster-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# 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
53 changes: 53 additions & 0 deletions kubernetes/spire/agent/agent-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: spire-agent
namespace: spire
data:
agent.conf: |
agent {
data_dir = "/run/spire"
log_level = "DEBUG"
server_address = "spire-server"
server_port = "8081"
socket_path = "/run/spire/sockets/agent.sock"
trust_bundle_path = "/run/spire/bundle/bundle.crt"
trust_domain = "tratteria.io"
}
plugins {
NodeAttestor "k8s_sat" {
plugin_data {
# NOTE: Change this to your cluster name
cluster = "docker-desktop"
}
}
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
node_name_env = "MY_NODE_NAME"
}
}
WorkloadAttestor "unix" {
plugin_data {
}
}
}
health_checks {
listener_enabled = true
bind_address = "0.0.0.0"
bind_port = "8080"
live_path = "/live"
ready_path = "/ready"
}
71 changes: 71 additions & 0 deletions kubernetes/spire/agent/agent-daemonset.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
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: cgr.dev/chainguard/wait-for-it
args: ["-t", "30", "spire-server:8081"]
containers:
- name: spire-agent
image: ghcr.io/spiffe/spire-agent:1.9.4
args: ["-config", "/run/spire/config/agent.conf"]
env:
- name: MY_NODE_NAME
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: spire-config
mountPath: /run/spire/config
readOnly: true
- name: spire-bundle
mountPath: /run/spire/bundle
- name: spire-agent-socket
mountPath: /run/spire/sockets
readOnly: false
livenessProbe:
httpGet:
path: /live
port: 8080
failureThreshold: 2
initialDelaySeconds: 15
periodSeconds: 60
timeoutSeconds: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
volumes:
- name: spire-config
configMap:
name: spire-agent
- name: spire-bundle
configMap:
name: spire-bundle
- name: spire-agent-socket
hostPath:
path: /run/spire/sockets
type: DirectoryOrCreate
59 changes: 59 additions & 0 deletions kubernetes/spire/deploy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
# Applying Spire Configurations
echo "\nApplying Spire Configurations...\n"

kubectl create namespace spire

# Create Server Bundle Configmap, Role & ClusterRoleBinding
kubectl apply \
-f server/server-account.yaml \
-f server/spire-bundle-configmap.yaml \
-f server/server-cluster-role.yaml

# Create Server Configmap
kubectl apply \
-f server/server-configmap.yaml \
-f server/server-statefulset.yaml \
-f server/server-service.yaml

# Configure and deploy the SPIRE Agent
kubectl apply \
-f agent/agent-account.yaml \
-f agent/agent-cluster-role.yaml

kubectl apply \
-f agent/agent-configmap.yaml \
-f agent/agent-daemonset.yaml

# Registering Workloads
echo "\nRegistering Workloads...\n"

NAMESPACE=spire
POD_NAME=spire-server-0

echo "Waiting for spire server to be ready..."
while true; do
POD_STATUS=$(kubectl get pod $POD_NAME -n $NAMESPACE -o jsonpath='{.status.phase}')
READY=$(kubectl get pod $POD_NAME -n $NAMESPACE -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')
if [[ "$POD_STATUS" == "Running" && "$READY" == "True" ]]; then
echo "Spire server is ready.\n"
break
else
echo "Waiting for spire server to be ready..."
sleep 5
fi
done

kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create \
-spiffeID spiffe://tratteria.io/ns/spire/sa/spire-agent \
-selector k8s_sat:cluster:docker-desktop \
-selector k8s_sat:agent_ns:spire \
-selector k8s_sat:agent_sa:spire-agent \
-node

kubectl exec -n spire spire-server-0 -- \
/opt/spire/bin/spire-server entry create --dns tratd.tratteria.svc\
-spiffeID spiffe://tratteria.io/tratteria \
-parentID spiffe://tratteria.io/ns/spire/sa/spire-agent \
-selector k8s:ns:tratteria \
-selector k8s:sa:tratd-service-account
4 changes: 4 additions & 0 deletions kubernetes/spire/destroy.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Destroying Spire Configurations
kubectl delete namespace spire
kubectl delete clusterrole spire-server-trust-role spire-agent-cluster-role
kubectl delete clusterrolebinding spire-server-trust-role-binding spire-agent-cluster-role-binding
5 changes: 5 additions & 0 deletions kubernetes/spire/server/server-account.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
apiVersion: v1
kind: ServiceAccount
metadata:
name: spire-server
namespace: spire
52 changes: 52 additions & 0 deletions kubernetes/spire/server/server-cluster-role.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# Role (namespace scoped) to be able to push certificate bundles to a configmap
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: spire-server-configmap-role
namespace: spire
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["patch", "get", "list"]
---
# Binds above role to spire-server service account
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: spire-server-configmap-role-binding
namespace: spire
subjects:
- kind: ServiceAccount
name: spire-server
namespace: spire
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: spire-server-configmap-role
---
# ClusterRole to allow spire-server node attestor to query Token Review API
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: spire-server-trust-role
rules:
- apiGroups: ["authentication.k8s.io"]
resources: ["tokenreviews"]
verbs: ["create"]
- apiGroups: ["admissionregistration.k8s.io"]
resources: ["mutatingwebhookconfigurations", "validatingwebhookconfigurations"]
verbs: ["get", "list", "patch", "watch"]
---
# 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
64 changes: 64 additions & 0 deletions kubernetes/spire/server/server-configmap.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
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 = "/tmp/spire-server/private/api.sock"
trust_domain = "tratteria.io"
data_dir = "/run/spire/data"
log_level = "DEBUG"
#AWS requires the use of RSA. EC cryptography is not supported
ca_key_type = "rsa-2048"
ca_subject = {
country = ["US"],
organization = ["SPIFFE"],
common_name = "",
}
}
plugins {
DataStore "sql" {
plugin_data {
database_type = "sqlite3"
connection_string = "/run/spire/data/datastore.sqlite3"
}
}
NodeAttestor "k8s_sat" {
plugin_data {
clusters = {
# NOTE: Change this to your cluster name
"docker-desktop" = {
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 {
webhook_label = "tratteria.io/webhook"
}
}
}
health_checks {
listener_enabled = true
bind_address = "0.0.0.0"
bind_port = "8080"
live_path = "/live"
ready_path = "/ready"
}
14 changes: 14 additions & 0 deletions kubernetes/spire/server/server-service.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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
Loading

0 comments on commit 1adf929

Please sign in to comment.