[Security] Handle cache expiry in token service (elastic/x-pack-elasticsearch#3565)
* [Security] Handle cache expiry in token service The keyCache on TokenService.KeyAndCache has a 60 minute expiry. If the token service was idle for more than 60 minutes, the current key would be expired and it would then fail to generate user tokens. Original commit: elastic/x-pack-elasticsearch@fd98130a27
This commit is contained in:
parent
29663c1f38
commit
b0552e1c6e
|
@ -427,7 +427,11 @@ public final class TokenService extends AbstractComponent {
|
|||
private Cipher getEncryptionCipher(byte[] iv, KeyAndCache keyAndCache) throws GeneralSecurityException {
|
||||
Cipher cipher = Cipher.getInstance(ENCRYPTION_CIPHER);
|
||||
BytesKey salt = keyAndCache.getSalt();
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyAndCache.getKey(salt), new GCMParameterSpec(128, iv), secureRandom);
|
||||
try {
|
||||
cipher.init(Cipher.ENCRYPT_MODE, keyAndCache.getOrComputeKey(salt), new GCMParameterSpec(128, iv), secureRandom);
|
||||
} catch (ExecutionException e) {
|
||||
throw new ElasticsearchSecurityException("Failed to compute secret key for active salt", e);
|
||||
}
|
||||
cipher.updateAAD(currentVersionBytes);
|
||||
cipher.updateAAD(salt.bytes);
|
||||
return cipher;
|
||||
|
@ -776,6 +780,13 @@ public final class TokenService extends AbstractComponent {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* For testing
|
||||
*/
|
||||
void clearActiveKeyCache() {
|
||||
this.keyCache.activeKeyCache.keyCache.invalidateAll();
|
||||
}
|
||||
|
||||
static final class KeyAndTimestamp implements Writeable {
|
||||
private final SecureString key;
|
||||
private final long timestamp;
|
||||
|
|
|
@ -43,8 +43,10 @@ import java.util.Base64;
|
|||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.time.Clock.systemUTC;
|
||||
import static org.elasticsearch.repositories.ESBlobStoreTestCase.randomBytes;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.notNullValue;
|
||||
import static org.mockito.Matchers.any;
|
||||
import static org.mockito.Matchers.anyString;
|
||||
import static org.mockito.Matchers.eq;
|
||||
|
@ -99,8 +101,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testAttachAndGetToken() throws Exception {
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
final UserToken token = tokenService.createUserToken(authentication);
|
||||
assertNotNull(token);
|
||||
|
@ -117,7 +118,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
|
||||
try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) {
|
||||
// verify a second separate token service with its own salt can also verify
|
||||
TokenService anotherService = new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService
|
||||
TokenService anotherService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService
|
||||
, clusterService);
|
||||
anotherService.refreshMetaData(tokenService.getTokenMetaData());
|
||||
PlainActionFuture<UserToken> future = new PlainActionFuture<>();
|
||||
|
@ -128,8 +129,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testRotateKey() throws Exception {
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
final UserToken token = tokenService.createUserToken(authentication);
|
||||
assertNotNull(token);
|
||||
|
@ -175,13 +175,12 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testKeyExchange() throws Exception {
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
int numRotations = 0;randomIntBetween(1, 5);
|
||||
for (int i = 0; i < numRotations; i++) {
|
||||
rotateKeys(tokenService);
|
||||
}
|
||||
TokenService otherTokenService = new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService,
|
||||
TokenService otherTokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService,
|
||||
clusterService);
|
||||
otherTokenService.refreshMetaData(tokenService.getTokenMetaData());
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
|
@ -210,8 +209,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testPruneKeys() throws Exception {
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
final UserToken token = tokenService.createUserToken(authentication);
|
||||
assertNotNull(token);
|
||||
|
@ -267,8 +265,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
|
||||
public void testPassphraseWorks() throws Exception {
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
final UserToken token = tokenService.createUserToken(authentication);
|
||||
assertNotNull(token);
|
||||
|
@ -285,7 +282,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
|
||||
try (ThreadContext.StoredContext ignore = requestContext.newStoredContext(true)) {
|
||||
// verify a second separate token service with its own passphrase cannot verify
|
||||
TokenService anotherService = new TokenService(Settings.EMPTY, Clock.systemUTC(), client, lifecycleService,
|
||||
TokenService anotherService = new TokenService(Settings.EMPTY, systemUTC(), client, lifecycleService,
|
||||
clusterService);
|
||||
PlainActionFuture<UserToken> future = new PlainActionFuture<>();
|
||||
anotherService.getAndValidateToken(requestContext, future);
|
||||
|
@ -293,10 +290,21 @@ public class TokenServiceTests extends ESTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
public void testGetTokenWhenKeyCacheHasExpired() throws Exception {
|
||||
TokenService tokenService = new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
|
||||
UserToken token = tokenService.createUserToken(authentication);
|
||||
assertThat(tokenService.getUserTokenString(token), notNullValue());
|
||||
|
||||
tokenService.clearActiveKeyCache();
|
||||
assertThat(tokenService.getUserTokenString(token), notNullValue());
|
||||
}
|
||||
|
||||
public void testInvalidatedToken() throws Exception {
|
||||
when(lifecycleService.isSecurityIndexExisting()).thenReturn(true);
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
final UserToken token = tokenService.createUserToken(authentication);
|
||||
assertNotNull(token);
|
||||
|
@ -382,7 +390,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
TokenService tokenService = new TokenService(Settings.builder()
|
||||
.put(XPackSettings.TOKEN_SERVICE_ENABLED_SETTING.getKey(), false)
|
||||
.build(),
|
||||
Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
systemUTC(), client, lifecycleService, clusterService);
|
||||
IllegalStateException e = expectThrows(IllegalStateException.class, () -> tokenService.createUserToken(null));
|
||||
assertEquals("tokens are not enabled", e.getMessage());
|
||||
|
||||
|
@ -424,7 +432,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
final int numBytes = randomIntBetween(1, TokenService.MINIMUM_BYTES + 32);
|
||||
final byte[] randomBytes = new byte[numBytes];
|
||||
random().nextBytes(randomBytes);
|
||||
TokenService tokenService = new TokenService(Settings.EMPTY, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
TokenService tokenService = new TokenService(Settings.EMPTY, systemUTC(), client, lifecycleService, clusterService);
|
||||
|
||||
ThreadContext requestContext = new ThreadContext(Settings.EMPTY);
|
||||
requestContext.putHeader("Authorization", "Bearer " + Base64.getEncoder().encodeToString(randomBytes));
|
||||
|
@ -438,7 +446,7 @@ public class TokenServiceTests extends ESTestCase {
|
|||
|
||||
public void testIndexNotAvailable() throws Exception {
|
||||
TokenService tokenService =
|
||||
new TokenService(tokenServiceEnabledSettings, Clock.systemUTC(), client, lifecycleService, clusterService);
|
||||
new TokenService(tokenServiceEnabledSettings, systemUTC(), client, lifecycleService, clusterService);
|
||||
Authentication authentication = new Authentication(new User("joe", "admin"), new RealmRef("native_realm", "native", "node1"), null);
|
||||
final UserToken token = tokenService.createUserToken(authentication);
|
||||
assertNotNull(token);
|
||||
|
|
Loading…
Reference in New Issue