X Tutup
Skip to content

Commit dfbda87

Browse files
committed
systemd-analyze: add new 'security' option to compare unit's overall exposure level with
--threshold option added to work with security verb and with the --offline option so that users can determine what qualifies as a security threat. The threshold set by the user is compared with the overall exposure level assigned to a unit file and if the exposure is higher than the threshold, 'security' will return a non-zero exit status. The default value of the --threshold option is 100. Example Run: 1. testcase.service is a unit file created for testing the --threshold option maanya-goenka@debian:~/systemd (systemd-security)$ cat<<EOF>testcase.service > [Service] > ExecStart = echo hello > EOF For the purposes of this demo, the security table outputted below has been cut to show only the first two security settings. maanya-goenka@debian:~/systemd (systemd-security)$ sudo build/systemd-analyze security --offline=true testcase.service /usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed. /usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether. /usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly. NAME DESCRIPTION EXPOSURE ✗ PrivateNetwork= Service has access to the host's network 0.5 ✗ User=/DynamicUser= Service runs as root user 0.4 → Overall exposure level for testcase.service: 9.6 UNSAFE 😨 maanya-goenka@debian:~/systemd (systemd-security)$ echo $? 0 2. Next, we use the same testcase.service file but add an additional --threshold=60 parameter. We would expect 'security' to exit with a non-zero status because the overall exposure level (= 96) is higher than the set threshold (= 60). maanya-goenka@debian:~/systemd (systemd-security)$ sudo build/systemd-analyze security --offline=true --threshold=60 testcase.service /usr/lib/systemd/system/plymouth-start.service:15: Unit configured to use KillMode=none. This is unsafe, as it disables systemd's process lifecycle management for the service. Please update your service to use a safer KillMode=, such as 'mixed' or 'control-group'. Support for KillMode=none is deprecated and will eventually be removed. /usr/lib/systemd/system/gdm.service:30: Standard output type syslog is obsolete, automatically updating to journal. Please update your unit file, and consider removing the setting altogether. /usr/lib/systemd/system/dbus.socket:5: ListenStream= references a path below legacy directory /var/run/, updating /var/run/dbus/system_bus_socket → /run/dbus/system_bus_socket; please update the unit file accordingly. NAME DESCRIPTION EXPOSURE ✗ PrivateNetwork= Service has access to the host's network 0.5 ✗ User=/DynamicUser= Service runs as root user 0.4 → Overall exposure level for testcase.service: 9.6 UNSAFE 😨 maanya-goenka@debian:~/systemd (systemd-security)$ echo $? 1
1 parent bb43d85 commit dfbda87

File tree

6 files changed

+50
-14
lines changed

6 files changed

+50
-14
lines changed

man/systemd-analyze.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,16 @@ Service b@0.service not loaded, b.socket cannot be started.
782782
an error.</para></listitem>
783783
</varlistentry>
784784

785+
<varlistentry>
786+
<term><option>--threshold=<replaceable>NUMBER</replaceable></option></term>
787+
788+
<listitem><para>With <command>security</command>, allow the user to set a custom value
789+
to compare the overall exposure level with, for the specified unit file(s). If a unit's
790+
overall exposure level, is greater than that set by the user, <command>security</command>
791+
will return an error. <option>--threshold=</option> can be used with <option>--offline=</option>
792+
as well and its default value is 100.</para></listitem>
793+
</varlistentry>
794+
785795
<varlistentry>
786796
<term><option>--iterations=<replaceable>NUMBER</replaceable></option></term>
787797

shell-completion/bash/systemd-analyze

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ _systemd_analyze() {
144144

145145
elif __contains_word "$verb" ${VERBS[SECURITY]}; then
146146
if [[ $cur = -* ]]; then
147-
comps='--help --version --no-pager --system --user -H --host -M --machine --offline'
147+
comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold'
148148
else
149149
if __contains_word "--user" ${COMP_WORDS[*]}; then
150150
mode=--user

shell-completion/zsh/_systemd-analyze

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ _arguments \
9191
'--image=[Add support for discrete images]:PATH' \
9292
'--recursive-errors=[When verifying a unit, control dependency verification]:MODE' \
9393
'--offline=[Perform a security review of the specified unit file(s)]:BOOL' \
94+
'--threshold=[Set a value to compare the overall security exposure level with]: NUMBER' \
9495
'--no-pager[Do not pipe output into a pager]' \
9596
'--man=[Do (not) check for existence of man pages]:boolean:(1 0)' \
9697
'--order[When generating graph for dot, show only order]' \

src/analyze/analyze-security.c

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1527,7 +1527,7 @@ static const struct security_assessor security_assessor_table[] = {
15271527
},
15281528
};
15291529

