fix(provision): security hardening from code review

- Add input validation: username regex + email format check in pipeline
- Quote variables in .provision-env to prevent shell injection
- Remove dead source command (each Woodpecker command is separate shell)
- Use jq to build JSON payloads (prevents injection via group names)
- Clean up git-crypt key on failure (use ; instead of &&)
- Add Kyverno ndots lifecycle ignore to webhook-handler deployment
This commit is contained in:
Viktor Barzin 2026-03-18 21:25:03 +00:00
parent e51c063600
commit 410c893647
2 changed files with 19 additions and 6 deletions

View file

@ -18,9 +18,19 @@ steps:
echo "Trigger with: POST /api/repos/1/pipelines {branch:master, variables:{USERNAME:x, EMAIL:y}}" echo "Trigger with: POST /api/repos/1/pipelines {branch:master, variables:{USERNAME:x, EMAIL:y}}"
exit 1 exit 1
fi fi
# Validate username: lowercase alphanumeric + dash/underscore, 2-63 chars
if ! echo "$USERNAME" | grep -qE '^[a-z0-9][a-z0-9_-]{0,61}[a-z0-9]$'; then
echo "ERROR: USERNAME must be 2-63 chars, lowercase alphanumeric/dash/underscore"
exit 1
fi
# Validate email: basic format check
if ! echo "$EMAIL" | grep -qE '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'; then
echo "ERROR: EMAIL must be a valid email address"
exit 1
fi
echo "Provisioning user: $USERNAME ($EMAIL)" echo "Provisioning user: $USERNAME ($EMAIL)"
echo "export PROVISION_USERNAME=$USERNAME" > .provision-env echo "export PROVISION_USERNAME='$USERNAME'" > .provision-env
echo "export PROVISION_EMAIL=$EMAIL" >> .provision-env echo "export PROVISION_EMAIL='$EMAIL'" >> .provision-env
- name: prepare - name: prepare
image: alpine image: alpine
@ -31,7 +41,7 @@ steps:
curl -k https://10.0.20.100:6443/api/v1/namespaces/woodpecker/configmaps/git-crypt-key \ curl -k https://10.0.20.100:6443/api/v1/namespaces/woodpecker/configmaps/git-crypt-key \
-H "Authorization:Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \ -H "Authorization:Bearer $(cat /var/run/secrets/kubernetes.io/serviceaccount/token)" \
| jq -r .data.key | base64 -d > /tmp/key | jq -r .data.key | base64 -d > /tmp/key
- "git-crypt unlock /tmp/key && rm /tmp/key" - "git-crypt unlock /tmp/key; rm -f /tmp/key"
# Vault: authenticate via K8s service account JWT # Vault: authenticate via K8s service account JWT
- | - |
SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token) SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
@ -44,10 +54,9 @@ steps:
image: alpine image: alpine
commands: commands:
- "apk update && apk add jq curl" - "apk update && apk add jq curl"
- "source .provision-env && source .vault-env"
# Read current platform secret # Read current platform secret
- | - |
source .provision-env && source .vault-env . .provision-env && . .vault-env
CURRENT=$(curl -s -H "X-Vault-Token: $VAULT_TOKEN" \ CURRENT=$(curl -s -H "X-Vault-Token: $VAULT_TOKEN" \
"$VAULT_ADDR/v1/secret/data/platform" | jq -r '.data.data') "$VAULT_ADDR/v1/secret/data/platform" | jq -r '.data.data')
@ -91,10 +100,11 @@ steps:
"$AUTHENTIK_URL/api/v3/core/groups/?name=$SOPS_GROUP" | jq -r '.results | length') "$AUTHENTIK_URL/api/v3/core/groups/?name=$SOPS_GROUP" | jq -r '.results | length')
if [ "$EXISTING" = "0" ]; then if [ "$EXISTING" = "0" ]; then
GROUP_PAYLOAD=$(jq -n --arg name "$SOPS_GROUP" '{"name": $name, "is_superuser": false}')
GROUP_PK=$(curl -s -X POST -H "Authorization: Bearer $AUTHENTIK_TOKEN" \ GROUP_PK=$(curl -s -X POST -H "Authorization: Bearer $AUTHENTIK_TOKEN" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"$AUTHENTIK_URL/api/v3/core/groups/" \ "$AUTHENTIK_URL/api/v3/core/groups/" \
-d "{\"name\": \"$SOPS_GROUP\", \"is_superuser\": false}" | jq -r '.pk') -d "$GROUP_PAYLOAD" | jq -r '.pk')
echo "Created Authentik group $SOPS_GROUP (pk=$GROUP_PK)" echo "Created Authentik group $SOPS_GROUP (pk=$GROUP_PK)"
else else
GROUP_PK=$(curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \ GROUP_PK=$(curl -s -H "Authorization: Bearer $AUTHENTIK_TOKEN" \

View file

@ -229,6 +229,9 @@ resource "kubernetes_deployment" "webhook_handler" {
} }
} }
} }
lifecycle {
ignore_changes = [spec[0].template[0].spec[0].dns_config]
}
} }
resource "kubernetes_service" "webhook_handler" { resource "kubernetes_service" "webhook_handler" {