- Notifications
You must be signed in to change notification settings - Fork4
Kubernetes Operator for automated registration and configuration of Digdir clients (ID-porten & Maskinporten)
License
nais/digdirator
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Digdirator is a Kubernetes operator for declarative registration and lifecycle management ofvarious resources withinDigdir throughitsself-service API.It currently supports:
The operator uses two custom resource definitions (CRDs):
apiVersion:nais.io/v1kind:IDPortenClientmetadata:name:my-appnamespace:my-teamspec:clientURI:"https://domain.example"frontchannelLogoutURI:"https://domain.example/oauth2/logout/frontchannel"redirectURIs: -"https://domain.example/oauth2/callback"postLogoutRedirectURIs: -"https://domain.example/oauth2/logout/callback"secretName:my-secret
For the full CRD specification with all possible options, seenais/liberator/config/crd/nais.io_idportenclients.yaml
AnIDPortenClient
resource contains:
- the client configuration
- the name of the Kubernetes secret that the application expects to contain the client's credentials
The Kubernetes secret contains the following keys:
Key | Description |
---|---|
IDPORTEN_CLIENT_ID | The application's client ID. |
IDPORTEN_CLIENT_JWK | The application's private JSON Web Key (JWK) for client authentication (RFC 7523, section 2.2). |
IDPORTEN_WELL_KNOWN_URL | The URL pointing to the ID-porten's well-known metadata document. |
IDPORTEN_ISSUER | Theissuer property from the metadata document. |
IDPORTEN_JWKS_URI | Thejwks_uri property from the metadata document. |
IDPORTEN_TOKEN_ENDPOINT | Thetoken_endpoint property from the metadata document. |
---apiVersion:nais.io/v1kind:MaskinportenClientmetadata:name:my-appnamespace:my-teamspec:secretName:my-secretscopes:exposes:# results in the fully qualified scope name:# `prefix:product:some/scope` -product:"product"name:"some/scope"enabled:trueconsumers: -orgno:"889640782"consumes: -name:"prefix:some/api.read"
For the full CRD specification with all possible options, seeconfig/crd/nais.io_maskinportenclients.yaml
AMaskinportenClient
resource contains:
- a list of scopes the applicationconsumes or needs access to (optional)
- a list of scopes the applicationexposes (optional)
- note: this will be extracted to its own CRD at some point
- the name of the Kubernetes secret that the application expects to contain the client's credentials
The Kubernetes secret contains the following keys:
Key | Description |
---|---|
MASKINPORTEN_CLIENT_ID | The application's client ID. |
MASKINPORTEN_CLIENT_JWK | The application's private JSON Web Key (JWK) for client authentication (RFC 7523, section 2.2). |
MASKINPORTEN_WELL_KNOWN_URL | The URL pointing to Maskinporten's well-known metadata document. |
MASKINPORTEN_ISSUER | Theissuer property from the metadata document. |
MASKINPORTEN_JWKS_URI | Thejwks_uri property from the metadata document. |
MASKINPORTEN_TOKEN_ENDPOINT | Thetoken_endpoint property from the metadata document. |
sequenceDiagram actor developer as Developer or<br/>Operator box rgba(33,66,99,0.5) Kubernetes Cluster participant crd as IDPortenClient or<br/>MaskinportenClient participant secrets as Secrets participant digdirator as Digdirator end participant kms as Google Cloud KMS participant digdir as Digdir Admin API loop forever digdirator ->> crd: watches end developer ->> crd: apply resource digdirator ->> crd: get resource crd ->> secrets: owns digdirator ->> secrets: get all owned by resource alt `spec.secretName` has changed digdirator ->> digdirator: create new private key else `spec.secretName` is unchanged digdirator ->> digdirator: re-use private key from existing secret end loop for each API request digdirator ->> digdirator: create client assertion digdirator ->> kms: sign client assertion end digdirator ->> digdir: create/update client digdirator ->> digdir: create/update client jwks alt if exposes maskinporten scopes digdirator ->> digdir: create/update scopes digdirator ->> digdir: add/remove consumers end digdirator ->> secrets: create/update loop for each owned secret alt name equals `spec.secretName` digdirator ->> secrets: keep secret else digdirator ->> secrets: delete if<br/>unreferenced end end
- When an
IDPortenClient
or aMaskinportenClient
resource is applied to the cluster (either by a developer or another operator), the Digdirator controller will reconcile it. - Digdirator reads the resource and retrieves all existing secrets owned by the resource.
- Digdirator then checks if the
spec.secretName
has changed:- If the secret name has changed, it creates a new private key for the application.
- If the secret name is unchanged, it reuses the private key from the existing secret.
- For each request to Digdir's admin API, Digdirator creates a client assertion for authentication.
- The application's configuration and public keys (JWKS) are registered/updated through the API.
- The JWKS contains all currently used public keys to ensure key rotation works properly.
- If the
MaskinportenClient
resource exposes Maskinporten scopes, these are also registered/updated. Consumers are added/removed as needed.
- The operator creates or updates the Kubernetes secret with the specified
spec.secretName
. - Finally, any unreferenced secrets are deleted to clean up resources.
- Secrets are considered referenced if mounted as files or environment variables in a
Pod
.ThePod
must have a labelapp=<name>
where<name>
is equal to.metadata.name
in theIDPortenClient
orMaskinportenClient
resource.
- Secrets are considered referenced if mounted as files or environment variables in a
make install
See the documentation over at Digdir for acquiring clients with the required scopes to access the self-service APIs:
- https://docs.digdir.no/docs/idporten/oidc/oidc_api_admin_maskinporten#hvordan-f%C3%A5-tilgang-
- https://docs.digdir.no/docs/idporten/oidc/oidc_api_admin#hvordan-f%C3%A5-tilgang-
Digdirator uses a single privileged client for administration of ID-porten and Maskinporten clients.It authenticates itself with the DigDir self-service APIs by using a JWT grant signed with the configured business certificate.
Digdirator currently depends on a Google Cloud Platform product, namely Cloud Key Management Service (KMS).The KMS is used to store the private key belonging to the business certificate.These are needed for authenticating the DigDir client with Maskinporten using the JWT-bearer authorization grant.
You should set upWorkload Identity for your GKE cluster.
Digdirator needs a Google IAM Service Account to access the GCP resources.With Workload Identity, this should work automagically as we use Google's libraries that should automatically pick up the Kubernetes Service Account tokens and perform the necessary exchanges.
The Google Service Account needs the following IAM role for eachkey in Cloud KMS:
- Cloud KMS CryptoKey Signer/Verifier (
roles/cloudkms.signerVerifier
)
FollowGoogle's documentation for importing keys.
The private key should be imported with the purpose set toASYMMETRIC_SIGN
, and the algorithm set to one of theRSASSA-PKCS1 v1_5
variants.
Digdirator can be configured using command-line flags:
Flag | Type | Default Value | Description |
---|---|---|---|
--cluster-name | string | The cluster in which this application should run. | |
--development-mode | string | false | Toggle for development mode. |
--digdir.admin.base-url | string | Base URL endpoint for interacting with DigDir self service API. | |
--digdir.admin.cert-chain | string | Full certificate chain in PEM format for business certificate used to sign JWT assertion. | |
--digdir.admin.client-id | string | Client ID / issuer for JWT assertion when authenticating with DigDir self service API. | |
--digdir.admin.kms-key-path | string | Resource path to Google KMS key used to sign JWT assertion. | |
--digdir.admin.scopes | string | idporten:dcr.write idporten:dcr.read idporten:scopes.write | List of space-separated scopes for JWT assertion when authenticating with DigDir self service API. |
--digdir.common.access-token-lifetime | int | 3600 | Default lifetime (in seconds) for access tokens for all clients. |
--digdir.common.client-name | string | ARBEIDS- OG VELFERDSETATEN | Default name for all provisioned clients. Appears in the login prompt for ID-porten. |
--digdir.common.client-uri | string | https://www.nav.no | Default client URI for all provisioned clients. Appears in the back-button for the login prompt for ID-porten. |
--digdir.common.session-lifetime | int | 7200 | Default lifetime (in seconds) for sessions (authorization and refresh token lifetime) for all clients. |
--digdir.idporten.well-known-url | string | URL toID-porten well-known discovery metadata document. | |
--digdir.maskinporten.default.client-scope | string | nav:test/api | Default scope for provisioned Maskinporten clients, if none specified in spec. |
--digdir.maskinporten.default.scope-prefix | string | nav | Default scope prefix for provisioned Maskinporten scopes. |
--digdir.maskinporten.well-known-url | string | URL toMaskinporten well-known discovery metadata document. | |
--features.maskinporten | boolean | false | Feature toggle for maskinporten. |
--leader-election.enabled | boolean | false | Toggle for enabling leader election. |
--leader-election.namespace | string | Namespace for the leader election resource. Needed if not running in-cluster (e.g. locally). | |
--metrics-address | string | :8080 | The address the metric endpoint binds to. |
At minimum, the following configuration must be provided:
cluster-name
digdir.admin.base-url
digdir.admin.cert-chain
digdir.admin.client-id
digdir.admin.kms-key-path
digdir.admin.scopes
digdir.idporten.well-known-url
digdir.maskinporten.well-known-url
The properties can also be set using environment variables using the following convention:
- convert to uppercase
- replace dashes (
-
) and dots (.
) with underscores (_
) - prefix the property with
DIGDIRATOR_
For example:
digdir.admin.base-url -> DIGDIRATOR_ADMIN_BASE_URL
Properties can also be specified using JSON, TOML or YAML config files.Digdirator looks for a file nameddigdirator.<ext>
in the directories [.
,/etc/
].
Example configuration in YAML:
# ./digdirator.yamlcluster-name:localdevelopment-mode:truefeatures:maskinporten:truedigdir:admin:base-url:"https://api.test.samarbeid.digdir.no"client-id:"some-client-id"cert-chain:|- -----BEGIN CERTIFICATE----- MII... -----END CERTIFICATE-----kms-key-path:"projects/<project-id>/locations/<location>/keyRings/<key-ring-name>/cryptoKeys/<key-name>/cryptoKeyVersions/<key-version>"scopes:"idporten:dcr.write idporten:dcr.read idporten:scopes.write"idporten:well-known-url:"https://test.idporten.no/idporten-oidc-provider/.well-known/openid-configuration"maskinporten:well-known-url:"https://test.maskinporten.no/.well-known/oauth-authorization-server"
If you're running locally, make sure you have access to the GCP resources and that you're authenticated with Application Default Credentials:
gcloud auth login --update-adc
Then, assuming you have a Kubernetes cluster running locally (e.g.usingminikube):
ulimit -n 4096# for controller-genmake runmake sample
The image is signed "keylessly" (is that a word?) usingSigstore cosign.To verify its authenticity run
cosign verify \--certificate-identity "https://github.com/nais/digdirator/.github/workflows/build.yml@refs/heads/master" \--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ghcr.io/nais/digdirator@sha256:<shasum>
The images are also attested with SBOMs in theCycloneDX format.You can verify these by running
cosign verify-attestation --type cyclonedx \--certificate-identity "https://github.com/nais/digdirator/.github/workflows/build.yml@refs/heads/master" \--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \ghcr.io/nais/digdirator@sha256:<shasum>
About
Kubernetes Operator for automated registration and configuration of Digdir clients (ID-porten & Maskinporten)
Topics
Resources
License
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Releases
Packages0
Uh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
Contributors14
Uh oh!
There was an error while loading.Please reload this page.