Sharing secrets between tenants
This How-to assumes you know how to create vaults and secrets.
When a tenant is sharing a service with other tenants and the service requires
client authentication, secrets must be shared. This can be achieved in two
steps, the first step is to give access to a vault to another tenant using
allow-tenant-access.
The second step is that the application that wants to access this vault needs
to specify which tenant that has the secret that one wants to access using
from-tenant
in the vault-secret or the value-from-secret section.
Here is an example: assuming platform-team will share a service with
application-team-a and application-team-b.
Expose the secret to a list of named tenants:
name: credentials
data:
password: reallysecret
user: admin
allow-application-access:
- "*"
allow-tenant-access:
- application-team-a
- application-team-b
EOF
The corresponding payload for a certificate secret:
name: cert
auto-cert:
issuing-ca: my-app-ca
ttl: 30d
host: my-app
cert-type: server
allow-application-access:
- "*"
allow-tenant-access:
- application-team-a
- application-team-b
To share with all tenants:
allow-tenant-access:
- "*"
Now the application teams can mount the secret into their applications as usual, but must specify the tenant providing the secret.
name: app-team-as-amazing-app
services:
- name: the-service
volumes:
- name: cert
vault-secret:
vault: my-app
secret: cert
from-tenant: platform-team
variables:
- name: MY_USER
value-from-vault-secret:
vault: my-app
secret: credentials
key: user
from-tenant: platform-team
Granting configuration access to a secret
A secret can be read by configuration objects, not only by applications:
webhooks and OTel exporters read secrets through their variables, remote
registries through their vault and secret credentials, and ACME external
account binding keys through hmac-key-from-vault. Which configuration may
read a secret, and from where, is decided by the secret's
allow-config-access list. Each entry names a REST path starting with
/v1/config/. As in API policies, * matches one path component, pre* a
component that starts with pre, and ** as the last component the rest of
the path. A path that matches no configuration, for example a misspelled one,
is refused. An entry grants the tenant owning the secret; tenants extends it
to other tenants, or to all of them with *.
The paths are:
| Configuration | Path |
|---|---|
| Webhook variable | /v1/config/volga/webhooks/<name>/variables/<variable> |
| OTel metric exporter variable | /v1/config/volga/otel-metric-exporters/<name>/variables/<variable> |
| OTel log exporter variable | /v1/config/volga/otel-log-exporters/<name>/variables/<variable> |
| Remote registry credentials | /v1/config/image-registry/remote-registries/<address>/credentials/<repository> |
| ACME external account binding key | /v1/config/strongbox/tls/ca/<ca>/acme/eab-keys/<kid>/hmac-key |
name: api-token
data:
value: 2c0a…
allow-config-access:
- path: /v1/config/volga/webhooks/*/variables/TOKEN
tenants:
- application-team-a
allow-config-access is separate from allow-tenant-access, which still
governs cross-tenant application variables and the from-tenant of exporter
variables; granting one does not grant the other. The owning tenant is granted
by an entry's path alone, but only for the paths listed.
In this release a webhook or exporter variable, registry credential or ACME
key that reads a secret without a matching allow-config-access entry still
works, but raises the alert vault-config-access-missing on the reading
tenant and, when the secret belongs to another tenant, on the owning tenant
too. The alert carries the
vault, the secret and the exact path to add, and clears once the read finds
a matching entry. A coming release refuses such reads, so add the entries
while the alert is only a warning.
A merge on allow-config-access keeps entries not mentioned, since the list
is keyed by path. To revoke a grant, replace the secret with the entries
that should remain.