Skip to content
This repository was archived by the owner on Oct 23, 2024. It is now read-only.

Commit d6de33b

Browse files
authored
[D2IQ-68540] enable custom TLS (#465)
* add option to providde custom certificates * customise keystore and truststore paths * add secrets when kerberos is disabled * Add docs for custom TLS * make it possible to use default dcos certificate for TLS encryption
1 parent 79c2785 commit d6de33b

File tree

10 files changed

+336
-44
lines changed

10 files changed

+336
-44
lines changed

docs/CustomTLS.md

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
# Use custom TLS certificate for Kafka
2+
3+
## Using Custom TLS settings Kafka package
4+
5+
To use the custom TLS certs for kafka service, we need to add the following options in the configuration of the package:
6+
7+
```
8+
"service": {
9+
"name" : "kafka",
10+
"transport_encryption": {
11+
"enabled": true,
12+
"key_store": "${SERVICE_NAME}/keystore",
13+
"key_store_password_file": "${SERVICE_NAME}/keystorepass",
14+
"trust_store": "${SERVICE_NAME}/truststore",
15+
"trust_store_password_file": "${SERVICE_NAME}/truststorepass"
16+
"allow_plaintext": false
17+
}
18+
}
19+
```
20+
21+
> Note: `transport_encryption.enabled:true` means that custom transport encryption is enabled. In future releases, we will separate the custom tls and default tls feature.
22+
23+
## Example with self-signed certificate
24+
25+
Generate CA-cert and CA-private-key, called `ca-cert` and `ca-key` respectively
26+
27+
```
28+
openssl req -new -newkey rsa:4096 -days 365 -x509 -subj "/C=US/ST=CA/L=SF/O=Mesosphere/OU=Mesosphere/CN=kafka" -keyout ca-key -out ca-cert -nodes
29+
```
30+
31+
Generate a keystore, called `broker.keystore`
32+
```
33+
keytool -genkey -keyalg RSA -keystore broker.keystore -validity 365 -storepass changeit -keypass changeit -dname "CN=kafka" -storetype JKS
34+
```
35+
36+
Generate Certificate Signing Request (CSR) called `cert-file`
37+
```
38+
keytool -keystore broker.keystore -certreq -file cert-file -storepass changeit -keypass changeit
39+
```
40+
41+
Sign the Generated certificate
42+
```
43+
openssl x509 -req -CA ca-cert -CAkey ca-key -in cert-file -out cert-signed -days 365 -CAcreateserial -passin pass:changeit
44+
```
45+
46+
Generate a truststore with ca-cert
47+
```
48+
keytool -keystore broker.truststore -alias CARoot -import -file ca-cert -storepass changeit -keypass changeit -noprompt
49+
```
50+
51+
Generate a truststore called `broker.truststore` with ca-cert
52+
```
53+
keytool -keystore broker.truststore -alias CARoot -importcert -file ca-cert -storepass changeit -keypass changeit -noprompt
54+
```
55+
56+
Generate a truststore with self-signed cert
57+
```
58+
keytool -keystore broker.truststore -alias CertSigned -importcert -file cert-signed -storepass changeit -keypass changeit -noprompt
59+
```
60+
61+
Attach the dcos cluster using
62+
```
63+
dcos cluster setup {CLUSTER_URL}
64+
```
65+
66+
Create Service account and its secret to use the TLS feature
67+
```
68+
dcos security org service-accounts keypair ${SERVICE_NAME}-private-key.pem ${SERVICE_NAME}-public-key.pem
69+
70+
dcos security org service-accounts create -p ${SERVICE_NAME}-public-key.pem -d "desc" "${SERVICE_NAME}"
71+
72+
dcos security secrets create-sa-secret --strict ${SERVICE_NAME}-private-key.pem "${SERVICE_NAME}" "${SERVICE_NAME}-secret"
73+
```
74+
75+
Assign permissions to the user
76+
77+
```
78+
dcos security org groups add_user superusers ${SERVICE_NAME}
79+
dcos security org users grant ${SERVICE_NAME}dcos:mesos:master:task:user:nobody create
80+
dcos security org users grant ${SERVICE_NAME}dcos:mesos:master:framework:role:${SERVICE_NAME}-role create
81+
dcos security org users grant ${SERVICE_NAME}dcos:mesos:master:reservation:role:${SERVICE_NAME}-role create
82+
dcos security org users grant ${SERVICE_NAME}dcos:mesos:master:volume:role:${SERVICE_NAME}-role create
83+
dcos security org users grant ${SERVICE_NAME}dcos:mesos:master:reservation:principal:${SERVICE_NAME} delete
84+
dcos security org users grant ${SERVICE_NAME}dcos:mesos:master:volume:principal:${SERVICE_NAME} delete
85+
dcos security org users grant ${SERVICE_NAME}dcos:secrets:default:/${SERVICE_NAME}/\* full
86+
dcos security org users grant ${SERVICE_NAME}dcos:secrets:list:default:/${SERVICE_NAME} read
87+
```
88+
89+
Now we are ready to install a Kafka cluster with custom transport encryption enabled.
90+
91+
Create a file named `dcos-kafka-options-customtls.json` with following configuration
92+
93+
```
94+
cat <<EOF >>dcos-kafka-options-customtls.json
95+
{
96+
"service": {
97+
"name": "${SERVICE_NAME}",
98+
"service_account": "${SERVICE_NAME}",
99+
"service_account_secret": "${SERVICE_NAME}-secret",
100+
"security": {
101+
"transport_encryption": {
102+
"enabled": true,
103+
"allow_plaintext": false,
104+
"key_store": "${SERVICE_NAME}/keystore",
105+
"key_store_password_file": "${SERVICE_NAME}/keystorepass",
106+
"trust_store": "${SERVICE_NAME}/truststore",
107+
"trust_store_password_file": "${SERVICE_NAME}/truststorepass"
108+
}
109+
}
110+
}
111+
}
112+
EOF
113+
```
114+
115+
Install the kafka service
116+
```
117+
dcos package install kafka --options=dcos-kafka-options-customtls.json --yes
118+
```
119+
120+
The custom transport encryption settings can be verified from the `server.properties` file of brokers.

frameworks/kafka/src/main/dist/server.properties.mustache

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,19 +44,33 @@ broker.rack={{ZONE}}
4444

4545
{{#SECURITY_TRANSPORT_ENCRYPTION_ENABLED}}
4646
############################# TLS Settings #############################
47+
48+
# Use default TLS credentials if custom TLS is not enabled
49+
{{^SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
4750
ssl.keystore.location={{MESOS_SANDBOX}}/broker.keystore
4851
ssl.keystore.password=notsecure
4952
ssl.key.password=notsecure
5053

5154
ssl.truststore.location={{MESOS_SANDBOX}}/broker.truststore
5255
ssl.truststore.password=notsecure
56+
{{/SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
5357

5458
ssl.enabled.protocols=TLSv1.2
5559

5660
{{#SECURITY_TRANSPORT_ENCRYPTION_CIPHERS}}
5761
ssl.cipher.suites={{SECURITY_TRANSPORT_ENCRYPTION_CIPHERS}}
5862
{{/SECURITY_TRANSPORT_ENCRYPTION_CIPHERS}}
5963

64+
# Use TLS certificate provided by user when custom TLS is enabled
65+
{{#SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
66+
ssl.keystore.location={{MESOS_SANDBOX}}/customtls/broker.keystore
67+
ssl.keystore.password={{CUSTOM_TLS_KEY_STORE_KEY}}
68+
ssl.key.password={{CUSTOM_TLS_KEY_STORE_KEY}}
69+
70+
ssl.truststore.location={{MESOS_SANDBOX}}/customtls/broker.truststore
71+
ssl.truststore.password={{CUSTOM_TLS_TRUST_STORE_KEY}}
72+
{{/SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
73+
6074
# If Kerberos is NOT enabled, then SSL authentication can be turned on.
6175
{{^SECURITY_KERBEROS_ENABLED}}
6276
{{#SECURITY_SSL_AUTHENTICATION_ENABLED}}

frameworks/kafka/src/main/dist/svc.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,20 @@ pods:
3131
keytab:
3232
secret: {{SECURITY_KERBEROS_KEYTAB_SECRET}}
3333
file: kafka.keytab
34+
{{#TASKCFG_ALL_SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
35+
tlsTrustStore:
36+
secret: {{CUSTOM_TLS_TRUST_STORE}}
37+
file: broker.truststore
38+
tlsTrustStorePass:
39+
secret: {{CUSTOM_TLS_TRUST_STORE_PASSWORD}}
40+
file: trust_store_pass
41+
tlsKeyStore:
42+
secret: {{CUSTOM_TLS_KEY_STORE}}
43+
file: broker.keystore
44+
tlsKeyStorePass:
45+
secret: {{CUSTOM_TLS_KEY_STORE_PASSWORD}}
46+
file: key_store_pass
47+
{{/TASKCFG_ALL_SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
3448
{{#TASKCFG_ALL_SECURE_JMX_ENABLED}}
3549
jmxPasswordFile:
3650
secret: {{SECURE_JMX_PWD_FILE}}
@@ -79,6 +93,21 @@ pods:
7993
file: jmx/key_store_pass
8094
{{/TASKCFG_ALL_SECURITY_KERBEROS_ENABLED}}
8195
{{/TASKCFG_ALL_SECURE_JMX_ENABLED}}
96+
{{#TASKCFG_ALL_SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
97+
secrets:
98+
tlsTrustStore:
99+
secret: {{CUSTOM_TLS_TRUST_STORE}}
100+
file: customtls/broker.truststore
101+
tlsTrustStorePass:
102+
secret: {{CUSTOM_TLS_TRUST_STORE_PASSWORD}}
103+
file: customtls/trust_store_pass
104+
tlsKeyStore:
105+
secret: {{CUSTOM_TLS_KEY_STORE}}
106+
file: customtls/broker.keystore
107+
tlsKeyStorePass:
108+
secret: {{CUSTOM_TLS_KEY_STORE_PASSWORD}}
109+
file: customtls/key_store_pass
110+
{{/TASKCFG_ALL_SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
82111
tasks:
83112
broker:
84113
cpus: {{BROKER_CPUS}}
@@ -175,6 +204,21 @@ pods:
175204
mkdir jmx_properties
176205
(umask u=rw,g=,o= && cp $MESOS_SANDBOX/jmx/key_file $MESOS_SANDBOX/jmx_properties/key_file)
177206
{{/TASKCFG_ALL_SECURE_JMX_ENABLED}}
207+
{{#TASKCFG_ALL_SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
208+
export KEY_STORE_PATH=$MESOS_SANDBOX/customtls/broker.keystore
209+
if [ -f "$MESOS_SANDBOX/customtls/broker.keystore" ]; then
210+
# load user provided trust store
211+
echo "Found broker.keystore" >&2
212+
export CUSTOM_TLS_KEY_STORE_PATH=$MESOS_SANDBOX/customtls/broker.truststore
213+
export CUSTOM_TLS_KEY_STORE_KEY=`cat $MESOS_SANDBOX/customtls/trust_store_pass`
214+
fi
215+
if [ -f "$MESOS_SANDBOX/customtls/broker.truststore" ]; then
216+
# load user provided trust store
217+
echo "Found broker.truststore" >&2
218+
export CUSTOM_TLS_TRUST_STORE_PATH=$MESOS_SANDBOX/customtls/broker.truststore
219+
export CUSTOM_TLS_TRUST_STORE_KEY=`cat $MESOS_SANDBOX/customtls/trust_store_pass`
220+
fi
221+
{{/TASKCFG_ALL_SECURITY_CUSTOM_TRANSPORT_ENCRYPTION_ENABLED}}
178222

179223
# setup-helper determines the correct listeners and security.inter.broker.protocol.
180224
# it relies on the task IP being stored in MESOS_CONTAINER_IP

frameworks/kafka/tests/test_ssl_auth.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,10 @@ def test_authn_client_can_read_and_write(
6767
"service_account": service_account["name"],
6868
"service_account_secret": service_account["secret"],
6969
"security": {
70-
"transport_encryption": {"enabled": True},
70+
"transport_encryption": {
71+
"enabled": True,
72+
"ciphers": "TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
73+
},
7174
"ssl_authentication": {"enabled": True},
7275
},
7376
}

frameworks/kafka/tests/test_ssl_kerberos_auth.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,10 @@ def kafka_server(kerberos, service_account):
7171
"realm": kerberos.get_realm(),
7272
"keytab_secret": kerberos.get_keytab_path(),
7373
},
74-
"transport_encryption": {"enabled": True},
74+
"transport_encryption": {
75+
"enabled": True,
76+
"ciphers": "TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
77+
},
7578
},
7679
}
7780
}
@@ -94,7 +97,9 @@ def kafka_server(kerberos, service_account):
9497
@pytest.fixture(scope="module", autouse=True)
9598
def kafka_client(kerberos):
9699
try:
97-
kafka_client = client.KafkaClient("kafka-client", config.PACKAGE_NAME, config.SERVICE_NAME, kerberos)
100+
kafka_client = client.KafkaClient(
101+
"kafka-client", config.PACKAGE_NAME, config.SERVICE_NAME, kerberos
102+
)
98103
kafka_client.install()
99104

100105
# TODO: This flag should be set correctly.
@@ -123,9 +128,7 @@ def test_client_can_read_and_write(kafka_client: client.KafkaClient, kafka_serve
123128
kafka_client.connect()
124129

125130
user = "client"
126-
write_success, read_successes, _ = kafka_client.can_write_and_read(
127-
user, topic_name
128-
)
131+
write_success, read_successes, _ = kafka_client.can_write_and_read(user, topic_name)
129132

130133
assert write_success, "Write failed (user={})".format(user)
131134
assert read_successes, (

frameworks/kafka/tests/test_ssl_kerberos_authz.py

Lines changed: 14 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,9 @@ def kerberos(configure_security):
5050
@pytest.fixture(scope="module", autouse=True)
5151
def kafka_client(kerberos):
5252
try:
53-
kafka_client = client.KafkaClient("kafka-client", config.PACKAGE_NAME, config.SERVICE_NAME, kerberos)
53+
kafka_client = client.KafkaClient(
54+
"kafka-client", config.PACKAGE_NAME, config.SERVICE_NAME, kerberos
55+
)
5456
kafka_client.install()
5557

5658
# TODO: This flag should be set correctly.
@@ -91,7 +93,10 @@ def test_authz_acls_required(
9193
"realm": kerberos.get_realm(),
9294
"keytab_secret": kerberos.get_keytab_path(),
9395
},
94-
"transport_encryption": {"enabled": True},
96+
"transport_encryption": {
97+
"enabled": True,
98+
"ciphers": "TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
99+
},
95100
"authorization": {"enabled": True, "super_users": "User:{}".format("super")},
96101
},
97102
}
@@ -121,9 +126,7 @@ def test_authz_acls_required(
121126
# Since no ACLs are specified, only the super user can read and write
122127
for user in ["super"]:
123128
log.info("Checking write / read permissions for user=%s", user)
124-
write_success, read_successes, _ = kafka_client.can_write_and_read(
125-
user, topic_name
126-
)
129+
write_success, read_successes, _ = kafka_client.can_write_and_read(user, topic_name)
127130
assert write_success, "Write failed (user={})".format(user)
128131
assert read_successes, (
129132
"Read failed (user={}): "
@@ -133,9 +136,7 @@ def test_authz_acls_required(
133136

134137
for user in ["authorized", "unauthorized"]:
135138
log.info("Checking lack of write / read permissions for user=%s", user)
136-
write_success, _, read_messages = kafka_client.can_write_and_read(
137-
user, topic_name
138-
)
139+
write_success, _, read_messages = kafka_client.can_write_and_read(user, topic_name)
139140
assert not write_success, "Write not expected to succeed (user={})".format(user)
140141
assert auth.is_not_authorized(read_messages), "Unauthorized expected (user={}".format(
141142
user
@@ -147,9 +148,7 @@ def test_authz_acls_required(
147148
# After adding ACLs the authorized user and super user should still have access to the topic.
148149
for user in ["authorized", "super"]:
149150
log.info("Checking write / read permissions for user=%s", user)
150-
write_success, read_successes, _ = kafka_client.can_write_and_read(
151-
user, topic_name
152-
)
151+
write_success, read_successes, _ = kafka_client.can_write_and_read(user, topic_name)
153152
assert write_success, "Write failed (user={})".format(user)
154153
assert read_successes, (
155154
"Read failed (user={}): "
@@ -159,9 +158,7 @@ def test_authz_acls_required(
159158

160159
for user in ["unauthorized"]:
161160
log.info("Checking lack of write / read permissions for user=%s", user)
162-
write_success, _, read_messages = kafka_client.can_write_and_read(
163-
user, topic_name
164-
)
161+
write_success, _, read_messages = kafka_client.can_write_and_read(user, topic_name)
165162
assert not write_success, "Write not expected to succeed (user={})".format(user)
166163
assert auth.is_not_authorized(read_messages), "Unauthorized expected (user={}".format(
167164
user
@@ -226,9 +223,7 @@ def test_authz_acls_not_required(
226223
# Since no ACLs are specified, all users can read and write.
227224
for user in ["authorized", "unauthorized", "super"]:
228225
log.info("Checking write / read permissions for user=%s", user)
229-
write_success, read_successes, _ = kafka_client.can_write_and_read(
230-
user, topic_name
231-
)
226+
write_success, read_successes, _ = kafka_client.can_write_and_read(user, topic_name)
232227
assert write_success, "Write failed (user={})".format(user)
233228
assert read_successes, (
234229
"Read failed (user={}): "
@@ -242,9 +237,7 @@ def test_authz_acls_not_required(
242237
# After adding ACLs the authorized user and super user should still have access to the topic.
243238
for user in ["authorized", "super"]:
244239
log.info("Checking write / read permissions for user=%s", user)
245-
write_success, read_successes, _ = kafka_client.can_write_and_read(
246-
user, topic_name
247-
)
240+
write_success, read_successes, _ = kafka_client.can_write_and_read(user, topic_name)
248241
assert write_success, "Write failed (user={})".format(user)
249242
assert read_successes, (
250243
"Read failed (user={}): "
@@ -254,9 +247,7 @@ def test_authz_acls_not_required(
254247

255248
for user in ["unauthorized"]:
256249
log.info("Checking lack of write / read permissions for user=%s", user)
257-
write_success, _, read_messages = kafka_client.can_write_and_read(
258-
user, topic_name
259-
)
250+
write_success, _, read_messages = kafka_client.can_write_and_read(user, topic_name)
260251
assert not write_success, "Write not expected to succeed (user={})".format(user)
261252
assert auth.is_not_authorized(read_messages), "Unauthorized expected (user={}".format(
262253
user

frameworks/kafka/tests/test_ssl_kerberos_custom_domain_auth.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ def kafka_server(kerberos, service_account):
7373
"realm": kerberos.get_realm(),
7474
"keytab_secret": kerberos.get_keytab_path(),
7575
},
76-
"transport_encryption": {"enabled": True},
76+
"transport_encryption": {
77+
"enabled": True,
78+
"ciphers": "TLS_RSA_WITH_AES_128_GCM_SHA256,TLS_RSA_WITH_AES_128_CBC_SHA256,TLS_RSA_WITH_AES_256_GCM_SHA384,TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_128_CBC_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384,TLS_DHE_RSA_WITH_AES_256_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384",
79+
},
7780
},
7881
}
7982
}

0 commit comments

Comments
 (0)