Skip to content

Commit f9c118c

Browse files
authored
Making optional configs mandatory (apache#18368)
1 parent 52e4501 commit f9c118c

3 files changed

Lines changed: 120 additions & 12 deletions

File tree

docs/development/extensions-core/druid-kerberos.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,12 +48,12 @@ druid.auth.authenticator.<authenticatorName>.<authenticatorProperty>
4848
The configuration examples in the rest of this document will use "kerberos" as the name of the authenticator being configured.
4949

5050
### Properties
51-
|Property|Possible Values|Description|Default|required|
52-
|--------|---------------|-----------|-------|--------|
53-
|`druid.auth.authenticator.kerberos.serverPrincipal`|`HTTP/_HOST@EXAMPLE.COM`| SPNEGO service principal used by druid processes|empty|Yes|
54-
|`druid.auth.authenticator.kerberos.serverKeytab`|`/etc/security/keytabs/spnego.service.keytab`|SPNego service keytab used by druid processes|empty|Yes|
51+
|Property|Possible Values|Description| Default | required |
52+
|--------|---------------|-----------|-----|--|
53+
|`druid.auth.authenticator.kerberos.serverPrincipal`|`HTTP/_HOST@EXAMPLE.COM`| SPNEGO service principal used by druid processes|Empty|Yes|
54+
|`druid.auth.authenticator.kerberos.serverKeytab`|`/etc/security/keytabs/spnego.service.keytab`|SPNego service keytab used by druid processes|Empty|Yes|
5555
|`druid.auth.authenticator.kerberos.authToLocal`|`RULE:[1:$1@$0](druid@EXAMPLE.COM)s/.*/druid DEFAULT`|It allows you to set a general rule for mapping principal names to local user names. It will be used if there is not an explicit mapping for the principal name that is being translated.|DEFAULT|No|
56-
|`druid.auth.authenticator.kerberos.cookieSignatureSecret`|`secretString`| Secret used to sign authentication cookies. It is advisable to explicitly set it, if you have multiple druid nodes running on same machine with different ports as the Cookie Specification does not guarantee isolation by port.|Random value|No|
56+
|`druid.auth.authenticator.kerberos.cookieSignatureSecret`|`secretString`| Secret used to sign authentication cookies|Empty|Yes|
5757
|`druid.auth.authenticator.kerberos.authorizerName`|Depends on available authorizers|Authorizer that requests should be directed to|Empty|Yes|
5858

5959
As a note, it is required that the SPNego principal in use by the druid processes must start with HTTP (This specified by [RFC-4559](https://tools.ietf.org/html/rfc4559)) and must be of the form "HTTP/_HOST@REALM".

extensions-core/druid-kerberos/src/main/java/org/apache/druid/security/kerberos/KerberosAuthenticator.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import com.fasterxml.jackson.annotation.JsonTypeName;
2626
import com.google.common.base.Joiner;
2727
import com.google.common.base.Preconditions;
28+
import org.apache.druid.error.DruidException;
2829
import org.apache.druid.guice.annotations.Self;
2930
import org.apache.druid.java.util.common.StringUtils;
3031
import org.apache.druid.java.util.common.logger.Logger;
@@ -75,7 +76,6 @@
7576
import java.util.Properties;
7677
import java.util.Set;
7778
import java.util.TimeZone;
78-
import java.util.concurrent.ThreadLocalRandom;
7979
import java.util.stream.Collectors;
8080

8181

@@ -84,6 +84,7 @@ public class KerberosAuthenticator implements Authenticator
8484
{
8585
private static final Logger log = new Logger(KerberosAuthenticator.class);
8686
public static final String SIGNED_TOKEN_ATTRIBUTE = "signedToken";
87+
private static final String COOKIE_SIGNATURE_SECRET_KEY = "cookieSignatureSecret";
8788

8889
private final String serverPrincipal;
8990
private final String serverKeytab;
@@ -98,14 +99,20 @@ public KerberosAuthenticator(
9899
@JsonProperty("serverPrincipal") String serverPrincipal,
99100
@JsonProperty("serverKeytab") String serverKeytab,
100101
@JsonProperty("authToLocal") String authToLocal,
101-
@JsonProperty("cookieSignatureSecret") String cookieSignatureSecret,
102+
@JsonProperty(COOKIE_SIGNATURE_SECRET_KEY) String cookieSignatureSecret,
102103
@JsonProperty("authorizerName") String authorizerName,
103104
@JsonProperty("name") String name,
104105
@JacksonInject @Self DruidNode node
105106
)
106107
{
107108
this.serverKeytab = serverKeytab;
108109
this.authToLocal = authToLocal == null ? "DEFAULT" : authToLocal;
110+
if (cookieSignatureSecret == null || cookieSignatureSecret.isEmpty()) {
111+
throw DruidException.forPersona(DruidException.Persona.OPERATOR)
112+
.ofCategory(DruidException.Category.INVALID_INPUT)
113+
.build("[%s] is not set for Kerberos authenticator", COOKIE_SIGNATURE_SECRET_KEY);
114+
}
115+
109116
this.cookieSignatureSecret = cookieSignatureSecret;
110117
this.authorizerName = authorizerName;
111118
this.name = Preconditions.checkNotNull(name);
@@ -140,8 +147,10 @@ public void init(FilterConfig filterConfig) throws ServletException
140147
Properties config = getConfiguration(configPrefix, filterConfig);
141148
String signatureSecret = config.getProperty(configPrefix + SIGNATURE_SECRET);
142149
if (signatureSecret == null) {
143-
signatureSecret = Long.toString(ThreadLocalRandom.current().nextLong());
144-
log.warn("'signature.secret' configuration not set, using a random value as secret");
150+
throw DruidException.defensive(
151+
"Config property[%s] is not set for Kerberos authenticator",
152+
SIGNATURE_SECRET
153+
);
145154
}
146155
final byte[] secretBytes = StringUtils.toUtf8(signatureSecret);
147156
SignerSecretProvider signerSecretProvider = new SignerSecretProvider()
@@ -381,9 +390,7 @@ public Map<String, String> getInitParameters()
381390
params.put("kerberos.keytab", serverKeytab);
382391
params.put(AuthenticationFilter.AUTH_TYPE, DruidKerberosAuthenticationHandler.class.getName());
383392
params.put("kerberos.name.rules", authToLocal);
384-
if (cookieSignatureSecret != null) {
385-
params.put("signature.secret", cookieSignatureSecret);
386-
}
393+
params.put(AuthenticationFilter.SIGNATURE_SECRET, cookieSignatureSecret);
387394
return params;
388395
}
389396

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.druid.security.kerberos;
21+
22+
import org.apache.druid.error.DruidException;
23+
import org.apache.druid.server.DruidNode;
24+
import org.junit.Assert;
25+
import org.junit.Test;
26+
27+
public class KerberosAuthenticatorTest
28+
{
29+
private static final String TEST_SERVER_PRINCIPAL = "HTTP/localhost@EXAMPLE.COM";
30+
private static final String TEST_SERVER_KEYTAB = "/path/to/keytab";
31+
private static final String TEST_AUTH_TO_LOCAL = "RULE:[1:$1@$0](.*@EXAMPLE.COM)s/@.*//";
32+
private static final String TEST_AUTHORIZER_NAME = "testAuthorizer";
33+
private static final String TEST_NAME = "testKerberos";
34+
private static final String TEST_COOKIE_SECRET = "test-secret-key";
35+
36+
private DruidNode createTestNode()
37+
{
38+
return new DruidNode("test", "localhost", false, 8080, null, true, false);
39+
}
40+
41+
42+
@Test
43+
public void testConstructorWithNullCookieSignatureSecret()
44+
{
45+
DruidNode node = createTestNode();
46+
47+
DruidException exception = Assert.assertThrows(
48+
DruidException.class,
49+
() -> new KerberosAuthenticator(
50+
TEST_SERVER_PRINCIPAL,
51+
TEST_SERVER_KEYTAB,
52+
TEST_AUTH_TO_LOCAL,
53+
null, // null cookie signature secret
54+
TEST_AUTHORIZER_NAME,
55+
TEST_NAME,
56+
node
57+
)
58+
);
59+
60+
Assert.assertEquals(DruidException.Persona.OPERATOR, exception.getTargetPersona());
61+
Assert.assertEquals(DruidException.Category.INVALID_INPUT, exception.getCategory());
62+
Assert.assertTrue(
63+
"Exception message should mention cookieSignatureSecret",
64+
exception.getMessage().contains("cookieSignatureSecret")
65+
);
66+
Assert.assertTrue(
67+
"Exception message should mention 'is not set'",
68+
exception.getMessage().contains("is not set")
69+
);
70+
}
71+
72+
@Test
73+
public void testConstructorWithEmptyCookieSignatureSecret()
74+
{
75+
DruidNode node = createTestNode();
76+
77+
DruidException exception = Assert.assertThrows(
78+
DruidException.class,
79+
() -> new KerberosAuthenticator(
80+
TEST_SERVER_PRINCIPAL,
81+
TEST_SERVER_KEYTAB,
82+
TEST_AUTH_TO_LOCAL,
83+
"", // empty cookie signature secret
84+
TEST_AUTHORIZER_NAME,
85+
TEST_NAME,
86+
node
87+
)
88+
);
89+
90+
Assert.assertEquals(DruidException.Persona.OPERATOR, exception.getTargetPersona());
91+
Assert.assertEquals(DruidException.Category.INVALID_INPUT, exception.getCategory());
92+
Assert.assertTrue(
93+
"Exception message should mention cookieSignatureSecret",
94+
exception.getMessage().contains("cookieSignatureSecret")
95+
);
96+
Assert.assertTrue(
97+
"Exception message should mention 'is not set'",
98+
exception.getMessage().contains("is not set")
99+
);
100+
}
101+
}

0 commit comments

Comments
 (0)