diff --git a/.forgejo/Dockerfile b/.forgejo/Dockerfile index fed065b..73d5916 100644 --- a/.forgejo/Dockerfile +++ b/.forgejo/Dockerfile @@ -10,7 +10,6 @@ FROM ghcr.io/catthehacker/ubuntu:go-24.04 RUN apt-get update && apt-get install -y --no-install-recommends \ stunnel4 \ netcat-openbsd \ - age \ && rm -rf /var/lib/apt/lists/* # Dagger CLI — pinned to match the engine version on the runner host diff --git a/.forgejo/workflows/deploy.yml b/.forgejo/workflows/deploy.yml index 418db7a..a7887b0 100644 --- a/.forgejo/workflows/deploy.yml +++ b/.forgejo/workflows/deploy.yml @@ -65,7 +65,6 @@ jobs: run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } - command -v age >/dev/null 2>&1 || { echo "ERROR: age is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) @@ -76,15 +75,11 @@ jobs: DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - - name: Decrypt production secrets - if: ${{ secrets.SECRETS_AGE_KEY != '' }} - env: - SECRETS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY }} - run: scripts/secrets-decrypt.sh - - name: Run Android Tests on Firebase Test Lab - if: env.FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY != '' + if: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY != '' }} env: + FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY: ${{ secrets.FIREBASE_TEST_LAB_SERVICE_ACCOUNT_KEY }} + FIREBASE_PROJECT_ID: ${{ vars.FIREBASE_PROJECT_ID }} DAGGER_NO_NAG: "1" run: task test-android-firebase @@ -108,7 +103,6 @@ jobs: run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } - command -v age >/dev/null 2>&1 || { echo "ERROR: age is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) @@ -119,15 +113,12 @@ jobs: DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - - name: Decrypt production secrets - if: ${{ secrets.SECRETS_AGE_KEY != '' }} - env: - SECRETS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY }} - run: scripts/secrets-decrypt.sh - - name: Publish Android to Play Store - if: env.PLAY_STORE_CONFIG_JSON != '' + if: ${{ secrets.PLAY_STORE_CONFIG_JSON != '' }} env: + ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + PLAY_STORE_CONFIG_JSON: ${{ secrets.PLAY_STORE_CONFIG_JSON }} DAGGER_NO_NAG: "1" run: task publish-android @@ -151,7 +142,6 @@ jobs: run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } - command -v age >/dev/null 2>&1 || { echo "ERROR: age is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) @@ -162,15 +152,15 @@ jobs: DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - - name: Decrypt production secrets - if: ${{ secrets.SECRETS_AGE_KEY != '' }} - env: - SECRETS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY }} - run: scripts/secrets-decrypt.sh - - name: Build & Deploy APK to server - if: env.SSH_PRIVATE_KEY != '' + if: ${{ secrets.SSH_PRIVATE_KEY != '' }} env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_HOST: ${{ secrets.SSH_HOST }} + ANDROID_KEYSTORE_BASE64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }} + ANDROID_KEYSTORE_PASSWORD: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} DAGGER_NO_NAG: "1" run: task deploy-apk @@ -194,7 +184,6 @@ jobs: run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } - command -v age >/dev/null 2>&1 || { echo "ERROR: age is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) @@ -205,15 +194,13 @@ jobs: DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - - name: Decrypt production secrets - if: ${{ secrets.SECRETS_AGE_KEY != '' }} - env: - SECRETS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY }} - run: scripts/secrets-decrypt.sh - - name: Build & Deploy Linux to server - if: env.SSH_PRIVATE_KEY != '' + if: ${{ secrets.SSH_PRIVATE_KEY != '' }} env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_HOST: ${{ secrets.SSH_HOST }} DAGGER_NO_NAG: "1" run: task deploy-linux @@ -239,7 +226,6 @@ jobs: run: | command -v dagger >/dev/null 2>&1 || { echo "ERROR: dagger is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } command -v task >/dev/null 2>&1 || { echo "ERROR: task is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } - command -v age >/dev/null 2>&1 || { echo "ERROR: age is not installed in the runner image. Add it to .forgejo/Dockerfile."; exit 1; } dpkg -s stunnel4 netcat-openbsd >/dev/null 2>&1 || { echo "ERROR: stunnel4/netcat-openbsd are not installed in the runner image. Add them to .forgejo/Dockerfile."; exit 1; } - name: Setup Dagger Remote Engine (via stunnel) @@ -250,15 +236,13 @@ jobs: DAGGER_CLIENT_KEY: ${{ secrets.DAGGER_CLIENT_KEY }} run: scripts/setup_dagger_remote.sh - - name: Decrypt production secrets - if: ${{ secrets.SECRETS_AGE_KEY != '' }} - env: - SECRETS_AGE_KEY: ${{ secrets.SECRETS_AGE_KEY }} - run: scripts/secrets-decrypt.sh - - name: Generate build history and deploy website - if: env.SSH_PRIVATE_KEY != '' + if: ${{ secrets.SSH_PRIVATE_KEY != '' }} env: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + SSH_KNOWN_HOSTS: ${{ secrets.SSH_KNOWN_HOSTS }} + SSH_USER: ${{ secrets.SSH_USER }} + SSH_HOST: ${{ secrets.SSH_HOST }} DAGGER_NO_NAG: "1" run: task publish-website diff --git a/.gitignore b/.gitignore index 2ee3bb3..9107d22 100644 --- a/.gitignore +++ b/.gitignore @@ -22,7 +22,6 @@ assets/changelog.txt .env.local .envrc .direnv/ -secrets.env # plaintext secrets — encrypted version (secrets.age) is committed # --- Android --- android/.gradle/ diff --git a/DAGGER.md b/DAGGER.md index e17cea1..5f7f3de 100644 --- a/DAGGER.md +++ b/DAGGER.md @@ -174,70 +174,10 @@ Run a secret manager co-located with the Dagger host. The CI job authenticates w - Vault itself becomes a security-critical single point of failure. - Operational overhead likely disproportionate for a small single-developer project. -### Option 5: Encrypted secrets file (age) — **implemented** - -Store all production secrets in a file (`secrets.env`) that is encrypted with -[age](https://age-encryption.org/) into `secrets.age`. The encrypted file is -committed to the repository. Only the age private key — a single string — is -stored in Codeberg as `SECRETS_AGE_KEY`. Any CI job or developer with the key -can decrypt the file and obtain all secrets. - -**How it works:** - -1. Generate a key pair once: - ```bash - age-keygen -o ~/.config/age/sharedinbox.key - age-keygen -y ~/.config/age/sharedinbox.key > .age-public-key - ``` -2. Copy `secrets.env.example` to `secrets.env`, fill in all values, then encrypt: - ```bash - scripts/secrets-encrypt.sh # reads public key from .age-public-key - git add secrets.age && git commit -m "chore: update encrypted secrets" - ``` -3. Add the private key content as `SECRETS_AGE_KEY` in Codeberg repository secrets. -4. CI jobs call `scripts/secrets-decrypt.sh` (with `SECRETS_AGE_KEY` set) before - any step that needs production credentials. The script writes each variable - to `$GITHUB_ENV` so subsequent steps see them automatically. - -**Keeping local and CI in sync:** -When you rotate a secret locally, update `secrets.env`, re-run -`scripts/secrets-encrypt.sh`, and commit the new `secrets.age`. CI will pick -up the fresh secrets on the next push — no manual CI variable updates needed. - -Multi-line values (SSH keys, certificates) must be stored as a single line -with `\n` escape sequences inside double quotes. Example: -``` -SSH_PRIVATE_KEY="
\n\n