From 4643d14df34309734882cba3e21a5d8f40afcaa2 Mon Sep 17 00:00:00 2001 From: Adrien Date: Sun, 11 Feb 2024 11:51:39 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20woodpecker=20CI/CD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ci-cd.yaml | 252 ++++++++++++++++++++++++ init.sh | 47 +++++ postgres.yaml | 2 +- vault-cicd-woodpecker-agent-policy.hcl | 4 + vault-cicd-woodpecker-server-policy.hcl | 12 ++ 5 files changed, 316 insertions(+), 1 deletion(-) create mode 100644 ci-cd.yaml create mode 100644 vault-cicd-woodpecker-agent-policy.hcl create mode 100644 vault-cicd-woodpecker-server-policy.hcl diff --git a/ci-cd.yaml b/ci-cd.yaml new file mode 100644 index 0000000..5e6b236 --- /dev/null +++ b/ci-cd.yaml @@ -0,0 +1,252 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: cicd + labels: + name: cicd + +--- +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-woodpecker + namespace: cicd +spec: + acme: + email: me@adrien.run + server: https://acme-v02.api.letsencrypt.org/directory + privateKeySecretRef: + name: letsencrypt-woodpecker + solvers: + - http01: + ingress: + class: traefik + +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: woodpecker-websecure-ingress + namespace: cicd + annotations: + cert-manager.io/cluster-issuer: letsencrypt-woodpecker + traefik.ingress.kubernetes.io/router.entrypoints: websecure +spec: + tls: + - hosts: + - woodpecker.adrien.run + secretName: tls-woodpecker-ingress + rules: + - host: woodpecker.adrien.run + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: woodpecker + port: + name: web + +--- +# Service account to allow pod access to Vault via K8s auth +apiVersion: v1 +kind: ServiceAccount +metadata: + name: woodpecker-server + namespace: cicd +automountServiceAccountToken: true + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: woodpecker-server + namespace: cicd + labels: + app: woodpecker-server +spec: + replicas: 1 + selector: + matchLabels: + app: woodpecker-server + template: + metadata: + labels: + app: woodpecker-server + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-inject-secret-woodpecker-server: "database/creds/cicd-woodpecker-server" + vault.hashicorp.com/agent-inject-template-woodpecker-server: | + {{ with secret "database/creds/cicd-woodpecker-server" -}} + export WOODPECKER_DATABASE_DATASOURCE=postgres://{{ .Data.username }}:{{ .Data.password }}@postgres.default:5432/cicd_woodpecker?sslmode=disable + {{- end }} + {{ with secret "cicd-woodpecker-server/oauth2-id" -}} + export WOODPECKER_GITEA_CLIENT={{ .Data.key }} + {{- end}} + {{ with secret "cicd-woodpecker-server/oauth2-secret" -}} + export WOODPECKER_GITEA_SECRET={{ .Data.key }} + {{- end}} + {{ with secret "cicd-woodpecker/agent-secret" -}} + export WOODPECKER_AGENT_SECRET={{ .Data.key }} + {{- end}} + vault.hashicorp.com/role: "cicd-woodpecker-server" + spec: + containers: + - name: woodpecker-server + image: woodpeckerci/woodpecker-server:latest-alpine + command: ["/bin/sh"] + args: ["-c", "source /vault/secrets/woodpecker-server ; /bin/woodpecker-server"] + env: + - name: WOODPECKER_DATABASE_DRIVER + value: postgres + - name: WOODPECKER_OPEN + value: "false" + - name: WOODPECKER_ADMIN + value: Adrien + - name: WOODPECKER_HOST + value: https://woodpecker.adrien.run + - name: WOODPECKER_LOG_LEVEL + value: trace + - name: WOODPECKER_GITEA + value: "true" + - name: WOODPECKER_GITEA_URL + value: https://git.adrien.run + ports: + - name: web + containerPort: 8000 + - name: agents + containerPort: 9000 + serviceAccountName: woodpecker-server + +--- +# Service account to allow pod access to Vault via K8s auth +apiVersion: v1 +kind: ServiceAccount +metadata: + name: woodpecker-agent + namespace: cicd + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pod-creator + namespace: cicd +rules: +- apiGroups: [""] + resources: ["pods", "pods/log"] + verbs: ["create", "get", "watch", "list", "delete"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: woodpecker-agent-pod-creator + namespace: cicd +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pod-creator +subjects: +- kind: ServiceAccount + name: woodpecker-agent + namespace: cicd + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pvc-creator + namespace: cicd +rules: +- apiGroups: [""] + resources: ["persistentvolumeclaims"] + verbs: ["create", "delete"] + +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: woodpecker-agent-pvc-creator + namespace: cicd +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pvc-creator +subjects: +- kind: ServiceAccount + name: woodpecker-agent + namespace: cicd + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: woodpecker-agent + namespace: cicd + labels: + app: woodpecker-agent +spec: + replicas: 1 + selector: + matchLabels: + app: woodpecker-agent + template: + metadata: + labels: + app: woodpecker-agent + annotations: + vault.hashicorp.com/agent-inject: "true" + vault.hashicorp.com/agent-inject-secret-woodpecker-agent: "database/creds/cicd-woodpecker-agent" + vault.hashicorp.com/agent-inject-template-woodpecker-agent: | + {{ with secret "cicd-woodpecker/agent-secret" -}} + export WOODPECKER_AGENT_SECRET={{ .Data.key }} + {{- end}} + vault.hashicorp.com/role: "cicd-woodpecker-agent" + spec: + containers: + - name: woodpecker-agent + image: woodpeckerci/woodpecker-agent:latest-alpine + command: ["/bin/sh"] + args: ["-c", "source /vault/secrets/woodpecker-agent ; /bin/woodpecker-agent"] + env: + - name: WOODPECKER_USERNAME + value: woodpecker-agent + - name: WOODPECKER_SERVER + value: woodpecker:9000 + - name: WOODPECKER_BACKEND_K8S_NAMESPACE + value: cicd + - name: WOODPECKER_BACKEND_K8S_STORAGE_CLASS + value: nfs + - name: WOODPECKER_BACKEND_K8S_VOLUME_SIZE + value: 1Gi + - name: WOODPECKER_DEBUG_PRETTY + value: "true" + - name: WOODPECKER_BACKEND + value: kubernetes + - name: WOODPECKER_LOG_LEVEL + value: trace + ports: + - name: web + containerPort: 3000 + serviceAccountName: woodpecker-agent + +--- +apiVersion: v1 +kind: Service +metadata: + name: woodpecker + namespace: cicd + labels: + app: woodpecker-server +spec: + ports: + - name: web + port: 8000 + targetPort: web + - name: agents + port: 9000 + targetPort: agents + selector: + app: woodpecker-server diff --git a/init.sh b/init.sh index 53abfd9..9b518ae 100755 --- a/init.sh +++ b/init.sh @@ -108,3 +108,50 @@ kubectl apply -f observability.yaml -n observability kubectl apply -f carrramba-cert.yaml kubectl apply -f carrramba-encore-rate-deployment.yaml + +# Install NFS server provisioner +helm repo add stable https://charts.helm.sh/stable +helm repo update +helm install nfs-server stable/nfs-server-provisioner --set persistence.enabled=true,persistence.storageClass=scw-bssd,persistence.size=10Gi + +# Install CICD +vault write database/config/cicd_woodpecker \ + plugin_name=postgresql-database-plugin \ + verify_connection=false \ + allowed_roles="*" \ + connection_url="postgresql://{{username}}:{{password}}@postgres:5432/cicd_woodpecker?sslmode=disable" \ + username="postgres" \ + password="password" + +vault policy write cicd-woodpecker-server vault-cicd-woodpecker-server-policy.hcl +vault policy write cicd-woodpecker-agent vault-cicd-woodpecker-agent-policy.hcl + +vault write --force /database/rotate-root/cicd_woodpecker + +vault write database/roles/cicd-woodpecker-server \ + db_name=cicd_woodpecker \ + creation_statements="CREATE ROLE \"{{name}}\" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'; \ + ALTER ROLE \"{{name}}\" SUPERUSER;" \ + revocation_statements="ALTER ROLE \"{{name}}\" NOLOGIN;"\ + default_ttl="768h" \ + max_ttl="768h" + # default_ttl="1h" \ + # max_ttl="24h" +vault write auth/kubernetes/role/cicd-woodpecker-server \ + bound_service_account_names=woodpecker-server \ + bound_service_account_namespaces=cicd \ + policies=cicd-woodpecker-server \ + ttl=1h + +vault write auth/kubernetes/role/cicd-woodpecker-agent \ + bound_service_account_names=woodpecker-agent \ + bound_service_account_namespaces=cicd \ + policies=cicd-woodpecker-agent \ + ttl=1h + +vault secrets enable -path=cicd-woodpecker-server -description="CI/CD Woodpecker server secrets" kv +vault kv put cicd-woodpecker-server/oauth2-secret key=$(pass dev/git.adrien.run/woodpecker-ci-oauth2-secret) +vault kv put cicd-woodpecker-server/oauth2-id key=$(pass dev/git.adrien.run/woodpecker-ci-oauth2-id) + +vault secrets enable -path=cicd-woodpecker -description="CI/CD Woodpecker server secrets" kv +vault kv put cicd-woodpecker/agent-secret key=$(pass dev/git.adrien.run/woodpecker-ci-agent-secret) diff --git a/postgres.yaml b/postgres.yaml index 7d6f235..58b59cd 100644 --- a/postgres.yaml +++ b/postgres.yaml @@ -97,6 +97,7 @@ data: initdb.sql: | CREATE DATABASE carrramba_encore_rate; CREATE EXTENSION IF NOT EXISTS pg_trgm; + CREATE DATABASE cicd_woodpecker; --- apiVersion: v1 kind: Service @@ -111,4 +112,3 @@ spec: targetPort: 5432 selector: app: postgres - diff --git a/vault-cicd-woodpecker-agent-policy.hcl b/vault-cicd-woodpecker-agent-policy.hcl new file mode 100644 index 0000000..278251c --- /dev/null +++ b/vault-cicd-woodpecker-agent-policy.hcl @@ -0,0 +1,4 @@ +path "cicd-woodpecker/*" { + capabilities = ["read"] +} + diff --git a/vault-cicd-woodpecker-server-policy.hcl b/vault-cicd-woodpecker-server-policy.hcl new file mode 100644 index 0000000..108147e --- /dev/null +++ b/vault-cicd-woodpecker-server-policy.hcl @@ -0,0 +1,12 @@ +path "database/creds/cicd-woodpecker-server" { + capabilities = ["read"] +} + +path "cicd-woodpecker/*" { + capabilities = ["read"] +} + +path "cicd-woodpecker-server/*" { + capabilities = ["read"] +} +