1
1
mirror of https://github.com/systemd/systemd-stable.git synced 2025-01-18 06:03:42 +03:00

Merge pull request #20525 from maanyagoenka/custom-security

systemd-analyze: add option to enable users to custom define security requirements in the form of a .json file
This commit is contained in:
Luca Boccassi 2021-08-31 21:15:41 +01:00 committed by GitHub
commit cc3001693d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 1192 additions and 27 deletions

View File

@ -792,6 +792,323 @@ Service b@0.service not loaded, b.socket cannot be started.
as well and its default value is 100.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>--security-policy=<replaceable>PATH</replaceable></option></term>
<listitem><para>With <command>security</command>, allow the user to define a custom set of
requirements formatted as a JSON file against which to compare the specified unit file(s)
and determine their overall exposure level to security threats.</para>
<table>
<title>Accepted Assessment Test Identifiers</title>
<tgroup cols='1'>
<colspec colname='directive' />
<thead>
<row>
<entry>Assessment Test Identifier</entry>
</row>
</thead>
<tbody>
<row>
<entry>UserOrDynamicUser</entry>
</row>
<row>
<entry>SupplementaryGroups</entry>
</row>
<row>
<entry>PrivateMounts</entry>
</row>
<row>
<entry>PrivateDevices</entry>
</row>
<row>
<entry>PrivateTmp</entry>
</row>
<row>
<entry>PrivateNetwork</entry>
</row>
<row>
<entry>PrivateUsers</entry>
</row>
<row>
<entry>ProtectControlGroups</entry>
</row>
<row>
<entry>ProtectKernelModules</entry>
</row>
<row>
<entry>ProtectKernelTunables</entry>
</row>
<row>
<entry>ProtectKernelLogs</entry>
</row>
<row>
<entry>ProtectClock</entry>
</row>
<row>
<entry>ProtectHome</entry>
</row>
<row>
<entry>ProtectHostname</entry>
</row>
<row>
<entry>ProtectSystem</entry>
</row>
<row>
<entry>RootDirectoryOrRootImage</entry>
</row>
<row>
<entry>LockPersonality</entry>
</row>
<row>
<entry>MemoryDenyWriteExecute</entry>
</row>
<row>
<entry>NoNewPrivileges</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_ADMIN</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SET_UID_GID_PCAP</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_PTRACE</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_TIME</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_NET_ADMIN</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_RAWIO</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_MODULE</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_AUDIT</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYSLOG</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_MKNOD</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_KILL</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_BOOT</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_MAC</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_LINUX_IMMUTABLE</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_IPC_LOCK</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_CHROOT</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_BLOCK_SUSPEND</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_WAKE_ALARM</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_LEASE</entry>
</row>
<row>
<entry>CapabilityBoundingSet_CAP_SYS_TTY_CONFIG</entry>
</row>
<row>
<entry>UMask</entry>
</row>
<row>
<entry>KeyringMode</entry>
</row>
<row>
<entry>ProtectProc</entry>
</row>
<row>
<entry>ProcSubset</entry>
</row>
<row>
<entry>NotifyAccess</entry>
</row>
<row>
<entry>RemoveIPC</entry>
</row>
<row>
<entry>Delegate</entry>
</row>
<row>
<entry>RestrictRealtime</entry>
</row>
<row>
<entry>RestrictSUIDSGID</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWUSER</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWNS</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWIPC</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWPID</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWCGROUP</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWUTS</entry>
</row>
<row>
<entry>RestrictNamespaces_CLONE_NEWNET</entry>
</row>
<row>
<entry>RestrictAddressFamilies_AF_INET_INET6</entry>
</row>
<row>
<entry>RestrictAddressFamilies_AF_UNIX</entry>
</row>
<row>
<entry>RestrictAddressFamilies_AF_NETLINK</entry>
</row>
<row>
<entry>RestrictAddressFamilies_AF_PACKET</entry>
</row>
<row>
<entry>RestrictAddressFamilies_OTHER</entry>
</row>
<row>
<entry>SystemCallArchitectures</entry>
</row>
<row>
<entry>SystemCallFilter_swap</entry>
</row>
<row>
<entry>SystemCallFilter_obsolete</entry>
</row>
<row>
<entry>SystemCallFilter_clock</entry>
</row>
<row>
<entry>SystemCallFilter_cpu_emulation</entry>
</row>
<row>
<entry>SystemCallFilter_debug</entry>
</row>
<row>
<entry>SystemCallFilter_mount</entry>
</row>
<row>
<entry>SystemCallFilter_module</entry>
</row>
<row>
<entry>SystemCallFilter_raw_io</entry>
</row>
<row>
<entry>SystemCallFilter_reboot</entry>
</row>
<row>
<entry>SystemCallFilter_privileged</entry>
</row>
<row>
<entry>SystemCallFilter_resources</entry>
</row>
<row>
<entry>IPAddressDeny</entry>
</row>
<row>
<entry>DeviceAllow</entry>
</row>
<row>
<entry>AmbientCapabilities</entry>
</row>
</tbody>
</tgroup>
</table>
<example>
<title>JSON Policy</title>
<para>The JSON file passed as a path parameter to <option>--security-policy=</option>
has a top-level JSON object, with keys being the assessment test identifiers mentioned
above. The values in the file should be JSON objects with one or more of the
following fields: description_na (string), description_good (string), description_bad
(string), weight (unsigned integer), and range (unsigned integer). If any of these fields
corresponding to a specific id of the unit file is missing from the JSON object, the
default built-in field value corresponding to that same id is used for security analysis
as default. The weight and range fields are used in determining the overall exposure level
of the unit files so by allowing users to manipulate these fields, 'security' gives them
the option to decide for themself which ids are more important and hence, should have a greater
effect on the exposure level. </para>
<programlisting>
{
"PrivateDevices":
{
"description_good": "Service has no access to hardware devices",
"description_bad": "Service potentially has access to hardware devices",
"weight": 1000,
"range": 1
},
"PrivateMounts":
{
"description_good": "Service cannot install system mounts",
"description_bad": "Service may install system mounts",
"weight": 1000,
"range": 1
},
"PrivateNetwork":
{
"description_good": "Service has no access to the host's network",
"description_bad": "Service has access to the host's network",
"weight": 2500,
"range": 1
},
"PrivateTmp":
{
"description_good": "Service has no access to other software's temporary files",
"description_bad": "Service has access to other software's temporary files",
"weight": 1000,
"range": 1
},
"PrivateUsers":
{
"description_good": "Service does not have access to other users",
"description_bad": "Service has access to other users",
"weight": 1000,
"range": 1
}
}
</programlisting>
</example>
</listitem>
</varlistentry>
<varlistentry>
<term><option>--iterations=<replaceable>NUMBER</replaceable></option></term>

View File

