core: rename ReadOnlySystem= to ProtectSystem= and add a third value for also mounting /etc read-only

Also, rename ProtectedHome= to ProtectHome=, to simplify things a bit.

With this in place we now have two neat options ProtectSystem= and
ProtectHome= for protecting the OS itself (and optionally its
configuration), and for protecting the user's data.
This commit is contained in:
Lennart Poettering 2014-06-04 18:07:55 +02:00
parent 4c02dd7153
commit 1b8689f949
20 changed files with 147 additions and 79 deletions

View File

@ -935,16 +935,21 @@
</varlistentry>
<varlistentry>
<term><varname>ReadOnlySystem=</varname></term>
<term><varname>ProtectSystem=</varname></term>
<listitem><para>Takes a boolean
argument. If true, mounts the
<filename>/usr</filename> and
<filename>/boot</filename> directories
read-only for processes invoked by
this unit. This setting ensures that
any modification of the vendor
supplied operating system is
argument or
<literal>full</literal>. If true,
mounts the <filename>/usr</filename>
and <filename>/boot</filename>
directories read-only for processes
invoked by this unit. If set to
<literal>full</literal> the
<filename>/etc</filename> is mounted
read-only, too. This setting ensures
that any modification of the vendor
supplied operating system (and
optionally its configuration) is
prohibited for the service. It is
recommended to enable this setting for
all long-running services, unless they
@ -962,7 +967,7 @@
</varlistentry>
<varlistentry>
<term><varname>ProtectedHome=</varname></term>
<term><varname>ProtectHome=</varname></term>
<listitem><para>Takes a boolean
argument or
@ -977,7 +982,7 @@
instead. It is recommended to enable
this setting for all long-running
services (in particular network-facing
one), to ensure they cannot get access
ones), to ensure they cannot get access
to private user data, unless the
services actually require access to
the user's private data. Note however,

View File

