|
| 1 | +# 🛡️ GPG Ephemeral Key Generator |
| 2 | + |
| 3 | +This GitHub composite action generates a new ephemeral GPG key on every build, signs it using a repo‑scoped subkey, and exports the fingerprint and public key. It’s designed for use in CI pipelines where artifacts need secure signing without long‑lived keys in GitHub Actions. |
| 4 | + |
| 5 | +--- |
| 6 | + |
| 7 | +## 🔧 How It Works |
| 8 | + |
| 9 | +- Generates a short‑lived RSA key (default RSA‑3072, expiration 1 day) |
| 10 | +- Signs it with your repo‑specific subkey (stored as `GPG_SUBKEY_B64`) |
| 11 | +- Returns: |
| 12 | + - `ephemeral-fingerprint` → use to sign artifacts |
| 13 | + - `ephemeral-public-key` → base64-encoded armored public key |
| 14 | + - `gnupg-home` → isolated GNUPGHOME path for downstream steps |
| 15 | + |
| 16 | +--- |
| 17 | + |
| 18 | +## 📦 Inputs |
| 19 | + |
| 20 | +| Name | Required | Description | |
| 21 | +|------------------|----------|-------------| |
| 22 | +| `subkey-armored` | ✅ | Base64‑encoded ASCII‑armored GPG subkey (secret) used to sign the ephemeral key | |
| 23 | +| `name` | ❌ | Name for the ephemeral key (default: Ephemeral Key) | |
| 24 | +| `comment` | ❌ | Metadata like build ID, ref, etc. A random suffix is appended | |
| 25 | +| `email` | ❌ | Email for ephemeral key (default: ci@build.local) | |
| 26 | +| `key-length` | ❌ | RSA key length (default: 3072) | |
| 27 | +| `expire-days` | ❌ | Expiration in days (default: 1) | |
| 28 | +| `cleanup` | ❌ | If `true`, remove GNUPGHOME after export (default: false) | |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## 🔑 Outputs |
| 33 | + |
| 34 | +| Name | Description | |
| 35 | +|-------------------------|-------------| |
| 36 | +| `ephemeral-fingerprint` | Fingerprint of the generated ephemeral key | |
| 37 | +| `ephemeral-public-key` | Base64‑encoded ASCII‑armored public key | |
| 38 | +| `gnupg-home` | Path to isolated GNUPGHOME for subsequent actions | |
| 39 | + |
| 40 | +--- |
| 41 | + |
| 42 | +## 🛠️ Setup (Per Repo) |
| 43 | + |
| 44 | +1. Create a GPG subkey tied to this repository, signed by your org's offline master key. |
| 45 | +2. Export it (subkey only): |
| 46 | + ```bash |
| 47 | + gpg --export-secret-subkeys --armor > repo-subkey.asc |
| 48 | + base64 < repo-subkey.asc | tr -d '\n' > subkey.b64 |
| 49 | + ``` |
| 50 | +3. Store as a GitHub Secret in your repo: |
| 51 | + `GPG_SUBKEY_B64` = contents of `subkey.b64` |
| 52 | + |
| 53 | +## ✅ Example: Prove Signing Works |
| 54 | + |
| 55 | +```yaml |
| 56 | +jobs: |
| 57 | + test-ephemeral-signing: |
| 58 | + runs-on: ubuntu-latest |
| 59 | + steps: |
| 60 | + - uses: actions/checkout@v4 |
| 61 | + |
| 62 | + - name: Generate ephemeral key |
| 63 | + id: gpg |
| 64 | + uses: OpenCHAMI/github-actions/actions/gpg-ephemeral-key@v1 |
| 65 | + with: |
| 66 | + subkey-armored: ${{ secrets.GPG_SUBKEY_B64 }} |
| 67 | + comment: "build:${{ github.run_id }}" |
| 68 | + key-length: '3072' |
| 69 | + expire-days: '1' |
| 70 | + cleanup: false |
| 71 | + |
| 72 | + - name: Sign and verify a test file |
| 73 | + run: | |
| 74 | + GNUPGHOME="${{ steps.gpg.outputs.gnupg-home }}"; export GNUPGHOME |
| 75 | + echo "hello" > test.txt |
| 76 | + gpg --batch --yes --local-user "${{ steps.gpg.outputs.ephemeral-fingerprint }}" --detach-sign --output test.txt.sig test.txt |
| 77 | + gpg --verify test.txt.sig test.txt |
| 78 | +
|
| 79 | + - name: Show trust chain |
| 80 | + run: | |
| 81 | + GNUPGHOME="${{ steps.gpg.outputs.gnupg-home }}"; export GNUPGHOME |
| 82 | + echo "Ephemeral: ${{ steps.gpg.outputs.ephemeral-fingerprint }}" |
| 83 | + gpg --list-keys --with-colons |
| 84 | + gpg --list-sigs "${{ steps.gpg.outputs.ephemeral-fingerprint }}" |
| 85 | +
|
| 86 | + - name: Cleanup |
| 87 | + if: always() |
| 88 | + run: rm -rf "${{ steps.gpg.outputs.gnupg-home }}" |
| 89 | +``` |
| 90 | +
|
| 91 | +## 🔐 Security Notes |
| 92 | +
|
| 93 | +- Ephemeral key is short‑lived; expiration limits future signing, not validation of past signatures. |
| 94 | +- Subkey is repo‑scoped, easy to rotate/revoke. |
| 95 | +- Use the isolated `GNUPGHOME` to avoid polluting runner defaults; set `cleanup: true` when possible. |
| 96 | +- Trust chain: `Ephemeral key ← Repo subkey ← Offline master key`. |
| 97 | + |
| 98 | +## 📝 License |
| 99 | + |
| 100 | +MIT |
0 commit comments