GitLab secrets

Status: Implemented Issue: https://gitlab.com/gitlab-org/cloud-native/gitlab-operator/-/work_items/2249 Parent: none

Goal

A GitLabCore gets the secrets its chart generates without a hook Job. No existing value is regenerated, lost, or reshaped.

Requirements

  • The definition accepts every GitLabSecrets the chart renders, except an ed25519 certificate algorithm or an odd RSA keySize, and rejects any other enum value.
  • A missing Secret or key is generated unless a rule below reports it. A present key keeps its value, even empty, except that secrets.yml gains fields as below. No key is removed, and no type changes.
  • An x509 pair, an SSH host key pair, and the certificate set of CA, TLS, and chain Secrets are each generated whole. A missing .pub, chain, or x509 certificate is derived. Other partial units are reported.
  • A data-bearing unit that status recorded as ready is reported instead of regenerated: a key when it is missing, and a railsSecrets field when it is absent, null, or empty. Data-bearing units are the railsSecrets fields, the SSH host key pairs, the certificate set, and the generators the chart marks persistent.
  • railsSecrets fills its other absent, null, and empty fields, keeps every other field, environment, and list, and reports a secrets.yml that does not parse.
  • Values match the Job’s recipes: character set, length, encoding, wrap, key type and size, PEM type, and certificate structure. Certificate validity follows caDays and certDays.
  • Entries that name one Secret merge. A key with two different generators, and an immutable Secret that lacks a key, are reported.
  • Secrets carry no owner reference, and get every label of the GitLabSecrets but operator.gitlab.com/release-*. A Secret that two GitLabSecrets declare gets a warning event and keeps its labels.
  • Deleting a GitLabCore deletes its GitLabSecrets and leaves every Secret it generated.
  • The Ready condition covers the current generation, and status each Secret. A reported Secret is not ready, and Ready says why.
  • A GitLabCore renders with provider: controller unless spec.chart.values sets another.
  • A GitLabCore applies the GitLabSecrets its chart renders first, owned by the GitLabCore. On every pass, and before every upgrade step, it applies nothing else and runs no hook until that GitLabSecrets is Ready for its current generation.
  • While it waits, Initialized is False with reason WaitingForSecrets and a message that holds the reason and message of Ready, and the phase is Preparing.
  • A GitLabSecrets the API server refuses, such as one with an ed25519 certificate or an odd RSA keySize, fails the pass before anything else is applied. Initialized is False with reason ApplyFailed and the message of the API server.
  • On chart 10.5 or later, a GitLabCore reaches Available in any watched namespace with no ServiceAccount or RBAC provisioned beforehand.
  • A render without a GitLabSecrets, as from chart 10.4 and earlier, is applied without the wait, and the shared secrets Job of the chart runs as a pre-install hook.

Out of scope

  • The CNG binary (chart issue 6658), a shared library, rotating values, certificate renewal, the v1beta1 GitLab resource, and FIPS.
  • shared-secrets.enabled: false, where the administrator supplies every Secret.
  • Adopting the Secrets the Job created, for example on an upgrade from chart 10.4 or for a GitLab installed with Helm, with every key and field value byte for byte.
  • Refusing another shared-secrets.provider in spec.chart.values, or a chart that renders no GitLabSecrets.

