Skip to content

Commit 917e1a9

Browse files
committed
HADOOP-19639. SecretManager configuration at runtime
- static configuration of SecretManager is required because it has some static method what use the selected algorithm - in case if class path not contains the config values (for example TEZ DAGAppMaster run) the default values will be loaded at runtime - the default values can cause failers in modern environments (they are not FIPS compliant) - new SecretManagerConfig created to be able to modify the SecretManager config without core-site.xml present on class path
1 parent 90a6f92 commit 917e1a9

File tree

3 files changed

+226
-44
lines changed

3 files changed

+226
-44
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/token/SecretManager.java

Lines changed: 9 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import java.io.IOException;
2222
import java.security.InvalidKeyException;
23-
import java.security.NoSuchAlgorithmException;
2423

2524
import javax.crypto.KeyGenerator;
2625
import javax.crypto.Mac;
@@ -32,8 +31,6 @@
3231

3332
import org.apache.hadoop.classification.InterfaceAudience;
3433
import org.apache.hadoop.classification.InterfaceStability;
35-
import org.apache.hadoop.conf.Configuration;
36-
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
3734
import org.apache.hadoop.ipc.RetriableException;
3835
import org.apache.hadoop.ipc.StandbyException;
3936

@@ -115,61 +112,29 @@ public void checkAvailableForRead() throws StandbyException {
115112
// Default to being available for read.
116113
}
117114

118-
private static final String SELECTED_ALGORITHM;
119-
private static final int SELECTED_LENGTH;
120-
121-
static {
122-
Configuration conf = new Configuration();
123-
String algorithm = conf.get(
124-
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_KEY,
125-
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_DEFAULT);
126-
LOG.debug("Selected hash algorithm: {}", algorithm);
127-
SELECTED_ALGORITHM = algorithm;
128-
int length = conf.getInt(
129-
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_KEY,
130-
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_DEFAULT);
131-
LOG.debug("Selected hash key length:{}", length);
132-
SELECTED_LENGTH = length;
133-
}
134-
135115
/**
136116
* A thread local store for the Macs.
137117
*/
138118
private static final ThreadLocal<Mac> threadLocalMac =
139-
new ThreadLocal<Mac>(){
140-
@Override
141-
protected Mac initialValue() {
142-
try {
143-
return Mac.getInstance(SELECTED_ALGORITHM);
144-
} catch (NoSuchAlgorithmException nsa) {
145-
throw new IllegalArgumentException("Can't find " + SELECTED_ALGORITHM, nsa);
146-
}
147-
}
148-
};
119+
ThreadLocal.withInitial(SecretManagerConfig::createMac);
149120

150121
/**
151122
* Key generator to use.
152123
*/
153-
private final KeyGenerator keyGen;
154-
{
155-
try {
156-
keyGen = KeyGenerator.getInstance(SELECTED_ALGORITHM);
157-
keyGen.init(SELECTED_LENGTH);
158-
} catch (NoSuchAlgorithmException nsa) {
159-
throw new IllegalArgumentException("Can't find " + SELECTED_ALGORITHM, nsa);
160-
}
161-
}
124+
private volatile KeyGenerator keyGen;
125+
private final Object keyGenLock = new Object();
162126

