# Signing archives, packages, checksums

Signing ensures that the artifacts have been generated by yourself, and your
users can verify that by comparing the generated signature with your public
signing key.

GoReleaser provides means to sign both executables and archives.

## Usage

Signing works in combination with checksum files, and it is generally enough
to sign the checksum files only.

The default is configured to create a detached signature for the checksum files
with [GnuPG](https://www.gnupg.org/), and your default key.

To enable signing just add this to your configuration:

```yaml title=".goreleaser.yaml"
signs:
  - artifacts: checksum
```

To customize the signing pipeline you can use the following options:

```yaml title=".goreleaser.yaml"
signs:
  - #
    # ID of the sign config, must be unique.
    #
    # Default: 'default'.
    id: foo

    # Name of the signature file.
    #
    # Default: '${artifact}.sig'.
    # Templates: allowed.
    signature: "${artifact}_sig"

    # Path to the signature command
    #
    # Default: 'gpg'.
    cmd: gpg2

    # Command line arguments for the command
    #
    # to sign with a specific key use
    # args: ["-u", "<key id, fingerprint, email, ...>", "--output", "${signature}", "--detach-sign", "${artifact}"]
    #
    # Default: ["--output", "${signature}", "--detach-sign", "${artifact}"].
    # Templates: allowed.
    args: ["--output", "${signature}", "${artifact}", "{{ .ProjectName }}"]

    # Which artifacts to sign
    #
    # Valid options are:
    # - none        no signing
    # - all:        all artifacts
    # - checksum:   checksum files
    # - source:     source archive
    # - package:    Linux packages (deb, rpm, apk, etc)
    # - installer:  Windows MSI installers (Pro only)
    # - diskimage:  macOS DMG disk images (Pro only)
    # - archive:    archives from archive pipe
    # - sbom:       any SBOMs generated for other artifacts
    # - binary:     binaries (only when `archives.format` is 'binary', use binaries_sign otherwise)
    #
    # Default: 'none'.
    artifacts: all

    # IDs of the artifacts to sign.
    #
    # If `artifacts` is checksum or source, this fields has no effect.
    ids:
      - foo
      - bar

    # Allows to further filter the artifacts.
    #
    # Artifacts that do not match this expression will be ignored.
    #
    # <!-- md:inline_pro -->.
    # <!-- md:inline_version v2.2 -->.
    # Templates: allowed.
    if: '{{ eq .Os "linux" }}'

    # Stdin data to be given to the signature command as stdin.
    #
    # Templates: allowed.
    stdin: "{{ .Env.GPG_PASSWORD }}"

    # StdinFile file to be given to the signature command as stdin.
    stdin_file: ./.password

    # Sets a certificate that your signing command should write to.
    #
    # You can later use `${certificate}` or `.Env.certificate` in the `args` section.
    #
    # This is particularly useful for keyless signing with cosign, and should
    # not usually be used otherwise.
    #
    # Note that this should be a name, not a path.
    #
    # Templates: allowed.
    certificate: '{{ trimsuffix .Env.artifact ".tar.gz" }}.pem'

    # List of environment variables that will be passed to the signing command
    # as well as the templates.
    env:
      - FOO=bar
      - HONK=honkhonk

    # By default, the stdout and stderr of the signing cmd are discarded unless
    # GoReleaser is running with `--verbose` set.
    # You can set this to true if you want them to be displayed regardless.
    output: true
```

### Available variable names

These environment variables might be available in the fields that accept
templates:

- `${artifact}`: the path to the artifact that will be signed
- `${artifactID}`: the ID of the artifact that will be signed
- `${certificate}`: the certificate filename, if provided
- `${signature}`: the signature filename

## Signing with cosign

You can sign your artifacts with [cosign][] as well.

Assuming you have a `cosign.key` in the repository root and a `COSIGN_PWD`
environment variable set, a simple usage example would look like this:

```yaml title=".goreleaser.yaml"
signs:
  - cmd: cosign
    stdin: "{{ .Env.COSIGN_PWD }}"
    args:
      - "sign-blob"
      - "--key=cosign.key"
      - "--output-signature=${signature}"
      - "${artifact}"
      - "--yes" # needed on cosign 2.0.0+
    artifacts: all
```

Your users can then verify the signature with:

```sh
cosign verify-blob -key cosign.pub -signature file.tar.gz.sig file.tar.gz
```

<!-- TODO: keyless signing with cosign example -->

## Signing executables

Executables can be signed after build using post hooks.

### With gon

!!! notice

    [gon][] was discontinued by its maintainer, but it lives on in a
    [fork][gon-fork], which we'll use here.

For example, you can use [gon][gon-fork] to create notarized macOS apps:

```yaml title=".goreleaser.yaml"
builds:
  - binary: foo
    id: foo
    goos:
      - linux
      - windows
    goarch:
      - amd64

  # notice that we need a separated build for the MacOS binary only:
  - binary: foo
    id: foo-macos
    goos:
      - darwin
    goarch:
      - amd64
    hooks:
      post: gon gon.hcl
```

And:

```terraform
# gon.hcl
#
# The path follows a pattern
# ./dist/BUILD-ID_TARGET/BINARY-NAME
source = ["./dist/foo-macos_darwin_amd64/foo"]
bundle_id = "com.mitchellh.example.terraform"

apple_id {
  username = "mitchell@example.com"
  password = "@env:AC_PASSWORD"
}

sign {
  application_identity = "Developer ID Application: Mitchell Hashimoto"
}
```

Note that notarizing may take some time, and will need to be run from a macOS
machine.

If you generate ZIP or DMG as part of your signing via gon you may need to
ensure their file names align with desired pattern of other artifacts as
GoReleaser doesn't control how these get generated beyond just executing `gon`
with given arguments. Relatedly you may need to list these additional artifacts
as `extra_files` in the `release` section to make sure they also get uploaded.

You can also check
[this issue](https://github.com/goreleaser/goreleaser/issues/1227) for more
details.

### With cosign

You can also use [cosign][] to sign the binaries directly, but you'll need to
manually add the `.sig` files to the release and/or archive:

```yaml title=".goreleaser.yaml"
builds:
  - hooks:
      post:
        - sh -c "COSIGN_PASSWORD=$COSIGN_PWD cosign sign-blob --key cosign.key --output-signature dist/{{ .ProjectName }}_{{ .Version }}_{{ .Target }}.sig {{ .Path }}"

# add to the release directly:
release:
  extra_files:
    - glob: dist/*.sig

# or just to the archives:
archives:
  - files:
      - dist/*.sig
```

While this works, I would recommend using the signing pipe directly.

## Signing Docker images and manifests

Please refer to [Docker Images Signing](docker_sign.md).

## Limitations

You can sign with any command that either outputs a file or modify the file
being signed.

If you want to sign with something that writes to `STDOUT` instead of a file,
you can wrap the command inside a `sh -c` execution, for instance:

```yaml title=".goreleaser.yaml"
signs:
  - cmd: sh
    args:
      - "-c"
      - 'echo "${artifact} is signed and I can prove it" | tee ${signature}'
    artifacts: all
```

And it will work just fine. Just make sure to always use the `${signature}`
template variable as the result file name and `${artifact}` as the origin file.

[gon]: https://github.com/mitchellh/gon
[gon-fork]: https://github.com/Bearer/gon
[cosign]: https://github.com/sigstore/cosign