FAQ

  • Where does the controller depart from the Job or the chart contract?
    • A partial unit is reported, not completed. Only a .pub, a chain, and an x509 certificate for an existing key are derived. An x509 certificate without its key is reported.
    • A data-bearing unit that was ready and is missing now is reported. The Job and the contract generate it again.
    • A missing Secret with a reported unit is not created, not even with its other keys. The Job and the contract create it.
    • A whole missing rsa, raw bytes, or x509 unit is added to an existing Secret. The Job adds only --from-literal keys.
    • A missing SSH host key pair is generated in an existing Secret if that pair was never ready. The Job never changes an existing host-key Secret.
    • A certificate set that holds some of its keys is completed only by deriving its chain. The Job issues a new CA and wildcard certificate on every run, and creates each missing Secret on its own, so it completes a partial set from a mismatched CA.
    • A certificate set that has none of its keys is issued into its existing Secrets. The Job creates only the Secrets that are missing.
    • A missing value is generated whenever a Secret changes, not once per hook run.
    • secrets.yml is rewritten only when a field is added. The Job rewrites it on every run, and drops undeclared fields and other environments, which the controller keeps.
    • An empty-string secrets.yml field that was never ready is regenerated, as the Job does. The contract fills absent or null fields only.
    • A random value has exactly length characters. The Job’s gen_random can come up short.
    • An SSH host key carries no comment, in its .pub or in its private key. ssh-keygen writes one into both.
    • A rewritten secrets.yml is indented by 2, drops --- and ..., joins the lines of folded scalars, tags merge keys as !!merge <<:, and ends with a newline. The Job writes no trailing newline.
    • A generated secrets.yml value that YAML would read as another type, such as all digits, is quoted. The Job leaves it bare. An existing bare value stays bare.
    • A secrets.yml whose root or environment is null, or not a mapping, is reported. So is one that is not UTF-8, holds a duplicate key or a second document, or has a declared field that an alias or a tag makes null or empty, or that a merge key provides. The Job writes the declared fields back over it.
    • An ed25519 certificate algorithm is refused. The Job of chart 10.4 and later issues it.
    • An odd RSA keySize is refused. The Job’s cfssl accepts it.
    • The operator.gitlab.com/release-* labels are left off, although the contract copies every label. The application labels stay when global.application.create is false, although the Job removes them.
    • A key declared with two different generators is reported, and neither generates it. The Job keeps the first.
    • A Secret that two GitLabSecrets declare keeps its labels. The Job overwrites them.
  • What happens to an existing Secret of another type? It keeps its type and stays ready. The controller emits a Normal event that names both types.
  • Is a ready key that is present but empty reported? No. A present key keeps its value, even empty, as in the Job. Only a secrets.yml field is filled, or reported, when it is empty. A part that would be derived from an empty key, such as a .pub or an x509 certificate, is reported instead.
  • Which sizes does the definition accept? A length from 1 to 4096. An even bits from 2048 to 8192 for rsa, x509, and a pem field. A days, caDays, and certDays from 1 to 106751, with certDays at most caDays. An x509 commonName of at most 64 characters. At most 128 secrets, 16 generators per entry, 64 fields, and one certificates entry, and a maximum length on every string, to bound the cost of the validation rules. The chart renders at most 28 entries, 2 generators per entry, and 8 fields. The Job takes other sizes, such as bits: 1024, which the chart does not render.
  • What else does the definition refuse? A Secret name that is not a DNS subdomain, and a key outside [-._a-zA-Z0-9]. A . in env or a field path, which never worked in the Job, because it reads them as a path. A railsSecrets generator beside another one in its entry, as the chart does. Two fields with one path, an x509 certKey equal to its keyKey, and certificate Secrets that repeat each other or a name in spec.secrets.
  • Why is a data-bearing value not regenerated after it was ready? Fresh values would lose data without a sign: db_key_base encrypts database columns, the host keys identify the instance to every Git client, and the database or OpenBao keeps the values of the generators the chart marks persistent. Token Secrets and signing keys are regenerated.
  • When is a missing data-bearing Secret generated anyway? The guard lives in status, so it is generated when the GitLabSecrets is recreated, also by deleting its GitLabCore, or restored without status, as Velero does by default. It is also generated when a GitLabSecrets that never saw it ready declares it, or after its entry left spec.secrets, for example while its component was off. Back those Secrets up.
  • Which chart does a GitLabCore need? 10.5 or later, the first release expected with chart merge requests 5277, 5420, and 5433. The Operator does not enforce it. An earlier chart runs the Job, which needs a ServiceAccount and RBAC that the Operator does not create. For more information, see ADR 31.
  • What happens with another shared-secrets.provider? The behavior is undefined. The GitLabCore does not refuse it.
  • Why wait for the GitLabSecrets? Every GitLab pod mounts Secrets it declares. A pod created before them fails to mount, and retries until they exist.
  • What happens when a running GitLabSecrets stops being ready? The GitLabCore applies nothing and reports Preparing until it is ready again, for example after an administrator fixes a reported unit. The running workloads keep running, but no drift is repaired, and a version that follows the chart repository keeps its target.
  • How do I force fresh values? Delete the Secret, then delete the GitLabSecrets. The GitLabCore recreates it with an empty status, and the controller generates what is missing. Check status for other reported units first: every missing data-bearing unit of the release regenerates too.
  • How do I fix a partial unit? Delete the part that remains. A unit that was never ready then regenerates. For a unit that was ready, such as an SSH pair that lost its private key, delete the lone .pub, then the GitLabSecrets, as for fresh values. A non-empty private key without its .pub needs no fix.
  • Why do the Secrets carry no owner reference? Garbage collection would delete them with their owner. Like after helm uninstall, an administrator removes them by hand.
  • Does encoding: base64 wrap? No. It stores standard base64 with no wrapping and no trailing newline, as the Job did. wrap: jsonArray wraps the encoded value.
  • Which SSH host keys are generated? RSA 3072, ECDSA P-256, and Ed25519, the set ssh-keygen -A makes in the Job’s image. An existing Secret keeps any other type.
  • Where does certificate validity come from? From caDays and certDays. The chart sets them to what the Job issues, 1825 and 365, because selfsign.expiry applies to neither backend. For more information, see chart issue 6693.
  • Why are Ed25519 certificates refused? Chrome and Firefox do not support an Ed25519 TLS server certificate. A set the Job already issued with Ed25519 is kept, because an existing certificate is never reissued. To replace it, set rsa or ecdsa, delete the three certificate Secrets, then the GitLabSecrets, as for forcing fresh values above. SSH host keys still include Ed25519.