forgejo-docs/docs/admin/signing.md
Gusted 0df093be8a admin: document instance signing
- Rewrite the instance commit signing documentation from scratch.
- Includes how to configure SSH signing.
- Includes section that https://github.com/Foxboron/ssh-tpm-agent could
be used for secure SSH signing.
- Ref: forgejo/forgejo#6897
2025-04-11 13:25:45 +00:00

8.2 KiB

title license
Instance Commit Signing CC-BY-SA-4.0

Forgejo has the ability to sign commits when Forgejo themselves generates those commits, such as:

  • Repository initialisation
  • Wiki changes
  • CRUD actions using the web editor or the API
  • Merges from pull requests

Configuration

In order for Forgejo to sign commits, it has to know how it should be signing commits and when to sign commits. Unless otherwise indicated, all configuration settings discussed on this page are for the [repository.signing] section.

Signing key

Forgejo offers two formats to sign commits with: GPG and SSH. If you meet the requirements of SSH, then it is strongly preferred to use that instead of GPG.

SSH

For Forgejo to do SSH commit signing, it needs a Git version equal to or newer than 2.34.0 and ssh-keygen binary equal to or newer than version 8.2p1.1

You need a dedicated OpenSSH key pair for instance signing. If you don't have such key pair yet you can generate one via ssh-keygen2 or you also could store the SSH key in TPM, there is a dedicated section with instructions on how to do that.

Forgejo needs to be told that it should use SSH signing and which SSH key to use, this should be configured as followed:

FORMAT = ssh
SIGNING_KEY = /absolute/path/to/public/ssh/key.pub

The value for the SIGNING_KEY setting needs to be an absolute path to the public key, where the private key needs to be available in the path without the .pub suffix.

Forgejo also needs to be told who the committer of the commit is, which requires a name and email and should be configured as followed:

SIGNING_NAME = "forgejo.org Instance"
SIGNING_EMAIL = "noreply@forgejo.org"

GPG

There are two ways to tell Forgejo which GPG key should be used for commit signing.

SIGNING_KEY = default

Will use the git config to determine the signing key: if the value of commit.gpgsign is set to true, then it will use the values of user.signingkey, user.name and user.email for the signing key, committer name and committer email respectively.


SIGNING_KEY = GPG-KEY-ID
SIGNING_NAME = "forgejo.org Instance"
SIGNING_EMAIL = "noreply@forgejo.org"

Will use the GPG keyid to search for the key in the GPG keyring. Forgejo searches for this key in a directory, which can be computed as follows: If a GNUPGHOME environment variable is set, this is used. Otherwise the .gnupg directory in the directory corresponding to the value of the HOME_PATH setting in the [git] section is used ([git].HOME_PATH/.gnupg so to say). It should be noted that by default, GPG does not use that keyring and you should take extra care when importing or generating the key, for example by setting the value of the GNUPGHOME environment to the directory Forgejo uses.

Signing operations

There are several operations for which Forgejo will generate a commit and thus be able to sign the commit. For each operation you can specify under which conditions Forgejo should sign the commit.

For each operation, you can combine the values as a comma-separated list. There are two special values that are valid values for each operation and cannot be combined with any other value for that operation: always and never. The first value, if set, will always sign the commit and the second value, if set, will never sign the commit.

Initial commit

When should Forgejo sign the initial commit when creating a repository. The possible values for the INITIAL_COMMIT setting are:

  • pubkey: Only if the user has added a GPG key to its account.
  • twofa: Only if the user is enrolled into two-factor authentication.

Wiki

When should Forgejo sign commits to the wiki. The possible values for the WIKI setting are:

  • pubkey: Only if the user has added a GPG key to its account.
  • twofa: Only if the user is enrolled into two-factor authentication.
  • parentsigned: Only if the parent commit is signed.

CRUD actions

When should Forgejo sign commits that are created for file changes via the web editor or API. The possible values for the CRUD_ACTIONS setting are:

  • pubkey: Only if the user has added a GPG key to its account.
  • twofa: Only if the user is enrolled into two-factor authentication.
  • parentsigned: Only if the parent commit is signed.

Pull request merges

When should Forgejo sign merge commits from pull requests. The possible values for the MERGES setting are:

  • pubkey: Only if the user has added a GPG key to its account.
  • twofa: Only if the user is enrolled into two-factor authentication.
  • basesigned: Only if the parent commit in the base repository is signed.
  • headsigned: Only if the head commit in the head branch is signed.
  • commitssigned: Only if all the commits in the head branch to the merge point are signed.
  • approved: Only if the pull request targets a protected branch and has at least one approval.

Obtaining the instance signing key

If a GPG instance signing key is set, the GPG public key can be obtained at the API route, /api/v1/signing-key.gpg. If a repository specific GPG key is set, it can be obtained at the API route, /api/v1/repos/{username}/{reponame}/signing-key.gpg

If a SSH instance signing key is set, the SSH public key can be obtained at the API route, /api/v1/signing-key.ssh.

Using ssh-tpm-agent

It is possible to use ssh-tpm-agent so that the SSH private key resides in a Trusted Platform Module (TPM) and therefore makes it harder to leak the SSH private key as it does not reside on the filesystem. To use this, the server that Forgejo runs on must have access to TPM 2.0. This section only explains how to make the SSH private key available to Forgejo, not how to configure Forgejo to use it.

Follow the instruction from ssh-tpm-agent to create a key or import an existing key. An instance key is expected to be a long-lived key3 and therefore it is advisable to follow the 'Import existing key' guide as it allows you to backup the private key in a safe place and in case of a recovery, restore the instance SSH key.

ssh-tpm-agent acts as an ssh-agent(1) and in order for Forgejo to use ssh-tpm-agent to sign commits with, it needs to have a SSH_AUTH_SOCK environment set when launching the Forgejo binary. How to pass this to Forgejo depends on how you run Forgejo, we consider two situation: a Systemd service on bare-metal or containerized (for example, via Docker).

In either case, the host will need to install the systemd unit service by running ssh-tpm-agent --install-user-units.

Systemd service

In the [Service] section, add the following (it is fine to have multiple Environment keys):

Environment=SSH_AUTH_SOCK="/socket/path"

Where /socket/path is replaced with the value of ssh-tpm-agent --print-socket.

Containerized

We take the default docker-compose file as an example. We add an environment variable and a volume mount to the compose file:

networks:
  forgejo:
    external: false

services:
  server:
    image: codeberg.org/forgejo/forgejo:10
    container_name: forgejo
    environment:
      - USER_UID=1000
      - USER_GID=1000
+      - SSH_AUTH_SOCK=$SOCKET_PATH
    restart: always
    networks:
      - forgejo
    volumes:
      - ./forgejo:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
+      - $SOCKET_PATH:$SOCKET_PATH
    ports:
      - "3000:3000"
      - "222:22"

Where $SOCKET_PATH is to be replaced with the value of ssh-tpm-agent --print-socket. Another volume would need to be added that exposes the public OpenSSH key, the container path should match with the path that is specified for the SIGNING_KEY setting.


  1. The git version check is already done by Forgejo, but for ssh-keygen only the presence of the binary is checked. ↩︎

  2. https://docs.codeberg.org/security/ssh-key/ contains instructions for generating an SSH key pair, you should not generate a FIDO2 (-sk type) key pair as that will not work with Forgejo. ↩︎

  3. Rotating instance keys is currently not possible. ↩︎