diff --git a/.forgejo/workflows/pipeline.yml b/.forgejo/workflows/pipeline.yml index c998c52..4ba8d09 100644 --- a/.forgejo/workflows/pipeline.yml +++ b/.forgejo/workflows/pipeline.yml @@ -18,7 +18,7 @@ jobs: run: | docker run --rm -v "$PWD:/repo" \ ghcr.io/gitleaks/gitleaks:v8.30.1 \ - detect --source /repo --redact -v + detect --source /repo --config /repo/.gitleaks.toml --redact -v - name: Test API run: | @@ -169,6 +169,9 @@ jobs: --from-file=.dockerconfigjson=/work/.ci/docker/config.json \ --dry-run=client -o yaml | kube apply -f - + kube apply -k deploy/secrets + kube wait sealedsecret.bitnami.com/nereus-db --namespace nereus \ + --for=condition=Synced --timeout=2m kube get secret nereus-db --namespace nereus >/dev/null kube apply -k deploy/observability @@ -196,3 +199,31 @@ jobs: - name: Remove cluster credentials if: always() run: rm -rf .ci + + mirror: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' && vars.GITHUB_MIRROR_ENABLED == 'true' + needs: deploy + runs-on: docker + steps: + - name: Check out repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd + with: + fetch-depth: 0 + + - name: Push main to GitHub + env: + GITHUB_MIRROR_SSH_KEY: ${{ secrets.GITHUB_MIRROR_SSH_KEY }} + run: | + test -n "$GITHUB_MIRROR_SSH_KEY" + install -d -m 0700 .ci/mirror + printf '%s\n' "$GITHUB_MIRROR_SSH_KEY" >.ci/mirror/id_ed25519 + chmod 0600 .ci/mirror/id_ed25519 + cat >.ci/mirror/known_hosts <<'EOF' + github.com ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOMqqnkVzrm0SdG6UOoqKLsabgH5C9okWi0dh2l9GKJl + EOF + GIT_SSH_COMMAND="ssh -i $PWD/.ci/mirror/id_ed25519 -o IdentitiesOnly=yes -o StrictHostKeyChecking=yes -o UserKnownHostsFile=$PWD/.ci/mirror/known_hosts" \ + git push git@github.com:Fi3w0/Nereus.git HEAD:main + + - name: Remove GitHub credentials + if: always() + run: rm -rf .ci diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 911a758..b78e7c4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: run: | docker run --rm -v "$PWD:/repo" \ ghcr.io/gitleaks/gitleaks:v8.30.1 \ - detect --source /repo --redact -v + detect --source /repo --config /repo/.gitleaks.toml --redact -v go: name: vet, test, lint diff --git a/.gitleaks.toml b/.gitleaks.toml new file mode 100644 index 0000000..15f695a --- /dev/null +++ b/.gitleaks.toml @@ -0,0 +1,12 @@ +title = "Nereus gitleaks configuration" + +[extend] +useDefault = true + +[[allowlists]] +description = "Encrypted SealedSecret values are ciphertext, not plaintext credentials." +condition = "AND" +targetRules = ["generic-api-key"] +regexTarget = "line" +paths = ['''^deploy/secrets/[^/]+-sealed\.yaml$'''] +regexes = ['''^\s+(POSTGRES_PASSWORD|DATABASE_URL):\s+Ag[A-Za-z0-9+/=]+\s*$'''] diff --git a/docs/ci-cd.md b/docs/ci-cd.md index 2568adf..d6ca446 100644 --- a/docs/ci-cd.md +++ b/docs/ci-cd.md @@ -21,9 +21,18 @@ Forgejo UI: - `REGISTRY_USERNAME`: account allowed to push the three Nereus packages. - `REGISTRY_PASSWORD`: package-scoped token for that account. - `KUBECONFIG_B64`: base64 encoding of a least-privilege deployment kubeconfig. +- `GITHUB_MIRROR_SSH_KEY`: private half of a write-enabled deploy key for + `git@github.com:Fi3w0/Nereus.git`. + +After registering that deploy key, set the Forgejo Actions repository variable +`GITHUB_MIRROR_ENABLED` to `true`. The mirror job stays skipped until both sides +are configured, so an absent GitHub credential cannot break production deploys. The workflow never prints these values. It writes the kubeconfig to the ephemeral workspace with mode `0600`, removes it in an `always()` step, and creates the Kubernetes registry pull secret through a pipe so its generated -manifest is not logged or committed. The pre-existing `nereus-db` Secret is -required and is only checked for presence. +manifest is not logged or committed. The deploy job applies the committed +database SealedSecret and waits for the controller to synchronize it before +starting the application rollout. After a successful production deployment, +the mirror job pushes `main` to GitHub over SSH and removes its temporary key in +an `always()` step.