Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.BaseDir.SHARED_REPO;
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ;
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Mode.READ_WRITE;
import static org.elasticsearch.entitlement.runtime.policy.entitlements.FilesEntitlement.Platform.LINUX;

/**
* Called by the agent during {@code agentmain} to configure the entitlement system,
Expand Down Expand Up @@ -182,22 +183,22 @@ private static PolicyManager createPolicyManager() {
FileData.ofRelativePath(Path.of(""), SHARED_REPO, READ_WRITE),

// OS release on Linux
FileData.ofPath(Path.of("/etc/os-release"), READ),
FileData.ofPath(Path.of("/etc/system-release"), READ),
FileData.ofPath(Path.of("/usr/lib/os-release"), READ),
FileData.ofPath(Path.of("/etc/os-release"), READ).withPlatform(LINUX),
FileData.ofPath(Path.of("/etc/system-release"), READ).withPlatform(LINUX),
FileData.ofPath(Path.of("/usr/lib/os-release"), READ).withPlatform(LINUX),
// read max virtual memory areas
FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ),
FileData.ofPath(Path.of("/proc/meminfo"), READ),
FileData.ofPath(Path.of("/proc/sys/vm/max_map_count"), READ).withPlatform(LINUX),
FileData.ofPath(Path.of("/proc/meminfo"), READ).withPlatform(LINUX),
// load averages on Linux
FileData.ofPath(Path.of("/proc/loadavg"), READ),
FileData.ofPath(Path.of("/proc/loadavg"), READ).withPlatform(LINUX),
// control group stats on Linux. cgroup v2 stats are in an unpredicable
// location under `/sys/fs/cgroup`, so unfortunately we have to allow
// read access to the entire directory hierarchy.
FileData.ofPath(Path.of("/proc/self/cgroup"), READ),
FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ),
FileData.ofPath(Path.of("/proc/self/cgroup"), READ).withPlatform(LINUX),
FileData.ofPath(Path.of("/sys/fs/cgroup/"), READ).withPlatform(LINUX),
// // io stats on Linux
FileData.ofPath(Path.of("/proc/self/mountinfo"), READ),
FileData.ofPath(Path.of("/proc/diskstats"), READ)
FileData.ofPath(Path.of("/proc/self/mountinfo"), READ).withPlatform(LINUX),
FileData.ofPath(Path.of("/proc/diskstats"), READ).withPlatform(LINUX)
)
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ private FileAccessTree(FilesEntitlement filesEntitlement, PathLookup pathLookup)
List<String> readPaths = new ArrayList<>();
List<String> writePaths = new ArrayList<>();
for (FilesEntitlement.FileData fileData : filesEntitlement.filesData()) {
var platform = fileData.platform();
if (platform != null && platform.isCurrent() == false) {
continue;
}
var mode = fileData.mode();
var paths = fileData.resolvePaths(pathLookup);
paths.forEach(path -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,26 +42,55 @@ public enum BaseDir {
HOME
}

public enum Platform {
LINUX,
MACOS,
WINDOWS;

private static final Platform current = findCurrent();

private static Platform findCurrent() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Elasticsearch already have a way to determine the platform? (Are we really the first to need this?)

If it does, should we use that instead of parsing the os.name system property?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are several places we repeat this logic, including inside Lucene. Here we don't have access to Lucene classes. I originally had a comment in this change to move this logic to elasticsearch.core, but I removed it. We could consider moving it still, though, but as a followup.

String os = System.getProperty("os.name");
if (os.startsWith("Linux")) {
return LINUX;
} else if (os.startsWith("Mac OS")) {
return MACOS;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect we'll want a UNIX covering both LINUX and MACOS at some point.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had a version of this (calling it POSIX because these aren't actually unix), but I'm unsure it will actually be necessary because the location of system files on linux/mac are usually very different, even though they begin with /.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok. FWIW the JDK calls it the UnixFileSystemProvider.

} else if (os.startsWith("Windows")) {
return WINDOWS;
} else {
throw new AssertionError("Unsupported platform [" + os + "]");
}
}

public boolean isCurrent() {
return this == current;
}
}

public sealed interface FileData {

Stream<Path> resolvePaths(PathLookup pathLookup);

Mode mode();

Platform platform();

FileData withPlatform(Platform platform);

static FileData ofPath(Path path, Mode mode) {
return new AbsolutePathFileData(path, mode);
return new AbsolutePathFileData(path, mode, null);
}

static FileData ofRelativePath(Path relativePath, BaseDir baseDir, Mode mode) {
return new RelativePathFileData(relativePath, baseDir, mode);
return new RelativePathFileData(relativePath, baseDir, mode, null);
}

static FileData ofPathSetting(String setting, Mode mode) {
return new PathSettingFileData(setting, mode);
return new PathSettingFileData(setting, mode, null);
}

static FileData ofRelativePathSetting(String setting, BaseDir baseDir, Mode mode) {
return new RelativePathSettingFileData(setting, baseDir, mode);
return new RelativePathSettingFileData(setting, baseDir, mode, null);
}

/**
Expand Down Expand Up @@ -145,32 +174,70 @@ private static Stream<Path> relativePathsCombination(Path[] baseDirs, Stream<Pat
return paths.stream();
}

private record AbsolutePathFileData(Path path, Mode mode) implements FileData {
private record AbsolutePathFileData(Path path, Mode mode, Platform platform) implements FileData {
@Override
public Stream<Path> resolvePaths(PathLookup pathLookup) {
return Stream.of(path);
}

@Override
public FileData withPlatform(Platform platform) {
if (platform == platform()) {
return this;
}
return new AbsolutePathFileData(path, mode, platform);
}
}

private record RelativePathFileData(Path relativePath, BaseDir baseDir, Mode mode) implements FileData, RelativeFileData {
private record RelativePathFileData(Path relativePath, BaseDir baseDir, Mode mode, Platform platform)
implements
FileData,
RelativeFileData {
@Override
public Stream<Path> resolveRelativePaths(PathLookup pathLookup) {
return Stream.of(relativePath);
}

@Override
public FileData withPlatform(Platform platform) {
if (platform == platform()) {
return this;
}
return new RelativePathFileData(relativePath, baseDir, mode, platform);
}
}

private record PathSettingFileData(String setting, Mode mode) implements FileData {
private record PathSettingFileData(String setting, Mode mode, Platform platform) implements FileData {
@Override
public Stream<Path> resolvePaths(PathLookup pathLookup) {
return resolvePathSettings(pathLookup, setting);
}

@Override
public FileData withPlatform(Platform platform) {
if (platform == platform()) {
return this;
}
return new PathSettingFileData(setting, mode, platform);
}
}

private record RelativePathSettingFileData(String setting, BaseDir baseDir, Mode mode) implements FileData, RelativeFileData {
private record RelativePathSettingFileData(String setting, BaseDir baseDir, Mode mode, Platform platform)
implements
FileData,
RelativeFileData {
@Override
public Stream<Path> resolveRelativePaths(PathLookup pathLookup) {
return resolvePathSettings(pathLookup, setting);
}

@Override
public FileData withPlatform(Platform platform) {
if (platform == platform()) {
return this;
}
return new RelativePathSettingFileData(setting, baseDir, mode, platform);
}
}

private static Stream<Path> resolvePathSettings(PathLookup pathLookup, String setting) {
Expand All @@ -191,6 +258,18 @@ private static Mode parseMode(String mode) {
}
}

private static Platform parsePlatform(String platform) {
if (platform.equals("linux")) {
return Platform.LINUX;
} else if (platform.equals("macos")) {
return Platform.MACOS;
} else if (platform.equals("windows")) {
return Platform.WINDOWS;
} else {
throw new PolicyValidationException("invalid platform: " + platform + ", valid values: [linux, macos, windows]");
}
}

private static BaseDir parseBaseDir(String baseDir) {
return switch (baseDir) {
case "config" -> BaseDir.CONFIG;
Expand Down Expand Up @@ -218,6 +297,7 @@ public static FilesEntitlement build(List<Object> paths) {
String pathSetting = file.remove("path_setting");
String relativePathSetting = file.remove("relative_path_setting");
String modeAsString = file.remove("mode");
String platformAsString = file.remove("platform");

if (file.isEmpty() == false) {
throw new PolicyValidationException("unknown key(s) [" + file + "] in a listed file for files entitlement");
Expand All @@ -234,36 +314,45 @@ public static FilesEntitlement build(List<Object> paths) {
throw new PolicyValidationException("files entitlement must contain 'mode' for every listed file");
}
Mode mode = parseMode(modeAsString);
Platform platform = null;
if (platformAsString != null) {
platform = parsePlatform(platformAsString);
}

BaseDir baseDir = null;
if (relativeTo != null) {
baseDir = parseBaseDir(relativeTo);
}

final FileData fileData;
if (relativePathAsString != null) {
if (baseDir == null) {
throw new PolicyValidationException("files entitlement with a 'relative_path' must specify 'relative_to'");
}

Path relativePath = Path.of(relativePathAsString);
if (FileData.isAbsolutePath(relativePathAsString)) {
throw new PolicyValidationException("'relative_path' [" + relativePathAsString + "] must be relative");
}
filesData.add(FileData.ofRelativePath(Path.of(relativePathAsString), baseDir, mode));
fileData = FileData.ofRelativePath(relativePath, baseDir, mode);
} else if (pathAsString != null) {
Path path = Path.of(pathAsString);
if (FileData.isAbsolutePath(pathAsString) == false) {
throw new PolicyValidationException("'path' [" + pathAsString + "] must be absolute");
}
filesData.add(FileData.ofPath(Path.of(pathAsString), mode));
fileData = FileData.ofPath(path, mode);
} else if (pathSetting != null) {
filesData.add(FileData.ofPathSetting(pathSetting, mode));
fileData = FileData.ofPathSetting(pathSetting, mode);
} else if (relativePathSetting != null) {
if (baseDir == null) {
throw new PolicyValidationException("files entitlement with a 'relative_path_setting' must specify 'relative_to'");
}
filesData.add(FileData.ofRelativePathSetting(relativePathSetting, baseDir, mode));
fileData = FileData.ofRelativePathSetting(relativePathSetting, baseDir, mode);
} else {
throw new AssertionError("File entry validation error");
}

filesData.add(fileData.withPlatform(platform));
}
return new FilesEntitlement(filesData);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,6 @@ org.elasticsearch.ml:
- relative_path: "ml-local-data/"
relative_to: data
mode: read_write
- path: \\.\pipe\
mode: read_write
platform: windows