@ -45,7 +45,8 @@ BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_exec_output, exec_output, ExecOutp
static BUS_DEFINE_PROPERTY_GET_ENUM(property_get_exec_input, exec_input, ExecInput);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protected_home, protected_home, ProtectedHome);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_home, protect_home, ProtectHome);
static BUS_DEFINE_PROPERTY_GET_ENUM(bus_property_get_protect_system, protect_system, ProtectSystem);
static int property_get_environment_files(
sd_bus *bus,
@ -629,8 +630,8 @@ const sd_bus_vtable bus_exec_vtable[] = {
SD_BUS_PROPERTY("PrivateTmp", "b", bus_property_get_bool, offsetof(ExecContext, private_tmp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateNetwork", "b", bus_property_get_bool, offsetof(ExecContext, private_network), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("PrivateDevices", "b", bus_property_get_bool, offsetof(ExecContext, private_devices), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectedHome", "s", bus_property_get_protected_home, offsetof(ExecContext, protected_home), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ReadOnlySystem", "b", bus_property_get_bool, offsetof(ExecContext, read_only_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectHome", "s", bus_property_get_protect_home, offsetof(ExecContext, protect_home), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("ProtectSystem", "s", bus_property_get_protect_system, offsetof(ExecContext, protect_system), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SameProcessGroup", "b", bus_property_get_bool, offsetof(ExecContext, same_pgrp), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("UtmpIdentifier", "s", NULL, offsetof(ExecContext, utmp_id), SD_BUS_VTABLE_PROPERTY_CONST),
SD_BUS_PROPERTY("SELinuxContext", "(bs)", property_get_selinux_context, 0, SD_BUS_VTABLE_PROPERTY_CONST),

View File

@ -1570,8 +1570,8 @@ int exec_spawn(ExecCommand *command,
context->mount_flags != 0 ||
(context->private_tmp && runtime && (runtime->tmp_dir || runtime->var_tmp_dir)) ||
context->private_devices ||
context->read_only_system ||
context->protected_home != PROTECTED_HOME_NO) {
context->protect_system != PROTECT_SYSTEM_NO ||
context->protect_home != PROTECT_HOME_NO) {
char *tmp = NULL, *var = NULL;
@ -1595,8 +1595,8 @@ int exec_spawn(ExecCommand *command,
tmp,
var,
context->private_devices,
context->protected_home,
context->read_only_system,
context->protect_home,
context->protect_system,
context->mount_flags);
if (err < 0) {
r = EXIT_NAMESPACE;
@ -2114,8 +2114,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
"%sPrivateTmp: %s\n"
"%sPrivateNetwork: %s\n"
"%sPrivateDevices: %s\n"
"%sProtectedHome: %s\n"
"%sReadOnlySystem: %s\n"
"%sProtectHome: %s\n"
"%sProtectSystem: %s\n"
"%sIgnoreSIGPIPE: %s\n",
prefix, c->umask,
prefix, c->working_directory ? c->working_directory : "/",
@ -2124,8 +2124,8 @@ void exec_context_dump(ExecContext *c, FILE* f, const char *prefix) {
prefix, yes_no(c->private_tmp),
prefix, yes_no(c->private_network),
prefix, yes_no(c->private_devices),
prefix, protected_home_to_string(c->protected_home),
prefix, yes_no(c->read_only_system),
prefix, protect_home_to_string(c->protect_home),
prefix, protect_system_to_string(c->protect_system),
prefix, yes_no(c->ignore_sigpipe));
STRV_FOREACH(e, c->environment)

View File

@ -157,8 +157,8 @@ struct ExecContext {
bool private_tmp;
bool private_network;
bool private_devices;
bool read_only_system;
ProtectedHome protected_home;
ProtectSystem protect_system;
ProtectHome protect_home;
bool no_new_privileges;

View File

@ -80,8 +80,8 @@ $1.InaccessibleDirectories, config_parse_namespace_path_strv, 0,
$1.PrivateTmp, config_parse_bool, 0, offsetof($1, exec_context.private_tmp)
$1.PrivateNetwork, config_parse_bool, 0, offsetof($1, exec_context.private_network)
$1.PrivateDevices, config_parse_bool, 0, offsetof($1, exec_context.private_devices)
$1.ReadOnlySystem, config_parse_bool, 0, offsetof($1, exec_context.read_only_system)
$1.ProtectedHome, config_parse_protected_home, 0, offsetof($1, exec_context)
$1.ProtectSystem, config_parse_protect_system, 0, offsetof($1, exec_context.protect_system)
$1.ProtectHome, config_parse_protect_home, 0, offsetof($1, exec_context.protect_home)
$1.MountFlags, config_parse_exec_mount_flags, 0, offsetof($1, exec_context)
$1.Personality, config_parse_personality, 0, offsetof($1, exec_context.personality)
$1.RuntimeDirectoryMode, config_parse_mode, 0, offsetof($1, exec_context.runtime_directory_mode)

View File

@ -3101,7 +3101,7 @@ int config_parse_no_new_privileges(
return 0;
}
int config_parse_protected_home(
int config_parse_protect_home(
const char* unit,
const char *filename,
unsigned line,
@ -3126,19 +3126,62 @@ int config_parse_protected_home(
k = parse_boolean(rvalue);
if (k > 0)
c->protected_home = PROTECTED_HOME_YES;
c->protect_home = PROTECT_HOME_YES;
else if (k == 0)
c->protected_home = PROTECTED_HOME_NO;
c->protect_home = PROTECT_HOME_NO;
else {
ProtectedHome h;
ProtectHome h;
h = protected_home_from_string(rvalue);
h = protect_home_from_string(rvalue);
if (h < 0){
log_syntax(unit, LOG_ERR, filename, line, -h, "Failed to parse protected home value, ignoring: %s", rvalue);
log_syntax(unit, LOG_ERR, filename, line, -h, "Failed to parse protect home value, ignoring: %s", rvalue);
return 0;
}
c->protected_home = h;
c->protect_home = h;
}
return 0;
}
int config_parse_protect_system(
const char* unit,
const char *filename,
unsigned line,
const char *section,
unsigned section_line,
const char *lvalue,
int ltype,
const char *rvalue,
void *data,
void *userdata) {
ExecContext *c = data;
int k;
assert(filename);
assert(lvalue);
assert(rvalue);
assert(data);
/* Our enum shall be a superset of booleans, hence first try
* to parse as as boolean, and then as enum */
k = parse_boolean(rvalue);
if (k > 0)
c->protect_system = PROTECT_SYSTEM_YES;
else if (k == 0)
c->protect_system = PROTECT_SYSTEM_NO;
else {
ProtectSystem s;
s = protect_system_from_string(rvalue);
if (s < 0){
log_syntax(unit, LOG_ERR, filename, line, -s, "Failed to parse protect system value, ignoring: %s", rvalue);
return 0;
}
c->protect_system = s;
}
return 0;

View File

@ -98,7 +98,8 @@ int config_parse_set_status(const char *unit, const char *filename, unsigned lin
int config_parse_namespace_path_strv(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_no_new_privileges(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_cpu_quota(const char *unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_protected_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_protect_home(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
int config_parse_protect_system(const char* unit, const char *filename, unsigned line, const char *section, unsigned section_line, const char *lvalue, int ltype, const char *rvalue, void *data, void *userdata);
/* gperf prototypes */
const struct ConfigPerfItem* load_fragment_gperf_lookup(const char *key, unsigned length);

View File

@ -337,8 +337,8 @@ int setup_namespace(
char* tmp_dir,
char* var_tmp_dir,
bool private_dev,
ProtectedHome protected_home,
bool read_only_system,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned mount_flags) {
BindMount *m, *mounts = NULL;
@ -356,8 +356,9 @@ int setup_namespace(
strv_length(read_only_dirs) +
strv_length(inaccessible_dirs) +
private_dev +
(protected_home != PROTECTED_HOME_NO ? 2 : 0) +
(read_only_system ? 2 : 0);
(protect_home != PROTECT_HOME_NO ? 2 : 0) +
(protect_system != PROTECT_SYSTEM_NO ? 2 : 0) +
(protect_system == PROTECT_SYSTEM_FULL ? 1 : 0);
if (n > 0) {
m = mounts = (BindMount *) alloca(n * sizeof(BindMount));
@ -391,14 +392,14 @@ int setup_namespace(
m++;
}
if (protected_home != PROTECTED_HOME_NO) {
r = append_mounts(&m, STRV_MAKE("-/home", "-/run/user"), protected_home == PROTECTED_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
if (protect_home != PROTECT_HOME_NO) {
r = append_mounts(&m, STRV_MAKE("-/home", "-/run/user"), protect_home == PROTECT_HOME_READ_ONLY ? READONLY : INACCESSIBLE);
if (r < 0)
return r;
}
if (read_only_system) {
r = append_mounts(&m, STRV_MAKE("/usr", "-/boot"), READONLY);
if (protect_system != PROTECT_SYSTEM_NO) {
r = append_mounts(&m, protect_system == PROTECT_SYSTEM_FULL ? STRV_MAKE("/usr", "/etc", "-/boot") : STRV_MAKE("/usr", "-/boot"), READONLY);
if (r < 0)
return r;
}
@ -604,10 +605,18 @@ fail:
return r;
}
static const char *const protected_home_table[_PROTECTED_HOME_MAX] = {
[PROTECTED_HOME_NO] = "no",
[PROTECTED_HOME_YES] = "yes",
[PROTECTED_HOME_READ_ONLY] = "read-only",
static const char *const protect_home_table[_PROTECT_HOME_MAX] = {
[PROTECT_HOME_NO] = "no",
[PROTECT_HOME_YES] = "yes",
[PROTECT_HOME_READ_ONLY] = "read-only",
};
DEFINE_STRING_TABLE_LOOKUP(protected_home, ProtectedHome);
DEFINE_STRING_TABLE_LOOKUP(protect_home, ProtectHome);
static const char *const protect_system_table[_PROTECT_SYSTEM_MAX] = {
[PROTECT_SYSTEM_NO] = "no",
[PROTECT_SYSTEM_YES] = "yes",
[PROTECT_SYSTEM_FULL] = "full",
};
DEFINE_STRING_TABLE_LOOKUP(protect_system, ProtectSystem);

View File

@ -25,13 +25,21 @@
#include "macro.h"
typedef enum ProtectedHome {
PROTECTED_HOME_NO,
PROTECTED_HOME_YES,
PROTECTED_HOME_READ_ONLY,
_PROTECTED_HOME_MAX,
_PROTECTED_HOME_INVALID = -1
} ProtectedHome;
typedef enum ProtectHome {
PROTECT_HOME_NO,
PROTECT_HOME_YES,
PROTECT_HOME_READ_ONLY,
_PROTECT_HOME_MAX,
_PROTECT_HOME_INVALID = -1
} ProtectHome;
typedef enum ProtectSystem {
PROTECT_SYSTEM_NO,
PROTECT_SYSTEM_YES,
PROTECT_SYSTEM_FULL,
_PROTECT_SYSTEM_MAX,
_PROTECT_SYSTEM_INVALID = -1
} ProtectSystem;
int setup_namespace(char **read_write_dirs,
char **read_only_dirs,
@ -39,8 +47,8 @@ int setup_namespace(char **read_write_dirs,
char *tmp_dir,
char *var_tmp_dir,
bool private_dev,
ProtectedHome protected_home,
bool read_only_system,
ProtectHome protect_home,
ProtectSystem protect_system,
unsigned mount_flags);
int setup_tmp_dirs(const char *id,
@ -49,5 +57,8 @@ int setup_tmp_dirs(const char *id,
int setup_netns(int netns_storage_socket[2]);
const char* protected_home_to_string(ProtectedHome p) _const_;
ProtectedHome protected_home_from_string(const char *s) _pure_;
const char* protect_home_to_string(ProtectHome p) _const_;
ProtectHome protect_home_from_string(const char *s) _pure_;
const char* protect_system_to_string(ProtectSystem p) _const_;
ProtectSystem protect_system_from_string(const char *s) _pure_;

View File

@ -60,8 +60,8 @@ int main(int argc, char *argv[]) {
tmp_dir,
var_tmp_dir,
true,
PROTECTED_HOME_NO,
false,
PROTECT_HOME_NO,
PROTECT_SYSTEM_NO,
0);
if (r < 0) {
log_error("Failed to setup namespace: %s", strerror(-r));

View File

@ -18,5 +18,5 @@ CapabilityBoundingSet=CAP_IPC_OWNER CAP_SETUID CAP_SETGID CAP_SETPCAP
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=full
ProtectHome=yes

View File

@ -18,5 +18,5 @@ WatchdogSec=1min
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=yes
ProtectHome=yes

View File

@ -17,8 +17,8 @@ SupplementaryGroups=systemd-journal
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=full
ProtectHome=yes
[Install]
Also=systemd-journal-gatewayd.socket

View File

@ -21,8 +21,6 @@ RestartSec=0
NotifyAccess=all
StandardOutput=null
CapabilityBoundingSet=CAP_SYS_ADMIN CAP_DAC_OVERRIDE CAP_SYS_PTRACE CAP_SYSLOG CAP_AUDIT_CONTROL CAP_CHOWN CAP_DAC_READ_SEARCH CAP_FOWNER CAP_SETUID CAP_SETGID
ReadOnlySystem=yes
ProtectedHome=yes
WatchdogSec=1min
# Increase the default a bit in order to allow many simultaneous

View File

@ -18,5 +18,5 @@ WatchdogSec=1min
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=yes
ProtectHome=yes

View File

@ -20,5 +20,5 @@ WatchdogSec=1min
PrivateTmp=yes
PrivateDevices=yes
PrivateNetwork=yes
ReadOnlySystem=yes
ProtectedHome=yes
PortectSystem=full
ProtectHome=yes

View File

@ -20,8 +20,8 @@ Restart=always
RestartSec=0
ExecStart=@rootlibexecdir@/systemd-networkd
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_NET_BROADCAST CAP_NET_RAW CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=full
ProtectHome=yes
WatchdogSec=1min
[Install]

View File

@ -16,8 +16,8 @@ Restart=always
RestartSec=0
ExecStart=@rootlibexecdir@/systemd-resolved
CapabilityBoundingSet=CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=full
ProtectHome=yes
[Install]
WantedBy=multi-user.target

View File

@ -16,5 +16,5 @@ BusName=org.freedesktop.timedate1
CapabilityBoundingSet=CAP_SYS_TIME
WatchdogSec=1min
PrivateTmp=yes
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=yes
ProtectHome=yes

View File

@ -23,8 +23,8 @@ ExecStart=@rootlibexecdir@/systemd-timesyncd
CapabilityBoundingSet=CAP_SYS_TIME CAP_SETUID CAP_SETGID CAP_SETPCAP CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER
PrivateTmp=yes
PrivateDevices=yes
ReadOnlySystem=yes
ProtectedHome=yes
ProtectSystem=full
ProtectHome=yes
WatchdogSec=1min
[Install]