Skip to content

Commit 3f78057

Browse files
authored
Fully adopt the upstream OIDC module and serve the SSO sandbox over TLS (#1153)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent aaed110 commit 3f78057

64 files changed

Lines changed: 613 additions & 202 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DEPENDENCIES

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
vendorpull https://github.com/sourcemeta/vendorpull 1dcbac42809cf87cb5b045106b863e17ad84ba02
22
uwebsockets https://github.com/uNetworking/uWebSockets v20.79.0
3-
core https://github.com/sourcemeta/core 42d50f3c1e2d63c456fbdf37d18854edab39b5a0
3+
core https://github.com/sourcemeta/core 99b6a5e8df44d5cdd78a364eac4a059315884f3d
44
blaze https://github.com/sourcemeta/blaze ca1949507ea5f4215f9a55ca796cd074602ff705
55
jsonbinpack https://github.com/sourcemeta/jsonbinpack f775b2df5fa89d5a70acb940a5c938173811fea7
66
jsonschema https://github.com/sourcemeta/jsonschema v16.3.0

docs/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ same response as any other unauthenticated request.
474474
| `/issuer` | String | :red_circle: **Yes** | N/A | The token issuer to trust, matched against the `iss` claim |
475475
| `/audience` | String | :red_circle: **Yes** | N/A | The audience this instance identifies as. A token is accepted when its `aud` claim includes this value, so a token minted for several audiences at once is accepted as long as this one is among them |
476476
| `/algorithms` | Array | :red_circle: **Yes** | N/A | The JSON Web Signature algorithms the policy accepts. One or more of `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `ES256`, `ES384`, `ES512`, and `EdDSA` |
477-
| `/jwksUri` | String | No | Discovered from the issuer | The URL of the issuer's JSON Web Key Set. When omitted, it is discovered from the issuer's OpenID Connect metadata at `{issuer}/.well-known/openid-configuration` |
477+
| `/jwksUri` | String | No | Discovered from the issuer | The URL of the issuer's JSON Web Key Set. When omitted, it is discovered from the issuer's OpenID Connect metadata at `{issuer}/.well-known/openid-configuration`, which requires the issuer to be an `https` URL that publishes a valid OpenID Provider metadata document. Set it explicitly for an issuer that does not meet that bar |
478478

479479
For example, the following instance keeps `/docs` public, gates `/partners`
480480
behind an API key, and protects `/internal` with a JWT policy that trusts a
@@ -535,7 +535,7 @@ follows is signed with a secret of the instance's own, unrelated to the provider
535535
| Property | Type | Required | Default | Description |
536536
|-----------------|------|----------|---------|-------------|
537537
| `/title` | String | No | The policy name | A human readable version of the policy name |
538-
| `/issuer` | String | :red_circle: **Yes** | N/A | The OpenID Connect issuer to trust, matched against the identity token's `iss` claim and used to discover the provider's metadata, including the signing key set that verifies tokens |
538+
| `/issuer` | String | :red_circle: **Yes** | N/A | The OpenID Connect issuer to trust, matched against the identity token's `iss` claim and used to discover the provider's metadata, including the signing key set that verifies tokens. It must be an `https` URL, as OpenID Connect Discovery requires. Front a provider that only speaks plain HTTP with TLS termination and trust its certificate authority |
539539
| `/clientId` | String | :red_circle: **Yes** | N/A | The client identifier registered with the provider for this instance |
540540
| `/clientSecret` | Object | :red_circle: **Yes** | N/A | The client secret shared with the provider, read from an environment variable so that it never lives in the configuration file |
541541
| `/clientSecret/environmentVariable` | String | :red_circle: **Yes** | N/A | The name of the environment variable that holds the client secret |

enterprise/Dockerfile

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,16 @@ RUN test -f /usr/lib/*/ossl-modules/fips.so
128128
# Remove packages not needed at runtime to reduce the image attack surface.
129129
# We keep debconf and perl-base because ca-certificates depends on debconf,
130130
# which in turn requires perl-base. Purging either removes ca-certificates and
131-
# wipes the CA bundle that cURL needs to verify TLS certificates
131+
# wipes the CA bundle that cURL needs to verify TLS certificates. We also keep
132+
# sed because update-ca-certificates needs it, so images that extend this one
133+
# can install custom certificate authorities
132134
RUN cp /bin/mount /bin/umount /tmp/ \
133135
&& apt-get --yes update \
134136
&& apt-get --yes purge --allow-remove-essential \
135137
adduser \
136138
e2fsprogs gzip hostname init-system-helpers \
137139
login logsave mount ncurses-base ncurses-bin passwd \
138-
sed sysvinit-utils tzdata util-linux util-linux-extra \
140+
sysvinit-utils tzdata util-linux util-linux-extra \
139141
&& apt-get --yes autoremove --allow-remove-essential \
140142
&& rm -rf /var/lib/apt/lists/* \
141143
&& mv /tmp/mount /tmp/umount /bin/

enterprise/authentication/authentication.cc

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
#include <sourcemeta/core/io.h>
66
#include <sourcemeta/core/jose.h>
77
#include <sourcemeta/core/json.h>
8+
#include <sourcemeta/core/oidc.h>
89

910
#include "authentication_format.h"
10-
#include "authentication_oidc.h"
1111

1212
#include <algorithm> // std::ranges::all_of
1313
#include <bit> // std::countr_zero
@@ -794,18 +794,28 @@ struct Authentication::Impl {
794794

795795
std::string location;
796796
if (jwks_uri.empty()) {
797-
const auto metadata{this->fetcher_(oidc_discovery_url(issuer))};
797+
const auto url{sourcemeta::core::oidc_discovery_url(issuer)};
798+
if (!url.has_value()) {
799+
return nullptr;
800+
}
801+
802+
const auto metadata{this->fetcher_(url.value())};
798803
if (!metadata.has_value()) {
799804
return nullptr;
800805
}
801806

802-
auto document{
803-
oidc_parse_provider_metadata(metadata.value().body, issuer)};
804-
if (!document.has_value() || !document.value().jwks_uri.has_value()) {
807+
auto parsed{sourcemeta::core::try_parse_json(metadata.value().body)};
808+
if (!parsed.has_value()) {
809+
return nullptr;
810+
}
811+
812+
const auto document{sourcemeta::core::OIDCProviderMetadata::from(
813+
std::move(parsed).value(), issuer)};
814+
if (!document.has_value()) {
805815
return nullptr;
806816
}
807817

808-
location = std::move(document.value().jwks_uri).value();
818+
location = document.value().jwks_uri();
809819
} else {
810820
location = jwks_uri;
811821
}

enterprise/authentication/authentication_oidc.h

Lines changed: 0 additions & 89 deletions
This file was deleted.

enterprise/e2e/auth-closed/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
FROM one
2+
COPY tls/ca.crt /usr/local/share/ca-certificates/sandbox-authority.crt
3+
RUN update-ca-certificates
24
COPY one.json .
35
COPY schemas schemas
46
RUN sourcemeta one.json --profile
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,6 @@
11
include ../../../test/e2e/common.mk
2+
3+
# The identity provider serves real TLS under this sandbox's committed
4+
# authority, so the client trusts that authority and dials the certificate's
5+
# name through the locally mapped port
6+
HURL_FLAGS = --cacert tls/ca.crt --connect-to keycloak:8443:localhost:8443

enterprise/e2e/auth-closed/compose.yml

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,10 @@ services:
44
command: start-dev --import-realm
55
environment:
66
- KC_HEALTH_ENABLED=true
7-
- KC_HOSTNAME=http://keycloak:8080
7+
- KC_HOSTNAME=https://keycloak:8443
8+
- KC_PROXY_HEADERS=xforwarded
89
ports:
9-
- "8080:8080"
10+
- "8443:8443"
1011
volumes:
1112
- ./realm.json:/opt/keycloak/data/import/realm.json:ro
1213
healthcheck:
@@ -15,6 +16,18 @@ services:
1516
timeout: 5s
1617
retries: 60
1718

19+
# TLS termination sharing the identity provider's network identity, so the
20+
# provider answers over https under the very name its issuer advertises
21+
keycloak-nginx:
22+
image: nginx:1.27-alpine
23+
network_mode: service:keycloak
24+
volumes:
25+
- ./nginx.conf:/etc/nginx/nginx.conf:ro
26+
- ./tls:/etc/nginx/tls:ro
27+
depends_on:
28+
keycloak:
29+
condition: service_started
30+
1831
sandbox:
1932
build:
2033
context: .
@@ -28,5 +41,7 @@ services:
2841
depends_on:
2942
keycloak:
3043
condition: service_healthy
44+
keycloak-nginx:
45+
condition: service_started
3146
ports:
3247
- "${PORT}:8001"

enterprise/e2e/auth-closed/hurl/exempt.all.hurl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ GET {{base}}/self/v1/auth/login/keycloak
4545
HTTP 303
4646
Cache-Control: no-store
4747
[Asserts]
48-
header "Location" startsWith "http://keycloak:8080/realms/main/protocol/openid-connect/auth?"
48+
header "Location" startsWith "https://keycloak:8443/realms/main/protocol/openid-connect/auth?"
4949
header "Location" contains "client_id=registry"
5050
header "Set-Cookie" startsWith "sourcemeta_one_transaction_keycloak="
5151
header "WWW-Authenticate" not exists

enterprise/e2e/auth-closed/hurl/jwt.all.hurl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# A token opens the closed registry just as the root key does
2-
POST http://localhost:8080/realms/main/protocol/openid-connect/token
2+
POST https://keycloak:8443/realms/main/protocol/openid-connect/token
33
[FormParams]
44
grant_type: client_credentials
55
client_id: ci-service
@@ -73,7 +73,7 @@ HTTP 200
7373
jsonpath "$.valid" == true
7474

7575
# A token minted for a different audience opens nothing
76-
POST http://localhost:8080/realms/main/protocol/openid-connect/token
76+
POST https://keycloak:8443/realms/main/protocol/openid-connect/token
7777
[FormParams]
7878
grant_type: client_credentials
7979
client_id: ci-wrong

0 commit comments

Comments
 (0)