Skip to content

Commit

Permalink
input to set private key trust level
Browse files Browse the repository at this point in the history
  • Loading branch information
crazy-max committed May 6, 2023
1 parent ff08495 commit 05fea50
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 27 deletions.
63 changes: 61 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ jobs:
if (!fs.existsSync(gnupgfolder)){
fs.mkdirSync(gnupgfolder);
}
fs.chmodSync(gnupgfolder, '0700');
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
if (err) throw err;
});
Expand All @@ -69,11 +70,11 @@ jobs:
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
-
name: Import GPG
id: import_gpg
uses: ./
with:
gpg_private_key: ${{ steps.test.outputs.pgp }}
passphrase: ${{ steps.test.outputs.passphrase }}
trust_level: 5
git_config_global: ${{ matrix.global }}
git_user_signingkey: true
git_commit_gpgsign: true
Expand Down Expand Up @@ -116,7 +117,6 @@ jobs:
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
-
name: Import GPG
id: import_gpg
uses: ./
with:
gpg_private_key: ${{ steps.test.outputs.pgp-base64 }}
Expand All @@ -126,3 +126,62 @@ jobs:
git_tag_gpgsign: true
git_push_gpgsign: if-asked
fingerprint: ${{ matrix.fingerprint }}

trust:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
key:
- test-key
level:
- ''
- 5
- 4
- 3
- 2
- 1
os:
- ubuntu-latest
- macOS-latest
- windows-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: GPG conf
uses: actions/github-script@v6
with:
script: |
const fs = require('fs');
const gnupgfolder = `${require('os').homedir()}/.gnupg`;
if (!fs.existsSync(gnupgfolder)){
fs.mkdirSync(gnupgfolder);
}
fs.chmodSync(gnupgfolder, '0700');
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
if (err) throw err;
});
-
name: Get test key and passphrase
uses: actions/github-script@v6
id: test
with:
script: |
const fs = require('fs');
core.setOutput('pgp', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pgp', {encoding: 'utf8'}));
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
-
name: Import GPG
id: import_gpg
uses: ./
with:
gpg_private_key: ${{ steps.test.outputs.pgp }}
passphrase: ${{ steps.test.outputs.passphrase }}
trust_level: ${{ matrix.level }}
-
name: List trust values
run: |
gpg --export-ownertrust
shell: bash
80 changes: 58 additions & 22 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ ___
* [Workflow](#workflow)
* [Sign commits](#sign-commits)
* [Use a subkey](#use-a-subkey)
* [Set key's trust level](#set-keys-trust-level)
* [Customizing](#customizing)
* [inputs](#inputs)
* [outputs](#outputs)
Expand Down Expand Up @@ -76,7 +77,6 @@ jobs:
uses: actions/checkout@v3
-
name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v5
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
Expand Down Expand Up @@ -139,7 +139,6 @@ jobs:
uses: actions/checkout@v3
-
name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v5
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
Expand All @@ -164,39 +163,76 @@ sub ed25519 2021-09-24 [S]

You can use the subkey with signing capability whose fingerprint is `C17D11ADF199F12A30A0910F1F80449BE0B08CB8`.

### Set key's trust level

With the `trust_level` input, you can specify the trust level of the GPG key.

Valid values are:
* `1`: unknown
* `2`: never
* `3`: marginal
* `4`: full
* `5`: ultimate

```yaml
name: import-gpg
on:
push:
branches: master
jobs:
import-gpg:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Import GPG key
uses: crazy-max/ghaction-import-gpg@v5
with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
passphrase: ${{ secrets.PASSPHRASE }}
trust_level: 5
```

## Customizing

### inputs

Following inputs can be used as `step.with` keys

| Name | Type | Description |
|---------------------------------------|---------|------------------------------------------------|
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) |
| `passphrase` | String | Passphrase of the GPG private key |
| `git_config_global` | Bool | Set Git config global (default `false`) |
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (default `false`) |
| `git_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
| `git_tag_gpgsign` | Bool | Sign all tags automatically. (default `false`) |
| `git_push_gpgsign` | String | Sign all pushes automatically. (default `if-asked`) |
| `git_committer_name` | String | Set commit author's name (defaults to the name associated with the GPG key) |
| `git_committer_email` | String | Set commit author's email (defaults to the email address associated with the GPG key) |
| `workdir` | String | Working directory (below repository root) (default `.`) |
| `fingerprint` | String | Specific fingerprint to use (subkey) |

| Name | Type | Description |
|-----------------------|--------|--------------------------------------------------------------------------------------------|
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) |
| `passphrase` | String | Passphrase of the GPG private key |
| `trust_level` | String | Set key's trust level |
| `git_config_global` | Bool | Set Git config global (default `false`) |
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (default `false`) |
| `git_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
| `git_tag_gpgsign` | Bool | Sign all tags automatically. (default `false`) |
| `git_push_gpgsign` | String | Sign all pushes automatically. (default `if-asked`) |
| `git_committer_name` | String | Set commit author's name (defaults to the name associated with the GPG key) |
| `git_committer_email` | String | Set commit author's email (defaults to the email address associated with the GPG key) |
| `workdir` | String | Working directory (below repository root) (default `.`) |
| `fingerprint` | String | Specific fingerprint to use (subkey) |

> **Note**
>
> `git_user_signingkey` needs to be enabled for `git_commit_gpgsign`, `git_tag_gpgsign`,
> `git_push_gpgsign`, `git_committer_name`, `git_committer_email` inputs.

### outputs

Following outputs are available

| Name | Type | Description |
|---------------|---------|---------------------------------------|
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
| `name` | String | Name associated with the GPG key |
| `email` | String | Email address associated with the GPG key |
| Name | Type | Description |
|---------------|--------|---------------------------------------------------------------------------------------------------------------------------------|
| `fingerprint` | String | Fingerprint of the GPG key (recommended as [user ID](https://www.gnupg.org/documentation/manuals/gnupg/Specify-a-User-ID.html)) |
| `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
| `name` | String | Name associated with the GPG key |
| `email` | String | Email address associated with the GPG key |

## Contributing

Expand Down
16 changes: 13 additions & 3 deletions __tests__/gpg.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ for (const userInfo of userInfos) {
describe('getKeygrip', () => {
it('returns the keygrip for a given fingerprint', async () => {
await gpg.importKey(userInfo.pgp);
for (const [i, fingerprint] of userInfo.fingerprints.entries()) {
for (const {idx, fingerprint} of userInfo.fingerprints.map((fingerprint, idx) => ({idx, fingerprint}))) {
await gpg.getKeygrip(fingerprint).then(keygrip => {
expect(keygrip.length).toEqual(userInfo.keygrips[i].length);
expect(keygrip).toEqual(userInfo.keygrips[i]);
expect(keygrip.length).toEqual(userInfo.keygrips[idx].length);
expect(keygrip).toEqual(userInfo.keygrips[idx]);
});
}
});
Expand All @@ -128,6 +128,16 @@ for (const userInfo of userInfos) {
});
});

describe('setTrustLevel', () => {
it('set trust level', async () => {
await gpg.importKey(userInfo.pgp);
await gpg.configureAgent(gpg.agentConfig);
expect(() => {
gpg.setTrustLevel(userInfo.keyID, '5');
}).not.toThrow();
});
});

describe('deleteKey', () => {
// eslint-disable-next-line jest/expect-expect
it('removes key from GnuPG', async () => {
Expand Down
3 changes: 3 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ inputs:
passphrase:
description: 'Passphrase of the GPG private key'
required: false
trust_level:
description: "Set key's trust level"
required: false
git_config_global:
description: 'Set Git config global'
default: 'false'
Expand Down
2 changes: 2 additions & 0 deletions src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as core from '@actions/core';
export interface Inputs {
gpgPrivateKey: string;
passphrase: string;
trustLevel: string;
gitConfigGlobal: boolean;
gitUserSigningkey: boolean;
gitCommitGpgsign: boolean;
Expand All @@ -18,6 +19,7 @@ export async function getInputs(): Promise<Inputs> {
return {
gpgPrivateKey: core.getInput('gpg_private_key', {required: true}),
passphrase: core.getInput('passphrase'),
trustLevel: core.getInput('trust_level'),
gitConfigGlobal: core.getBooleanInput('git_config_global'),
gitUserSigningkey: core.getBooleanInput('git_user_signingkey'),
gitCommitGpgsign: core.getBooleanInput('git_commit_gpgsign'),
Expand Down
14 changes: 14 additions & 0 deletions src/gpg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,20 @@ export const presetPassphrase = async (keygrip: string, passphrase: string): Pro
return await gpgConnectAgent(`KEYINFO ${keygrip}`);
};

export const setTrustLevel = async (keyID: string, trust: string): Promise<void> => {
await exec
.getExecOutput('gpg', ['--batch', '--no-tty', '--command-fd', '0', '--edit-key', keyID], {
ignoreReturnCode: true,
silent: true,
input: Buffer.from(`trust\n${trust}\ny\nquit\n`)
})
.then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr);
}
});
};

export const deleteKey = async (fingerprint: string): Promise<void> => {
await exec
.getExecOutput('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], {
Expand Down
8 changes: 8 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ async function run(): Promise<void> {
});
}

if (inputs.trustLevel) {
await core.group(`Setting key's trust level`, async () => {
await gpg.setTrustLevel(privateKey.keyID, inputs.trustLevel).then(() => {
core.info(`Trust level set to ${inputs.trustLevel} for ${privateKey.keyID}`);
});
});
}

await core.group(`Setting outputs`, async () => {
core.info(`fingerprint=${fingerprint}`);
core.setOutput('fingerprint', fingerprint);
Expand Down

0 comments on commit 05fea50

Please sign in to comment.