-
Notifications
You must be signed in to change notification settings - Fork 9
Domain Access Control
Keiko Domain Access Control (DAC) actively limits plugins' run-time permissions by preventing them from executing actions that they are not allowed to. It is capable of both logging almost all plugins' activity, and prevent suspicious operations from happening in the first place — before your server is damaged.
Keiko can't know for sure which actions you consider "safe" and which you consider "malicious". The rules system helps it determine that. Each action has an own section in the RuntimeProtect configuration (runtimeprotect.yml
), with its own set of rules.
Each operation has a log
option, which specifies the level of logging that Keiko should use when a plugin performs this operation. For example, if you set log: info
in settings for operation file_read
, Keiko will print an "info" message whenever a plugin reads a file, as shown in the second picture at the top of this page. In these notifications, you can see:
- which operations was performed (there are both system operations (such as file read and connection open) and minecraft operations (such as player op and player de-op));
- name of the plugin that performed this operation;
- fully qualified name of the class that performed this operation (this class is inside the plugin whose name is reported);
- name of the method that performed this operation (this method is inside the class whose fully qualified name is reported).
Such rules are only effective for certain identities performing the action with certain arguments. By "identity" we mean, generally speaking, code source, which can be one of:
-
PLUGIN
— Name of plugin (as specified in itsplugin.yml
) that invoked this action. -
SOURCE
— Fully qualified class name (with package) and method name that invoked this action. -
ALL
— Used to create an argument-specific rule for all plugins and sources (no identity filtering).
Run-time identities specification is similar to the static inspections exclusions syntax
The basic rule syntax effective for all types of actions is:
<"ALLOW"/"DENY"> <identity> <argument-1> [argument-2] [...argument-n]
where <identity>
should be something that identifies/filters code source, e.g.:
-
ALL
— to accept calls from all sources (no filtering); -
PLUGIN=PluginName
— to only accept calls from plugin with namePluginName
; -
SOURCE=fully.qualified.ClassName
— to only accept calls from class with nameClassName
located in packagefully.qualified
; -
SOURCE=fully.qualified.ClassName#methodName
— to only accept calls from method with namemethodName
of class with nameClassName
located in packagefully.qualified
.
NOTE #1: class constructors are also methods. Their name is
<init>
. Constructors are called whenever a new instance of a class (object) is created at runtime. Any class has a constructor. Constructors "without" additional code (that is explicitly written by programmer) are called default. For example, when the program creates a new instance of, say, classAccountsManager
located in packagexyz.test
, it invokes methodxyz.test.AccountsManager#<init>
.
NOTE #2: static blocks are methods as well. Their name is
<clinit>
. Code in static blocks is normally called only once per class — upon its load by ClassLoader, usually upon the first access of other code to this class.
You may have noticed that only one argument is marked as "required" in the above-stated syntax. That is not really correct. Actually, this means that there will always be at least one argument in all actions' rules configuration (just because Keiko's rules parser works that way), and the actual number of arguments is totally action-dependent.
Make sure to look at configuration comments to each action in runtimeprotect.yml for a more accurate syntax specific to the action you want to edit rules for.
Actions whose rules take no arguments (e.g. properties_access
, socket_factory_set
, etc.) must be given *
(an asterisk) as an argument. Just because.
Bottom-most rules have higher priority. This means that if rule ALLOW ALL *
is below rule DENY ALL *
, then Keiko will allow all plugins to perform this operation on all arguments. That is, the above operation will be overwritten. This may be used to define the "default" rules (for plugins that have no explicit rules for them below in the rules
list), as shown in the example below.
Let's take a look at the list of rules for action file_read
and explain everything in detail:
# File read control.
#
# Rules syntax:
#
# <"ALLOW"/"DENY"> <identity> <file name>
#
# Examples:
#
# # Allow all plugins to create any plugins in their plugins/ folder:
# - ALLOW ALL {plugins_folder}/{plugin_name}
#
# # Forbid plugin "NotMalware4sure" to write in server's "server.properties" file:
# - DENY PLUGIN=NotMalware4sure {server_folder}/server.properties
1. file_read:
2. log: off
3. rules:
4. - DENY ALL *
5. - ALLOW ALL {plugins_folder}/{plugin_name}
6. - ALLOW ALL {plugins_folder}/{plugin_name}/*
7. - ALLOW ALL {plugin_jar_path}
8. - ALLOW ALL {plugins_folder}/bStats/config.yml
9. - ALLOW ALL {plugins_folder}/PluginMetrics/config.yml
10. - ALLOW ALL {java_folder}/*
11. - ALLOW ALL /tmp
12. - ALLOW SOURCE=com.comphenix.protocol.ProtocolLib#checkConflictingVersions {plugins_folder}
13. - ALLOW SOURCE=com.comphenix.protocol.ProtocolLib#checkConflictingVersions {plugins_folder}/*
In line 2
we tell Keiko that we do not want to see console messages like Plugin XXX has just read file YYY
.
line 3
: begin our rules list for operation file_read
.
line 4
: prevent all plugins from reading any files. Plugins/files that will not match any of the filters below this rule will get their file read calls suspended. But plugins/files that will match an ALLOW
filter below will overwrite the rule in this line (#4), and will be allowed to perform the file read operation.
line 5
: allow all plugins to read contents of their own folder in plugins/
(e.g. for plugin Reflex that's plugins/Reflex
.
line 6
: allow all plugins to read all files in their own folder in plugins/
(see above for details).
line 7
: allow all plugins to read their own JAR file (needed by plugins to extract files inside their JARs such as config.yml
and other).
lines 8-9
: allow all plugins to read configuration files of bStats
and PluginMetrics
— used for statistics.
line 10
: allow all plugins to read files of the local JRE (Java Runtime Environment) installation — needed by plugins that interact with security in any way, specifically, for encryption (Java Cryptography Extension).
line 11
: allow all plugins to read contents of the machine's temporary folder (appears to be used somewhere deep, internally).
lines 12-13
: allow method checkConflictingVersions
of class ProtocolLib
in package com.comphenix.protocol
to read contents of the server's plugins/
folder and everything inside. Used by ProtocolLib to find incompatible plugins' versions.
As you may have noticed, there are several "strange" "magic" words/symbols used in DAC rules. Here is a complete list of all of them:
Placeholder | Description |
---|---|
{java_folder} |
Denotes your JRE (Java Runtime Environment) installation folder (e.g. /opt/jvm/jdk1.8.0_251/jre ). |
{server_folder} |
Denotes your Minecraft server folder (e.g. /home/john/minecraft/skywars-server-1 ). |
{plugins_folder} |
Denotes your Minecraft server's plugins/ folder (e.g. /home/john/minecraft/skywars-server-1/plugins ). |
{plugin_name} |
Denotes name of the plugin that invoked this action (as specified in its plugin.yml ). |
{plugin_jar_path} |
Denotes absolute path to JAR file of the plugin that invoked this action (e.g. /home/john/minecraft/skywars-server-1/plugins/CoolChatFilter.jar ). |
* (wildcard/asterisk) [Simple RegEx] |
Implies any character sequence, including empty sequence. Can be literally whatever string of any length, including no string (emptiness). |
? (question mark) [Simple RegEx] |
Implies at least zero and at most one character. |
+ (plus sign) [Simple RegEx] |
Implies at least one character (like * , but does not match empty sequences). |
Ordinary regular expressions are also supported! This means that you can also use stuff like
[a-zA-Z]{2,5}
to match from 2 to 5 Latin alphabet characters (both lower- and upper-case), and more! Google a bit to learn more about RegEx.
It may sometimes be needed to match symbols *
/?
as is, i.e. do not treat them as wildcards. You can do so by adding prefix NO_WILDCARDS ::
(with a trailing space!):
Wildcards:
command_execution:
...
rules:
- "ALLOW ALL rm -rf plugins/*"
# This will allow all plugins to execute commands like "rm -rf plugins/",
# "rm -rf plugins/j", "rm -rf plugins/WQOIEOsajfd-qw214.jar", etc.
No wildcards:
command_execution:
...
rules:
- "ALLOW ALL NO_WILDCARDS :: rm -rf plugins/*"
# This will allow all plugins to execute exactly one command:
# "rm -rf plugins/*". Commands like "rm -rf plugins/sdf" will NOT work because
# the asterisk * symbol here is not a wildcard - it is a regular symbol. Same for "?".
NOTE: the
NO_WILDCARDS ::
prefix also disables all other RegExs in the rule (e.g.[A-Z]
and similar).
Can't find what you're looking for? Ask in Keiko's Discord server or open an issue on GitHub!