Skip to content

Commit fecb7de

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 fecb7de

File tree

3 files changed

+221
-44
lines changed

3 files changed

+221
-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: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
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+
SELECTED_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: {}", SELECTED_ALGORITHM);
46+
SELECTED_LENGTH = conf.getInt(
47+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_KEY,
48+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_DEFAULT);
49+
LOG.debug("Selected hash key length: {}", SELECTED_LENGTH);
50+
}
51+
52+
/**
53+
* Returns the currently selected cryptographic algorithm.
54+
*
55+
* @return the name of the selected algorithm
56+
*/
57+
public static String getSelectedAlgorithm() {
58+
return SELECTED_ALGORITHM;
59+
}
60+
61+
/**
62+
* Returns the currently selected key length in bits.
63+
*
64+
* @return the selected key length
65+
*/
66+
public static int getSelectedLength() {
67+
return SELECTED_LENGTH;
68+
}
69+
70+
/**
71+
* Sets the cryptographic algorithm to use.
72+
*
73+
* @param algorithm the algorithm name (e.g., "HmacSHA256", "AES")
74+
*/
75+
public static void setSelectedAlgorithm(String algorithm) {
76+
SELECTED_ALGORITHM = algorithm;
77+
LOG.debug("Selected hash algorithm set to {}", algorithm);
78+
79+
}
80+
81+
/**
82+
* Sets the cryptographic key length to use (in bits).
83+
*
84+
* @param length the key length
85+
*/
86+
public static void setSelectedLength(int length) {
87+
SELECTED_LENGTH = length;
88+
LOG.debug("Selected hash key length set to{}", length);
89+
}
90+
91+
92+
/**
93+
* Creates a new {@link KeyGenerator} instance configured with the currently selected
94+
* algorithm and key length.
95+
*
96+
* @return a new {@code KeyGenerator} instance
97+
* @throws IllegalArgumentException if the specified algorithm is not available
98+
*/
99+
public static KeyGenerator createKeyGenerator() {
100+
LOG.debug("Creating key generator instance {}, {}", SELECTED_ALGORITHM, SELECTED_LENGTH);
101+
try {
102+
KeyGenerator keyGen = KeyGenerator.getInstance(SELECTED_ALGORITHM);
103+
keyGen.init(SELECTED_LENGTH);
104+
return keyGen;
105+
} catch (NoSuchAlgorithmException nsa) {
106+
throw new IllegalArgumentException("Can't find " + SELECTED_ALGORITHM, nsa);
107+
}
108+
}
109+
110+
/**
111+
* Creates a new {@link Mac} instance using the currently selected algorithm.
112+
*
113+
* @return a new {@code Mac} instance
114+
* @throws IllegalArgumentException if the specified algorithm is not available
115+
*/
116+
public static Mac createMac() {
117+
LOG.debug("Creating mac instance {}", SELECTED_ALGORITHM);
118+
try {
119+
return Mac.getInstance(SELECTED_ALGORITHM);
120+
} catch (NoSuchAlgorithmException nsa) {
121+
throw new IllegalArgumentException("Can't find " + SELECTED_ALGORITHM, nsa);
122+
}
123+
}
124+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
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 final String defaultAlgorithm =
17+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_DEFAULT;
18+
private final int defaultLength =
19+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_DEFAULT;
20+
private final String strongAlgorithm = "HmacSHA256";
21+
private final int strongLength = 256;
22+
23+
@Test
24+
public void testDefaults() {
25+
assertEquals(defaultAlgorithm, SecretManagerConfig.getSelectedAlgorithm());
26+
assertEquals(defaultLength, SecretManagerConfig.getSelectedLength());
27+
}
28+
29+
@Test
30+
public void testUpdateByConfig() {
31+
Configuration conf = new Configuration();
32+
conf.set(
33+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_GENERATOR_ALGORITHM_KEY,
34+
strongAlgorithm);
35+
conf.setInt(
36+
CommonConfigurationKeysPublic.HADOOP_SECURITY_SECRET_MANAGER_KEY_LENGTH_KEY,
37+
strongLength);
38+
SecretManagerConfig.update(conf);
39+
assertEquals(strongAlgorithm, SecretManagerConfig.getSelectedAlgorithm());
40+
assertEquals(strongLength, SecretManagerConfig.getSelectedLength());
41+
}
42+
43+
@Test
44+
public void testUpdateAlgorithmBySetter() {
45+
SecretManagerConfig.setSelectedAlgorithm(strongAlgorithm);
46+
assertEquals(strongAlgorithm, SecretManagerConfig.getSelectedAlgorithm());
47+
assertEquals(defaultLength, SecretManagerConfig.getSelectedLength());
48+
}
49+
50+
@Test
51+
public void testUpdateLengthBySetter() {
52+
SecretManagerConfig.setSelectedLength(strongLength);
53+
assertEquals(defaultAlgorithm, SecretManagerConfig.getSelectedAlgorithm());
54+
assertEquals(strongLength, SecretManagerConfig.getSelectedLength());
55+
}
56+
57+
@Test
58+
public void testMacCreation() {
59+
SecretManagerConfig.setSelectedAlgorithm(strongAlgorithm);
60+
Mac mac = SecretManagerConfig.createMac();
61+
assertEquals(strongAlgorithm, mac.getAlgorithm());
62+
}
63+
64+
@Test
65+
public void testMacCreationUnknownAlgorithm() {
66+
SecretManagerConfig.setSelectedAlgorithm("testMacCreationUnknownAlgorithm_NO_ALG");
67+
assertThrows(IllegalArgumentException.class, SecretManagerConfig::createMac);
68+
}
69+
70+
@Test
71+
public void testKeygenCreation() {
72+
SecretManagerConfig.setSelectedAlgorithm(strongAlgorithm);
73+
KeyGenerator keyGenerator = SecretManagerConfig.createKeyGenerator();
74+
assertEquals(strongAlgorithm, keyGenerator.getAlgorithm());
75+
}
76+
77+
@Test
78+
public void testKeygenCreationUnknownAlgorithm() {
79+
SecretManagerConfig.setSelectedAlgorithm("testKeygenCreationUnknownAlgorithm_NO_ALG");
80+
assertThrows(IllegalArgumentException.class, SecretManagerConfig::createKeyGenerator);
81+
}
82+
83+
@AfterEach
84+
public void tearDown() {
85+
SecretManagerConfig.setSelectedAlgorithm(defaultAlgorithm);
86+
SecretManagerConfig.setSelectedLength(defaultLength);
87+
}
88+
}

0 commit comments

Comments
 (0)