Skip to content

Commit

Permalink
Add audience claim check (#363) (#397)
Browse files Browse the repository at this point in the history
  • Loading branch information
maia-iyer authored Apr 6, 2024
1 parent f762363 commit 88d6718
Show file tree
Hide file tree
Showing 16 changed files with 4,723 additions and 3,899 deletions.
7 changes: 6 additions & 1 deletion api/agent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -776,8 +776,13 @@ func NewAuth(authPlugin *ast.ObjectItem) (auth.Auth, error) {
return nil, errors.Errorf("Couldn't parse Auth config: %v", err)
}

// Log warning if audience is nil that aud claim is not checked
if config.Audience == "" {
fmt.Printf("WARNING: Auth plugin has no expected audience configured - `aud` claim will not be checked (please populate 'config > plugins > UserManagement KeycloakAuth > plugin_data > audience')")
}

// create verifier TODO make json an option?
verifier, err := auth.NewKeycloakVerifier(true, config.IssuerURL)
verifier, err := auth.NewKeycloakVerifier(true, config.IssuerURL, config.Audience)
if err != nil {
return nil, errors.Errorf("Couldn't configure Auth: %v", err)
}
Expand Down
1 change: 1 addition & 0 deletions api/agent/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,4 +110,5 @@ type pluginDataStoreSQL struct {

type pluginAuthKeycloak struct {
IssuerURL string `hcl:"issuer"`
Audience string `hcl:"audience"`
}
4 changes: 4 additions & 0 deletions docs/conf/agent/full.conf
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ plugins {
# for cloud deployment it would be something like:
# issuer = "http://<ingress_access>/realms/tornjak"

# audience - expected value for aud claim in JWT
# if not included or set, there will be no audience check
# recommended to ensure JWT was meant for Tornjak Backend resource server
audience = "tornjak-backend"
}
}

Expand Down
2 changes: 1 addition & 1 deletion docs/config-tornjak-server.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ For examples on enabling TLS and mTLS connections, please see [our TLS and mTLS
| Type | Name | Description |
| ---- | ---- | ----------- |
| DataStore | [sql]() | Default SQL storage for Tornjak metadata |
| UserManagement | [keycloak](/docs/plugin_server_auth_keycloak.md) | Requires JWT Bearer Access Token provided for each request. More details in [our auth feature doc](/docs/feature_auth.md) |
| UserManagement | [keycloak](/docs/plugin_server_auth_keycloak.md) | Requires JWT Bearer Access Token provided for each request. More details in [our auth feature doc](/docs/user-management.md) |

### Plugin configuration

Expand Down
56 changes: 0 additions & 56 deletions docs/feature_auth.md

This file was deleted.

13 changes: 9 additions & 4 deletions docs/plugin_server_auth_keycloak.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
# Server plugin: Authorization "keycloak"

Please see our documentation on the [authorization feature](./feature_auth.md) for more complete details.
Please see our documentation on the [authorization feature](./user-management.md) for more complete details.

Note that configuring this requires the frontend to be configured to obtain access tokens at the relevant auth server.

The configuration has the following key-value pairs:

| Key | Description | Required |
| ----------- | ----------------------------------------------------------------------- | -------- |
| issuer | Issuer URL for OIDC Discovery with external IAM System | True |
| Key | Description | Required |
| ----------- | ----------------------------------------------------------------------- | ------------------- |
| issuer | Issuer URL for OIDC Discovery with external IAM System | True |
| audience | Expected audience value in received JWT tokens | False (Recommended) |

A sample configuration file for syntactic referense is below:

```hcl
UserManagement "KeycloakAuth" {
plugin_data {
issuer = "http://localhost:8080/realms/tornjak"
audience = "tornjak-backend"
}
}
```

NOTE: If audience field is missing or empty, the server will log an error and NOT perform an audience check.
It is highly recommended `audience` is populated to ensure only tokens meant for the Tornjak Backend are accepted.
17 changes: 15 additions & 2 deletions docs/user-management.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,25 @@ Specifically, it is very easy to set up a Tornjak secured by Keycloak today, by
1. **Configure Auth Server**
To configure the Auth Server, please see our [Medium blog](https://medium.com/universal-workload-identity/step-by-step-guide-to-setup-keycloak-configuration-for-tornjak-dbe5c3049034) for a walkthrough on configuring the Auth Server. For more in-depth documentation on this setup, please see [this document on Keycloak configuration](./keycloak-configuration.md).

Alternatively, see [our keycloak example](/examples/keycloak) for ready deployment files into your local Kubernetes cluster.

1. **Enable User Management for Tornjak Backend**
Once the Auth Server is set up, we can deploy the Tornjak Backend to require access tokens from our Auth Server,
as detailed in
[this followup Medium blog](https://medium.com/universal-workload-identity/guide-to-integrating-tornjak-with-keycloak-for-access-control-to-spire-40a3d5ee5f5a),
with more details on the general configuration
[here](https://github.com/spiffe/tornjak/blob/main/docs/config-tornjak-agent.md).
[here](/docs/config-tornjak-server.md). Most notably, populate a new plugin section for keycloak as defined [here](/docs/plugin_server_auth_keycloak.md) like so:
```
...
UserManagement "KeycloakAuth" {
plugin_data {
# issuer - Issuer URL for OIDC
issuer = "http://host.docker.internal:8080/realms/tornjak"
audience = "tornjak-backend"
}
}
...
```

1. **Configure Tornjak Frontend**
Finally, the Frontend must be deployed and configured to obtain access tokens from this auth server.
Expand All @@ -57,7 +70,7 @@ REACT_APP_AUTH_SERVER_URI=http://localhost:8080/ npm start
Alternatively, we can do the same on the containerized version:

```
docker run -p 3000:3000 -d -e REACT_APP_API_SERVER_URI='http://localhost:10000' -e REACT_APP_AUTH_SERVER_URI='http://localhost:8080' ghcr.io/spiffe/tornjak-frontend:v1.2.2
docker run -p 3000:3000 -d -e REACT_APP_API_SERVER_URI='http://localhost:10000' -e REACT_APP_AUTH_SERVER_URI='http://localhost:8080' ghcr.io/spiffe/tornjak-frontend:v1.5.0
`
```

Expand Down
4 changes: 4 additions & 0 deletions examples/keycloak/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,7 @@ kubectl port-forward service/keycloak-service 8080:8080

Then access Keycloak via browser on [http://localhost:8080](http://localhost:8080)
and open the *Administration Console*

The credentials in this example have username and password both `admin`. You may configure this in `statefulset.yaml`

The Tornjak Realm has two users: `admin` and `viewer`.
160 changes: 69 additions & 91 deletions examples/keycloak/client/tornjak-keycloak-client-import.json
Original file line number Diff line number Diff line change
@@ -1,93 +1,71 @@
{

"clientId": "Tornjak-React-auth",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": true,
"clientAuthenticatorType": "client-secret",
"secret": "**********",
"redirectUris": [
"*"
],
"webOrigins": ["*"],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": true,
"authorizationServicesEnabled": false,
"publicClient": true,
"frontchannelLogout": true,
"clientId": "Tornjak-React-auth",
"name": "Tornjak",
"description": "",
"rootUrl": "",
"adminUrl": "",
"baseUrl": "",
"surrogateAuthRequired": false,
"enabled": true,
"alwaysDisplayInConsole": true,
"clientAuthenticatorType": "client-secret",
"redirectUris": [
"http://localhost:3000/*"
],
"webOrigins": [
"*"
],
"notBefore": 0,
"bearerOnly": false,
"consentRequired": false,
"standardFlowEnabled": true,
"implicitFlowEnabled": false,
"directAccessGrantsEnabled": false,
"serviceAccountsEnabled": false,
"publicClient": true,
"frontchannelLogout": true,
"protocol": "openid-connect",
"attributes": {
"oidc.ciba.grant.enabled": "false",
"backchannel.logout.session.required": "true",
"post.logout.redirect.uris": "http://localhost:3000/*",
"display.on.consent.screen": "false",
"oauth2.device.authorization.grant.enabled": "false",
"backchannel.logout.revoke.offline.tokens": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": true,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{
"name": "tornjak-backend-aud",
"protocol": "openid-connect",
"attributes": {
"saml.assertion.signature": "false",
"saml.force.post.binding": "false",
"saml.multivalued.roles": "false",
"saml.encrypt": "false",
"oauth2.device.authorization.grant.enabled": "false",
"backchannel.logout.revoke.offline.tokens": "false",
"saml.server.signature": "false",
"saml.server.signature.keyinfo.ext": "false",
"use.refresh.tokens": "true",
"exclude.session.state.from.auth.response": "false",
"oidc.ciba.grant.enabled": "false",
"saml.artifact.binding": "false",
"backchannel.logout.session.required": "true",
"client_credentials.use_refresh_token": "true",
"saml_force_name_id_format": "false",
"saml.client.signature": "false",
"tls.client.certificate.bound.access.tokens": "false",
"saml.authnstatement": "false",
"display.on.consent.screen": "false",
"saml.onetimeuse.condition": "false"
},
"authenticationFlowBindingOverrides": {},
"fullScopeAllowed": false,
"nodeReRegistrationTimeout": -1,
"protocolMappers": [
{

"name": "Client IP Address",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientAddress",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientAddress",
"jsonType.label": "String"
}
},
{

"name": "Tornjak",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientId",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientId",
"jsonType.label": "String"
}
},
{

"name": "Client Host",
"protocol": "openid-connect",
"protocolMapper": "oidc-usersessionmodel-note-mapper",
"consentRequired": false,
"config": {
"user.session.note": "clientHost",
"id.token.claim": "true",
"access.token.claim": "true",
"claim.name": "clientHost",
"jsonType.label": "String"
}
}
]
}
"protocolMapper": "oidc-audience-mapper",
"consentRequired": false,
"config": {
"id.token.claim": "false",
"access.token.claim": "true",
"included.custom.audience": "tornjak-backend",
"userinfo.token.claim": "false"
}
}
],
"defaultClientScopes": [
"web-origins",
"acr",
"profile",
"roles",
"email"
],
"optionalClientScopes": [
"address",
"phone",
"offline_access",
"microprofile-jwt"
],
"access": {
"view": true,
"configure": true,
"manage": true
}
}
Loading

0 comments on commit 88d6718

Please sign in to comment.