@ -144,7 +144,7 @@ _systemd_analyze() {
elif __contains_word "$verb" ${VERBS[SECURITY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold'
comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold --security-policy'
else
if __contains_word "--user" ${COMP_WORDS[*]}; then
mode=--user

View File

@ -92,6 +92,7 @@ _arguments \
'--recursive-errors=[When verifying a unit, control dependency verification]:MODE' \
'--offline=[Perform a security review of the specified unit file(s)]:BOOL' \
'--threshold=[Set a value to compare the overall security exposure level with]: NUMBER' \
'--security-policy=[Allow user to use customized requirements to compare unit file(s) against]: PATH' \
'--no-pager[Do not pipe output into a pager]' \
'--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \
'--order[When generating graph for dot, show only order]' \

View File

@ -107,6 +107,7 @@ typedef struct SecurityInfo {
struct security_assessor {
const char *id;
const char *json_field;
const char *description_good;
const char *description_bad;
const char *description_na;
@ -754,6 +755,7 @@ static int assess_ambient_capabilities(
static const struct security_assessor security_assessor_table[] = {
{
.id = "User=/DynamicUser=",
.json_field = "UserOrDynamicUser",
.description_bad = "Service runs as root user",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#User=",
.weight = 2000,
@ -762,6 +764,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SupplementaryGroups=",
.json_field = "SupplementaryGroups",
.description_good = "Service has no supplementary groups",
.description_bad = "Service runs with supplementary groups",
.description_na = "Service runs as root, option does not matter",
@ -772,6 +775,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "PrivateDevices=",
.json_field = "PrivateDevices",
.description_good = "Service has no access to hardware devices",
.description_bad = "Service potentially has access to hardware devices",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateDevices=",
@ -782,6 +786,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "PrivateMounts=",
.json_field = "PrivateMounts",
.description_good = "Service cannot install system mounts",
.description_bad = "Service may install system mounts",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateMounts=",
@ -792,6 +797,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "PrivateNetwork=",
.json_field = "PrivateNetwork",
.description_good = "Service has no access to the host's network",
.description_bad = "Service has access to the host's network",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateNetwork=",
@ -802,6 +808,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "PrivateTmp=",
.json_field = "PrivateTmp",
.description_good = "Service has no access to other software's temporary files",
.description_bad = "Service has access to other software's temporary files",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateTmp=",
@ -813,6 +820,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "PrivateUsers=",
.json_field = "PrivateUsers",
.description_good = "Service does not have access to other users",
.description_bad = "Service has access to other users",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#PrivateUsers=",
@ -823,6 +831,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectControlGroups=",
.json_field = "ProtectControlGroups",
.description_good = "Service cannot modify the control group file system",
.description_bad = "Service may modify the control group file system",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectControlGroups=",
@ -833,6 +842,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectKernelModules=",
.json_field = "ProtectKernelModules",
.description_good = "Service cannot load or read kernel modules",
.description_bad = "Service may load or read kernel modules",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelModules=",
@ -843,6 +853,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectKernelTunables=",
.json_field = "ProtectKernelTunables",
.description_good = "Service cannot alter kernel tunables (/proc/sys, …)",
.description_bad = "Service may alter kernel tunables",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelTunables=",
@ -853,6 +864,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectKernelLogs=",
.json_field = "ProtectKernelLogs",
.description_good = "Service cannot read from or write to the kernel log ring buffer",
.description_bad = "Service may read from or write to the kernel log ring buffer",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectKernelLogs=",
@ -863,6 +875,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectClock=",
.json_field = "ProtectClock",
.description_good = "Service cannot write to the hardware clock or system clock",
.description_bad = "Service may write to the hardware clock or system clock",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectClock=",
@ -873,6 +886,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectHome=",
.json_field = "ProtectHome",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHome=",
.weight = 1000,
.range = 10,
@ -881,6 +895,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectHostname=",
.json_field = "ProtectHostname",
.description_good = "Service cannot change system host/domainname",
.description_bad = "Service may change system host/domainname",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectHostname=",
@ -891,6 +906,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectSystem=",
.json_field = "ProtectSystem",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectSystem=",
.weight = 1000,
.range = 10,
@ -899,6 +915,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RootDirectory=/RootImage=",
.json_field = "RootDirectoryOrRootImage",
.description_good = "Service has its own root directory/image",
.description_bad = "Service runs within the host's root directory",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RootDirectory=",
@ -909,6 +926,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "LockPersonality=",
.json_field = "LockPersonality",
.description_good = "Service cannot change ABI personality",
.description_bad = "Service may change ABI personality",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#LockPersonality=",
@ -919,6 +937,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "MemoryDenyWriteExecute=",
.json_field = "MemoryDenyWriteExecute",
.description_good = "Service cannot create writable executable memory mappings",
.description_bad = "Service may create writable executable memory mappings",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#MemoryDenyWriteExecute=",
@ -929,6 +948,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "NoNewPrivileges=",
.json_field = "NoNewPrivileges",
.description_good = "Service processes cannot acquire new privileges",
.description_bad = "Service processes may acquire new privileges",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NoNewPrivileges=",
@ -939,6 +959,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_ADMIN",
.json_field = "CapabilityBoundingSet_CAP_SYS_ADMIN",
.description_good = "Service has no administrator privileges",
.description_bad = "Service has administrator privileges",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -949,6 +970,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
.json_field = "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
.description_good = "Service cannot change UID/GID identities/capabilities",
.description_bad = "Service may change UID/GID identities/capabilities",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -961,6 +983,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_PTRACE",
.json_field = "CapabilityBoundingSet_CAP_SYS_PTRACE",
.description_good = "Service has no ptrace() debugging abilities",
.description_bad = "Service has ptrace() debugging abilities",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -971,6 +994,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_TIME",
.json_field = "CapabilityBoundingSet_CAP_SYS_TIME",
.description_good = "Service processes cannot change the system clock",
.description_bad = "Service processes may change the system clock",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -981,6 +1005,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_NET_ADMIN",
.json_field = "CapabilityBoundingSet_CAP_NET_ADMIN",
.description_good = "Service has no network configuration privileges",
.description_bad = "Service has network configuration privileges",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -991,6 +1016,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_RAWIO",
.json_field = "CapabilityBoundingSet_CAP_SYS_RAWIO",
.description_good = "Service has no raw I/O access",
.description_bad = "Service has raw I/O access",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1001,6 +1027,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_MODULE",
.json_field = "CapabilityBoundingSet_CAP_SYS_MODULE",
.description_good = "Service cannot load kernel modules",
.description_bad = "Service may load kernel modules",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1011,6 +1038,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_AUDIT_*",
.json_field = "CapabilityBoundingSet_CAP_AUDIT",
.description_good = "Service has no audit subsystem access",
.description_bad = "Service has audit subsystem access",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1023,6 +1051,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYSLOG",
.json_field = "CapabilityBoundingSet_CAP_SYSLOG",
.description_good = "Service has no access to kernel logging",
.description_bad = "Service has access to kernel logging",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1033,6 +1062,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_(NICE|RESOURCE)",
.json_field = "CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE",
.description_good = "Service has no privileges to change resource use parameters",
.description_bad = "Service has privileges to change resource use parameters",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1044,6 +1074,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_MKNOD",
.json_field = "CapabilityBoundingSet_CAP_MKNOD",
.description_good = "Service cannot create device nodes",
.description_bad = "Service may create device nodes",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1054,6 +1085,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_(CHOWN|FSETID|SETFCAP)",
.json_field = "CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP",
.description_good = "Service cannot change file ownership/access mode/capabilities",
.description_bad = "Service may change file ownership/access mode/capabilities unrestricted",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1066,6 +1098,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_(DAC_*|FOWNER|IPC_OWNER)",
.json_field = "CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER",
.description_good = "Service cannot override UNIX file/IPC permission checks",
.description_bad = "Service may override UNIX file/IPC permission checks",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1079,6 +1112,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_KILL",
.json_field = "CapabilityBoundingSet_CAP_KILL",
.description_good = "Service cannot send UNIX signals to arbitrary processes",
.description_bad = "Service may send UNIX signals to arbitrary processes",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1089,6 +1123,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_NET_(BIND_SERVICE|BROADCAST|RAW)",
.json_field = "CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW)",
.description_good = "Service has no elevated networking privileges",
.description_bad = "Service has elevated networking privileges",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1101,6 +1136,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_BOOT",
.json_field = "CapabilityBoundingSet_CAP_SYS_BOOT",
.description_good = "Service cannot issue reboot()",
.description_bad = "Service may issue reboot()",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1111,6 +1147,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_MAC_*",
.json_field = "CapabilityBoundingSet_CAP_MAC",
.description_good = "Service cannot adjust SMACK MAC",
.description_bad = "Service may adjust SMACK MAC",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1122,6 +1159,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_LINUX_IMMUTABLE",
.json_field = "CapabilityBoundingSet_CAP_LINUX_IMMUTABLE",
.description_good = "Service cannot mark files immutable",
.description_bad = "Service may mark files immutable",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1132,6 +1170,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_IPC_LOCK",
.json_field = "CapabilityBoundingSet_CAP_IPC_LOCK",
.description_good = "Service cannot lock memory into RAM",
.description_bad = "Service may lock memory into RAM",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1142,6 +1181,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_CHROOT",
.json_field = "CapabilityBoundingSet_CAP_SYS_CHROOT",
.description_good = "Service cannot issue chroot()",
.description_bad = "Service may issue chroot()",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1152,6 +1192,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_BLOCK_SUSPEND",
.json_field = "CapabilityBoundingSet_CAP_BLOCK_SUSPEND",
.description_good = "Service cannot establish wake locks",
.description_bad = "Service may establish wake locks",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1162,6 +1203,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_WAKE_ALARM",
.json_field = "CapabilityBoundingSet_CAP_WAKE_ALARM",
.description_good = "Service cannot program timers that wake up the system",
.description_bad = "Service may program timers that wake up the system",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1172,6 +1214,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_LEASE",
.json_field = "CapabilityBoundingSet_CAP_LEASE",
.description_good = "Service cannot create file leases",
.description_bad = "Service may create file leases",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1182,6 +1225,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_TTY_CONFIG",
.json_field = "CapabilityBoundingSet_CAP_SYS_TTY_CONFIG",
.description_good = "Service cannot issue vhangup()",
.description_bad = "Service may issue vhangup()",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1192,6 +1236,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "CapabilityBoundingSet=~CAP_SYS_PACCT",
.json_field = "CapabilityBoundingSet_CAP_SYS_PACCT",
.description_good = "Service cannot use acct()",
.description_bad = "Service may use acct()",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#CapabilityBoundingSet=",
@ -1202,6 +1247,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "UMask=",
.json_field = "UMask",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#UMask=",
.weight = 100,
.range = 10,
@ -1209,6 +1255,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "KeyringMode=",
.json_field = "KeyringMode",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#KeyringMode=",
.description_good = "Service doesn't share key material with other services",
.description_bad = "Service shares key material with other service",
@ -1218,6 +1265,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProtectProc=",
.json_field = "ProtectProc",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProtectProc=",
.description_good = "Service has restricted access to process tree (/proc hidepid=)",
.description_bad = "Service has full access to process tree (/proc hidepid=)",
@ -1227,6 +1275,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "ProcSubset=",
.json_field = "ProcSubset",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#ProcSubset=",
.description_good = "Service has no access to non-process /proc files (/proc subset=)",
.description_bad = "Service has full access to non-process /proc files (/proc subset=)",
@ -1236,6 +1285,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "NotifyAccess=",
.json_field = "NotifyAccess",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#NotifyAccess=",
.description_good = "Service child processes cannot alter service state",
.description_bad = "Service child processes may alter service state",
@ -1245,6 +1295,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RemoveIPC=",
.json_field = "RemoveIPC",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RemoveIPC=",
.description_good = "Service user cannot leave SysV IPC objects around",
.description_bad = "Service user may leave SysV IPC objects around",
@ -1256,6 +1307,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "Delegate=",
.json_field = "Delegate",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#Delegate=",
.description_good = "Service does not maintain its own delegated control group subtree",
.description_bad = "Service maintains its own delegated control group subtree",
@ -1267,6 +1319,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictRealtime=",
.json_field = "RestrictRealtime",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictRealtime=",
.description_good = "Service realtime scheduling access is restricted",
.description_bad = "Service may acquire realtime scheduling",
@ -1277,6 +1330,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictSUIDSGID=",
.json_field = "RestrictSUIDSGID",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictSUIDSGID=",
.description_good = "SUID/SGID file creation by service is restricted",
.description_bad = "Service may create SUID/SGID files",
@ -1287,6 +1341,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWUSER",
.json_field = "RestrictNamespaces_CLONE_NEWUSER",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create user namespaces",
.description_bad = "Service may create user namespaces",
@ -1297,6 +1352,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWNS",
.json_field = "RestrictNamespaces_CLONE_NEWNS",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create file system namespaces",
.description_bad = "Service may create file system namespaces",
@ -1307,6 +1363,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWIPC",
.json_field = "RestrictNamespaces_CLONE_NEWIPC",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create IPC namespaces",
.description_bad = "Service may create IPC namespaces",
@ -1317,6 +1374,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWPID",
.json_field = "RestrictNamespaces_CLONE_NEWPID",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create process namespaces",
.description_bad = "Service may create process namespaces",
@ -1327,6 +1385,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWCGROUP",
.json_field = "RestrictNamespaces_CLONE_NEWCGROUP",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create cgroup namespaces",
.description_bad = "Service may create cgroup namespaces",
@ -1337,6 +1396,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWNET",
.json_field = "RestrictNamespaces_CLONE_NEWNET",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create network namespaces",
.description_bad = "Service may create network namespaces",
@ -1347,6 +1407,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictNamespaces=~CLONE_NEWUTS",
.json_field = "RestrictNamespaces_CLONE_NEWUTS",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictNamespaces=",
.description_good = "Service cannot create hostname namespaces",
.description_bad = "Service may create hostname namespaces",
@ -1357,6 +1418,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictAddressFamilies=~AF_(INET|INET6)",
.json_field = "RestrictAddressFamilies_AF_INET_INET6",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
.description_good = "Service cannot allocate Internet sockets",
.description_bad = "Service may allocate Internet sockets",
@ -1367,6 +1429,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictAddressFamilies=~AF_UNIX",
.json_field = "RestrictAddressFamilies_AF_UNIX",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
.description_good = "Service cannot allocate local sockets",
.description_bad = "Service may allocate local sockets",
@ -1377,6 +1440,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictAddressFamilies=~AF_NETLINK",
.json_field = "RestrictAddressFamilies_AF_NETLINK",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
.description_good = "Service cannot allocate netlink sockets",
.description_bad = "Service may allocate netlink sockets",
@ -1387,6 +1451,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictAddressFamilies=~AF_PACKET",
.json_field = "RestrictAddressFamilies_AF_PACKET",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
.description_good = "Service cannot allocate packet sockets",
.description_bad = "Service may allocate packet sockets",
@ -1397,6 +1462,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "RestrictAddressFamilies=~…",
.json_field = "RestrictAddressFamilies_OTHER",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#RestrictAddressFamilies=",
.description_good = "Service cannot allocate exotic sockets",
.description_bad = "Service may allocate exotic sockets",
@ -1407,6 +1473,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallArchitectures=",
.json_field = "SystemCallArchitectures",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallArchitectures=",
.weight = 1000,
.range = 10,
@ -1415,6 +1482,7 @@ static const struct security_assessor security_assessor_table[] = {
#if HAVE_SECCOMP
{
.id = "SystemCallFilter=~@swap",
.json_field = "SystemCallFilter_swap",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1423,6 +1491,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@obsolete",
.json_field = "SystemCallFilter_obsolete",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 250,
.range = 10,
@ -1431,6 +1500,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@clock",
.json_field = "SystemCallFilter_clock",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1439,6 +1509,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@cpu-emulation",
.json_field = "SystemCallFilter_cpu_emulation",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 250,
.range = 10,
@ -1447,6 +1518,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@debug",
.json_field = "SystemCallFilter_debug",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1455,6 +1527,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@mount",
.json_field = "SystemCallFilter_mount",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1463,6 +1536,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@module",
.json_field = "SystemCallFilter_module",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1471,6 +1545,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@raw-io",
.json_field = "SystemCallFilter_raw_io",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1479,6 +1554,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@reboot",
.json_field = "SystemCallFilter_reboot",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 1000,
.range = 10,
@ -1487,6 +1563,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@privileged",
.json_field = "SystemCallFilter_privileged",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 700,
.range = 10,
@ -1495,6 +1572,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "SystemCallFilter=~@resources",
.json_field = "SystemCallFilter_resources",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#SystemCallFilter=",
.weight = 700,
.range = 10,
@ -1504,6 +1582,7 @@ static const struct security_assessor security_assessor_table[] = {
#endif
{
.id = "IPAddressDeny=",
.json_field = "IPAddressDeny",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#IPAddressDeny=",
.weight = 1000,
.range = 10,
@ -1511,6 +1590,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "DeviceAllow=",
.json_field = "DeviceAllow",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#DeviceAllow=",
.weight = 1000,
.range = 10,
@ -1518,6 +1598,7 @@ static const struct security_assessor security_assessor_table[] = {
},
{
.id = "AmbientCapabilities=",
.json_field = "AmbientCapabilities",
.url = "https://www.freedesktop.org/software/systemd/man/systemd.exec.html#AmbientCapabilities=",
.description_good = "Service process does not receive ambient capabilities",
.description_bad = "Service process receives ambient capabilities",
@ -1527,7 +1608,109 @@ static const struct security_assessor security_assessor_table[] = {
},
};
static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecurityFlags flags, unsigned threshold) {
static JsonVariant* security_assessor_find_in_policy(const struct security_assessor *a, JsonVariant *policy, const char *name) {
JsonVariant *item;
assert(a);
if (!policy)
return NULL;
if (!json_variant_is_object(policy)) {
log_debug("Specificied policy is not a JSON object, ignoring.");
return NULL;
}
item = json_variant_by_key(policy, a->json_field);
if (!item)
return NULL;
if (!json_variant_is_object(item)) {
log_debug("Item for '%s' in policy JSON object is not an object, ignoring.", a->id);
return NULL;
}
return name ? json_variant_by_key(item, name) : item;
}
static uint64_t access_weight(const struct security_assessor *a, JsonVariant *policy) {
JsonVariant *val;
assert(a);
val = security_assessor_find_in_policy(a, policy, "weight");
if (val) {
if (json_variant_is_unsigned(val))
return json_variant_unsigned(val);
log_debug("JSON field 'weight' of policy for %s is not an unsigned integer, ignoring.", a->id);
}
return a->weight;
}
static uint64_t access_range(const struct security_assessor *a, JsonVariant *policy) {
JsonVariant *val;
assert(a);
val = security_assessor_find_in_policy(a, policy, "range");
if (val) {
if (json_variant_is_unsigned(val))
return json_variant_unsigned(val);
log_debug("JSON field 'range' of policy for %s is not an unsigned integer, ignoring.", a->id);
}
return a->range;
}
static const char *access_description_na(const struct security_assessor *a, JsonVariant *policy) {
JsonVariant *val;
assert(a);
val = security_assessor_find_in_policy(a, policy, "description_na");
if (val) {
if (json_variant_is_string(val))
return json_variant_string(val);
log_debug("JSON field 'description_na' of policy for %s is not a string, ignoring.", a->id);
}
return a->description_na;
}
static const char *access_description_good(const struct security_assessor *a, JsonVariant *policy) {
JsonVariant *val;
assert(a);
val = security_assessor_find_in_policy(a, policy, "description_good");
if (val) {
if (json_variant_is_string(val))
return json_variant_string(val);
log_debug("JSON field 'description_good' of policy for %s is not a string, ignoring.", a->id);
}
return a->description_good;
}
static const char *access_description_bad(const struct security_assessor *a, JsonVariant *policy) {
JsonVariant *val;
assert(a);
val = security_assessor_find_in_policy(a, policy, "description_bad");
if (val) {
if (json_variant_is_string(val))
return json_variant_string(val);
log_debug("JSON field 'description_bad' of policy for %s is not a string, ignoring.", a->id);
}
return a->description_bad;
}
static int assess(const SecurityInfo *info,
Table *overview_table,
AnalyzeSecurityFlags flags,
unsigned threshold,
JsonVariant *policy) {
static const struct {
uint64_t exposure;
const char *name;
@ -1565,6 +1748,8 @@ static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecuri
_cleanup_free_ char *d = NULL;
uint64_t badness;
void *data;
uint64_t weight = access_weight(a, policy);
uint64_t range = access_range(a, policy);
data = (uint8_t *) info + a->offset;
@ -1579,29 +1764,30 @@ static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecuri
return r;
}
assert(a->range > 0);
assert(range > 0);
if (badness != UINT64_MAX) {
assert(badness <= a->range);
assert(badness <= range);
badness_sum += DIV_ROUND_UP(badness * a->weight, a->range);
weight_sum += a->weight;
badness_sum += DIV_ROUND_UP(badness * weight, range);
weight_sum += weight;
}
if (details_table) {
const char *checkmark, *description, *color = NULL;
const char *id = a->id;
if (badness == UINT64_MAX) {
checkmark = " ";
description = a->description_na;
description = access_description_na(a, policy);
color = NULL;
} else if (badness == a->range) {
checkmark = special_glyph(SPECIAL_GLYPH_CROSS_MARK);
description = a->description_bad;
description = access_description_bad(a, policy);
color = ansi_highlight_red();
} else if (badness == 0) {
checkmark = special_glyph(SPECIAL_GLYPH_CHECK_MARK);
description = a->description_good;
description = access_description_good(a, policy);
color = ansi_highlight_green();
} else {
checkmark = special_glyph(SPECIAL_GLYPH_CROSS_MARK);
@ -1612,17 +1798,20 @@ static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecuri
if (d)
description = d;
if (json_variant_by_key(policy, a->json_field) != NULL)
id = a->json_field;
r = table_add_many(details_table,
TABLE_STRING, checkmark,
TABLE_SET_MINIMUM_WIDTH, 1,
TABLE_SET_MAXIMUM_WIDTH, 1,
TABLE_SET_ELLIPSIZE_PERCENT, 0,
TABLE_SET_COLOR, color,
TABLE_STRING, a->id, TABLE_SET_URL, a->url,
TABLE_STRING, id, TABLE_SET_URL, a->url,
TABLE_STRING, description,
TABLE_UINT64, a->weight, TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, weight, TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, badness, TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, a->range, TABLE_SET_ALIGN_PERCENT, 100,
TABLE_UINT64, range, TABLE_SET_ALIGN_PERCENT, 100,
TABLE_EMPTY, TABLE_SET_ALIGN_PERCENT, 100);
if (r < 0)
return table_log_add_error(r);
@ -2192,8 +2381,12 @@ static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *in
return 0;
}
static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table,
AnalyzeSecurityFlags flags, unsigned threshold) {
static int analyze_security_one(sd_bus *bus,
const char *name,
Table *overview_table,
AnalyzeSecurityFlags flags,
unsigned threshold,
JsonVariant *policy) {
_cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
if (!info)
@ -2210,7 +2403,7 @@ static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_t
if (r < 0)
return r;
r = assess(info, overview_table, flags, threshold);
r = assess(info, overview_table, flags, threshold, policy);
if (r < 0)
return r;
@ -2396,7 +2589,7 @@ static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, Security
return 0;
}
static int offline_security_check(Unit *u, unsigned threshold) {
static int offline_security_check(Unit *u, unsigned threshold, JsonVariant *policy) {
_cleanup_(table_unrefp) Table *overview_table = NULL;
AnalyzeSecurityFlags flags = 0;
_cleanup_(security_info_freep) SecurityInfo *info = NULL;
@ -2411,10 +2604,17 @@ static int offline_security_check(Unit *u, unsigned threshold) {
if (r < 0)
return r;
return assess(info, overview_table, flags, threshold);
return assess(info, overview_table, flags, threshold, policy);
}
static int offline_security_checks(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, unsigned threshold, const char *root) {
static int offline_security_checks(char **filenames,
JsonVariant *policy,
UnitFileScope scope,
bool check_man,
bool run_generators,
unsigned threshold,
const char *root) {
const ManagerTestRunFlags flags =
MANAGER_TEST_RUN_MINIMAL |
MANAGER_TEST_RUN_ENV_GENERATORS |
@ -2473,7 +2673,7 @@ static int offline_security_checks(char **filenames, UnitFileScope scope, bool c
}
for (size_t i = 0; i < count; i++) {
k = offline_security_check(units[i], threshold);
k = offline_security_check(units[i], threshold, policy);
if (k < 0 && r == 0)
r = k;
}
@ -2481,8 +2681,16 @@ static int offline_security_checks(char **filenames, UnitFileScope scope, bool c
return r;
}
int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags) {
int analyze_security(sd_bus *bus,
char **units,
JsonVariant *policy,
UnitFileScope scope,
bool check_man,
bool run_generators,
bool offline,
unsigned threshold,
const char *root,
AnalyzeSecurityFlags flags) {
_cleanup_(table_unrefp) Table *overview_table = NULL;
int ret = 0, r;
@ -2490,7 +2698,7 @@ int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_
assert(bus);
if (offline)
return offline_security_checks(units, scope, check_man, run_generators, threshold, root);
return offline_security_checks(units, policy, scope, check_man, run_generators, threshold, root);
if (strv_length(units) != 1) {
overview_table = table_new("unit", "exposure", "predicate", "happy");
@ -2550,7 +2758,7 @@ int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_
flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
STRV_FOREACH(i, list) {
r = analyze_security_one(bus, *i, overview_table, flags, threshold);
r = analyze_security_one(bus, *i, overview_table, flags, threshold, policy);
if (r < 0 && ret >= 0)
ret = r;
}
@ -2585,7 +2793,7 @@ int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_
} else
name = mangled;
r = analyze_security_one(bus, name, overview_table, flags, threshold);
r = analyze_security_one(bus, name, overview_table, flags, threshold, policy);
if (r < 0 && ret >= 0)
ret = r;
}

View File

@ -5,6 +5,7 @@
#include "sd-bus.h"
#include "json.h"
#include "unit-file.h"
typedef enum AnalyzeSecurityFlags {
@ -13,5 +14,13 @@ typedef enum AnalyzeSecurityFlags {
ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2,
} AnalyzeSecurityFlags;
int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags);
int analyze_security(sd_bus *bus,
char **units,
JsonVariant *policy,
UnitFileScope scope,
bool check_man,
bool run_generators,
bool offline,
unsigned threshold,
const char *root,
AnalyzeSecurityFlags flags);

View File

@ -91,6 +91,7 @@ static bool arg_man = true;
static bool arg_generators = false;
static char *arg_root = NULL;
static char *arg_image = NULL;
static char *arg_security_policy = NULL;
static bool arg_offline = false;
static unsigned arg_threshold = 100;
static unsigned arg_iterations = 1;
@ -100,6 +101,7 @@ STATIC_DESTRUCTOR_REGISTER(arg_dot_from_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_dot_to_patterns, strv_freep);
STATIC_DESTRUCTOR_REGISTER(arg_root, freep);
STATIC_DESTRUCTOR_REGISTER(arg_image, freep);
STATIC_DESTRUCTOR_REGISTER(arg_security_policy, freep);
typedef struct BootTimes {
usec_t firmware_time;
@ -2154,7 +2156,9 @@ static int do_verify(int argc, char *argv[], void *userdata) {
static int do_security(int argc, char *argv[], void *userdata) {
_cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *policy = NULL;
int r;
unsigned line, column;
r = acquire_bus(&bus, NULL);
if (r < 0)
@ -2162,7 +2166,35 @@ static int do_security(int argc, char *argv[], void *userdata) {
(void) pager_open(arg_pager_flags);
return analyze_security(bus, strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_offline, arg_threshold, arg_root, 0);
if (arg_security_policy) {
r = json_parse_file(/*f=*/ NULL, arg_security_policy, /*flags=*/ 0, &policy, &line, &column);
if (r < 0)
return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", arg_security_policy, line, column);
} else {
_cleanup_fclose_ FILE *f = NULL;
_cleanup_free_ char *pp = NULL;
r = search_and_fopen_nulstr("systemd-analyze-security.policy", "re", /*root=*/ NULL, CONF_PATHS_NULSTR("systemd"), &f, &pp);
if (r < 0 && r != -ENOENT)
return r;
if (f != NULL) {
r = json_parse_file(f, pp, /*flags=*/ 0, &policy, &line, &column);
if (r < 0)
return log_error_errno(r, "[%s:%u:%u] Failed to parse JSON policy: %m", pp, line, column);
}
}
return analyze_security(bus,
strv_skip(argv, 1),
policy,
arg_scope,
arg_man,
arg_generators,
arg_offline,
arg_threshold,
arg_root,
/*flags=*/ 0);
}
static int help(int argc, char *argv[], void *userdata) {
@ -2214,6 +2246,8 @@ static int help(int argc, char *argv[], void *userdata) {
" --threshold=N Exit with a non-zero status when overall\n"
" exposure level is over threshold value\n"
" --version Show package version\n"
" --security-policy=PATH Use custom JSON security policy instead\n"
" of built-in one\n"
" --no-pager Do not pipe output into a pager\n"
" --system Operate on system systemd instance\n"
" --user Operate on user systemd instance\n"
@ -2266,6 +2300,7 @@ static int parse_argv(int argc, char *argv[]) {
ARG_RECURSIVE_ERRORS,
ARG_OFFLINE,
ARG_THRESHOLD,
ARG_SECURITY_POLICY,
};
static const struct option options[] = {
@ -2278,6 +2313,7 @@ static int parse_argv(int argc, char *argv[]) {
{ "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
{ "offline", required_argument, NULL, ARG_OFFLINE },
{ "threshold", required_argument, NULL, ARG_THRESHOLD },
{ "security-policy", required_argument, NULL, ARG_SECURITY_POLICY },
{ "system", no_argument, NULL, ARG_SYSTEM },
{ "user", no_argument, NULL, ARG_USER },
{ "global", no_argument, NULL, ARG_GLOBAL },
@ -2409,6 +2445,12 @@ static int parse_argv(int argc, char *argv[]) {
break;
case ARG_SECURITY_POLICY:
r = parse_path_argument(optarg, /* suppress_root= */ false, &arg_security_policy);
if (r < 0)
return r;
break;
case ARG_ITERATIONS:
r = safe_atou(optarg, &arg_iterations);
if (r < 0)
@ -2447,6 +2489,10 @@ static int parse_argv(int argc, char *argv[]) {
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --user is not supported for cat-config right now.");
if (arg_security_policy && !streq_ptr(argv[optind], "security"))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
"Option --security-policy= is only supported for security.");
if ((arg_root || arg_image) && (!STRPTR_IN_SET(argv[optind], "cat-config", "verify")) &&
(!(streq_ptr(argv[optind], "security") && arg_offline)))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),

View File

@ -0,0 +1 @@
../TEST-01-BASIC/Makefile

9
test/TEST-63-ANALYZE/test.sh Executable file
View File

@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -e
TEST_DESCRIPTION="test analyze"
# shellcheck source=test/test-functions
. "${TEST_BASE_DIR:?}/test-functions"
do_test "$@"

574
test/units/testsuite-63.sh Executable file
View File

@ -0,0 +1,574 @@
#!/usr/bin/env bash
# shellcheck disable=SC2016
set -eux
systemd-analyze log-level debug
mkdir -p /tmp/img/usr/lib/systemd/system/
mkdir -p /tmp/img/opt/
touch /tmp/img/opt/script0.sh
chmod +x /tmp/img/opt/script0.sh
cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
[Service]
ExecStart = /opt/script0.sh
EOF
set +e
# Default behaviour is to recurse through all dependencies when unit is loaded
systemd-analyze verify --root=/tmp/img/ testfile.service \
&& { echo 'unexpected success'; exit 1; }
# As above, recurses through all dependencies when unit is loaded
systemd-analyze verify --recursive-errors=yes --root=/tmp/img/ testfile.service \
&& { echo 'unexpected success'; exit 1; }
# Recurses through unit file and its direct dependencies when unit is loaded
systemd-analyze verify --recursive-errors=one --root=/tmp/img/ testfile.service \
&& { echo 'unexpected success'; exit 1; }
set -e
# zero exit status since dependencies are ignored when unit is loaded
systemd-analyze verify --recursive-errors=no --root=/tmp/img/ testfile.service
rm /tmp/img/usr/lib/systemd/system/testfile.service
cat <<EOF >/tmp/testfile.service
[Unit]
foo = bar
[Service]
ExecStart = echo hello
EOF
cat <<EOF >/tmp/testfile2.service
[Unit]
Requires = testfile.service
[Service]
ExecStart = echo hello
EOF
# Zero exit status since no additional dependencies are recursively loaded when the unit file is loaded
systemd-analyze verify --recursive-errors=no /tmp/testfile2.service
set +e
# Non-zero exit status since all associated dependencies are recusrively loaded when the unit file is loaded
systemd-analyze verify --recursive-errors=yes /tmp/testfile2.service \
&& { echo 'unexpected success'; exit 1; }
set -e
rm /tmp/testfile.service
rm /tmp/testfile2.service
cat <<EOF >/tmp/testfile.service
[Service]
ExecStart = echo hello
EOF
# Zero exit status since the value used for comparison determine exposure to security threats is by default 100
systemd-analyze security --offline=true /tmp/testfile.service
set +e
#The overall exposure level assigned to the unit is greater than the set threshold
systemd-analyze security --threshold=90 --offline=true /tmp/testfile.service \
&& { echo 'unexpected success'; exit 1; }
set -e
rm /tmp/testfile.service
cat <<EOF >/tmp/img/usr/lib/systemd/system/testfile.service
[Service]
ExecStart = echo hello
PrivateNetwork = yes
PrivateDevices = yes
PrivateUsers = yes
EOF
# The new overall exposure level assigned to the unit is less than the set thresholds
# Verifies that the --offline= option works with --root=
systemd-analyze security --threshold=90 --offline=true --root=/tmp/img/ testfile.service
# Added an additional "INVALID_ID" id to the .json to verify that nothing breaks when input is malformed
# The PrivateNetwork id description and weight was changed to verify that 'security' is actually reading in
# values from the .json file when required. The default weight for "PrivateNetwork" is 2500, and the new weight
# assigned to that id in the .json file is 6000. This increased weight means that when the "PrivateNetwork" key is
# set to 'yes' (as above in the case of testfile.service) in the content of the unit file, the overall exposure
# level for the unit file should decrease to account for that increased weight.
cat <<EOF >/tmp/testfile.json
{"User_Or_DynamicUser":
{"description_bad": "Service runs as root user",
"weight": 2000,
"range": 10
},
"SupplementaryGroups":
{"description_good": "Service has no supplementary groups",
"description_bad": "Service runs with supplementary groups",
"description_na": "Service runs as root, option does not matter",
"weight": 200,
"range": 1
},
"PrivateDevices":
{"description_good": "Service has no access to hardware devices",
"description_bad": "Service potentially has access to hardware devices",
"weight": 1000,
"range": 1
},
"PrivateMounts":
{"description_good": "Service cannot install system mounts",
"description_bad": "Service may install system mounts",
"weight": 1000,
"range": 1
},
"PrivateNetwork":
{"description_good": "Service doesn't have access to the host's network",
"description_bad": "Service has access to the host's network",
"weight": 6000,
"range": 1
},
"PrivateTmp":
{"description_good": "Service has no access to other software's temporary files",
"description_bad": "Service has access to other software's temporary files",
"weight": 1000,
"range": 1
},
"PrivateUsers":
{"description_good": "Service does not have access to other users",
"description_bad": "Service has access to other users",
"weight": 1000,
"range": 1
},
"ProtectControlGroups":
{"description_good": "Service cannot modify the control group file system",
"description_bad": "Service may modify the control group file system",
"weight": 1000,
"range": 1
},
"ProtectKernelModules":
{"description_good": "Service cannot load or read kernel modules",
"description_bad": "Service may load or read kernel modules",
"weight": 1000,
"range": 1
},
"ProtectKernelTunables":
{"description_good": "Service cannot alter kernel tunables (/proc/sys, …)",
"description_bad": "Service may alter kernel tunables",
"weight": 1000,
"range": 1
},
"ProtectKernelLogs":
{"description_good": "Service cannot read from or write to the kernel log ring buffer",
"description_bad": "Service may read from or write to the kernel log ring buffer",
"weight": 1000,
"range": 1
},
"ProtectClock":
{"description_good": "Service cannot write to the hardware clock or system clock",
"description_bad": "Service may write to the hardware clock or system clock",
"weight": 1000,
"range": 1
},
"ProtectHome":
{"weight": 1000,
"range": 10
},
"ProtectHostname":
{"description_good": "Service cannot change system host/domainname",
"description_bad": "Service may change system host/domainname",
"weight": 50,
"range": 1
},
"ProtectSystem":
{"weight": 1000,
"range": 10
},
"RootDirectory_Or_RootImage":
{"description_good": "Service has its own root directory/image",
"description_bad": "Service runs within the host's root directory",
"weight": 200,
"range": 1
},
"LockPersonality":
{"description_good": "Service cannot change ABI personality",
"description_bad": "Service may change ABI personality",
"weight": 100,
"range": 1
},
"MemoryDenyWriteExecute":
{"description_good": "Service cannot create writable executable memory mappings",
"description_bad": "Service may create writable executable memory mappings",
"weight": 100,
"range": 1
},
"NoNewPrivileges":
{"description_good": "Service processes cannot acquire new privileges",
"description_bad": "Service processes may acquire new privileges",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_ADMIN":
{"description_good": "Service has no administrator privileges",
"description_bad": "Service has administrator privileges",
"weight": 1500,
"range": 1
},
"CapabilityBoundingSet_CAP_SET_UID_GID_PCAP":
{"description_good": "Service cannot change UID/GID identities/capabilities",
"description_bad": "Service may change UID/GID identities/capabilities",
"weight": 1500,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_PTRACE":
{"description_good": "Service has no ptrace() debugging abilities",
"description_bad": "Service has ptrace() debugging abilities",
"weight": 1500,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_TIME":
{"description_good": "Service processes cannot change the system clock",
"description_bad": "Service processes may change the system clock",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_NET_ADMIN":
{"description_good": "Service has no network configuration privileges",
"description_bad": "Service has network configuration privileges",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_RAWIO":
{"description_good": "Service has no raw I/O access",
"description_bad": "Service has raw I/O access",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_MODULE":
{"description_good": "Service cannot load kernel modules",
"description_bad": "Service may load kernel modules",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_AUDIT":
{"description_good": "Service has no audit subsystem access",
"description_bad": "Service has audit subsystem access",
"weight": 500,
"range": 1
},
"CapabilityBoundingSet_CAP_SYSLOG":
{"description_good": "Service has no access to kernel logging",
"description_bad": "Service has access to kernel logging",
"weight": 500,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_NICE_RESOURCE":
{"description_good": "Service has no privileges to change resource use parameters",
"description_bad": "Service has privileges to change resource use parameters",
"weight": 500,
"range": 1
},
"CapabilityBoundingSet_CAP_MKNOD":
{"description_good": "Service cannot create device nodes",
"description_bad": "Service may create device nodes",
"weight": 500,
"range": 1
},
"CapabilityBoundingSet_CAP_CHOWN_FSETID_SETFCAP":
{"description_good": "Service cannot change file ownership/access mode/capabilities",
"description_bad": "Service may change file ownership/access mode/capabilities unrestricted",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_DAC_FOWNER_IPC_OWNER":
{"description_good": "Service cannot override UNIX file/IPC permission checks",
"description_bad": "Service may override UNIX file/IPC permission checks",
"weight": 1000,
"range": 1
},
"CapabilityBoundingSet_CAP_KILL":
{"description_good": "Service cannot send UNIX signals to arbitrary processes",
"description_bad": "Service may send UNIX signals to arbitrary processes",
"weight": 500,
"range": 1
},
"CapabilityBoundingSet_CAP_NET_BIND_SERVICE_BROADCAST_RAW":
{"description_good": "Service has no elevated networking privileges",
"description_bad": "Service has elevated networking privileges",
"weight": 500,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_BOOT":
{"description_good": "Service cannot issue reboot()",
"description_bad": "Service may issue reboot()",
"weight": 100,
"range": 1
},
"CapabilityBoundingSet_CAP_MAC":
{"description_good": "Service cannot adjust SMACK MAC",
"description_bad": "Service may adjust SMACK MAC",
"weight": 100,
"range": 1
},
"CapabilityBoundingSet_CAP_LINUX_IMMUTABLE":
{"description_good": "Service cannot mark files immutable",
"description_bad": "Service may mark files immutable",
"weight": 75,
"range": 1
},
"CapabilityBoundingSet_CAP_IPC_LOCK":
{"description_good": "Service cannot lock memory into RAM",
"description_bad": "Service may lock memory into RAM",
"weight": 50,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_CHROOT":
{"description_good": "Service cannot issue chroot()",
"description_bad": "Service may issue chroot()",
"weight": 50,
"range": 1
},
"CapabilityBoundingSet_CAP_BLOCK_SUSPEND":
{"description_good": "Service cannot establish wake locks",
"description_bad": "Service may establish wake locks",
"weight": 25,
"range": 1
},
"CapabilityBoundingSet_CAP_WAKE_ALARM":
{"description_good": "Service cannot program timers that wake up the system",
"description_bad": "Service may program timers that wake up the system",
"weight": 25,
"range": 1
},
"CapabilityBoundingSet_CAP_LEASE":
{"description_good": "Service cannot create file leases",
"description_bad": "Service may create file leases",
"weight": 25,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_TTY_CONFIG":
{"description_good": "Service cannot issue vhangup()",
"description_bad": "Service may issue vhangup()",
"weight": 25,
"range": 1
},
"CapabilityBoundingSet_CAP_SYS_PACCT":
{"description_good": "Service cannot use acct()",
"description_bad": "Service may use acct()",
"weight": 25,
"range": 1
},
"UMask":
{"weight": 100,
"range": 10
},
"KeyringMode":
{"description_good": "Service doesn't share key material with other services",
"description_bad": "Service shares key material with other service",
"weight": 1000,
"range": 1
},
"ProtectProc":
{"description_good": "Service has restricted access to process tree(/proc hidepid=)",
"description_bad": "Service has full access to process tree(/proc hidepid=)",
"weight": 1000,
"range": 3
},
"ProcSubset":
{"description_good": "Service has no access to non-process/proc files(/proc subset=)",
"description_bad": "Service has full access to non-process/proc files(/proc subset=)",
"weight": 10,
"range": 1
},
"NotifyAccess":
{"description_good": "Service child processes cannot alter service state",
"description_bad": "Service child processes may alter service state",
"weight": 1000,
"range": 1
},
"RemoveIPC":
{"description_good": "Service user cannot leave SysV IPC objects around",
"description_bad": "Service user may leave SysV IPC objects around",
"description_na": "Service runs as root, option does not apply",
"weight": 100,
"range": 1
},
"Delegate":
{"description_good": "Service does not maintain its own delegated control group subtree",
"description_bad": "Service maintains its own delegated control group subtree",
"weight": 100,
"range": 1
},
"RestrictRealtime":
{"description_good": "Service realtime scheduling access is restricted",
"description_bad": "Service may acquire realtime scheduling",
"weight": 500,
"range": 1
},
"RestrictSUIDSGID":
{"description_good": "SUID/SGIDfilecreationbyserviceisrestricted",
"description_bad": "ServicemaycreateSUID/SGIDfiles",
"weight": 1000,
"range": 1
},
"RestrictNamespaces_CLONE_NEWUSER":
{"description_good": "Servicecannotcreateusernamespaces",
"description_bad": "Servicemaycreateusernamespaces",
"weight": 1500,
"range": 1
},
"RestrictNamespaces_CLONE_NEWNS":
{"description_good": "Service cannot create file system namespaces",
"description_bad": "Service may create file system namespaces",
"weight": 500,
"range": 1
},
"RestrictNamespaces_CLONE_NEWIPC":
{"description_good": "Service cannot create IPC namespaces",
"description_bad": "Service may create IPC namespaces",
"weight": 500,
"range": 1
},
"RestrictNamespaces_CLONE_NEWPID":
{"description_good": "Service cannot create process namespaces",
"description_bad": "Service may create process namespaces",
"weight": 500,
"range": 1
},
"RestrictNamespaces_CLONE_NEWCGROUP":
{"description_good": "Service cannot create cgroup namespaces",
"description_bad": "Service may create cgroup namespaces",
"weight": 500,
"range": 1
},
"RestrictNamespaces_CLONE_NEWNET":
{"description_good": "Service cannot create network namespaces",
"description_bad": "Service may create network namespaces",
"weight": 500,
"range": 1
},
"RestrictNamespaces_CLONE_NEWUTS":
{"description_good": "Service cannot create hostname namespaces",
"description_bad": "Service may create hostname namespaces",
"weight": 100,
"range": 1
},
"RestrictAddressFamilies_AF_INET_INET6":
{"description_good": "Service cannot allocate Internet sockets",
"description_bad": "Service may allocate Internet sockets",
"weight": 1500,
"range": 1
},
"RestrictAddressFamilies_AF_UNIX":
{"description_good": "Service cannot allocate local sockets",
"description_bad": "Service may allocate local sockets",
"weight": 25,
"range": 1
},
"RestrictAddressFamilies_AF_NETLINK":
{"description_good": "Service cannot allocate netlink sockets",
"description_bad": "Service may allocate netlink sockets",
"weight": 200,
"range": 1
},
"RestrictAddressFamilies_AF_PACKET":
{"description_good": "Service cannot allocate packet sockets",
"description_bad": "Service may allocate packet sockets",
"weight": 1000,
"range": 1
},
"RestrictAddressFamilies_OTHER":
{"description_good": "Service cannot allocate exotic sockets",
"description_bad": "Service may allocate exotic sockets",
"weight": 1250,
"range": 1
},
"SystemCallArchitectures":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_swap":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_obsolete":
{"weight": 250,
"range": 10
},
"SystemCallFilter_clock":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_cpu_emulation":
{"weight": 250,
"range": 10
},
"SystemCallFilter_debug":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_mount":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_module":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_raw_io":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_reboot":
{"weight": 1000,
"range": 10
},
"SystemCallFilter_privileged":
{"weight": 700,
"range": 10
},
"SystemCallFilter_resources":
{"weight": 700,
"range": 10
},
"IPAddressDeny":
{"weight": 1000,
"range": 10
},
"DeviceAllow":
{"weight": 1000,
"range": 10
},
"AmbientCapabilities":
{"description_good": "Service process does not receive ambient capabilities",
"description_bad": "Service process receives ambient capabilities",
"weight": 500,
"range": 1
},
"INVALID_ID":
{"weight": 1000,
"range": 10
}
}
EOF
# Reads in custom security requirements from the parsed .json file and uses these for comparison
systemd-analyze security --threshold=90 --offline=true \
--security-policy=/tmp/testfile.json \
--root=/tmp/img/ testfile.service
set +e
systemd-analyze security --threshold=50 --offline=true \
--security-policy=/tmp/testfile.json \
--root=/tmp/img/ testfile.service \
&& { echo 'unexpected success'; exit 1; }
set -e
rm /tmp/img/usr/lib/systemd/system/testfile.service
systemd-analyze log-level info
echo OK >/testok
exit 0