1530-
static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecurityFlags flags) {
1530+
static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecurityFlags flags, unsigned threshold) {
15311531
static const struct {
15321532
uint64_t exposure;
15331533
const char *name;
@@ -1723,6 +1723,10 @@ static int assess(const SecurityInfo *info, Table *overview_table, AnalyzeSecuri
17231723
return table_log_add_error(r);
17241724
}
17251725

1726+
/* Return error when overall exposure level is over threshold */
1727+
if (exposure > threshold)
1728+
return -EINVAL;
1729+
17261730
return 0;
17271731
}
17281732

@@ -2188,7 +2192,9 @@ static int acquire_security_info(sd_bus *bus, const char *name, SecurityInfo *in
21882192
return 0;
21892193
}
21902194

2191-
static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table, AnalyzeSecurityFlags flags) {
2195+
static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_table,
2196+
AnalyzeSecurityFlags flags, unsigned threshold) {
2197+
21922198
_cleanup_(security_info_freep) SecurityInfo *info = security_info_new();
21932199
if (!info)
21942200
return log_oom();
@@ -2204,7 +2210,7 @@ static int analyze_security_one(sd_bus *bus, const char *name, Table *overview_t
22042210
if (r < 0)
22052211
return r;
22062212

2207-
r = assess(info, overview_table, flags);
2213+
r = assess(info, overview_table, flags, threshold);
22082214
if (r < 0)
22092215
return r;
22102216

@@ -2390,7 +2396,7 @@ static int get_security_info(Unit *u, ExecContext *c, CGroupContext *g, Security
23902396
return 0;
23912397
}
23922398

2393-
static int offline_security_check(Unit *u) {
2399+
static int offline_security_check(Unit *u, unsigned threshold) {
23942400
_cleanup_(table_unrefp) Table *overview_table = NULL;
23952401
AnalyzeSecurityFlags flags = 0;
23962402
_cleanup_(security_info_freep) SecurityInfo *info = NULL;
@@ -2405,10 +2411,10 @@ static int offline_security_check(Unit *u) {
24052411
if (r < 0)
24062412
return r;
24072413

2408-
return assess(info, overview_table, flags);
2414+
return assess(info, overview_table, flags, threshold);
24092415
}
24102416

2411-
static int offline_security_checks(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, const char *root) {
2417+
static int offline_security_checks(char **filenames, UnitFileScope scope, bool check_man, bool run_generators, unsigned threshold, const char *root) {
24122418
const ManagerTestRunFlags flags =
24132419
MANAGER_TEST_RUN_MINIMAL |
24142420
MANAGER_TEST_RUN_ENV_GENERATORS |
@@ -2467,22 +2473,24 @@ static int offline_security_checks(char **filenames, UnitFileScope scope, bool c
24672473
}
24682474

24692475
for (size_t i = 0; i < count; i++) {
2470-
k = offline_security_check(units[i]);
2476+
k = offline_security_check(units[i], threshold);
24712477
if (k < 0 && r == 0)
24722478
r = k;
24732479
}
24742480

24752481
return r;
24762482
}
24772483

2478-
int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators, bool offline, const char *root, AnalyzeSecurityFlags flags) {
2484+
int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
2485+
bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags) {
2486+
24792487
_cleanup_(table_unrefp) Table *overview_table = NULL;
24802488
int ret = 0, r;
24812489

24822490
assert(bus);
24832491

24842492
if (offline)
2485-
return offline_security_checks(units, scope, check_man, run_generators, root);
2493+
return offline_security_checks(units, scope, check_man, run_generators, threshold, root);
24862494

24872495
if (strv_length(units) != 1) {
24882496
overview_table = table_new("unit", "exposure", "predicate", "happy");
@@ -2542,7 +2550,7 @@ int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_
25422550
flags |= ANALYZE_SECURITY_SHORT|ANALYZE_SECURITY_ONLY_LOADED|ANALYZE_SECURITY_ONLY_LONG_RUNNING;
25432551

25442552
STRV_FOREACH(i, list) {
2545-
r = analyze_security_one(bus, *i, overview_table, flags);
2553+
r = analyze_security_one(bus, *i, overview_table, flags, threshold);
25462554
if (r < 0 && ret >= 0)
25472555
ret = r;
25482556
}
@@ -2577,7 +2585,7 @@ int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_
25772585
} else
25782586
name = mangled;
25792587

2580-
r = analyze_security_one(bus, name, overview_table, flags);
2588+
r = analyze_security_one(bus, name, overview_table, flags, threshold);
25812589
if (r < 0 && ret >= 0)
25822590
ret = r;
25832591
}

src/analyze/analyze-security.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ typedef enum AnalyzeSecurityFlags {
1313
ANALYZE_SECURITY_ONLY_LONG_RUNNING = 1 << 2,
1414
} AnalyzeSecurityFlags;
1515

16-
int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators, bool offline, const char *root, AnalyzeSecurityFlags flags);
16+
int analyze_security(sd_bus *bus, char **units, UnitFileScope scope, bool check_man, bool run_generators,
17+
bool offline, unsigned threshold, const char *root, AnalyzeSecurityFlags flags);

src/analyze/analyze.c

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ static bool arg_generators = false;
9292
static char *arg_root = NULL;
9393
static char *arg_image = NULL;
9494
static bool arg_offline = false;
95+
static unsigned arg_threshold = 100;
9596
static unsigned arg_iterations = 1;
9697
static usec_t arg_base_time = USEC_INFINITY;
9798

@@ -2161,7 +2162,7 @@ static int do_security(int argc, char *argv[], void *userdata) {
21612162

21622163
(void) pager_open(arg_pager_flags);
21632164

2164-
return analyze_security(bus, strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_offline, arg_root, 0);
2165+
return analyze_security(bus, strv_skip(argv, 1), arg_scope, arg_man, arg_generators, arg_offline, arg_threshold, arg_root, 0);
21652166
}
21662167

21672168
static int help(int argc, char *argv[], void *userdata) {
@@ -2210,6 +2211,8 @@ static int help(int argc, char *argv[], void *userdata) {
22102211
" -h --help Show this help\n"
22112212
" --recursive-errors=MODE Control which units are verified\n"
22122213
" --offline=BOOL Perform a security review on unit file(s)\n"
2214+
" --threshold=N Exit with a non-zero status when overall\n"
2215+
" exposure level is over threshold value\n"
22132216
" --version Show package version\n"
22142217
" --no-pager Do not pipe output into a pager\n"
22152218
" --system Operate on system systemd instance\n"
@@ -2262,6 +2265,7 @@ static int parse_argv(int argc, char *argv[]) {
22622265
ARG_BASE_TIME,
22632266
ARG_RECURSIVE_ERRORS,
22642267
ARG_OFFLINE,
2268+
ARG_THRESHOLD,
22652269
};
22662270

22672271
static const struct option options[] = {
@@ -2273,6 +2277,7 @@ static int parse_argv(int argc, char *argv[]) {
22732277
{ "image", required_argument, NULL, ARG_IMAGE },
22742278
{ "recursive-errors", required_argument, NULL, ARG_RECURSIVE_ERRORS },
22752279
{ "offline", required_argument, NULL, ARG_OFFLINE },
2280+
{ "threshold", required_argument, NULL, ARG_THRESHOLD },
22762281
{ "system", no_argument, NULL, ARG_SYSTEM },
22772282
{ "user", no_argument, NULL, ARG_USER },
22782283
{ "global", no_argument, NULL, ARG_GLOBAL },
@@ -2397,6 +2402,13 @@ static int parse_argv(int argc, char *argv[]) {
23972402
return r;
23982403
break;
23992404

2405+
case ARG_THRESHOLD:
2406+
r = safe_atou(optarg, &arg_threshold);
2407+
if (r < 0 || arg_threshold > 100)
2408+
return log_error_errno(r < 0 ? r : SYNTHETIC_ERRNO(EINVAL), "Failed to parse threshold: %s", optarg);
2409+
2410+
break;
2411+
24002412
case ARG_ITERATIONS:
24012413
r = safe_atou(optarg, &arg_iterations);
24022414
if (r < 0)
@@ -2422,6 +2434,10 @@ static int parse_argv(int argc, char *argv[]) {
24222434
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
24232435
"Option --offline= is only supported for security right now.");
24242436

2437+
if (arg_threshold != 100 && !streq_ptr(argv[optind], "security"))
2438+
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
2439+
"Option --threshold= is only supported for security right now.");
2440+
24252441
if (arg_scope == UNIT_FILE_GLOBAL &&
24262442
!STR_IN_SET(argv[optind] ?: "time", "dot", "unit-paths", "verify"))
24272443
return log_error_errno(SYNTHETIC_ERRNO(EINVAL),

0 commit comments

Comments
 (0)
X Tutup