Skip to content

Commit

Permalink
Internal REST tests can update keystore file
Browse files Browse the repository at this point in the history
  • Loading branch information
n1v0lg committed Dec 7, 2023
1 parent 6313275 commit 4d8ab46
Show file tree
Hide file tree
Showing 5 changed files with 89 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,18 @@ private void copyExtraConfigFiles() {
});
}

public void writeToKeystoreFile() {
final Path keystoreFile = workingDir.resolve("config").resolve("elasticsearch.keystore");
try {
Files.deleteIfExists(keystoreFile);
} catch (IOException e) {
throw new RuntimeException(e);
}
createKeystore();
addKeystoreSettings();
addKeystoreFiles();
}

private void createKeystore() {
if (spec.getKeystorePassword() == null || spec.getKeystorePassword().isEmpty()) {
runToolScript("elasticsearch-keystore", null, "-v", "create");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,11 @@ public InputStream getNodeLog(int index, LogType logType) {
return nodes.get(index).getLog(logType);
}

@Override
public void writeToKeystoreFile() {
execute(() -> nodes.parallelStream().forEach(Node::writeToKeystoreFile));
}

protected void waitUntilReady() {
writeUnicastHostsFile();
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,12 @@ public InputStream getNodeLog(int index, LogType logType) {
return handle.getNodeLog(index, logType);
}

@Override
public void writeToKeystoreFile() {
checkHandle();
handle.writeToKeystoreFile();
}

protected H getHandle() {
return handle;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,9 @@ public interface LocalClusterHandle extends ClusterHandle {
* Returns an {@link InputStream} for the given node log.
*/
InputStream getNodeLog(int index, LogType logType);

/**
* Writes current keystore settings to key-store file on each node.
*/
void writeToKeystoreFile();
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

import org.apache.http.HttpHost;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.client.Request;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.ResponseException;
import org.elasticsearch.client.RestClient;
Expand All @@ -31,11 +32,13 @@
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.util.concurrent.ThreadContext;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.core.PathUtils;
import org.elasticsearch.core.Strings;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.test.TestSecurityClient;
import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.test.cluster.MutableSettingsProvider;
import org.elasticsearch.test.cluster.local.LocalClusterSpec;
import org.elasticsearch.test.cluster.local.distribution.DistributionType;
import org.elasticsearch.test.cluster.util.resource.Resource;
Expand Down Expand Up @@ -81,6 +84,11 @@ public class JwtRestIT extends ESRestTestCase {
]}""".replaceAll("\\s", "");
public static final String HMAC_PASSPHRASE = "test-HMAC/secret passphrase-value";
private static final String VALID_SHARED_SECRET = "test-secret";
private static final MutableSettingsProvider keystoreSettings = new MutableSettingsProvider() {
{
put("xpack.security.authc.realms.jwt.jwt2.client_authentication.shared_secret", VALID_SHARED_SECRET);
}
};

@ClassRule
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
Expand All @@ -105,10 +113,10 @@ public class JwtRestIT extends ESRestTestCase {
.setting("xpack.security.http.ssl.certificate_authorities", "ca.crt")
.setting("xpack.security.http.ssl.client_authentication", "optional")
.settings(JwtRestIT::realmSettings)
.keystore("xpack.security.authc.realms.jwt.jwt2.client_authentication.shared_secret", VALID_SHARED_SECRET)
.keystore("xpack.security.authc.realms.jwt.jwt2.hmac_key", HMAC_PASSPHRASE)
.keystore("xpack.security.authc.realms.jwt.jwt3.hmac_jwkset", HMAC_JWKSET)
.keystore("xpack.security.authc.realms.jwt.jwt3.client_authentication.shared_secret", VALID_SHARED_SECRET)
.keystore(keystoreSettings)
.user("admin_user", "admin-password")
.user("test_file_user", "test-password", "viewer", false)
.build();
Expand Down Expand Up @@ -170,6 +178,7 @@ private static Map<String, String> realmSettings(LocalClusterSpec.LocalNodeSpec
settings.put("xpack.security.authc.realms.jwt.jwt2.required_claims.token_use", "access");
settings.put("xpack.security.authc.realms.jwt.jwt2.authorization_realms", "lookup_native");
settings.put("xpack.security.authc.realms.jwt.jwt2.client_authentication.type", "shared_secret");
settings.put("xpack.security.authc.realms.jwt.jwt2.client_authentication.rotation_grace_period", "0s");

// Place PKI realm after JWT realm to verify realm chain fall-through
settings.put("xpack.security.authc.realms.pki.pki_realm.order", "4");
Expand Down Expand Up @@ -499,6 +508,57 @@ public void testAuthenticationFailureIfDelegatedAuthorizationFails() throws Exce
}
}

public void testReloadClientSecret() throws Exception {
final String principal = SERVICE_SUBJECT.get();
final String username = getUsernameFromPrincipal(principal);
final List<String> roles = randomRoles();
createUser(username, roles, Map.of());

try {
getSecurityClient(buildAndSignJwtForRealm2(principal), Optional.of(VALID_SHARED_SECRET)).authenticate();

// secret not update yet, so authentication fails
final String newValidSharedSecret = "new-valid-secret";
assertThat(
expectThrows(
ResponseException.class,
() -> getSecurityClient(buildAndSignJwtForRealm2(principal), Optional.of(newValidSharedSecret)).authenticate()
).getResponse(),
hasStatusCode(RestStatus.UNAUTHORIZED)
);

writeSettingToKeystoreThenReload(
"xpack.security.authc.realms.jwt.jwt2.client_authentication.shared_secret",
newValidSharedSecret
);

// secret updated, so authentication succeeds
getSecurityClient(buildAndSignJwtForRealm2(principal), Optional.of(newValidSharedSecret)).authenticate();

// removing setting also works and leads to authentication failure
writeSettingToKeystoreThenReload("xpack.security.authc.realms.jwt.jwt2.client_authentication.shared_secret", null);
assertThat(
expectThrows(
ResponseException.class,
() -> getSecurityClient(buildAndSignJwtForRealm2(principal), Optional.of(newValidSharedSecret)).authenticate()
).getResponse(),
hasStatusCode(RestStatus.UNAUTHORIZED)
);
} finally {
deleteUser(username);
}
}

private void writeSettingToKeystoreThenReload(String setting, @Nullable String value) throws IOException {
if (value == null) {
keystoreSettings.remove(setting);
} else {
keystoreSettings.put(setting, value);
}
cluster.writeToKeystoreFile();
assertOK(adminClient().performRequest(new Request("POST", "/_nodes/reload_secure_settings")));
}

public void testFailureOnInvalidClientAuthentication() throws Exception {
final String principal = SERVICE_SUBJECT.get();
final String username = getUsernameFromPrincipal(principal);
Expand Down

0 comments on commit 4d8ab46

Please sign in to comment.