From b37a5dfdcc1a81f6c644a93f663590fe5de49e30 Mon Sep 17 00:00:00 2001 From: Michael Smithhisler Date: Tue, 18 Feb 2025 17:29:44 +0000 Subject: [PATCH] backport of commit ae21ae54a767f3f51486a50ce8bf4e44748c779a --- nomad/acl_endpoint.go | 10 +- website/content/api-docs/task-api.mdx | 4 +- .../docs/concepts/acl/auth-methods/jwt.mdx | 36 ++++++ .../docs/concepts/acl/auth-methods/oidc.mdx | 117 ++++++++++++++++++ .../docs/concepts/{acl.mdx => acl/index.mdx} | 19 +++ .../docs/concepts/architecture/federation.mdx | 10 +- .../partials/jwt_claim_mapping_details.mdx | 80 ++++++++++++ website/data/docs-nav-data.json | 20 ++- 8 files changed, 286 insertions(+), 10 deletions(-) create mode 100644 website/content/docs/concepts/acl/auth-methods/jwt.mdx create mode 100644 website/content/docs/concepts/acl/auth-methods/oidc.mdx rename website/content/docs/concepts/{acl.mdx => acl/index.mdx} (86%) create mode 100644 website/content/partials/jwt_claim_mapping_details.mdx diff --git a/nomad/acl_endpoint.go b/nomad/acl_endpoint.go index 782ed732562..2551421cc30 100644 --- a/nomad/acl_endpoint.go +++ b/nomad/acl_endpoint.go @@ -2792,9 +2792,15 @@ func (a *ACL) OIDCCompleteAuth( internalClaimBytes, err := json.MarshalIndent(oidcInternalClaims.List, "", " ") if err != nil { - vlog.Debug("failed to marshal OIDC internal claims list") + vlog.Debug("failed to marshal OIDC internal list claims") } - vlog.Debug("claims after mapping to nomad identity attributes", "internal_claims", string(internalClaimBytes)) + vlog.Debug("list claims after mapping to nomad identity attributes", "internal_claims", string(internalClaimBytes)) + + internalClaimBytes, err = json.MarshalIndent(oidcInternalClaims.Value, "", " ") + if err != nil { + vlog.Debug("failed to marshal OIDC internal value claims") + } + vlog.Debug("value claims after mapping to nomad identity attributes", "internal_claims", string(internalClaimBytes)) } // Create a new binder object based on the current state snapshot to diff --git a/website/content/api-docs/task-api.mdx b/website/content/api-docs/task-api.mdx index 8f2b43ca014..b4c89d7cbdf 100644 --- a/website/content/api-docs/task-api.mdx +++ b/website/content/api-docs/task-api.mdx @@ -97,8 +97,8 @@ $ nomad node status -filter 'Meta.example == "Hello World!"' - Using the Task API Unix Domain Socket on Windows [requires][windows] Windows build 17063 or later. -[acl]: /nomad/docs/concepts/acl -[acl-tokens]: /nomad/docs/concepts/acl#token +[acl]: /nomad/docs/concepts/acl/ +[acl-tokens]: /nomad/docs/concepts/acl/#token [alloc-exec]: /nomad/docs/commands/alloc/exec [anon]: /nomad/tutorials/access-control/access-control#acl-policies [bind_addr]: /nomad/docs/configuration diff --git a/website/content/docs/concepts/acl/auth-methods/jwt.mdx b/website/content/docs/concepts/acl/auth-methods/jwt.mdx new file mode 100644 index 00000000000..d154049e9b7 --- /dev/null +++ b/website/content/docs/concepts/acl/auth-methods/jwt.mdx @@ -0,0 +1,36 @@ +--- +layout: docs +page_title: JSON Web Token (JWT) Auth Method +description: >- + Use the JWT auth method to authenticate to Nomad with a JSON web token and receive an ACL token with privileges based on JWT identity attributes. Learn how to configure the auth method parameters using this reference page and example configuration. +--- + +# JSON Web Token (JWT) Auth Method + +Use the `jwt` auth method to authenticate with Nomad by providing a +[JWT](https://en.wikipedia.org/wiki/JSON_Web_Token) directly. The JWT is +cryptographically verified using locally-provided keys, or, if configured, you may use an +OIDC Discovery service to fetch the appropriate keys. + +Refer to [auth-method create] for the parameters required to create a JWT auth-method with a given verification method. + +## JWT Verification + +Nomad verifies JWT signatures against public keys from the issuer. This +process uses one of these methods: + +- **Static Keys** - A set of public keys is stored directly in the + configuration. + +- **JWKS** - Configure a JSON Web Key Set ([JWKS](https://tools.ietf.org/html/rfc7517)) + URL and optional certificate chain. Nomad fetches keys from + this endpoint during authentication. + +- **OIDC Discovery** - Configure an OIDC Discovery URL and optional certificate chain. Nomad fetches keys from this URL during authentication. When you use OIDC Discovery, Nomad applies OIDC validation criteria such as `iss` and `aud`. + +If you need multiple methods, create another auth method of this type +with a different name. + +@include 'jwt_claim_mapping_details.mdx' + +[auth-method create]: /nomad/docs/commands/acl/auth-method/create diff --git a/website/content/docs/concepts/acl/auth-methods/oidc.mdx b/website/content/docs/concepts/acl/auth-methods/oidc.mdx new file mode 100644 index 00000000000..6968abb087b --- /dev/null +++ b/website/content/docs/concepts/acl/auth-methods/oidc.mdx @@ -0,0 +1,117 @@ +--- +layout: docs +page_title: OpenID Connect (OIDC) Auth Method +description: >- + Use the OIDC auth method type to authenticate to Nomad through a web browser with an OpenID Connect provider. Learn how to configure the auth method parameters using this reference page and example configuration. +--- + +# OpenID Connect (OIDC) Auth Method + +Use the `oidc` auth method to authenticate to Nomad with +[OIDC](https://en.wikipedia.org/wiki/OpenID_Connect). This method allows +authentication via a configured OIDC provider using the user's web browser. +Initiate this method from the Nomad UI or the command line. + +## Prerequisites + +- General knowledge of [OIDC concepts](https://developer.okta.com/blog/2017/07/25/oidc-primer-part-1) +- [Nomad Access Control List fundamentals][ACL Overview]. + +Refer to [auth-method create] for the parameters required to create an OIDC auth-method. + +## JWT Verification + +Nomad uses OIDC discovery to verify JWT signatures against public +keys from the issuer. Nomad first fetches keys from the OIDC +Discovery URL during authentication and then applies OIDC +validation criteria such as `iss` and `aud`. + +## OIDC Authentication + +Nomad includes two built-in OIDC login flows: the Nomad UI, and the CLI using +[`nomad login`](/nomad/docs/commands/login). + +### Redirect URIs + +Properly setting redirect URIs is an important part of OIDC auth method +configuration. You must configure these in both Nomad and the OIDC +provider, and these configurations must align. + +Specify the redirect URIs for an auth method with the +`AllowedRedirectURIs` parameter in the auth method config. The Nomad UI +and CLI use different redirect URIs, so you need to configure one or both, +depending on your installation. + +**Note:** Redirect URI is used interchangeably with callback address. + +Logging in via the UI requires the redirect URI +`http://{host:port}/ui/settings/tokens`. + +Logging in via the CLI requires the redirect +URI `http://{host:port}/oidc/callback`. + +### OIDC Login + +#### Nomad UI + +1. Select one of the provider links in the Nomad homepage or navigate directly to `/ui/settings/tokens`. +1. Click one of the buttons for your OIDC auth method of choice. +1. Complete the authentication with the configured provider. + +#### CLI + +Execute the `nomad login -method=oidc` command to log in. +If the `-oidc-callback-addr` flag is not specified, it will default to `localhost:4649`. + +```shell-session +$ nomad login -method=oidc -oidc-callback-addr= + +Complete the login via your OIDC provider. Launching browser to: + + https://myco.auth0.com/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A4649%2Foidc%2Fcallback&client_id=r3qXc2bix9eF... +``` + +Your browser opens to the generated URL to complete the provider's login. +Enter the URL manually if the browser does not automatically open. + +## OIDC Configuration Troubleshooting + +The amount of configuration required for OIDC is relatively small, but it can +be tricky to debug why things aren't working. The following are tips for setting up OIDC: + +- Monitor the log output for the Nomad servers for important +information about OIDC validation failures. + +- Ensure correct redirect URIs in Nomad and on the provider. URIs +need to match exactly. Check http/https, 127.0.0.1/localhost, +port numbers, and whether trailing slashes are present. + +- The `BoundAudiences` option is typically + not required. OIDC providers use the `client_id` as the audience and + OIDC validation expects this. + +- Check your provider for scopes that are required to receive all of + the information you need. You often need to request the scopes + `profile` and `groups`, which you may set with + `OIDCScopes=["profile", "groups"]` in the auth method configuration. + +- If you're seeing claim-related errors in logs, review the provider's docs + very carefully to see how they're naming and structuring their claims. + Depending on the provider, you may be able to construct a simple `curl` + [implicit grant](https://developer.okta.com/blog/2018/05/24/what-is-the-oauth2-implicit-grant-type) + request to obtain a JWT that you can inspect. This example decodes the + JWT located in the `access_token` field of a JSON response. + + jq --raw-output '.access_token / "." | .[1] | @base64d' jwt.json + +- With debug level logging, use the [VerboseLogging] option in the auth + method configuration to log the received OIDC token. This can be helpful + when debugging provider setup and verifying that the received claims are + what you expect. Since claims data is logged verbatim and may contain + sensitive information, do not use this option in production. + +@include 'jwt_claim_mapping_details.mdx' + +[ACL Overview]: /nomad/docs/concepts/acl +[auth-method create]: /nomad/docs/commands/acl/auth-method/create +[VerboseLogging]: /nomad/api-docs/acl/auth-methods#verboselogging diff --git a/website/content/docs/concepts/acl.mdx b/website/content/docs/concepts/acl/index.mdx similarity index 86% rename from website/content/docs/concepts/acl.mdx rename to website/content/docs/concepts/acl/index.mdx index 6e8e2020058..2da93992bf3 100644 --- a/website/content/docs/concepts/acl.mdx +++ b/website/content/docs/concepts/acl/index.mdx @@ -63,6 +63,25 @@ Connect (OIDC)][oidc] SSO workflow which allows users to log in to Nomad via applications such as [Auth0][auth0], [Okta][okta], and [Vault][vault], and non-interactive login via externally-issued [JSON Web Tokens (JWT)][jwt]. +Since both the `oidc` and `jwt` auth methods ultimately operate on JWTs as +bearer tokens, use the following to determine which method fits your use case: + +- **JWT** + + - Ideal for machine-oriented, headless login where an operator may have already + arranged for a valid JWT to be dropped on a VM or provided to a container. + - User or application performing the Nomad login must have a valid JWT + to begin login. + - Does not require browser interaction. + +- **OIDC** + + - Ideal for human-oriented, interactive login where an operator or administrator + may have deployed SSO widely and doesn't want to distribute Nomad ACL tokens + to every authorized user. + - User performing the Nomad login does not need a JWT. + - Requires browser interaction. + ## Binding Rule Binding rules provide a mapping between a Nomad user's SSO authorisation claims diff --git a/website/content/docs/concepts/architecture/federation.mdx b/website/content/docs/concepts/architecture/federation.mdx index fe924074297..196087d46d1 100644 --- a/website/content/docs/concepts/architecture/federation.mdx +++ b/website/content/docs/concepts/architecture/federation.mdx @@ -57,11 +57,11 @@ the job on all the specified regions, removing the need for multiple job specification copies and registration on each region. Multiregion jobs do not provide regional failover in the event of failure. -[acl_policy]: /nomad/docs/concepts/acl#policy -[acl_role]: /nomad/docs/concepts/acl#role -[acl_auth_method]: /nomad/docs/concepts/acl#auth-method -[acl_binding_rule]: /nomad/docs/concepts/acl#binding-rule -[acl_token]: /nomad/docs/concepts/acl#token +[acl_policy]: /nomad/docs/concepts/acl/#policy +[acl_role]: /nomad/docs/concepts/acl/#role +[acl_auth_method]: /nomad/docs/concepts/acl/#auth-method +[acl_binding_rule]: /nomad/docs/concepts/acl/#binding-rule +[acl_token]: /nomad/docs/concepts/acl/#token [node_pool]: /nomad/docs/concepts/node-pools [namespace]: /nomad/docs/other-specifications/namespace [quota]: /nomad/docs/other-specifications/quota diff --git a/website/content/partials/jwt_claim_mapping_details.mdx b/website/content/partials/jwt_claim_mapping_details.mdx new file mode 100644 index 00000000000..674428c2a00 --- /dev/null +++ b/website/content/partials/jwt_claim_mapping_details.mdx @@ -0,0 +1,80 @@ +## Trusted Identity Attributes via Claim Mappings + +The authentication step can return data from JWT claims as trusted +identity attributes for use in binding rule selectors and bind name +interpolation. + +The `ClaimMappings` and `ListClaimMappings` attributes control how Nomad maps claims +to identity attributes. Both are maps of items to copy, +with elements of the form `"":""`. + +Use `ClaimMappings` to map singular values and `ListClaimMappings` to map lists of values. + +This examples contains `ClaimMappings` and `ListClaimMappings`. The configuration +instructs Nomad to copy the values in the JWT claims `"givenName"` and `"surname"` +to attributes named `"value.first_name"` and `"value.last_name"` respectively. +Additionally, Nomad should copy the list of values in the JWT +claim `"groups"` to an attribute named `"list.roles"`. + +```json +{ + "Name": "example-auth-method", + "Type": "", + "Description": "Example auth method", + "Config": { + "ClaimMappings": { + "givenName": "first_name", + "surname": "last_name" + }, + "ListClaimMappings": { + "groups": "roles" + } + } +} +``` + +The following table shows the resulting attributes and +the ways they may be used in rule bindings: + +| Attributes | Supported selector operations | Can be interpolated | +| ------------------ | -------------------------------------------------- | ------------------- | +| `value.first_name` | Equal, Not Equal, In, Not In, Matches, Not Matches | yes | +| `value.last_name` | Equal, Not Equal, In, Not In, Matches, Not Matches | yes | +| `list.groups` | In, Not In, Is Empty, Is Not Empty | no | + +Refer to the [binding-rule] documentation for more examples on using selectors. + +### Claim Specifications and JSON Pointer + +Use the `ClaimMappings` and `ListClaimMappings` fields to point to data +within the JWT. If the desired key is at the top of level of the JWT, you may +provide the name directly. If it is nested at a lower level, you may use a JSON +Pointer. + +This example shows decoded JWT claims. + +```json +{ + "division": "North America", + "groups": { + "primary": "Engineering", + "secondary": "Software" + }, + "iss": "https://my-corp-app-name.auth0.com/", + "sub": "auth0|eiw7OWoh5ieSh7ieyahC3ief0uyuraphaengae9d", + "aud": "V1RPi2MYptMV1RPi2MYptMV1RPi2MYpt", + "iat": 1589224148, + "exp": 1589260148, + "nonce": "eKiihooH3Fah8Ieshah4leeti6ien3" +} +``` + +Use the following syntax to reference data: + +- Top-level key: Use direct reference. For example, `"division"` refers to `"North America"`. +- Nested key: Use JSON Pointer syntax. For example, `"/groups/primary"` refers to `"Engineering"`. + +You may use any valid JSON Pointer as a selector. Refer to the [JSON Pointer +RFC](https://tools.ietf.org/html/rfc6901) for a full description of the syntax. + +[binding-rule]: /nomad/docs/commands/acl/binding-rule/create#examples diff --git a/website/data/docs-nav-data.json b/website/data/docs-nav-data.json index 31b86770957..51529ae7df4 100644 --- a/website/data/docs-nav-data.json +++ b/website/data/docs-nav-data.json @@ -127,7 +127,25 @@ }, { "title": "ACL", - "path": "concepts/acl" + "routes": [ + { + "title": "Overview", + "path": "concepts/acl" + }, + { + "title": "Auth Methods", + "routes": [ + { + "title": "JWT", + "path": "concepts/acl/auth-methods/jwt" + }, + { + "title": "OIDC", + "path": "concepts/acl/auth-methods/oidc" + } + ] + } + ] }, { "title": "Architecture",