mirror of
https://github.com/elastic/elasticsearch.git
synced 2025-06-27 17:10:22 -04:00
Handle NotEntitledException
in SSL file utils (#123491)
SSL file utils currently only handle security manager access control exceptions around file read checks. This PR extends these to support entitlement checks as well. There is no easy way to unit test this since we can't run unit tests with entitlements enabled (for now). The PR includes a REST test instead. Relates: https://github.com/elastic/elasticsearch/issues/121960
This commit is contained in:
parent
c7e7dbe904
commit
a77626368f
11 changed files with 109 additions and 1 deletions
|
@ -10,6 +10,7 @@ apply plugin: "elasticsearch.publish"
|
|||
|
||||
dependencies {
|
||||
api project(':libs:core')
|
||||
api project(':libs:entitlement')
|
||||
|
||||
testImplementation(project(":test:framework")) {
|
||||
exclude group: 'org.elasticsearch', module: 'ssl-config'
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
module org.elasticsearch.sslconfig {
|
||||
requires org.elasticsearch.base;
|
||||
requires org.elasticsearch.entitlement;
|
||||
|
||||
exports org.elasticsearch.common.ssl;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
package org.elasticsearch.common.ssl;
|
||||
|
||||
import org.elasticsearch.core.Tuple;
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -127,6 +128,8 @@ public final class PemKeyConfig implements SslKeyConfig {
|
|||
return privateKey;
|
||||
} catch (AccessControlException e) {
|
||||
throw SslFileUtil.accessControlFailure(KEY_FILE_TYPE, List.of(path), e, configBasePath);
|
||||
} catch (NotEntitledException e) {
|
||||
throw SslFileUtil.notEntitledFailure(KEY_FILE_TYPE, List.of(path), e, configBasePath);
|
||||
} catch (IOException e) {
|
||||
throw SslFileUtil.ioException(KEY_FILE_TYPE, List.of(path), e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
package org.elasticsearch.common.ssl;
|
||||
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Path;
|
||||
|
@ -99,6 +101,8 @@ public final class PemTrustConfig implements SslTrustConfig {
|
|||
return PemUtils.readCertificates(paths);
|
||||
} catch (AccessControlException e) {
|
||||
throw SslFileUtil.accessControlFailure(CA_FILE_TYPE, paths, e, basePath);
|
||||
} catch (NotEntitledException e) {
|
||||
throw SslFileUtil.notEntitledFailure(CA_FILE_TYPE, paths, e, basePath);
|
||||
} catch (IOException e) {
|
||||
throw SslFileUtil.ioException(CA_FILE_TYPE, paths, e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
package org.elasticsearch.common.ssl;
|
||||
|
||||
import org.elasticsearch.core.CharArrays;
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
|
@ -112,6 +113,8 @@ public final class PemUtils {
|
|||
return privateKey;
|
||||
} catch (AccessControlException e) {
|
||||
throw SslFileUtil.accessControlFailure("PEM private key", List.of(path), e, null);
|
||||
} catch (NotEntitledException e) {
|
||||
throw SslFileUtil.notEntitledFailure("PEM private key", List.of(path), e, null);
|
||||
} catch (IOException e) {
|
||||
throw SslFileUtil.ioException("PEM private key", List.of(path), e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
package org.elasticsearch.common.ssl;
|
||||
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.AccessDeniedException;
|
||||
|
@ -78,7 +80,15 @@ final class SslFileUtil {
|
|||
return new SslConfigException(message, cause);
|
||||
}
|
||||
|
||||
static SslConfigException notEntitledFailure(String fileType, List<Path> paths, NotEntitledException cause, Path basePath) {
|
||||
return innerAccessControlFailure(fileType, paths, cause, basePath);
|
||||
}
|
||||
|
||||
static SslConfigException accessControlFailure(String fileType, List<Path> paths, AccessControlException cause, Path basePath) {
|
||||
return innerAccessControlFailure(fileType, paths, cause, basePath);
|
||||
}
|
||||
|
||||
private static SslConfigException innerAccessControlFailure(String fileType, List<Path> paths, Exception cause, Path basePath) {
|
||||
String message = "cannot read configured " + fileType + " [" + pathsToString(paths) + "] because ";
|
||||
if (paths.size() == 1) {
|
||||
message += "access to read the file is blocked";
|
||||
|
|
|
@ -11,6 +11,7 @@ package org.elasticsearch.common.ssl;
|
|||
|
||||
import org.elasticsearch.core.Nullable;
|
||||
import org.elasticsearch.core.Tuple;
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
@ -168,6 +169,8 @@ public class StoreKeyConfig implements SslKeyConfig {
|
|||
return KeyStoreUtil.readKeyStore(path, type, storePassword);
|
||||
} catch (AccessControlException e) {
|
||||
throw SslFileUtil.accessControlFailure("[" + type + "] keystore", List.of(path), e, configBasePath);
|
||||
} catch (NotEntitledException e) {
|
||||
throw SslFileUtil.notEntitledFailure("[" + type + "] keystore", List.of(path), e, configBasePath);
|
||||
} catch (IOException e) {
|
||||
throw SslFileUtil.ioException("[" + type + "] keystore", List.of(path), e);
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
package org.elasticsearch.common.ssl;
|
||||
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.security.AccessControlException;
|
||||
|
@ -95,6 +97,8 @@ public final class StoreTrustConfig implements SslTrustConfig {
|
|||
return KeyStoreUtil.readKeyStore(path, type, password);
|
||||
} catch (AccessControlException e) {
|
||||
throw SslFileUtil.accessControlFailure(fileTypeForException(), List.of(path), e, configBasePath);
|
||||
} catch (NotEntitledException e) {
|
||||
throw SslFileUtil.notEntitledFailure(fileTypeForException(), List.of(path), e, configBasePath);
|
||||
} catch (IOException e) {
|
||||
throw SslFileUtil.ioException(fileTypeForException(), List.of(path), e, getAdditionalErrorDetails());
|
||||
} catch (GeneralSecurityException e) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
module org.elasticsearch.xcore {
|
||||
requires org.elasticsearch.cli;
|
||||
requires org.elasticsearch.entitlement;
|
||||
requires org.elasticsearch.base;
|
||||
requires org.elasticsearch.grok;
|
||||
requires org.elasticsearch.server;
|
||||
|
|
|
@ -12,6 +12,7 @@ import org.elasticsearch.ElasticsearchException;
|
|||
import org.elasticsearch.action.support.PlainActionFuture;
|
||||
import org.elasticsearch.common.ssl.SslConfiguration;
|
||||
import org.elasticsearch.core.TimeValue;
|
||||
import org.elasticsearch.entitlement.runtime.api.NotEntitledException;
|
||||
import org.elasticsearch.watcher.FileChangesListener;
|
||||
import org.elasticsearch.watcher.FileWatcher;
|
||||
import org.elasticsearch.watcher.ResourceWatcherService;
|
||||
|
@ -109,7 +110,7 @@ public final class SSLConfigurationReloader {
|
|||
fileWatcher.addListener(changeListener);
|
||||
try {
|
||||
resourceWatcherService.add(fileWatcher, Frequency.HIGH);
|
||||
} catch (IOException | AccessControlException e) {
|
||||
} catch (IOException | AccessControlException | NotEntitledException e) {
|
||||
logger.error("failed to start watching directory [{}] for ssl configurations [{}] - {}", path, configurations, e);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.xpack.security.ssl;
|
||||
|
||||
import org.elasticsearch.common.io.Streams;
|
||||
import org.elasticsearch.common.settings.SecureString;
|
||||
import org.elasticsearch.common.settings.Settings;
|
||||
import org.elasticsearch.common.util.concurrent.ThreadContext;
|
||||
import org.elasticsearch.test.cluster.ElasticsearchCluster;
|
||||
import org.elasticsearch.test.cluster.LogType;
|
||||
import org.elasticsearch.test.cluster.MutableSettingsProvider;
|
||||
import org.elasticsearch.test.rest.ESRestTestCase;
|
||||
import org.elasticsearch.xpack.security.SecurityOnTrialLicenseRestTestCase;
|
||||
import org.junit.ClassRule;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.hamcrest.Matchers.is;
|
||||
|
||||
public class SslEntitlementRestIT extends ESRestTestCase {
|
||||
|
||||
private static final MutableSettingsProvider settingsProvider = new MutableSettingsProvider();
|
||||
|
||||
@ClassRule
|
||||
public static ElasticsearchCluster cluster = ElasticsearchCluster.local()
|
||||
.apply(SecurityOnTrialLicenseRestTestCase.commonTrialSecurityClusterConfig)
|
||||
.settings(settingsProvider)
|
||||
.systemProperty("es.entitlements.enabled", "true")
|
||||
.build();
|
||||
|
||||
@Override
|
||||
protected String getTestRestCluster() {
|
||||
return cluster.getHttpAddresses();
|
||||
}
|
||||
|
||||
public void testSslEntitlementInaccessiblePath() throws IOException {
|
||||
settingsProvider.put("xpack.security.transport.ssl.key", "/bad/path/transport.key");
|
||||
settingsProvider.put("xpack.security.transport.ssl.certificate", "/bad/path/transport.crt");
|
||||
expectThrows(Exception.class, () -> cluster.restart(false));
|
||||
AtomicBoolean found = new AtomicBoolean(false);
|
||||
for (int i = 0; i < cluster.getNumNodes(); i++) {
|
||||
try (InputStream log = cluster.getNodeLog(i, LogType.SERVER)) {
|
||||
Streams.readAllLines(log, line -> {
|
||||
if (line.contains("failed to load SSL configuration") && line.contains("because access to read the file is blocked")) {
|
||||
found.set(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
assertThat("cluster logs did not include events of blocked file access", found.get(), is(true));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings restAdminSettings() {
|
||||
String token = basicAuthHeaderValue("admin_user", new SecureString("admin-password".toCharArray()));
|
||||
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", token).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Settings restClientSettings() {
|
||||
String token = basicAuthHeaderValue("admin_user", new SecureString("admin-password".toCharArray()));
|
||||
return Settings.builder().put(ThreadContext.PREFIX + ".Authorization", token).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean preserveClusterUponCompletion() {
|
||||
// as the cluster is dead its state can not be wiped successfully so we have to bypass wiping the cluster
|
||||
return true;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue