1
0
mirror of https://github.com/systemd/systemd.git synced 2024-11-01 17:51:22 +03:00
systemd/shell-completion/bash/systemd-analyze
Maanya Goenka 4b4a8ef741 systemd-analyze: add new option to generate JSON output of security analysis table
The new option --json= works with the 'security' verb and takes in one of three format flags.
These are off which is the default, pretty and short which use JSON format flags for output.
When set to true, it generates a JSON formatted output of the security analysis table. The
format is a JSON array with objects containing the following fields: set which indicates if
the id has been set or not, name which is what is used to refer to the id, json_field
which is the equivalent JSON formatted id name only used for JSON outputs, description which
is an outline of the id state, and exposure which is an unsigned integer in the range 0.0..10.0,
where a higher value corresponds to a higher security threat. The JSON version of the table is
printed on the standard output file.

Example Run:

The unit file testfile.service was created to test the --json= option

maanya-goenka@debian:~/systemd (json-security)$ cat <<EOF >testfile.service

> [Service]
> ExecStart = echo hello
> PrivateNetwork = yes
> PrivateMounts = yes
> PrivateDevices = yes
> EOF

Both the JSON output and the security analysis table below have been truncated to increase readability.
1. Testing for when --json=off

maanya-goenka@debian:~/systemd (json-security)$ sudo build/systemd-analyze security --json=off --root= --offline=true
testfile.service --no-pager

/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/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.
/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.
/home/maanya-goenka/systemd/foo.service:2: Unknown key name 'foo' in section 'Unit', ignoring.

    NAME                                                      DESCRIPTION                                                       EXPOSURE
✓   PrivateNetwork=                                           Service has no access to the host's network
✗   User=/DynamicUser=                                        Service runs as root user                                              0.4
✗   CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)              Service may change UID/GID identities/capabilities                     0.3
✗   CapabilityBoundingSet=~CAP_NET_ADMIN                      Service has administrator privileges                                   0.3

→ Overall exposure level for testfile.service: 8.3 EXPOSED 🙁

2. Testing for when --json=pretty

maanya-goenka@debian:~/systemd (json-security)$ sudo build/systemd-analyze security --json=pretty --root= --offline=true
testfile.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/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.
/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.
/home/maanya-goenka/systemd/foo.service:2: Unknown key name 'foo' in section 'Unit', ignoring.

[
        {
                "set" : true,
                "name" : "PrivateNetwork=",
		"json-field" : "PrivateNetwork",
                "description" : "Service has no access to the host's network",
                "exposure" : null
        },
        {
                "set" : false,
                "name" : "User=/DynamicUser=",
		"json-field" : "UserOrDynamicUser",
                "decsription" : "Service runs as root user",
                "exposure" : "0.4"
        },
        {
                "set" : false,
                "name" : "CapabilityBoundingSet=~CAP_SET(UID|GID|PCAP)",
		"json_field" : "CapabilityBoundingSet_CAP_SET_UID_GID_PCAP",
                "description" : "Service may change UID/GID identities/capabilities",
                "exposure" : "0.3"
        },
        {
                "set" : false,
                "name" : "CapabilityBoundingSet=~CAP_NET_ADMIN",
		"json_field" : "CapabilityBoundingSet_CAP_NET_ADMIN",
                "description" : "Service has administrator privileges",
                "exposure" : "0.3"
        },
        ...
]

3. Testing for when --json=short

maanya-goenka@debian:~/systemd (json-security)$ sudo build/systemd-analyze security --json=short --root= --offline=true
testfile.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/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.
/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.
/home/maanya-goenka/systemd/foo.service:2: Unknown key name 'foo' in section 'Unit', ignoring.

[{"set":true,"name":"PrivateNetwork=", "json_field":"PrivateNetwork", "description":"Service has no access to the host's network","exposure":null}, ...]
2021-09-06 19:55:27 +01:00

176 lines
5.9 KiB
Bash

