Skip to content

Commit ec7f4cc

Browse files
authored
[Entitlements] Add FileStore instrumentation + tests (#122348)
1 parent e8bbf27 commit ec7f4cc

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
@@ -50,6 +50,7 @@
5050
import java.nio.channels.SocketChannel;
5151
import java.nio.channels.spi.SelectorProvider;
5252
import java.nio.charset.Charset;
53+
import java.nio.file.FileStore;
5354
import java.nio.file.LinkOption;
5455
import java.nio.file.OpenOption;
5556
import java.nio.file.Path;
@@ -523,4 +524,23 @@ public interface EntitlementChecker {
523524

524525
// file system providers
525526
void checkNewInputStream(Class<?> callerClass, FileSystemProvider that, Path path, OpenOption... options);
527+
528+
// file store
529+
void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type);
530+
531+
void checkGetAttribute(Class<?> callerClass, FileStore that, String attribute);
532+
533+
void checkGetBlockSize(Class<?> callerClass, FileStore that);
534+
535+
void checkGetTotalSpace(Class<?> callerClass, FileStore that);
536+
537+
void checkGetUnallocatedSpace(Class<?> callerClass, FileStore that);
538+
539+
void checkGetUsableSpace(Class<?> callerClass, FileStore that);
540+
541+
void checkIsReadOnly(Class<?> callerClass, FileStore that);
542+
543+
void checkName(Class<?> callerClass, FileStore that);
544+
545+
void checkType(Class<?> callerClass, FileStore that);
526546
}

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
@@ -27,21 +27,21 @@
2727
@SuppressForbidden(reason = "Explicitly checking APIs that are forbidden")
2828
class FileCheckActions {
2929

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

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

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

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

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

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
@@ -27,11 +27,13 @@
2727
import org.elasticsearch.entitlement.runtime.policy.entitlements.InboundNetworkEntitlement;
2828
import org.elasticsearch.entitlement.runtime.policy.entitlements.LoadNativeLibrariesEntitlement;
2929
import org.elasticsearch.entitlement.runtime.policy.entitlements.OutboundNetworkEntitlement;
30+
import org.elasticsearch.entitlement.runtime.policy.entitlements.ReadStoreAttributesEntitlement;
3031

3132
import java.lang.instrument.Instrumentation;
3233
import java.lang.reflect.Constructor;
3334
import java.lang.reflect.InvocationTargetException;
3435
import java.nio.channels.spi.SelectorProvider;
36+
import java.nio.file.FileStore;
3537
import java.nio.file.FileSystems;
3638
import java.nio.file.OpenOption;
3739
import java.nio.file.Path;
@@ -43,6 +45,7 @@
4345
import java.util.Set;
4446
import java.util.stream.Collectors;
4547
import java.util.stream.Stream;
48+
import java.util.stream.StreamSupport;
4649

4750
/**
4851
* Called by the agent during {@code agentmain} to configure the entitlement system,
@@ -58,6 +61,11 @@ public class EntitlementInitialization {
5861

5962
private static ElasticsearchEntitlementChecker manager;
6063

64+
interface InstrumentationInfoFunction {
65+
InstrumentationService.InstrumentationInfo of(String methodName, Class<?>... parameterTypes) throws ClassNotFoundException,
66+
NoSuchMethodException;
67+
}
68+
6169
// Note: referenced by bridge reflectively
6270
public static EntitlementChecker checker() {
6371
return manager;
@@ -71,22 +79,26 @@ public static void initialize(Instrumentation inst) throws Exception {
7179

7280
Map<MethodKey, CheckMethod> checkMethods = new HashMap<>(INSTRUMENTATION_SERVICE.lookupMethods(latestCheckerInterface));
7381
var fileSystemProviderClass = FileSystems.getDefault().provider().getClass();
74-
Stream.of(
75-
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
76-
FileSystemProvider.class,
77-
"newInputStream",
78-
fileSystemProviderClass,
79-
EntitlementChecker.class,
80-
"checkNewInputStream",
81-
Path.class,
82-
OpenOption[].class
83-
),
84-
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
85-
SelectorProvider.class,
86-
"inheritedChannel",
87-
SelectorProvider.provider().getClass(),
88-
EntitlementChecker.class,
89-
"checkSelectorProviderInheritedChannel"
82+
83+
Stream.concat(
84+
fileStoreChecks(),
85+
Stream.of(
86+
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
87+
FileSystemProvider.class,
88+
"newInputStream",
89+
fileSystemProviderClass,
90+
EntitlementChecker.class,
91+
"checkNewInputStream",
92+
Path.class,
93+
OpenOption[].class
94+
),
95+
INSTRUMENTATION_SERVICE.lookupImplementationMethod(
96+
SelectorProvider.class,
97+
"inheritedChannel",
98+
SelectorProvider.provider().getClass(),
99+
EntitlementChecker.class,
100+
"checkSelectorProviderInheritedChannel"
101+
)
90102
)
91103
).forEach(instrumentation -> checkMethods.put(instrumentation.targetMethod(), instrumentation.checkMethod()));
92104

@@ -120,6 +132,7 @@ private static PolicyManager createPolicyManager() {
120132
"org.elasticsearch.server",
121133
List.of(
122134
new ExitVMEntitlement(),
135+
new ReadStoreAttributesEntitlement(),
123136
new CreateClassLoaderEntitlement(),
124137
new InboundNetworkEntitlement(),
125138
new OutboundNetworkEntitlement(),
@@ -139,6 +152,45 @@ private static PolicyManager createPolicyManager() {
139152
return new PolicyManager(serverPolicy, agentEntitlements, pluginPolicies, resolver, AGENTS_PACKAGE_NAME, ENTITLEMENTS_MODULE);
140153
}
141154

155+
private static Stream<InstrumentationService.InstrumentationInfo> fileStoreChecks() {
156+
var fileStoreClasses = StreamSupport.stream(FileSystems.getDefault().getFileStores().spliterator(), false)
157+
.map(FileStore::getClass)
158+
.distinct();
159+
return fileStoreClasses.flatMap(fileStoreClass -> {
160+
var instrumentation = new InstrumentationInfoFunction() {
161+
@Override
162+
public InstrumentationService.InstrumentationInfo of(String methodName, Class<?>... parameterTypes)
163+
throws ClassNotFoundException, NoSuchMethodException {
164+
return INSTRUMENTATION_SERVICE.lookupImplementationMethod(
165+
FileStore.class,
166+
methodName,
167+
fileStoreClass,
168+
EntitlementChecker.class,
169+
"check" + Character.toUpperCase(methodName.charAt(0)) + methodName.substring(1),
170+
parameterTypes
171+
);
172+
}
173+
};
174+
175+
try {
176+
return Stream.of(
177+
instrumentation.of("getFileStoreAttributeView", Class.class),
178+
instrumentation.of("getAttribute", String.class),
179+
instrumentation.of("getBlockSize"),
180+
instrumentation.of("getTotalSpace"),
181+
instrumentation.of("getUnallocatedSpace"),
182+
instrumentation.of("getUsableSpace"),
183+
instrumentation.of("isReadOnly"),
184+
instrumentation.of("name"),
185+
instrumentation.of("type")
186+
187+
);
188+
} catch (NoSuchMethodException | ClassNotFoundException e) {
189+
throw new RuntimeException(e);
190+
}
191+
});
192+
}
193+
142194
/**
143195
* Returns the "most recent" checker class compatible with the current runtime Java version.
144196
* 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
@@ -55,6 +55,7 @@
5555
import java.nio.channels.SocketChannel;
5656
import java.nio.channels.spi.SelectorProvider;
5757
import java.nio.charset.Charset;
58+
import java.nio.file.FileStore;
5859
import java.nio.file.LinkOption;
5960
import java.nio.file.OpenOption;
6061
import java.nio.file.Path;
@@ -998,4 +999,49 @@ public void checkSelectorProviderInheritedChannel(Class<?> callerClass, Selector
998999
public void checkNewInputStream(Class<?> callerClass, FileSystemProvider that, Path path, OpenOption... options) {
9991000
// TODO: policyManger.checkFileSystemRead(path);
10001001
}
1002+
1003+
@Override
1004+
public void checkGetFileStoreAttributeView(Class<?> callerClass, FileStore that, Class<?> type) {
1005+
policyManager.checkWriteStoreAttributes(callerClass);
1006+
}
1007+
1008+
@Override
1009+
public void checkGetAttribute(Class<?> callerClass, FileStore that, String attribute) {
1010+
policyManager.checkReadStoreAttributes(callerClass);
1011+
}
1012+
1013+
@Override
1014+
public void checkGetBlockSize(Class<?> callerClass, FileStore that) {
1015+
policyManager.checkReadStoreAttributes(callerClass);
1016+
}
1017+
1018+
@Override
1019+
public void checkGetTotalSpace(Class<?> callerClass, FileStore that) {
1020+
policyManager.checkReadStoreAttributes(callerClass);
1021+
}
1022+
1023+
@Override
1024+
public void checkGetUnallocatedSpace(Class<?> callerClass, FileStore that) {
1025+
policyManager.checkReadStoreAttributes(callerClass);
1026+
}
1027+
1028+
@Override
1029+
public void checkGetUsableSpace(Class<?> callerClass, FileStore that) {
1030+
policyManager.checkReadStoreAttributes(callerClass);
1031+
}
1032+
1033+
@Override
1034+
public void checkIsReadOnly(Class<?> callerClass, FileStore that) {
1035+
policyManager.checkReadStoreAttributes(callerClass);
1036+
}
1037+
1038+
@Override
1039+
public void checkName(Class<?> callerClass, FileStore that) {
1040+
policyManager.checkReadStoreAttributes(callerClass);
1041+
}
1042+
1043+
@Override
1044+
public void checkType(Class<?> callerClass, FileStore that) {
1045+
policyManager.checkReadStoreAttributes(callerClass);
1046+
}
10011047
}

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)