From 7e1195dc9a02db90577e2685b32617900f5d071a Mon Sep 17 00:00:00 2001 From: Ryan Ernst Date: Sat, 8 Mar 2025 07:02:36 -0800 Subject: [PATCH] Add exclusive access files for security module (#123676) This commit fills out missing entitlements for the security module. Specifically they are config files which require exclusive access. --- .../runtime/policy/FileAccessTree.java | 36 ++++++++++++--- .../policy/entitlements/FilesEntitlement.java | 3 +- .../runtime/policy/FileAccessTreeTests.java | 32 ++++--------- .../runtime/policy/PolicyManagerTests.java | 20 +++++--- .../common/ssl/PemKeyConfig.java | 8 +--- .../common/ssl/PemTrustConfig.java | 7 +-- .../elasticsearch/common/ssl/PemUtils.java | 6 +-- .../elasticsearch/common/ssl/SslFileUtil.java | 3 +- .../common/ssl/StoreKeyConfig.java | 6 +-- .../common/ssl/StoreTrustConfig.java | 7 +-- .../index/analysis/Analysis.java | 3 +- .../elasticsearch/watcher/FileWatcher.java | 3 +- .../index/shard/StoreRecoveryTests.java | 3 +- .../xpack/security/PrivilegedFileWatcher.java | 10 ++++ .../authc/file/FileUserRolesStore.java | 3 +- .../service/FileServiceAccountTokenStore.java | 3 +- .../security/authz/store/FileRolesStore.java | 3 +- .../operator/FileOperatorUsersStore.java | 3 +- .../plugin-metadata/entitlement-policy.yaml | 46 +++++++++++++------ 19 files changed, 115 insertions(+), 90 deletions(-) diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java index b951a4d7b42f..cf283feacf07 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTree.java @@ -23,8 +23,12 @@ import java.nio.file.Path; import java.nio.file.Paths; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.BiConsumer; import static java.util.Comparator.comparing; @@ -42,27 +46,47 @@ public final class FileAccessTree { /** * An intermediary structure to help globally validate exclusive paths, and then build exclusive paths for individual modules. */ - record ExclusivePath(String componentName, String moduleName, String path) { + record ExclusivePath(String componentName, Set moduleNames, String path) { @Override public String toString() { - return "[[" + componentName + "] [" + moduleName + "] [" + path + "]]"; + return "[[" + componentName + "] " + moduleNames + " [" + path + "]]"; } } static List buildExclusivePathList(List exclusiveFileEntitlements, PathLookup pathLookup) { - List exclusivePaths = new ArrayList<>(); + Map exclusivePaths = new LinkedHashMap<>(); for (ExclusiveFileEntitlement efe : exclusiveFileEntitlements) { for (FilesEntitlement.FileData fd : efe.filesEntitlement().filesData()) { if (fd.exclusive()) { List paths = fd.resolvePaths(pathLookup).toList(); for (Path path : paths) { - exclusivePaths.add(new ExclusivePath(efe.componentName(), efe.moduleName(), normalizePath(path))); + String normalizedPath = normalizePath(path); + var exclusivePath = exclusivePaths.computeIfAbsent( + normalizedPath, + k -> new ExclusivePath(efe.componentName(), new HashSet<>(), normalizedPath) + ); + if (exclusivePath.componentName().equals(efe.componentName()) == false) { + throw new IllegalArgumentException( + "Path [" + + normalizedPath + + "] is already exclusive to [" + + exclusivePath.componentName() + + "]" + + exclusivePath.moduleNames + + ", cannot add exclusive access for [" + + efe.componentName() + + "][" + + efe.moduleName + + "]" + ); + } + exclusivePath.moduleNames.add(efe.moduleName()); } } } } - return exclusivePaths.stream().sorted(comparing(ExclusivePath::path, PATH_ORDER)).distinct().toList(); + return exclusivePaths.values().stream().sorted(comparing(ExclusivePath::path, PATH_ORDER)).distinct().toList(); } static void validateExclusivePaths(List exclusivePaths) { @@ -97,7 +121,7 @@ public final class FileAccessTree { ) { List updatedExclusivePaths = new ArrayList<>(); for (ExclusivePath exclusivePath : exclusivePaths) { - if (exclusivePath.componentName().equals(componentName) == false || exclusivePath.moduleName().equals(moduleName) == false) { + if (exclusivePath.componentName().equals(componentName) == false || exclusivePath.moduleNames().contains(moduleName) == false) { updatedExclusivePaths.add(exclusivePath.path()); } } diff --git a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java index adf234f16071..c771da019d2b 100644 --- a/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java +++ b/libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/entitlements/FilesEntitlement.java @@ -164,7 +164,8 @@ public record FilesEntitlement(List filesData) implements Entitlement public Stream resolveRelativePaths(PathLookup pathLookup) { Stream result = pathLookup.settingResolver() .apply(setting) - .filter(s -> s.toLowerCase(Locale.ROOT).startsWith("https://") == false); + .filter(s -> s.toLowerCase(Locale.ROOT).startsWith("https://") == false) + .distinct(); return result.map(Path::of); } diff --git a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java index 938a946a95c6..b6f3a18a6a50 100644 --- a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java +++ b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/FileAccessTreeTests.java @@ -26,6 +26,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Set; import static org.elasticsearch.core.PathUtils.getDefaultFileSystem; import static org.elasticsearch.entitlement.runtime.policy.FileAccessTree.buildExclusivePathList; @@ -386,7 +387,7 @@ public class FileAccessTreeTests extends ESTestCase { original.moduleName(), new FilesEntitlement(List.of(originalFileData.withPlatform(WINDOWS))) ); - var originalExclusivePath = new ExclusivePath("component1", "module1", normalizePath(path("/a/b"))); + var originalExclusivePath = new ExclusivePath("component1", Set.of("module1"), normalizePath(path("/a/b"))); // Some basic tests @@ -406,27 +407,14 @@ public class FileAccessTreeTests extends ESTestCase { var distinctEntitlements = List.of(original, differentComponent, differentModule, differentPath); var distinctPaths = List.of( originalExclusivePath, - new ExclusivePath("component2", original.moduleName(), originalExclusivePath.path()), - new ExclusivePath(original.componentName(), "module2", originalExclusivePath.path()), - new ExclusivePath(original.componentName(), original.moduleName(), normalizePath(path("/c/d"))) + new ExclusivePath("component2", Set.of(original.moduleName()), originalExclusivePath.path()), + new ExclusivePath(original.componentName(), Set.of("module2"), originalExclusivePath.path()), + new ExclusivePath(original.componentName(), Set.of(original.moduleName()), normalizePath(path("/c/d"))) ); - assertEquals( - "Distinct elements should not be combined", - distinctPaths, - buildExclusivePathList(distinctEntitlements, TEST_PATH_LOOKUP) - ); - - // Do merge things we should - - List interleavedEntitlements = new ArrayList<>(); - distinctEntitlements.forEach(e -> { - interleavedEntitlements.add(e); - interleavedEntitlements.add(original); - }); - assertEquals( - "Identical elements should be combined wherever they are in the list", - distinctPaths, - buildExclusivePathList(interleavedEntitlements, TEST_PATH_LOOKUP) + var iae = expectThrows(IllegalArgumentException.class, () -> buildExclusivePathList(distinctEntitlements, TEST_PATH_LOOKUP)); + assertThat( + iae.getMessage(), + equalTo("Path [/a/b] is already exclusive to [component1][module1], cannot add exclusive access for [component2][module1]") ); var equivalentEntitlements = List.of(original, differentMode, differentPlatform); @@ -486,7 +474,7 @@ public class FileAccessTreeTests extends ESTestCase { static List exclusivePaths(String componentName, String moduleName, String... paths) { List exclusivePaths = new ArrayList<>(); for (String path : paths) { - exclusivePaths.add(new ExclusivePath(componentName, moduleName, normalizePath(path(path)))); + exclusivePaths.add(new ExclusivePath(componentName, Set.of(moduleName), normalizePath(path(path)))); } return exclusivePaths; } diff --git a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java index a0868563b87e..425afe47d547 100644 --- a/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java +++ b/libs/entitlement/src/test/java/org/elasticsearch/entitlement/runtime/policy/PolicyManagerTests.java @@ -38,6 +38,7 @@ import static java.util.Map.entry; import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.ALL_UNNAMED; import static org.elasticsearch.entitlement.runtime.policy.PolicyManager.SERVER_COMPONENT_NAME; import static org.hamcrest.Matchers.aMapWithSize; +import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.sameInstance; @@ -444,9 +445,9 @@ public class PolicyManagerTests extends ESTestCase { } public void testFilesEntitlementsWithExclusive() { - var baseTestPath = Path.of("/tmp").toAbsolutePath(); - var testPath1 = Path.of("/tmp/test").toAbsolutePath(); - var testPath2 = Path.of("/tmp/test/foo").toAbsolutePath(); + var baseTestPath = Path.of("/base").toAbsolutePath(); + var testPath1 = Path.of("/base/test").toAbsolutePath(); + var testPath2 = Path.of("/base/test/foo").toAbsolutePath(); var iae = expectThrows( IllegalArgumentException.class, () -> new PolicyManager( @@ -458,7 +459,7 @@ public class PolicyManagerTests extends ESTestCase { "test", List.of( new Scope( - "test", + "test.module1", List.of( new FilesEntitlement( List.of(FilesEntitlement.FileData.ofPath(testPath1, FilesEntitlement.Mode.READ).withExclusive(true)) @@ -472,7 +473,7 @@ public class PolicyManagerTests extends ESTestCase { "test", List.of( new Scope( - "test", + "test.module2", List.of( new FilesEntitlement( List.of(FilesEntitlement.FileData.ofPath(testPath1, FilesEntitlement.Mode.READ).withExclusive(true)) @@ -490,8 +491,13 @@ public class PolicyManagerTests extends ESTestCase { Set.of() ) ); - assertTrue(iae.getMessage().contains("duplicate/overlapping exclusive paths found in files entitlements:")); - assertTrue(iae.getMessage().contains(Strings.format("[test] [%s]]", testPath1.toString()))); + assertThat( + iae.getMessage(), + equalTo( + "Path [/base/test] is already exclusive to [plugin1][test.module1]," + + " cannot add exclusive access for [plugin2][test.module2]" + ) + ); iae = expectThrows( IllegalArgumentException.class, diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java index b1ddb89cdee7..069f04252190 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemKeyConfig.java @@ -10,11 +10,9 @@ 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; -import java.security.AccessControlException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.PrivateKey; @@ -126,10 +124,8 @@ public final class PemKeyConfig implements SslKeyConfig { throw new SslConfigException("could not load ssl private key file [" + path + "]"); } return privateKey; - } catch (AccessControlException e) { + } catch (SecurityException 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) { @@ -140,7 +136,7 @@ public final class PemKeyConfig implements SslKeyConfig { private List getCertificates(Path path) { try { return PemUtils.readCertificates(Collections.singleton(path)); - } catch (AccessControlException e) { + } catch (SecurityException e) { throw SslFileUtil.accessControlFailure(CERT_FILE_TYPE, List.of(path), e, configBasePath); } catch (IOException e) { throw SslFileUtil.ioException(CERT_FILE_TYPE, List.of(path), e); diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java index 04ea83ce6fa1..16aa02ef694d 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemTrustConfig.java @@ -9,12 +9,9 @@ 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; -import java.security.AccessControlException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.Certificate; @@ -99,10 +96,8 @@ public final class PemTrustConfig implements SslTrustConfig { private List readCertificates(List paths) { try { return PemUtils.readCertificates(paths); - } catch (AccessControlException e) { + } catch (SecurityException 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) { diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java index bb9c8d69513b..8b11356d26fc 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/PemUtils.java @@ -10,7 +10,6 @@ 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; @@ -19,7 +18,6 @@ import java.math.BigInteger; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.security.AccessControlException; import java.security.AlgorithmParameters; import java.security.GeneralSecurityException; import java.security.KeyFactory; @@ -111,10 +109,8 @@ public final class PemUtils { throw new SslConfigException("could not load ssl private key file [" + path + "]"); } return privateKey; - } catch (AccessControlException e) { + } catch (SecurityException 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) { diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslFileUtil.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslFileUtil.java index e94ef627ec36..e715b86d6cfb 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslFileUtil.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/SslFileUtil.java @@ -16,7 +16,6 @@ import java.io.IOException; import java.nio.file.AccessDeniedException; import java.nio.file.NoSuchFileException; import java.nio.file.Path; -import java.security.AccessControlException; import java.security.GeneralSecurityException; import java.security.UnrecoverableKeyException; import java.util.List; @@ -84,7 +83,7 @@ final class SslFileUtil { return innerAccessControlFailure(fileType, paths, cause, basePath); } - static SslConfigException accessControlFailure(String fileType, List paths, AccessControlException cause, Path basePath) { + static SslConfigException accessControlFailure(String fileType, List paths, SecurityException cause, Path basePath) { return innerAccessControlFailure(fileType, paths, cause, basePath); } diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java index af400d5dce6f..d1583297599d 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreKeyConfig.java @@ -11,11 +11,9 @@ 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; -import java.security.AccessControlException; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -167,10 +165,8 @@ public class StoreKeyConfig implements SslKeyConfig { private KeyStore readKeyStore(Path path) { try { return KeyStoreUtil.readKeyStore(path, type, storePassword); - } catch (AccessControlException e) { + } catch (SecurityException 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) { diff --git a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java index 16c57f7dfc82..52850ba6a003 100644 --- a/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java +++ b/libs/ssl-config/src/main/java/org/elasticsearch/common/ssl/StoreTrustConfig.java @@ -9,11 +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; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.cert.X509Certificate; @@ -95,10 +92,8 @@ public final class StoreTrustConfig implements SslTrustConfig { private KeyStore readKeyStore(Path path) { try { return KeyStoreUtil.readKeyStore(path, type, password); - } catch (AccessControlException e) { + } catch (SecurityException 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) { diff --git a/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java b/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java index a93a48046356..992e6c6d57c1 100644 --- a/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java +++ b/server/src/main/java/org/elasticsearch/index/analysis/Analysis.java @@ -64,7 +64,6 @@ import java.nio.charset.CharacterCodingException; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; -import java.security.AccessControlException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -247,7 +246,7 @@ public class Analysis { } catch (IOException ioe) { String message = Strings.format("IOException while reading %s: %s", settingPath, path); throw new IllegalArgumentException(message, ioe); - } catch (AccessControlException ace) { + } catch (SecurityException ace) { throw new IllegalArgumentException(Strings.format("Access denied trying to read file %s: %s", settingPath, path), ace); } } diff --git a/server/src/main/java/org/elasticsearch/watcher/FileWatcher.java b/server/src/main/java/org/elasticsearch/watcher/FileWatcher.java index b9ea8504f72a..4620e65534d3 100644 --- a/server/src/main/java/org/elasticsearch/watcher/FileWatcher.java +++ b/server/src/main/java/org/elasticsearch/watcher/FileWatcher.java @@ -20,7 +20,6 @@ import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; -import java.security.AccessControlException; import java.util.Arrays; import java.util.stream.StreamSupport; @@ -256,7 +255,7 @@ public class FileWatcher extends AbstractResourceWatcher { FileObserver child = new FileObserver(file); child.init(initial); return child; - } catch (AccessControlException e) { + } catch (SecurityException e) { // don't have permissions, use a placeholder logger.debug(() -> Strings.format("Don't have permissions to watch path [%s]", file), e); return new DeniedObserver(file); diff --git a/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java b/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java index 92afe312e5d1..bc3b44b90824 100644 --- a/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java +++ b/server/src/test/java/org/elasticsearch/index/shard/StoreRecoveryTests.java @@ -45,7 +45,6 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.attribute.BasicFileAttributes; -import java.security.AccessControlException; import java.util.Arrays; import java.util.Map; import java.util.function.Predicate; @@ -259,7 +258,7 @@ public class StoreRecoveryTests extends ESTestCase { BasicFileAttributes sourceAttr = Files.readAttributes(path.resolve("foo.bar"), BasicFileAttributes.class); // we won't get here - no permission ;) return destAttr.fileKey() != null && destAttr.fileKey().equals(sourceAttr.fileKey()); - } catch (AccessControlException ex) { + } catch (SecurityException ex) { return true; // if we run into that situation we know it's supported. } catch (UnsupportedOperationException ex) { return false; diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PrivilegedFileWatcher.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PrivilegedFileWatcher.java index 583bb93c2a52..e6bdfd9cde14 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PrivilegedFileWatcher.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/PrivilegedFileWatcher.java @@ -10,6 +10,7 @@ package org.elasticsearch.xpack.security; import org.elasticsearch.watcher.FileWatcher; import java.io.IOException; +import java.io.InputStream; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; @@ -34,6 +35,15 @@ public class PrivilegedFileWatcher extends FileWatcher { super(path); } + public PrivilegedFileWatcher(Path path, boolean checkFileContents) { + super(path, checkFileContents); + } + + @Override + protected InputStream newInputStream(Path path) throws IOException { + return Files.newInputStream(path); + } + @Override protected boolean fileExists(Path path) { return doPrivileged((PrivilegedAction) () -> Files.exists(path)); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java index ffc14ca96a76..06606570699d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/file/FileUserRolesStore.java @@ -20,6 +20,7 @@ import org.elasticsearch.xpack.core.XPackPlugin; import org.elasticsearch.xpack.core.security.authc.RealmConfig; import org.elasticsearch.xpack.core.security.support.NoOpLogger; import org.elasticsearch.xpack.core.security.support.Validation; +import org.elasticsearch.xpack.security.PrivilegedFileWatcher; import org.elasticsearch.xpack.security.support.SecurityFiles; import java.io.IOException; @@ -57,7 +58,7 @@ public class FileUserRolesStore { file = resolveFile(config.env()); userRoles = parseFileLenient(file, logger); listeners = new CopyOnWriteArrayList<>(Collections.singletonList(listener)); - FileWatcher watcher = new FileWatcher(file.getParent()); + FileWatcher watcher = new PrivilegedFileWatcher(file.getParent()); watcher.addListener(new FileListener()); try { watcherService.add(watcher, ResourceWatcherService.Frequency.HIGH); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java index 5a5d42b92bc8..dff79c56b32d 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/service/FileServiceAccountTokenStore.java @@ -24,6 +24,7 @@ import org.elasticsearch.xpack.core.security.action.service.TokenInfo; import org.elasticsearch.xpack.core.security.action.service.TokenInfo.TokenSource; import org.elasticsearch.xpack.core.security.authc.support.Hasher; import org.elasticsearch.xpack.core.security.support.NoOpLogger; +import org.elasticsearch.xpack.security.PrivilegedFileWatcher; import org.elasticsearch.xpack.security.authc.service.ServiceAccount.ServiceAccountId; import org.elasticsearch.xpack.security.support.CacheInvalidatorRegistry; import org.elasticsearch.xpack.security.support.FileLineParser; @@ -59,7 +60,7 @@ public class FileServiceAccountTokenStore extends CachingServiceAccountTokenStor super(env.settings(), threadPool); this.clusterService = clusterService; file = resolveFile(env); - FileWatcher watcher = new FileWatcher(file.getParent()); + FileWatcher watcher = new PrivilegedFileWatcher(file.getParent()); watcher.addListener(new FileReloadListener(file, this::tryReload)); try { resourceWatcherService.add(watcher, ResourceWatcherService.Frequency.HIGH); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java index 5dff642e3b34..c1d13e21a246 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authz/store/FileRolesStore.java @@ -37,6 +37,7 @@ import org.elasticsearch.xpack.core.security.authz.store.RoleRetrievalResult; import org.elasticsearch.xpack.core.security.authz.support.DLSRoleQueryValidator; import org.elasticsearch.xpack.core.security.support.NoOpLogger; import org.elasticsearch.xpack.core.security.support.Validation; +import org.elasticsearch.xpack.security.PrivilegedFileWatcher; import org.elasticsearch.xpack.security.authz.FileRoleValidator; import java.io.IOException; @@ -110,7 +111,7 @@ public class FileRolesStore implements BiConsumer, ActionListener