input to set private key trust level

This commit is contained in:
CrazyMax 2023-05-06 18:20:11 +02:00
parent ff084959ff
commit 05fea506c1
No known key found for this signature in database
GPG key ID: 3248E46B6BB8C7F7
7 changed files with 158 additions and 26 deletions

View file

@ -55,6 +55,7 @@ jobs:
if (!fs.existsSync(gnupgfolder)){ if (!fs.existsSync(gnupgfolder)){
fs.mkdirSync(gnupgfolder); fs.mkdirSync(gnupgfolder);
} }
fs.chmodSync(gnupgfolder, '0700');
fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => { fs.copyFile('__tests__/fixtures/gpg.conf', `${gnupgfolder}/gpg.conf`, (err) => {
if (err) throw err; if (err) throw err;
}); });
@ -69,11 +70,11 @@ jobs:
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'})); core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
- -
name: Import GPG name: Import GPG
id: import_gpg
uses: ./ uses: ./
with: with:
gpg_private_key: ${{ steps.test.outputs.pgp }} gpg_private_key: ${{ steps.test.outputs.pgp }}
passphrase: ${{ steps.test.outputs.passphrase }} passphrase: ${{ steps.test.outputs.passphrase }}
trust_level: 5
git_config_global: ${{ matrix.global }} git_config_global: ${{ matrix.global }}
git_user_signingkey: true git_user_signingkey: true
git_commit_gpgsign: true git_commit_gpgsign: true
@ -116,7 +117,6 @@ jobs:
core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'})); core.setOutput('passphrase', fs.readFileSync('__tests__/fixtures/${{ matrix.key }}.pass', {encoding: 'utf8'}));
- -
name: Import GPG name: Import GPG
id: import_gpg
uses: ./ uses: ./
with: with:
gpg_private_key: ${{ steps.test.outputs.pgp-base64 }} gpg_private_key: ${{ steps.test.outputs.pgp-base64 }}
@ -126,3 +126,62 @@ jobs:
git_tag_gpgsign: true git_tag_gpgsign: true
git_push_gpgsign: if-asked git_push_gpgsign: if-asked
fingerprint: ${{ matrix.fingerprint }} 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

View file

@ -19,6 +19,7 @@ ___
* [Workflow](#workflow) * [Workflow](#workflow)
* [Sign commits](#sign-commits) * [Sign commits](#sign-commits)
* [Use a subkey](#use-a-subkey) * [Use a subkey](#use-a-subkey)
* [Set key's trust level](#set-keys-trust-level)
* [Customizing](#customizing) * [Customizing](#customizing)
* [inputs](#inputs) * [inputs](#inputs)
* [outputs](#outputs) * [outputs](#outputs)
@ -76,7 +77,6 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- -
name: Import GPG key name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v5 uses: crazy-max/ghaction-import-gpg@v5
with: with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
@ -139,7 +139,6 @@ jobs:
uses: actions/checkout@v3 uses: actions/checkout@v3
- -
name: Import GPG key name: Import GPG key
id: import_gpg
uses: crazy-max/ghaction-import-gpg@v5 uses: crazy-max/ghaction-import-gpg@v5
with: with:
gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
@ -164,6 +163,40 @@ sub ed25519 2021-09-24 [S]
You can use the subkey with signing capability whose fingerprint is `C17D11ADF199F12A30A0910F1F80449BE0B08CB8`. 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 ## Customizing
### inputs ### inputs
@ -171,9 +204,10 @@ You can use the subkey with signing capability whose fingerprint is `C17D11ADF19
Following inputs can be used as `step.with` keys Following inputs can be used as `step.with` keys
| Name | Type | Description | | Name | Type | Description |
|---------------------------------------|---------|------------------------------------------------| |-----------------------|--------|--------------------------------------------------------------------------------------------|
| `gpg_private_key` | String | GPG private key exported as an ASCII armored version or its base64 encoding (**required**) | | `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 | | `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_config_global` | Bool | Set Git config global (default `false`) |
| `git_user_signingkey` | Bool | Set GPG signing keyID for this Git repository (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_commit_gpgsign` | Bool | Sign all commits automatically. (default `false`) |
@ -184,6 +218,8 @@ Following inputs can be used as `step.with` keys
| `workdir` | String | Working directory (below repository root) (default `.`) | | `workdir` | String | Working directory (below repository root) (default `.`) |
| `fingerprint` | String | Specific fingerprint to use (subkey) | | `fingerprint` | String | Specific fingerprint to use (subkey) |
> **Note**
>
> `git_user_signingkey` needs to be enabled for `git_commit_gpgsign`, `git_tag_gpgsign`, > `git_user_signingkey` needs to be enabled for `git_commit_gpgsign`, `git_tag_gpgsign`,
> `git_push_gpgsign`, `git_committer_name`, `git_committer_email` inputs. > `git_push_gpgsign`, `git_committer_name`, `git_committer_email` inputs.
@ -192,7 +228,7 @@ Following inputs can be used as `step.with` keys
Following outputs are available Following outputs are available
| Name | Type | Description | | 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)) | | `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 | | `keyid` | String | Low 64 bits of the X.509 certificate SHA-1 fingerprint |
| `name` | String | Name associated with the GPG key | | `name` | String | Name associated with the GPG key |

View file

@ -107,10 +107,10 @@ for (const userInfo of userInfos) {
describe('getKeygrip', () => { describe('getKeygrip', () => {
it('returns the keygrip for a given fingerprint', async () => { it('returns the keygrip for a given fingerprint', async () => {
await gpg.importKey(userInfo.pgp); 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 => { await gpg.getKeygrip(fingerprint).then(keygrip => {
expect(keygrip.length).toEqual(userInfo.keygrips[i].length); expect(keygrip.length).toEqual(userInfo.keygrips[idx].length);
expect(keygrip).toEqual(userInfo.keygrips[i]); expect(keygrip).toEqual(userInfo.keygrips[idx]);
}); });
} }
}); });
@ -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', () => { describe('deleteKey', () => {
// eslint-disable-next-line jest/expect-expect // eslint-disable-next-line jest/expect-expect
it('removes key from GnuPG', async () => { it('removes key from GnuPG', async () => {

View file

@ -13,6 +13,9 @@ inputs:
passphrase: passphrase:
description: 'Passphrase of the GPG private key' description: 'Passphrase of the GPG private key'
required: false required: false
trust_level:
description: "Set key's trust level"
required: false
git_config_global: git_config_global:
description: 'Set Git config global' description: 'Set Git config global'
default: 'false' default: 'false'

View file

@ -3,6 +3,7 @@ import * as core from '@actions/core';
export interface Inputs { export interface Inputs {
gpgPrivateKey: string; gpgPrivateKey: string;
passphrase: string; passphrase: string;
trustLevel: string;
gitConfigGlobal: boolean; gitConfigGlobal: boolean;
gitUserSigningkey: boolean; gitUserSigningkey: boolean;
gitCommitGpgsign: boolean; gitCommitGpgsign: boolean;
@ -18,6 +19,7 @@ export async function getInputs(): Promise<Inputs> {
return { return {
gpgPrivateKey: core.getInput('gpg_private_key', {required: true}), gpgPrivateKey: core.getInput('gpg_private_key', {required: true}),
passphrase: core.getInput('passphrase'), passphrase: core.getInput('passphrase'),
trustLevel: core.getInput('trust_level'),
gitConfigGlobal: core.getBooleanInput('git_config_global'), gitConfigGlobal: core.getBooleanInput('git_config_global'),
gitUserSigningkey: core.getBooleanInput('git_user_signingkey'), gitUserSigningkey: core.getBooleanInput('git_user_signingkey'),
gitCommitGpgsign: core.getBooleanInput('git_commit_gpgsign'), gitCommitGpgsign: core.getBooleanInput('git_commit_gpgsign'),

View file

@ -206,6 +206,20 @@ export const presetPassphrase = async (keygrip: string, passphrase: string): Pro
return await gpgConnectAgent(`KEYINFO ${keygrip}`); 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> => { export const deleteKey = async (fingerprint: string): Promise<void> => {
await exec await exec
.getExecOutput('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], { .getExecOutput('gpg', ['--batch', '--yes', '--delete-secret-keys', fingerprint], {

View file

@ -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 () => { await core.group(`Setting outputs`, async () => {
core.info(`fingerprint=${fingerprint}`); core.info(`fingerprint=${fingerprint}`);
core.setOutput('fingerprint', fingerprint); core.setOutput('fingerprint', fingerprint);