# systemd-analyze(1) completion -*- shell-script -*-
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# This file is part of systemd.
#
# Copyright © 2010 Ran Benita
#
# systemd is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# systemd is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with systemd; If not, see <http://www.gnu.org/licenses/>.
__contains_word () {
local w word=$1; shift
for w in "$@"; do
[[ $w = "$word" ]] && return
done
}
__get_machines() {
local a b
machinectl list --full --no-legend --no-pager | { while read a b; do echo " $a"; done; };
}
__get_services() {
systemctl list-units --no-legend --no-pager --plain -t service --all $1 | \
{ while read -r a b c; do [[ $b == "loaded" ]]; echo " $a"; done }
}
__get_syscall_sets() {
local line
systemd-analyze syscall-filter --no-pager | while IFS= read -r line; do
if [[ $line == @* ]]; then
printf '%s\n' "$line"
fi
done
}
_systemd_analyze() {
local i verb comps mode
local cur=${COMP_WORDS[COMP_CWORD]} prev=${COMP_WORDS[COMP_CWORD-1]} words cword
local -A OPTS=(
[STANDALONE]='-h --help --version --system --user --global --order --require --no-pager
--man=no --generators=yes'
[ARG]='-H --host -M --machine --fuzz --from-pattern --to-pattern --root'
)
local -A VERBS=(
[STANDALONE]='time blame plot dump unit-paths exit-status calendar timestamp timespan'
[CRITICAL_CHAIN]='critical-chain'
[DOT]='dot'
[VERIFY]='verify'
[SECCOMP_FILTER]='syscall-filter'
[CAT_CONFIG]='cat-config'
[SECURITY]='security'
[CONDITION]='condition'
)
local CONFIGS='systemd/bootchart.conf systemd/coredump.conf systemd/journald.conf
systemd/journal-remote.conf systemd/journal-upload.conf systemd/logind.conf
systemd/resolved.conf systemd/networkd.conf systemd/resolved.conf
systemd/sleep.conf systemd/system.conf systemd/timedated.conf
systemd/timesyncd.conf systemd/user.conf udev/udev.conf'
_init_completion || return
for ((i=0; i < COMP_CWORD; i++)); do
if __contains_word "${COMP_WORDS[i]}" ${VERBS[*]} &&
! __contains_word "${COMP_WORDS[i-1]}" ${OPTS[ARG]}; then
verb=${COMP_WORDS[i]}
break
fi
done
if __contains_word "$prev" ${OPTS[ARG]}; then
case $prev in
--host|-H)
comps=$(compgen -A hostname)
;;
--machine|-M)
comps=$( __get_machines )
;;
esac
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
fi
if [[ -z ${verb-} && $cur = -* ]]; then
COMPREPLY=( $(compgen -W '${OPTS[*]}' -- "$cur") )
return 0
fi
if [[ -z ${verb-} ]]; then
comps=${VERBS[*]}
elif __contains_word "$verb" ${VERBS[STANDALONE]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager'
fi
elif __contains_word "$verb" ${VERBS[CRITICAL_CHAIN]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --fuzz --no-pager'
fi
elif __contains_word "$verb" ${VERBS[DOT]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --from-pattern --to-pattern --order --require'
fi
elif __contains_word "$verb" ${VERBS[SECCOMP_FILTER]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager'
else
comps=$( __get_syscall_sets )
fi
elif __contains_word "$verb" ${VERBS[VERIFY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --man=no --generators=yes --root --image --recursive-errors=no --recursive-errors=yes --recursive-errors=one'
else
comps=$( compgen -A file -- "$cur" )
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[CAT_CONFIG]}; then
if [[ $cur = -* ]]; then
comps='--help --version --root --no-pager'
elif [[ -z $cur ]]; then
comps="$CONFIGS"
compopt -o filenames
else
comps="$CONFIGS $( compgen -A file -- "$cur" )"
compopt -o filenames
fi
elif __contains_word "$verb" ${VERBS[SECURITY]}; then
if [[ $cur = -* ]]; then
comps='--help --version --no-pager --system --user -H --host -M --machine --offline --threshold --security-policy --json=off --json=pretty --json=short'
else
if __contains_word "--user" ${COMP_WORDS[*]}; then
mode=--user
else
mode=--system
fi
comps=$( __get_services $mode )
fi
elif __contains_word "$verb" ${VERBS[CONDITION]}; then
if [[ $cur = -* ]]; then
comps='--help --version --system --user --global --no-pager --root --image'
elif [[ $prev = "-u" ]] || [[ $prev = "--unit" ]]; then
if __contains_word "--user" ${COMP_WORDS[*]}; then
mode=--user
else
mode=--system
fi
comps=$( __get_services $mode )
fi
fi
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
return 0
}
complete -F _systemd_analyze systemd-analyze