Skip to content

Object Storage Configuration Contract

Paulo Gomes da Cruz Junior edited this page Oct 23, 2026 · 1 revision

type: deployment-guide description: Configuration contract for object storage endpoints, credentials, buckets, and signing settings across PR, test, and prod deployments. tags: [deployment, openshift, object-storage, minio, secrets] timestamp: 2026-10-23T00:00:00Z

Object Storage Configuration Contract

This page defines where the object storage configuration comes from, how it reaches the backend, and how operators rotate, verify, and diagnose it.

Configuration keys

Spring Boot binds the environment variables to properties under ca.bc.gov.nrs.object-storage.

Property Environment variable
endpoint OBJECT_STORAGE_ENDPOINT
presigner-endpoint OBJECT_STORAGE_PRESIGNER_ENDPOINT
access-key OBJECT_STORAGE_ACCESS_KEY
secret-key OBJECT_STORAGE_SECRET_KEY
bucket OBJECT_STORAGE_BUCKET
region OBJECT_STORAGE_REGION
force-path-style OBJECT_STORAGE_FORCE_PATH_STYLE
  • endpoint is the S3 endpoint URL.
  • presigner-endpoint is optional. It is the client-facing host used to sign presigned URLs. Empty for PR/dev because the MinIO service has no public Route, so URLs are signed against the in-cluster endpoint. Set it only when the store needs a different signing host than the API host.
  • access-key is the access key id or username.
  • secret-key is the secret access key.
  • bucket is the bucket name.
  • region is the SigV4 region. Default ca-central-1.
  • force-path-style is true for path-style addressing (https://host/bucket/key); false for virtual-host addressing.

Targets and namespaces

  • PR / dev uses OpenShift namespace cffaf3-dev. Object storage is the shared MinIO service in cffaf3-tools, at http://nr-waste-plus-tools-minio.cffaf3-tools.svc.cluster.local:9000. The service is in-cluster only and has no Route. The path uses a per-PR bucket on the shared MinIO instance, named <base>-pr-<PR number> (base bucket nr-waste). The per-PR bucket is created and cleaned up by the PR pipeline.
  • test uses OpenShift namespace cffaf3-test and the BC Gov object store at https://nrs.objectstore.gov.bc.ca:443.
  • prod uses OpenShift namespace cffaf3-prod and the same BC Gov object store endpoint.

One bucket is used per environment.

Where the values live

Source and precedence

  • Values are supplied ONLY through GitHub Actions secrets. The endpoint is never hardcoded in a workflow.
  • The resolution order for every value is workflow input, then GitHub environment secret, then GitHub repository secret, then the built-in default in the workflow.
  • Repository-level secrets carry the PR/dev defaults: the MinIO endpoint, the base bucket nr-waste, region ca-central-1, and force-path-style=true.
  • The test and prod GitHub environments override the repository values for the BC Gov object store endpoint, access key, secret key, and bucket.

PR/dev path

.github/workflows/pr-open.yml resolves the endpoint and base bucket in its vars job. Reusable-workflow with: inputs cannot read the secrets context, so the values are resolved there and passed to .github/workflows/.deploy.yml as the object-storage-endpoint and object-storage-bucket inputs. The per-PR bucket is derived as <base>-pr-<PR number>. The workflow also sets force-path-style to true. It does not set a presigner endpoint because the shared MinIO service has no public Route.

test/prod path

.github/workflows/merge.yml declares environment: test or environment: prod, so environment secrets take precedence over repository secrets.

OpenShift Secret and backend binding

The init job in .github/workflows/.deploy.yml passes the resolved values as parameters to common/openshift.init.yml. The template writes them into the OpenShift Secret nr-waste-plus-${ZONE} under these stringData keys:

  • object-storage-endpoint
  • object-storage-presigner-endpoint
  • object-storage-access-key
  • object-storage-secret-key
  • object-storage-bucket
  • object-storage-region
  • object-storage-force-path-style

The template is applied with overwrite: true. GitHub is the source of truth, and the OpenShift Secret is rewritten on every deploy.

The backend reads these keys with secretKeyRef from that Secret in backend/openshift.deploy.yml. Each key is declared optional: true, so an unset key leaves the default in backend/src/main/resources/application.yml in place. The property binding and client setup are in:

  • backend/src/main/java/ca/bc/gov/nrs/hrs/configuration/ObjectStorageProperties.java
  • backend/src/main/java/ca/bc/gov/nrs/hrs/configuration/ObjectStorageConfiguration.java

Credential selection

When the resolved endpoint is the tools MinIO, the deploy uses the repository secrets MINIO_ROOT_USER and MINIO_ROOT_PASSWORD as the credentials instead of the S3 access and secret keys. Every other endpoint uses OBJECT_STORAGE_ACCESS_KEY and OBJECT_STORAGE_SECRET_KEY.

Fail-closed gate

The init job in .github/workflows/.deploy.yml runs the Require object storage credentials step before anything is applied.

  • For the tools MinIO endpoint, MINIO_ROOT_USER, MINIO_ROOT_PASSWORD, and a non-empty bucket are required.
  • For any other endpoint, a non-empty OBJECT_STORAGE_ENDPOINT that is an http(s) URL, plus OBJECT_STORAGE_ACCESS_KEY, OBJECT_STORAGE_SECRET_KEY, and a non-empty bucket are required.
  • If a value is missing, the job fails with a message naming the missing secret. A deployment never starts against an unconfigured or half-configured store.

Rotation

  1. Update the secret in GitHub: use the repository secret, or the test or prod environment secret.

  2. Re-run the Merge deployment for that environment. The merge workflow supports workflow_dispatch.

  3. The init job rewrites the OpenShift Secret nr-waste-plus-${ZONE} from the new values.

  4. The application pods roll automatically. Each application template passes a RANDOM_EXPRESSION parameter, so a re-applied deployment changes the pod template and replaces the pods.

  5. Verify the update:

    gh secret list --env prod
    oc get secret nr-waste-plus-prod -n cffaf3-prod -o yaml

gh secret list shows names and update timestamps only. The OpenShift command shows the values that reached the cluster.

Verification

Check the GitHub side:

gh secret list --repo bcgov/nr-waste-plus
gh secret list --env test --repo bcgov/nr-waste-plus
gh secret list --env prod --repo bcgov/nr-waste-plus

Check the OpenShift side:

oc get secret nr-waste-plus-prod -n cffaf3-prod -o yaml
oc set env deploy/nr-waste-plus-backend -n cffaf3-prod --list | grep OBJECT_STORAGE

The first OpenShift command shows the Secret that reached the cluster. The second lists the values that the running pods actually received.

Troubleshooting

SignatureDoesNotMatch

The configured region does not match the region the store expects in its SigV4 signature. Check OBJECT_STORAGE_REGION.

HTTP 403 with credentials that look valid

Check force-path-style; the store may only accept path-style addressing. A key scoped to another bucket cannot access this one. Also check the bucket policy.

A presigned URL is generated but cannot be reached

The client-facing host differs from the signing host. Set presigner-endpoint. The PR/dev MinIO has no public Route, so its presigned URLs are only reachable from inside the cluster.

Credentials are missing at runtime

DefaultCredentialsProvider or a no-credentials-found error means the OpenShift Secret carries empty values. This happens when a deployment ran before the GitHub secrets existed. Re-run the deployment after setting the secrets.

The deploy stops at "Require object storage credentials"

The message names the missing secret. Set the value at the environment level for test/prod, or at the repository level for PR/dev.

Source files

  • .github/workflows/pr-open.yml
  • .github/workflows/merge.yml
  • .github/workflows/.deploy.yml
  • .github/workflows/.tools-deploy.yml
  • .github/workflows/.tools-cleanup.yml
  • common/openshift.init.yml
  • backend/openshift.deploy.yml
  • backend/src/main/resources/application.yml
  • backend/src/main/java/ca/bc/gov/nrs/hrs/configuration/ObjectStorageProperties.java
  • backend/src/main/java/ca/bc/gov/nrs/hrs/configuration/ObjectStorageConfiguration.java

Related documentation

Waste Plus Wiki

Home

Architecture

Codes

Decisions

Knowledge Base

Development

Changes

Clone this wiki locally