GitLab secrets

Status: Proposed R9 and R11 to R14, and the FAQ answers that rely on a GitLabCore, arrive with the GitLabCore integration. 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 always renders with provider: controller. It applies nothing else until its GitLabSecrets is Ready, says so in a condition, and reaches Available in any watched namespace with no ServiceAccount or RBAC provisioned beforehand.
  • A GitLabCore is refused before anything is applied, with a condition that names the value, when spec.chart.values sets another shared-secrets.provider, shared-secrets.selfsign.keyAlgorithm: ed25519, or an odd shared-secrets.selfsign.keySize with rsa. It is also refused, with a condition, when its chart renders no GitLabSecrets with shared secrets on.
  • With shared-secrets.enabled: false, the administrator supplies every Secret, and the GitLabCore runs without a GitLabSecrets.
  • Secrets the Job already created, for example for an earlier GitLabCore on an older chart or a GitLab installed with Helm, are adopted with every existing key and field value byte for byte.

Out of scope

  • The CNG binary (chart issue 6658), a shared library, rotating values, certificate renewal, the v1beta1 GitLab resource, and FIPS.

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.
    • The certificate set 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 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.
  • 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. Back those Secrets up.
  • Why is provider: job refused? The Job needs RBAC created for it, and the Operator creates no RBAC. A GitLabCore therefore needs the first chart release with chart merge requests 5277, 5420, and 5433, expected to be 10.5.0. For more information, see ADR 31.
  • 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.