OpenMRS Supply-Chain Security Features

OpenMRS Supply-Chain Security Features

Three features protect the OpenMRS build and release pipeline. Together, they cover the
full chain: artifacts are signed when published, verified when consumed, and
Docker images are signed so deployers can prove where an image came from.

Feature

What it protects

Where it lives

Feature

What it protects

Where it lives

Artifact signing & checksums

Maven artifacts (.jar, .omod, ...)

This parent POM (maven-gpg-plugin); backfill script proposed for openmrs-contrib-gha-workflows

PGP verification

Downloaded org.openmrs artifacts

openmrs-contrib-pgpverify-maven-plugin, wired into this parent POM (SDK integration in progress)

Cosign for Docker

Reference Application release images

openmrs-distro-referenceapplication (GitHub Actions)


1. Artifact signing & checksums (Maven / GPG)

What it does

When a module built on this parent POM is released, maven-gpg-plugin signs every
published artifact with the OpenMRS Bot code-signing key, producing a detached .asc
signature next to each file. Maven also publishes the standard .md5 / .sha1
checksums. This is what PGP verification (below) later checks against.

Signing is off by default (gpg.skip=true) so ordinary local builds don't need a
key. It turns on automatically when a passphrase is supplied.

How to use it

Inherit this parent POM in your module (no extra config needed):

<parent> <groupId>org.openmrs.maven.parents</groupId> <artifactId>maven-parent-openmrs-module</artifactId> <version><!-- latest --></version> </parent>

Signing activates when you release with a passphrase, via either:

# environment variable (used in CI) MAVEN_GPG_PASSPHRASE=... mvn deploy # or a Maven property mvn deploy -Dgpg.passphrase=...

Both switch gpg.skip to false automatically. The openmrs-module-signing
integration test in this repo exercises the whole flow with an ephemeral key.

Backfilling old artifacts

Artifacts published before signing existed can be covered by a script that downloads
existing artifacts and generates the missing .md5, .sha1, .sha256, and .asc
files:
backfill-gpg-signatures.sh.


2. PGP verification (openmrs-pgpverify-maven-plugin)

What it does

Verifies that resolved org.openmrs artifacts were signed by the OpenMRS Bot key
before the build proceeds. It only checks a whitelist of groupIds (default
org.openmrs, verified against key CA12619FDE8CD6A93FAFE458A6F9608DCC73473F) and
ignores everything else, so it stays version-independent and doesn't need every
third-party dependency enumerated.

The signing key is pinned by fingerprint: a key fetched from a keyserver or keyring
is trusted only if its fingerprint (or its master key's) matches the allowed set, so no
server can swap in a different key under the same id.

It has two goals:

  • verify: checks the resolved dependency tree; bound to the verify phase.

  • verify-files: checks an explicit list of files you already downloaded (used by
    the SDK when it fetches modules/WARs into a distribution).

How to use it

In a module build (already wired into this parent POM, nothing to add). To use it
standalone:

<plugin> <groupId>org.openmrs.maven.plugins</groupId> <artifactId>openmrs-pgpverify-maven-plugin</artifactId> <version><!-- latest release --></version> <executions> <execution> <goals> <goal>verify</goal> </goals> </execution> </executions> </plugin>

With no configuration it verifies org.openmrs.* against the Bot key.

Useful parameters:

Parameter

Default

Purpose

Parameter

Default

Purpose

groups

org.openmrs, Bot key

Whitelisted groupIds and their allowed fingerprints.

keysFile

none

External keys file (local path or https URL) so the key can be rotated without recompiling.

keyServer

https://keyserver.ubuntu.com (unless keyRings set)

Where keys are fetched by id.

keyRings

none

Local/remote public key rings, consulted before the key server (enables offline verification).

failOnMissingSignature

true

Fail when a whitelisted artifact has no .asc.

verifySnapshots

false

Verify SNAPSHOT artifacts too.

skip

false

Skip the check.

In the SDK (work in progress): support for the SDK to call verify-files on the
org.openmrs artifacts it downloads during openmrs-sdk:setup and
openmrs-sdk:build-distro is under development and not yet released.


3. Cosign for Docker (Reference Application images)

What it does

Every Reference Application release image is signed with
cosign using keyless (OIDC) signing, where GitHub
Actions' identity is the signer and the signature is recorded in the public
Sigstore transparency log. There is no private key to manage. Signed images include the backend
(and its DB / no-demo / nightly-with-data variants), frontend, gateway, certbot, and
monitoring-init.

How it's produced

Each build-* workflow in openmrs-distro-referenceapplication runs, after pushing the
image:

permissions: id-token: write # required for keyless signing via OIDC - name: Install cosign uses: sigstore/cosign-installer@... # v3.9.2 - name: Sign the image (keyless) run: cosign sign --yes "${IMAGE}@${DIGEST}"

Signing uses the image digest, not the tag, so it always signs exactly what was
pushed.

How to verify an image

Anyone pulling an image can confirm it came from the OpenMRS release pipeline:

cosign verify openmrs/openmrs-reference-application-3-backend:<version> \ --certificate-identity-regexp 'https://github.com/openmrs/openmrs-distro-referenceapplication/.*' \ --certificate-oidc-issuer https://token.actions.githubusercontent.com

The command succeeds only if the image was signed by that repository's workflow.