163127
/**
164128
* Generate a new random secret key.
165129
* @return the new key
166130
*/
167131
protected SecretKey generateSecret() {
168-
SecretKey key;
169-
synchronized (keyGen) {
170-
key = keyGen.generateKey();
132+
synchronized (keyGenLock) {
133+
if (keyGen == null) {
134+
keyGen = SecretManagerConfig.createKeyGenerator();
135+
}
136+
return keyGen.generateKey();
171137
}
172-
return key;
173138
}
174139

175140
/**
@@ -197,6 +162,6 @@ public static byte[] createPassword(byte[] identifier,
197162
* @return the secret key
198163
*/
199164
protected static SecretKey createSecretKey(byte[] key) {
200-
return new SecretKeySpec(key, SELECTED_ALGORITHM);
165+
return new SecretKeySpec(key, SecretManagerConfig.getSelectedAlgorithm());
201166
}
202167
}
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
2+
package org.apache.hadoop.security.token;
3+
4+
import org.apache.hadoop.conf.Configuration;
5+
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
6+
import org.slf4j.Logger;
7+
import org.slf4j.LoggerFactory;
8+
9+
import javax.crypto.KeyGenerator;
10+
import javax.crypto.Mac;
11+
import javax.crypto.SecretKey;
12+
import java.security.NoSuchAlgorithmException;
13+
14+
/**
15+
* Provides configuration and utility methods for managing cryptographic key generation
16+
* and message authentication code (MAC) generation using specified algorithms and key lengths.
17+
* <p>
18+
* This class supports static access to the selected cryptographic algorithm and key length,
19+
* and provides methods to create configured {@link javax.crypto.KeyGenerator} and {@link javax.crypto.Mac} instances.
20+
* The configuration is initialized statically from a provided {@link Configuration} object.
21+
* <p>
22+
* The {@link SecretManager} has some static method, so static configuration is required
23+
*/
24+
public class SecretManagerConfig {
25+
private static final Logger LOG = LoggerFactory.getLogger(SecretManagerConfig.class);
26+
private static String SELECTED_ALGORITHM;
27+
private static int SELECTED_LENGTH;
28+
29+
static {
30+
update(new Configuration());
31+
}
32+
33+
/**
34+
* Updates the selected cryptographic algorithm and key length using the provided
35+
* Hadoop {@link Configuration}. This method reads the values for
36+
* {@code HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_KEY} and
37+
* {@code HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_KEY}, or uses default values if not set.
38+
*
39+
* @param conf the configuration object containing cryptographic settings
40+
*/
41+
public static void update(Configuration conf) {
42+
String algorithm = conf.get(
43+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_KEY,
44+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_DEFAULT);
45+
LOG.debug("Selected hash algorithm: {}", algorithm);
46+
SELECTED_ALGORITHM = algorithm;
47+
int length = conf.getInt(
48+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_KEY,
49+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_DEFAULT);
50+
LOG.debug("Selected hash key length: {}", length);
51+
SELECTED_LENGTH = length;
52+
}
53+
54+
/**
55+
* Returns the currently selected cryptographic algorithm.
56+
*
57+
* @return the name of the selected algorithm
58+
*/
59+
public static String getSelectedAlgorithm() {
60+
return SELECTED_ALGORITHM;
61+
}
62+
63+
/**
64+
* Returns the currently selected key length in bits.
65+
*
66+
* @return the selected key length
67+
*/
68+
public static int getSelectedLength() {
69+
return SELECTED_LENGTH;
70+
}
71+
72+
/**
73+
* Sets the cryptographic algorithm to use.
74+
*
75+
* @param algorithm the algorithm name (e.g., "HmacSHA256", "AES")
76+
*/
77+
public static void setSelectedAlgorithm(String algorithm) {
78+
SELECTED_ALGORITHM = algorithm;
79+
LOG.debug("Selected hash algorithm set to {}", algorithm);
80+
81+
}
82+
83+
/**
84+
* Sets the cryptographic key length to use (in bits).
85+
*
86+
* @param length the key length
87+
*/
88+
public static void setSelectedLength(int length) {
89+
SELECTED_LENGTH = length;
90+
LOG.debug("Selected hash key length set to{}", length);
91+
}
92+
93+
94+
/**
95+
* Creates a new {@link KeyGenerator} instance configured with the currently selected
96+
* algorithm and key length.
97+
*
98+
* @return a new {@code KeyGenerator} instance
99+
* @throws IllegalArgumentException if the specified algorithm is not available
100+
*/
101+
public static KeyGenerator createKeyGenerator() {
102+
LOG.debug("Creating key generator instance {}, {}", SELECTED_ALGORITHM, SELECTED_LENGTH);
103+
try {
104+
KeyGenerator keyGen = KeyGenerator.getInstance(SELECTED_ALGORITHM);
105+
keyGen.init(SELECTED_LENGTH);
106+
return keyGen;
107+
} catch (NoSuchAlgorithmException nsa) {
108+
throw new IllegalArgumentException("Can't find " + SELECTED_ALGORITHM, nsa);
109+
}
110+
}
111+
112+
/**
113+
* Creates a new {@link Mac} instance using the currently selected algorithm.
114+
*
115+
* @return a new {@code Mac} instance
116+
* @throws IllegalArgumentException if the specified algorithm is not available
117+
*/
118+
public static Mac createMac() {
119+
LOG.debug("Creating mac instance {}", SELECTED_ALGORITHM);
120+
try {
121+
return Mac.getInstance(SELECTED_ALGORITHM);
122+
} catch (NoSuchAlgorithmException nsa) {
123+
throw new IllegalArgumentException("Can't find " + SELECTED_ALGORITHM, nsa);
124+
}
125+
}
126+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package org.apache.hadoop.security.token;
2+
3+
import org.apache.hadoop.conf.Configuration;
4+
import org.apache.hadoop.fs.CommonConfigurationKeysPublic;
5+
import org.junit.jupiter.api.AfterEach;
6+
import org.junit.jupiter.api.Test;
7+
8+
import javax.crypto.KeyGenerator;
9+
import javax.crypto.Mac;
10+
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
import static org.junit.jupiter.api.Assertions.assertThrows;
13+
14+
public class TestSecurityManagerConfig {
15+
16+
private static final String DEFAULT_ALGORITHM =
17+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_DEFAULT;
18+
private static final int DEFAULT_LENGTH =
19+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_DEFAULT;
20+
21+
private static final String STRONG_ALGORITHM = "HmacSHA256";
22+
private static final int STRONG_LENGTH = 256;
23+
24+
@Test
25+
public void testDefaults() {
26+
assertEquals(DEFAULT_ALGORITHM, SecretManagerConfig.getSelectedAlgorithm());
27+
assertEquals(DEFAULT_LENGTH, SecretManagerConfig.getSelectedLength());
28+
}
29+
30+
@Test
31+
public void testUpdateByConfig() {
32+
Configuration conf = new Configuration();
33+
conf.set(
34+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_KEY,
35+
STRONG_ALGORITHM
36+
);
37+
conf.setInt(
38+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_KEY,
39+
STRONG_LENGTH
40+
);
41+
SecretManagerConfig.update(conf);
42+
assertEquals(STRONG_ALGORITHM, SecretManagerConfig.getSelectedAlgorithm());
43+
assertEquals(STRONG_LENGTH, SecretManagerConfig.getSelectedLength());
44+
}
45+
46+
@Test
47+
public void testUpdateAlgorithmBySetter() {
48+
SecretManagerConfig.setSelectedAlgorithm(STRONG_ALGORITHM);
49+
assertEquals(STRONG_ALGORITHM, SecretManagerConfig.getSelectedAlgorithm());
50+
assertEquals(DEFAULT_LENGTH, SecretManagerConfig.getSelectedLength());
51+
}
52+
53+
@Test
54+
public void testUpdateLengthBySetter() {
55+
SecretManagerConfig.setSelectedLength(STRONG_LENGTH);
56+
assertEquals(DEFAULT_ALGORITHM, SecretManagerConfig.getSelectedAlgorithm());
57+
assertEquals(STRONG_LENGTH, SecretManagerConfig.getSelectedLength());
58+
}
59+
60+
@Test
61+
public void testMacCreation() {
62+
SecretManagerConfig.setSelectedAlgorithm(STRONG_ALGORITHM);
63+
Mac mac = SecretManagerConfig.createMac();
64+
assertEquals(STRONG_ALGORITHM, mac.getAlgorithm());
65+
}
66+
67+
@Test
68+
public void testMacCreationUnknownAlgorithm() {
69+
SecretManagerConfig.setSelectedAlgorithm("testMacCreationUnknownAlgorithm_NO_ALG");
70+
assertThrows(IllegalArgumentException.class, SecretManagerConfig::createMac);
71+
}
72+
73+
@Test
74+
public void testKeygenCreation() {
75+
SecretManagerConfig.setSelectedAlgorithm(STRONG_ALGORITHM);
76+
KeyGenerator keyGenerator = SecretManagerConfig.createKeyGenerator();
77+
assertEquals(STRONG_ALGORITHM, keyGenerator.getAlgorithm());
78+
}
79+
80+
@Test
81+
public void testKeygenCreationUnknownAlgorithm() {
82+
SecretManagerConfig.setSelectedAlgorithm("testKeygenCreationUnknownAlgorithm_NO_ALG");
83+
assertThrows(IllegalArgumentException.class, SecretManagerConfig::createKeyGenerator);
84+
}
85+
86+
@AfterEach
87+
public void tearDown() {
88+
SecretManagerConfig.setSelectedAlgorithm(DEFAULT_ALGORITHM);
89+
SecretManagerConfig.setSelectedLength(DEFAULT_LENGTH);
90+
}
91+
}

0 commit comments

Comments
 (0)