Skip to content

Commit 6dbdc94

Browse files
authored
KNOX-3359 - Support Single-Purpose EKU Certificates (#1291)
1 parent 7b34b7c commit 6dbdc94

46 files changed

Lines changed: 2240 additions & 33 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with this
3+
# work for additional information regarding copyright ownership. The ASF
4+
# licenses this file to you under the Apache License, Version 2.0 (the
5+
# "License"); you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
# KNOX-3359: single-EKU WITHOUT mTLS compose OVERRIDE.
17+
#
18+
# single-EKU on, inbound client-auth OFF, outbound two-way SSL OFF. No keystores
19+
# are mounted: Knox self-generates a serverAuth-only server identity at startup,
20+
# so the default baked /gateway.sh entrypoint is used as-is.
21+
services:
22+
knox:
23+
volumes:
24+
- ./single-eku-no-mtls/gateway-site.xml:/knox-runtime/conf/gateway-site.xml:ro
25+
26+
tests:
27+
volumes:
28+
- ../tests:/tests
29+
environment:
30+
- KNOX_GATEWAY_URL=https://knox:8443/
31+
- KNOX_SINGLE_EKU_NO_MTLS=true
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one or more
2+
# contributor license agreements. See the NOTICE file distributed with this
3+
# work for additional information regarding copyright ownership. The ASF
4+
# licenses this file to you under the Apache License, Version 2.0 (the
5+
# "License"); you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
#
16+
# KNOX-3359: single-EKU compose OVERRIDE.
17+
#
18+
# This file is intentionally separate from docker-compose.yml. The default
19+
# stack's existing integration tests make requests WITHOUT a client cert, so
20+
# turning on gateway.client.auth.needed=true on the shared `knox` service would
21+
# break every one of them. This override is only applied for the single-EKU
22+
# test run.
23+
#
24+
# Run the single-EKU integration test (from repo root):
25+
#
26+
# docker compose \
27+
# -f .github/workflows/compose/docker-compose.yml \
28+
# -f .github/workflows/compose/docker-compose.single-eku.yml \
29+
# up -d knox
30+
# docker compose \
31+
# -f .github/workflows/compose/docker-compose.yml \
32+
# -f .github/workflows/compose/docker-compose.single-eku.yml \
33+
# run --rm tests python -m unittest test_single_eku_mtls -v
34+
# docker compose \
35+
# -f .github/workflows/compose/docker-compose.yml \
36+
# -f .github/workflows/compose/docker-compose.single-eku.yml down
37+
#
38+
# The override:
39+
# - mounts the single-EKU gateway-site.xml over the baked one,
40+
# - mounts the dev keystores into /knox-runtime/conf/single-eku-keystores,
41+
# - sets KNOX_SINGLE_EKU=true on the tests service so test_single_eku_mtls.py
42+
# un-skips (it is skipped by default in the normal stack), and
43+
# - mounts + points the PEM client material for the mTLS happy-path test.
44+
services:
45+
knox:
46+
# Create the keystore-password aliases (so the dev "horton" password is
47+
# resolved deterministically) before starting the gateway.
48+
command: /gateway-single-eku.sh
49+
volumes:
50+
- ./single-eku/gateway-site.xml:/knox-runtime/conf/gateway-site.xml:ro
51+
- ./single-eku/keystores:/knox-runtime/conf/single-eku-keystores:ro
52+
- ./single-eku/gateway-single-eku.sh:/gateway-single-eku.sh:ro
53+
54+
tests:
55+
volumes:
56+
- ../tests:/tests
57+
- ./single-eku/keystores:/single-eku-keystores:ro
58+
environment:
59+
- KNOX_GATEWAY_URL=https://knox:8443/
60+
- KNOX_SINGLE_EKU=true
61+
- KNOX_CLIENT_CERT=/single-eku-keystores/client-cert.pem
62+
- KNOX_CLIENT_KEY=/single-eku-keystores/client-key.pem

.github/workflows/compose/docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ services:
114114
&& pylint *.py
115115
&& echo 'Waiting for knox...'
116116
&& sleep 30
117-
&& pytest --junitxml=test-results.xml"
117+
&& pytest --ignore=test_single_eku_mtls.py --ignore=test_single_eku_no_mtls.py --junitxml=test-results.xml"
118118
depends_on:
119119
- knox
120120

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/usr/bin/env bash
2+
# Licensed to the Apache Software Foundation (ASF) under one or more
3+
# contributor license agreements. See the NOTICE file distributed with this
4+
# work for additional information regarding copyright ownership. The ASF
5+
# licenses this file to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
#
17+
# Generates the dev-only single-EKU keystore fixtures used by the
18+
# test_single_eku_mtls.py integration test:
19+
# - host-client_keystore.jks : a key pair whose certificate carries ONLY the
20+
# clientAuth EKU (the single-EKU client identity)
21+
# - server-identity_keystore.jks: a key pair whose certificate carries ONLY the
22+
# serverAuth EKU (the single-EKU server identity).
23+
# single-EKU mode refuses to start unless the
24+
# server identity is serverAuth-only, and the
25+
# image's auto-generated identity cert has no
26+
# EKU at all, so a dedicated fixture is required.
27+
# - global_truststore.jks : the server truststore trusting that client cert
28+
#
29+
# These are committed, self-signed, dev-only fixtures (NOT secrets). The store
30+
# password is the same dev password the apache/knox-dev keystores use so the
31+
# master-secret fallback can resolve them. JKS is used only for this dev
32+
# fixture; production keystore type comes from config and is never hardcoded in
33+
# Knox Java code.
34+
set -euo pipefail
35+
DIR="${1:?usage: gen-single-eku-keystores.sh <output-dir>}"
36+
PASS="horton"
37+
mkdir -p "$DIR"
38+
39+
# Client identity: a key pair whose cert carries ONLY the clientAuth EKU
40+
keytool -genkeypair -alias gateway-httpclient-key -keyalg RSA -keysize 2048 \
41+
-dname "CN=knox-client" -ext "eku=clientAuth" -validity 3650 \
42+
-keystore "$DIR/host-client_keystore.jks" -storetype JKS \
43+
-storepass "$PASS" -keypass "$PASS"
44+
45+
# Server identity: a key pair whose cert carries ONLY the serverAuth EKU. The
46+
# alias must match the gateway identity key alias (gateway-identity) so Knox
47+
# uses it as the inbound TLS server certificate. SANs cover the compose
48+
# hostname so it is a valid (if self-signed) cert for https://knox:8443.
49+
keytool -genkeypair -alias gateway-identity -keyalg RSA -keysize 2048 \
50+
-dname "CN=knox" -ext "eku=serverAuth" -ext "san=dns:knox,dns:localhost" \
51+
-validity 3650 \
52+
-keystore "$DIR/server-identity_keystore.jks" -storetype JKS \
53+
-storepass "$PASS" -keypass "$PASS"
54+
55+
# Export the client cert and import it into the server truststore (clients trusted by Knox)
56+
keytool -exportcert -alias gateway-httpclient-key -rfc \
57+
-keystore "$DIR/host-client_keystore.jks" -storepass "$PASS" \
58+
-file "$DIR/client.cer"
59+
keytool -importcert -noprompt -alias knox-client \
60+
-keystore "$DIR/global_truststore.jks" -storetype JKS -storepass "$PASS" \
61+
-file "$DIR/client.cer"
62+
rm -f "$DIR/client.cer"
63+
64+
# PEM client material for the Python tests container (requests cert=(cert, key)).
65+
keytool -exportcert -alias gateway-httpclient-key -rfc \
66+
-keystore "$DIR/host-client_keystore.jks" -storepass "$PASS" \
67+
-file "$DIR/client-cert.pem"
68+
keytool -importkeystore -srckeystore "$DIR/host-client_keystore.jks" \
69+
-srcstoretype JKS -srcstorepass "$PASS" -srcalias gateway-httpclient-key \
70+
-destkeystore "$DIR/client.p12" -deststoretype PKCS12 -deststorepass "$PASS"
71+
openssl pkcs12 -in "$DIR/client.p12" -nocerts -nodes -passin pass:"$PASS" \
72+
| openssl pkey -out "$DIR/client-key.pem"
73+
rm -f "$DIR/client.p12"
74+
75+
echo "Generated single-EKU dev keystores in $DIR"
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
Licensed to the Apache Software Foundation (ASF) under one
4+
or more contributor license agreements. See the NOTICE file
5+
distributed with this work for additional information
6+
regarding copyright ownership. The ASF licenses this file
7+
to you under the Apache License, Version 2.0 (the
8+
"License"); you may not use this file except in compliance
9+
with the License. You may obtain a copy of the License at
10+
11+
http://www.apache.org/licenses/LICENSE-2.0
12+
13+
Unless required by applicable law or agreed to in writing, software
14+
distributed under the License is distributed on an "AS IS" BASIS,
15+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
See the License for the specific language governing permissions and
17+
limitations under the License.
18+
-->
19+
<configuration>
20+
21+
<!--
22+
KNOX-3359 single-EKU WITHOUT mTLS. single-EKU is enabled; inbound client
23+
authentication and outbound two-way SSL are both OFF. No keystores are
24+
provided: Knox self-generates a serverAuth-only server identity at
25+
startup (default keystore, default alias/password). Proves single-EKU no
26+
longer forces mTLS and self-bootstraps its identity.
27+
-->
28+
<property>
29+
<name>gateway.tls.single.eku.enabled</name>
30+
<value>true</value>
31+
</property>
32+
33+
<property>
34+
<name>gateway.service.alias.impl</name>
35+
<value>org.apache.knox.gateway.services.security.impl.DefaultAliasService</value>
36+
</property>
37+
<property>
38+
<name>gateway.port</name>
39+
<value>8443</value>
40+
<description>The HTTP port for the Gateway.</description>
41+
</property>
42+
43+
<property>
44+
<name>gateway.path</name>
45+
<value>gateway</value>
46+
<description>The default context path for the gateway.</description>
47+
</property>
48+
49+
<property>
50+
<name>gateway.gateway.conf.dir</name>
51+
<value>deployments</value>
52+
<description>The directory within GATEWAY_HOME that contains gateway topology files and deployments.</description>
53+
</property>
54+
55+
<!-- @since 0.10 Websocket configs -->
56+
<property>
57+
<name>gateway.websocket.feature.enabled</name>
58+
<value>true</value>
59+
<description>Enable/Disable websocket feature.</description>
60+
</property>
61+
62+
<property>
63+
<name>gateway.scope.cookies.feature.enabled</name>
64+
<value>false</value>
65+
<description>Enable/Disable cookie scoping feature.</description>
66+
</property>
67+
68+
<!-- @since 2.0.0 WebShell configs -->
69+
<!-- must have websocket enabled to use webshell -->
70+
<property>
71+
<name>gateway.webshell.feature.enabled</name>
72+
<value>true</value>
73+
<description>Enable/Disable webshell feature.</description>
74+
</property>
75+
<property>
76+
<name>gateway.webshell.max.concurrent.sessions</name>
77+
<value>20</value>
78+
<description>Maximum number of total concurrent webshell sessions</description>
79+
</property>
80+
<property>
81+
<name>gateway.webshell.read.buffer.size</name>
82+
<value>1024</value>
83+
<description>Web Shell buffer size for reading</description>
84+
</property>
85+
86+
<!-- @since 2.0.0 websocket JWT validation configs -->
87+
<property>
88+
<name>gateway.websocket.JWT.validation.feature.enabled</name>
89+
<value>true</value>
90+
<description>Enable/Disable websocket JWT validation at websocket layer.</description>
91+
</property>
92+
93+
<!-- @since 1.5.0 homepage logout -->
94+
<property>
95+
<name>knox.homepage.logout.enabled</name>
96+
<value>true</value>
97+
<description>Enable/disable logout from the Knox Homepage.</description>
98+
</property>
99+
100+
<!-- @since 1.6.0 token management related properties -->
101+
<property>
102+
<name>gateway.knox.token.eviction.grace.period</name>
103+
<value>0</value>
104+
<description>A duration (in seconds) beyond a token’s expiration to wait before evicting its state. This configuration only applies when server-managed token state is enabled either in gateway-site or at the topology level.</description>
105+
</property>
106+
107+
<!-- Knox Admin related config -->
108+
<property>
109+
<name>gateway.knox.admin.groups</name>
110+
<value>admin</value>
111+
</property>
112+
113+
<!-- DEMO LDAP config for Hadoop Group Provider -->
114+
<property>
115+
<name>gateway.group.config.hadoop.security.group.mapping</name>
116+
<value>org.apache.hadoop.security.LdapGroupsMapping</value>
117+
</property>
118+
<property>
119+
<name>gateway.group.config.use.ldap.service</name>
120+
<value>true</value>
121+
</property>
122+
<property>
123+
<name>gateway.dispatch.whitelist.services</name>
124+
<value>DATANODE,HBASEUI,HDFSUI,JOBHISTORYUI,NODEUI,YARNUI,knoxauth</value>
125+
<description>The comma-delimited list of service roles for which the gateway.dispatch.whitelist should be applied.</description>
126+
</property>
127+
<property>
128+
<name>gateway.dispatch.whitelist</name>
129+
<value>^https?:\/\/(www\.local\.com|localhost|127\.0\.0\.1|0:0:0:0:0:0:0:1|::1):[0-9].*$</value>
130+
<description>The whitelist to be applied for dispatches associated with the service roles specified by gateway.dispatch.whitelist.services.
131+
If the value is DEFAULT, a domain-based whitelist will be derived from the Knox host.</description>
132+
</property>
133+
<property>
134+
<name>gateway.xforwarded.header.context.append.servicename</name>
135+
<value>LIVYSERVER</value>
136+
<description>Add service name to x-forward-context header for the list of services defined above.</description>
137+
</property>
138+
<property>
139+
<name>gateway.strict.transport.enabled</name>
140+
<value>true</value>
141+
</property>
142+
<property>
143+
<name>gateway.strict.transport.option</name>
144+
<value>max-age=300; includeSubDomains</value>
145+
</property>
146+
147+
<!-- KnoxLDAP Service Configuration -->
148+
<property>
149+
<name>gateway.ldap.enabled</name>
150+
<value>true</value>
151+
</property>
152+
<property>
153+
<name>gateway.ldap.port</name>
154+
<value>33390</value>
155+
</property>
156+
<property>
157+
<name>gateway.ldap.base.dn</name>
158+
<value>dc=hadoop,dc=apache,dc=org</value>
159+
</property>
160+
<property>
161+
<name>gateway.ldap.recursive.group.resolution</name>
162+
<value>true</value>
163+
</property>
164+
<property>
165+
<name>gateway.ldap.interceptor.names</name>
166+
<value>demoldap</value>
167+
</property>
168+
169+
<!-- LDAP Backend specific configuration (proxying to demo ldap) -->
170+
<property>
171+
<name>gateway.ldap.interceptor.demoldap.interceptorType</name>
172+
<value>backend</value>
173+
</property>
174+
<property>
175+
<name>gateway.ldap.interceptor.demoldap.backendType</name>
176+
<value>ldap</value>
177+
</property>
178+
<property>
179+
<name>gateway.ldap.interceptor.demoldap.url</name>
180+
<value>ldap://ldap:33389</value>
181+
</property>
182+
<property>
183+
<name>gateway.ldap.interceptor.demoldap.remoteBaseDn</name>
184+
<value>dc=hadoop,dc=apache,dc=org</value>
185+
</property>
186+
<property>
187+
<name>gateway.ldap.interceptor.demoldap.systemUsername</name>
188+
<value>uid=guest,ou=people,dc=hadoop,dc=apache,dc=org</value>
189+
</property>
190+
<property>
191+
<name>gateway.ldap.interceptor.demoldap.systemPassword</name>
192+
<value>guest-password</value>
193+
</property>
194+
<property>
195+
<name>gateway.ldap.interceptor.demoldap.userSearchBase</name>
196+
<value>ou=people,dc=hadoop,dc=apache,dc=org</value>
197+
</property>
198+
<property>
199+
<name>gateway.ldap.interceptor.demoldap.groupSearchBase</name>
200+
<value>ou=groups,dc=hadoop,dc=apache,dc=org</value>
201+
</property>
202+
<property>
203+
<name>gateway.ldap.interceptor.demoldap.groupMemberAttribute</name>
204+
<value>member</value>
205+
</property>
206+
207+
</configuration>

0 commit comments

Comments
 (0)