Skip to content

Commit 2bf93c0

Browse files
authored
[Entitlements] Add FileStore instrumentation + tests (elastic#122348) (elastic#122366)
1 parent 0e7d854 commit 2bf93c0

File tree

8 files changed

+236
-22
lines changed

8 files changed

+236
-22
lines changed

libs/entitlement/bridge/src/main/java/org/elasticsearch/entitlement/bridge/EntitlementChecker.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import java.nio.channels.SocketChannel;
4444
import java.nio.channels.spi.SelectorProvider;
4545
import java.nio.charset.Charset;
46+
import java.nio.file.FileStore;
4647
import java.nio.file.LinkOption;
4748
import java.nio.file.OpenOption;
4849
import java.nio.file.Path;
@@ -491,4 +492,23 @@ public interface EntitlementChecker {
491492

492493
// file system providers
493494
void checkNewInputStream(Class<?> callerClass, FileSystemProvider that, Path path, OpenOption... options);
495+
496+
// file store
497+
void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type);
498+
499+
void checkGetAttribute(Class<?> callerClass, FileStore that, String attribute);
500+
501+
void checkGetBlockSize(Class<?> callerClass, FileStore that);
502+
503+
void checkGetTotalSpace(Class<?> callerClass, FileStore that);
504+
505+
void checkGetUnallocatedSpace(Class<?> callerClass, FileStore that);
506+
507+
void checkGetUsableSpace(Class<?> callerClass, FileStore that);
508+
509+
void checkIsReadOnly(Class<?> callerClass, FileStore that);
510+
511+
void checkName(Class<?> callerClass, FileStore that);
512+
513+
void checkType(Class<?> callerClass, FileStore that);
494514
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/FileCheckActions.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,21 @@
2828
@SuppressForbidden(reason = "Explicitly checking APIs that are forbidden")
2929
class FileCheckActions {
3030

31-
private static Path testRootDir = Paths.get(System.getProperty("es.entitlements.testdir"));
31+
static Path testRootDir = Paths.get(System.getProperty("es.entitlements.testdir"));
3232

33-
private static Path readDir() {
33+
static Path readDir() {
3434
return testRootDir.resolve("read_dir");
3535
}
3636

37-
private static Path readWriteDir() {
37+
static Path readWriteDir() {
3838
return testRootDir.resolve("read_write_dir");
3939
}
4040

41-
private static Path readFile() {
41+
static Path readFile() {
4242
return testRootDir.resolve("read_file");
4343
}
4444

45-
private static Path readWriteFile() {
45+
static Path readWriteFile() {
4646
return testRootDir.resolve("read_write_file");
4747
}
4848

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.qa.test;
11+
12+
import java.io.IOException;
13+
import java.nio.file.Files;
14+
import java.nio.file.attribute.FileStoreAttributeView;
15+
16+
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.ALWAYS_DENIED;
17+
import static org.elasticsearch.entitlement.qa.test.EntitlementTest.ExpectedAccess.SERVER_ONLY;
18+
19+
class FileStoreActions {
20+
21+
@EntitlementTest(expectedAccess = ALWAYS_DENIED)
22+
static void checkGetFileStoreAttributeView() throws IOException {
23+
Files.getFileStore(FileCheckActions.readWriteFile()).getFileStoreAttributeView(FileStoreAttributeView.class);
24+
}
25+
26+
@EntitlementTest(expectedAccess = SERVER_ONLY)
27+
static void checkGetAttribute() throws IOException {
28+
try {
29+
Files.getFileStore(FileCheckActions.readFile()).getAttribute("zfs:compression");
30+
} catch (UnsupportedOperationException e) {
31+
// It's OK if the attribute view is not available or it does not support reading the attribute
32+
}
33+
}
34+
35+
@EntitlementTest(expectedAccess = SERVER_ONLY)
36+
static void checkGetBlockSize() throws IOException {
37+
Files.getFileStore(FileCheckActions.readWriteFile()).getBlockSize();
38+
}
39+
40+
@EntitlementTest(expectedAccess = SERVER_ONLY)
41+
static void checkGetTotalSpace() throws IOException {
42+
Files.getFileStore(FileCheckActions.readWriteFile()).getTotalSpace();
43+
}
44+
45+
@EntitlementTest(expectedAccess = SERVER_ONLY)
46+
static void checkGetUnallocatedSpace() throws IOException {
47+
Files.getFileStore(FileCheckActions.readWriteFile()).getUnallocatedSpace();
48+
}
49+
50+
@EntitlementTest(expectedAccess = SERVER_ONLY)
51+
static void checkGetUsableSpace() throws IOException {
52+
Files.getFileStore(FileCheckActions.readFile()).getUsableSpace();
53+
}
54+
55+
@EntitlementTest(expectedAccess = SERVER_ONLY)
56+
static void checkIsReadOnly() throws IOException {
57+
Files.getFileStore(FileCheckActions.readFile()).isReadOnly();
58+
}
59+
60+
@EntitlementTest(expectedAccess = SERVER_ONLY)
61+
static void checkName() throws IOException {
62+
Files.getFileStore(FileCheckActions.readFile()).name();
63+
}
64+
65+
@EntitlementTest(expectedAccess = SERVER_ONLY)
66+
static void checkType() throws IOException {
67+
Files.getFileStore(FileCheckActions.readFile()).type();
68+
}
69+
70+
private FileStoreActions() {}
71+
}

libs/entitlement/qa/entitlement-test-plugin/src/main/java/org/elasticsearch/entitlement/qa/test/RestEntitlementsCheckAction.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,8 @@ static CheckAction alwaysDenied(CheckedRunnable<Exception> action) {
185185
getTestEntries(FileCheckActions.class),
186186
getTestEntries(SpiActions.class),
187187
getTestEntries(SystemActions.class),
188-
getTestEntries(NativeActions.class)
188+
getTestEntries(NativeActions.class),
189+
getTestEntries(FileStoreActions.class)
189190
)
190191
.flatMap(Function.identity())
191192
.filter(entry -> entry.getValue().fromJavaVersion() == null || Runtime.version().feature() >= entry.getValue().fromJavaVersion())

libs/entitlement/src/main/java/org/elasticsearch/entitlement/initialization/EntitlementInitialization.java

Lines changed: 68 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@
2929
import org.elasticsearch.entitlement.runtime.policy.entitlements.InboundNetworkEntitlement;
3030
import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement;
3131
import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement;
32+
import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadStoreAttributesEntitlement;
3233

3334
import java.lang.instrument.Instrumentation;
3435
import java.lang.reflect.Constructor;
3536
import java.lang.reflect.InvocationTargetException;
3637
import java.nio.channels.spi.SelectorProvider;
38+
import java.nio.file.FileStore;
3739
import java.nio.file.FileSystems;
3840
import java.nio.file.OpenOption;
3941
import java.nio.file.Path;
@@ -46,6 +48,7 @@
4648
import java.util.Set;
4749
import java.util.stream.Collectors;
4850
import java.util.stream.Stream;
51+
import java.util.stream.StreamSupport;
4952

5053
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ_WRITE;
5154

@@ -63,6 +66,11 @@ public class EntitlementInitialization {
6366

6467
private static ElasticsearchEntitlementChecker manager;
6568

69+
interface InstrumentationInfoFunction {
70+
InstrumentationService.InstrumentationInfo of(String methodName, Class<?>... parameterTypes) throws ClassNotFoundException,
71+
NoSuchMethodException;
72+
}
73+
6674
// Note: referenced by bridge reflectively
6775
public static EntitlementChecker checker() {
6876
return manager;
@@ -76,22 +84,26 @@ public static void initialize(Instrumentation inst) throws Exception {
7684

7785
Map<MethodKey, CheckMethod> checkMethods = new HashMap<>(INSTRUMENTATION_SERVICE.lookupMethods(latestCheckerInterface));
7886
var fileSystemProviderClass = FileSystems.getDefault().provider().getClass();
79-
Stream.of(
80-
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
81-
FileSystemProvider.class,
82-
"newInputStream",
83-
fileSystemProviderClass,
84-
EntitlementChecker.class,
85-
"checkNewInputStream",
86-
Path.class,
87-
OpenOption[].class
88-
),
89-
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
90-
SelectorProvider.class,
91-
"inheritedChannel",
92-
SelectorProvider.provider().getClass(),
93-
EntitlementChecker.class,
94-
"checkSelectorProviderInheritedChannel"
87+
88+
Stream.concat(
89+
fileStoreChecks(),
90+
Stream.of(
91+
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
92+
FileSystemProvider.class,
93+
"newInputStream",
94+
fileSystemProviderClass,
95+
EntitlementChecker.class,
96+
"checkNewInputStream",
97+
Path.class,
98+
OpenOption[].class
99+
),
100+
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
101+
SelectorProvider.class,
102+
"inheritedChannel",
103+
SelectorProvider.provider().getClass(),
104+
EntitlementChecker.class,
105+
"checkSelectorProviderInheritedChannel"
106+
)
95107
)
96108
).forEach(instrumentation -> checkMethods.put(instrumentation.targetMethod(), instrumentation.checkMethod()));
97109

@@ -126,6 +138,7 @@ private static PolicyManager createPolicyManager() {
126138
"org.elasticsearch.server",
127139
List.of(
128140
new ExitVMEntitlement(),
141+
new ReadStoreAttributesEntitlement(),
129142
new CreateClassLoaderEntitlement(),
130143
new InboundNetworkEntitlement(),
131144
new OutboundNetworkEntitlement(),
@@ -151,6 +164,45 @@ private static PolicyManager createPolicyManager() {
151164
return new PolicyManager(serverPolicy, agentEntitlements, pluginPolicies, resolver, AGENTS_PACKAGE_NAME, ENTITLEMENTS_MODULE);
152165
}
153166

167+
private static Stream<InstrumentationService.InstrumentationInfo> fileStoreChecks() {
168+
var fileStoreClasses = StreamSupport.stream(FileSystems.getDefault().getFileStores().spliterator(), false)
169+
.map(FileStore::getClass)
170+
.distinct();
171+
return fileStoreClasses.flatMap(fileStoreClass -> {
172+
var instrumentation = new InstrumentationInfoFunction() {
173+
@Override
174+
public InstrumentationService.InstrumentationInfo of(String methodName, Class<?>... parameterTypes)
175+
throws ClassNotFoundException, NoSuchMethodException {
176+
return INSTRUMENTATION_SERVICE.lookupImplementationMethod(
177+
FileStore.class,
178+
methodName,
179+
fileStoreClass,
180+
EntitlementChecker.class,
181+
"check" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1),
182+
parameterTypes
183+
);
184+
}
185+
};
186+
187+
try {
188+
return Stream.of(
189+
instrumentation.of("getFileStoreAttributeView", Class.class),
190+
instrumentation.of("getAttribute", String.class),
191+
instrumentation.of("getBlockSize"),
192+
instrumentation.of("getTotalSpace"),
193+
instrumentation.of("getUnallocatedSpace"),
194+
instrumentation.of("getUsableSpace"),
195+
instrumentation.of("isReadOnly"),
196+
instrumentation.of("name"),
197+
instrumentation.of("type")
198+
199+
);
200+
} catch (NoSuchMethodException | ClassNotFoundException e) {
201+
throw new RuntimeException(e);
202+
}
203+
});
204+
}
205+
154206
/**
155207
* Returns the "most recent" checker class compatible with the current runtime Java version.
156208
* For checkers, we have (optionally) version specific classes, each with a prefix (e.g. Java23).

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/api/ElasticsearchEntitlementChecker.java

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import java.nio.channels.SocketChannel;
4949
import java.nio.channels.spi.SelectorProvider;
5050
import java.nio.charset.Charset;
51+
import java.nio.file.FileStore;
5152
import java.nio.file.LinkOption;
5253
import java.nio.file.OpenOption;
5354
import java.nio.file.Path;
@@ -987,4 +988,49 @@ public void checkSelectorProviderInheritedChannel(Class<?> callerClass, Selector
987988
public void checkNewInputStream(Class<?> callerClass, FileSystemProvider that, Path path, OpenOption... options) {
988989
// TODO: policyManger.checkFileSystemRead(path);
989990
}
991+
992+
@Override
993+
public void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type) {
994+
policyManager.checkWriteStoreAttributes(callerClass);
995+
}
996+
997+
@Override
998+
public void checkGetAttribute(Class<?> callerClass, FileStore that, String attribute) {
999+
policyManager.checkReadStoreAttributes(callerClass);
1000+
}
1001+
1002+
@Override
1003+
public void checkGetBlockSize(Class<?> callerClass, FileStore that) {
1004+
policyManager.checkReadStoreAttributes(callerClass);
1005+
}
1006+
1007+
@Override
1008+
public void checkGetTotalSpace(Class<?> callerClass, FileStore that) {
1009+
policyManager.checkReadStoreAttributes(callerClass);
1010+
}
1011+
1012+
@Override
1013+
public void checkGetUnallocatedSpace(Class<?> callerClass, FileStore that) {
1014+
policyManager.checkReadStoreAttributes(callerClass);
1015+
}
1016+
1017+
@Override
1018+
public void checkGetUsableSpace(Class<?> callerClass, FileStore that) {
1019+
policyManager.checkReadStoreAttributes(callerClass);
1020+
}
1021+
1022+
@Override
1023+
public void checkIsReadOnly(Class<?> callerClass, FileStore that) {
1024+
policyManager.checkReadStoreAttributes(callerClass);
1025+
}
1026+
1027+
@Override
1028+
public void checkName(Class<?> callerClass, FileStore that) {
1029+
policyManager.checkReadStoreAttributes(callerClass);
1030+
}
1031+
1032+
@Override
1033+
public void checkType(Class<?> callerClass, FileStore that) {
1034+
policyManager.checkReadStoreAttributes(callerClass);
1035+
}
9901036
}

libs/entitlement/src/main/java/org/elasticsearch/entitlement/runtime/policy/PolicyManager.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.elasticsearch.entitlement.runtime.policy.entitlements.InboundNetworkEntitlement;
2121
import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement;
2222
import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement;
23+
import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadStoreAttributesEntitlement;
2324
import org.elasticsearch.entitlement.runtime.policy.entitlements.SetHttpsConnectionPropertiesEntitlement;
2425
import org.elasticsearch.entitlement.runtime.policy.entitlements.WriteSystemPropertiesEntitlement;
2526
import org.elasticsearch.logging.LogManager;
@@ -181,6 +182,14 @@ public void checkStartProcess(Class<?> callerClass) {
181182
neverEntitled(callerClass, () -> "start process");
182183
}
183184

185+
public void checkWriteStoreAttributes(Class<?> callerClass) {
186+
neverEntitled(callerClass, () -> "change file store attributes");
187+
}
188+
189+
public void checkReadStoreAttributes(Class<?> callerClass) {
190+
checkEntitlementPresent(callerClass, ReadStoreAttributesEntitlement.class);
191+
}
192+
184193
/**
185194
* @param operationDescription is only called when the operation is not trivially allowed, meaning the check is about to fail;
186195
* therefore, its performance is not a major concern.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the "Elastic License
4+
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
5+
* Public License v 1"; you may not use this file except in compliance with, at
6+
* your election, the "Elastic License 2.0", the "GNU Affero General Public
7+
* License v3.0 only", or the "Server Side Public License, v 1".
8+
*/
9+
10+
package org.elasticsearch.entitlement.runtime.policy.entitlements;
11+
12+
/**
13+
* Describes an entitlement for reading file store attributes (e.g. disk space)
14+
*/
15+
public record ReadStoreAttributesEntitlement() implements Entitlement {}

0 commit comments

Comments
 (0)