mirror of
https://github.com/systemd/systemd.git
synced 2026-11-06 05:17:24 +00:00
units: add optional varlink-sftp socket and service
With the varlink protocol upgrade feature we can use provide
file system access via varlink easily by re-using the existing
sftp standard. SFTP is independant of ssh and provides a well
defined and widely used file transfer protocol.
So here we add two new (optional) varlink interfaces:
- io.systemd.Sftp.Root.Connect
- io.systemd.Sftp.RootReadOnly.Connect
They are behind polkit auth as io.systemd.sftp.root{,-read-only}.
Note that handing this is out is equivalent for giving root
access: even ReadOnly mode will allow to read secrets like
/etc/shadow so its inherently risky. The two actions mostly
exist to prevent accidents not as a (strong) protection
boundary. The unit description warns about this and the polkit
prompt also describes the full implications.
The use cases are:
- file access via the varlink-http-bridge [1]
- file access over AF_VSOCK
The install is optional and turned off by default.
[1] https://github.com/systemd/varlink-http-bridge
This commit is contained in:
1 parent
4a91fd14e5
commit
7321925477
13 files changed
+287
-14
No files matched your search
@@ -266,6 +266,15 @@ CHANGES WITH 263 in spe:
|
||||
via polkit that the client is authorized for the specified action
|
||||
before the connection is upgraded and the command is executed.
|
||||
|
||||
* New optional varlink-sftp-root.socket and varlink-sftp-root-ro.socket
|
||||
units expose the local file system via SFTP over an upgraded Varlink
|
||||
connection, read-write and read-only respectively. Access is
|
||||
controlled via the polkit actions io.systemd.sftp.root and
|
||||
io.systemd.sftp.root-read-only. They are built only with
|
||||
-Dvarlink-sftp=true, and are disabled by default. Note that both
|
||||
run as root so allowing access is equivalent to r/w root access or
|
||||
r/o /etc/shadow access.
|
||||
|
||||
CHANGES WITH 262:
|
||||
|
||||
Announcements of Future Feature Removals and Incompatible Changes:
|
||||
|
||||
+15
-13
@@ -785,19 +785,20 @@ ln_s = ln.full_path() + ' -frsT -- "${DESTDIR:-}@0@" "${DESTDIR:-}@1@"'
|
||||
# If -Dxxx-path option is found, use that. Otherwise, use the default from the
|
||||
# middle column; a full path is used directly, a relative path is converted to
|
||||
# /usr/bin/foo or /usr/sbin/foo, depending on whether split-bin is enabled.
|
||||
progs = [['quotaon', 'quotaon' ],
|
||||
['quotacheck', 'quotacheck' ],
|
||||
['kmod', '/usr/bin/kmod' ],
|
||||
['kexec', 'kexec' ],
|
||||
['sulogin', 'sulogin' ],
|
||||
['swapon', 'swapon' ],
|
||||
['swapoff', 'swapoff' ],
|
||||
['agetty', 'agetty' ],
|
||||
['mount', '/usr/bin/mount', 'MOUNT_PATH'],
|
||||
['umount', '/usr/bin/umount', 'UMOUNT_PATH'],
|
||||
['loadkeys', '/usr/bin/loadkeys', 'KBD_LOADKEYS'],
|
||||
['setfont', '/usr/bin/setfont', 'KBD_SETFONT'],
|
||||
['nologin', 'nologin', ],
|
||||
progs = [['quotaon', 'quotaon' ],
|
||||
['quotacheck', 'quotacheck' ],
|
||||
['kmod', '/usr/bin/kmod' ],
|
||||
['kexec', 'kexec' ],
|
||||
['sulogin', 'sulogin' ],
|
||||
['swapon', 'swapon' ],
|
||||
['swapoff', 'swapoff' ],
|
||||
['agetty', 'agetty' ],
|
||||
['mount', '/usr/bin/mount', 'MOUNT_PATH'],
|
||||
['umount', '/usr/bin/umount', 'UMOUNT_PATH'],
|
||||
['loadkeys', '/usr/bin/loadkeys', 'KBD_LOADKEYS'],
|
||||
['setfont', '/usr/bin/setfont', 'KBD_SETFONT'],
|
||||
['nologin', 'nologin' ],
|
||||
['sftp-server', '/usr/libexec/openssh/sftp-server', 'SFTP_SERVER'],
|
||||
]
|
||||
foreach prog : progs
|
||||
path = get_option(prog[0] + '-path')
|
||||
@@ -1862,6 +1863,7 @@ foreach tuple : [
|
||||
['utmp', get_option('libc') != 'musl', 'musl does not support it'],
|
||||
['userdb'],
|
||||
['varlink-monitord'],
|
||||
['varlink-sftp'],
|
||||
['vconsole'],
|
||||
['xdg-autostart'],
|
||||
]
|
||||
|
||||
@@ -63,6 +63,7 @@ option('nologin-path', type : 'string', description : 'path to nologin')
|
||||
option('quotacheck-path', type : 'string', description : 'path to quotacheck')
|
||||
option('quotaon-path', type : 'string', description : 'path to quotaon')
|
||||
option('setfont-path', type : 'string', description : 'path to setfont')
|
||||
option('sftp-server-path', type : 'string', description : 'path to the SFTP server binary')
|
||||
option('sulogin-path', type : 'string', description : 'path to sulogin')
|
||||
option('swapoff-path', type : 'string', description : 'path to swapoff')
|
||||
option('swapon-path', type : 'string', description : 'path to swapon')
|
||||
@@ -286,6 +287,8 @@ option('validatefs', type : 'feature',
|
||||
description : 'install systemd-validatefs and associated files')
|
||||
option('varlink-monitord', type : 'boolean',
|
||||
description : 'install the systemd-varlink-monitord daemon')
|
||||
option('varlink-sftp', type : 'boolean', value : false,
|
||||
description : 'install the varlink-sftp sockets and services')
|
||||
option('vconsole', type : 'boolean',
|
||||
description : 'support for vconsole configuration')
|
||||
option('veritysetup', type : 'feature',
|
||||
|
||||
@@ -24,7 +24,7 @@ if ((LLVM)) && [[ -n "$SANITIZERS" ]]; then
|
||||
MKOSI_RUSTFLAGS="-Clink-arg=-shared-libasan -Clink-arg=-Wl,-rpath=$(realpath "$(clang --print-runtime-dir)")"
|
||||
fi
|
||||
|
||||
MKOSI_MESON_OPTIONS="-D mode=developer -D vcs-tag=${VCS_TAG:-true} -D b_sanitize=${SANITIZERS:-none} -Dtime-epoch=1744207869"
|
||||
MKOSI_MESON_OPTIONS="-D mode=developer -D vcs-tag=${VCS_TAG:-true} -D b_sanitize=${SANITIZERS:-none} -Dtime-epoch=1744207869 -D varlink-sftp=true"
|
||||
if ((WIPE)) && [[ -d "$BUILDDIR/meson-private" ]]; then
|
||||
MKOSI_MESON_OPTIONS="$MKOSI_MESON_OPTIONS --wipe"
|
||||
fi
|
||||
|
||||
@@ -78,3 +78,6 @@ disable systemd-journal-remote.*
|
||||
disable systemd-journal-upload.*
|
||||
disable systemd-sysext-sysroot.service
|
||||
disable systemd-time-wait-sync.service
|
||||
|
||||
disable varlink-sftp-root.*
|
||||
disable varlink-sftp-root-ro.*
|
||||
@@ -0,0 +1,40 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?> <!--*-nxml-*-->
|
||||
<!DOCTYPE policyconfig PUBLIC "-//freedesktop//DTD PolicyKit Policy Configuration 1.0//EN"
|
||||
"https://www.freedesktop.org/standards/PolicyKit/1/policyconfig.dtd">
|
||||
|
||||
<!--
|
||||
SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
This file is part of systemd.
|
||||
|
||||
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.
|
||||
-->
|
||||
|
||||
<policyconfig>
|
||||
|
||||
<vendor>The systemd Project</vendor>
|
||||
<vendor_url>https://systemd.io</vendor_url>
|
||||
|
||||
<action id="io.systemd.sftp.root">
|
||||
<description gettext-domain="systemd">Access the local file system via SFTP</description>
|
||||
<message gettext-domain="systemd">Authentication is required to access the local file system via SFTP with full root privileges.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
|
||||
<action id="io.systemd.sftp.root-read-only">
|
||||
<description gettext-domain="systemd">Read the local file system via SFTP</description>
|
||||
<message gettext-domain="systemd">Authentication is required to read the local file system via SFTP with root privileges. This allows reading any file, including secrets such as /etc/shadow.</message>
|
||||
<defaults>
|
||||
<allow_any>auth_admin</allow_any>
|
||||
<allow_inactive>auth_admin</allow_inactive>
|
||||
<allow_active>auth_admin</allow_active>
|
||||
</defaults>
|
||||
</action>
|
||||
</policyconfig>
|
||||
@@ -15,3 +15,8 @@ executables += [
|
||||
]
|
||||
|
||||
install_emptydir(varlinkbridgesdir)
|
||||
|
||||
if conf.get('ENABLE_VARLINK_SFTP') == 1
|
||||
install_data('io.systemd.sftp.policy',
|
||||
install_dir : polkitpolicydir)
|
||||
endif
|
||||
Executable
+73
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
set -eux
|
||||
set -o pipefail
|
||||
|
||||
SFTP_SERVER="$(systemctl cat varlink-sftp-root.service 2>/dev/null | sed -n 's/^ConditionPathExists=//p')" || :
|
||||
if [[ ! -x "$SFTP_SERVER" ]] || ! command -v sftp >/dev/null; then
|
||||
echo "sftp-server or sftp not available, skipping." >&2
|
||||
exit 77
|
||||
fi
|
||||
|
||||
TMPDIR_SFTP="$(mktemp -d)"
|
||||
|
||||
at_exit() {
|
||||
set +e
|
||||
systemctl stop varlink-sftp-root.socket varlink-sftp-root-ro.socket varlink-sftp-root.service varlink-sftp-root-ro.service
|
||||
if [[ -e /etc/polkit-1/rules.d/varlink-sftp-test.rules ]]; then
|
||||
rm -f /etc/polkit-1/rules.d/varlink-sftp-test.rules
|
||||
systemctl try-reload-or-restart polkit.service
|
||||
fi
|
||||
rm -rf "$TMPDIR_SFTP"
|
||||
}
|
||||
trap at_exit EXIT
|
||||
|
||||
sftp_varlink() {
|
||||
sftp -b - -D "varlinkctl call --upgrade /run/varlink/registry/$1 $1.Connect"
|
||||
}
|
||||
|
||||
sftp_varlink_testuser() {
|
||||
run0 --pipe -u testuser sftp -b - -D "varlinkctl call --upgrade /run/varlink/registry/$1 $1.Connect"
|
||||
}
|
||||
|
||||
systemctl start varlink-sftp-root.socket varlink-sftp-root-ro.socket
|
||||
chmod 755 "$TMPDIR_SFTP"
|
||||
echo hello >"$TMPDIR_SFTP/src"
|
||||
|
||||
# Without a polkit rule an unprivileged user is denied on both endpoints
|
||||
(! sftp_varlink_testuser io.systemd.Sftp.RootReadOnly <<<"get $TMPDIR_SFTP/src /tmp/varlink-sftp-denied")
|
||||
test ! -e /tmp/varlink-sftp-denied
|
||||
|
||||
# Grant testuser read-only access only and verify the two actions are enforced independently.
|
||||
# Varlink polkit checks only send a pidfd subject, which needs kernel >= 6.5 and polkit >= 124.
|
||||
if [[ -f /usr/share/dbus-1/system-services/org.freedesktop.PolicyKit1.service ]] &&
|
||||
[[ -f /usr/share/polkit-1/actions/io.systemd.sftp.policy ]] &&
|
||||
systemd-analyze compare-versions "$(uname -r)" ge 6.5 &&
|
||||
systemd-analyze compare-versions "$(pkcheck --version | awk '{print $3}')" ge 124; then
|
||||
mkdir -p /etc/polkit-1/rules.d
|
||||
cat >/etc/polkit-1/rules.d/varlink-sftp-test.rules <<'EOF2'
|
||||
polkit.addRule(function(action, subject) {
|
||||
if (action.id == "io.systemd.sftp.root-read-only" && subject.user == "testuser") {
|
||||
return polkit.Result.YES;
|
||||
}
|
||||
});
|
||||
EOF2
|
||||
systemctl try-reload-or-restart polkit.service
|
||||
|
||||
sftp_varlink_testuser io.systemd.Sftp.RootReadOnly <<<"get $TMPDIR_SFTP/src /tmp/varlink-sftp-testuser-get"
|
||||
cmp "$TMPDIR_SFTP/src" /tmp/varlink-sftp-testuser-get
|
||||
rm -f /tmp/varlink-sftp-testuser-get
|
||||
(! sftp_varlink_testuser io.systemd.Sftp.Root <<<"put $TMPDIR_SFTP/src $TMPDIR_SFTP/testuser-rw")
|
||||
test ! -e "$TMPDIR_SFTP/testuser-rw"
|
||||
|
||||
rm -f /etc/polkit-1/rules.d/varlink-sftp-test.rules
|
||||
systemctl try-reload-or-restart polkit.service
|
||||
fi
|
||||
|
||||
(! sftp_varlink io.systemd.Sftp.RootReadOnly <<<"put $TMPDIR_SFTP/src $TMPDIR_SFTP/ro")
|
||||
test ! -e "$TMPDIR_SFTP/ro"
|
||||
sftp_varlink io.systemd.Sftp.RootReadOnly <<<"get $TMPDIR_SFTP/src $TMPDIR_SFTP/ro-get"
|
||||
cmp "$TMPDIR_SFTP/src" "$TMPDIR_SFTP/ro-get"
|
||||
|
||||
sftp_varlink io.systemd.Sftp.Root <<<"put $TMPDIR_SFTP/src $TMPDIR_SFTP/rw"
|
||||
cmp "$TMPDIR_SFTP/src" "$TMPDIR_SFTP/rw"
|
||||
@@ -1203,6 +1203,22 @@ units = [
|
||||
'conditions' : ['ENABLE_MACHINED'],
|
||||
'symlinks' : ['remote-fs.target.wants/', 'machines.target.wants/'],
|
||||
},
|
||||
{
|
||||
'file' : 'varlink-sftp-root.service.in',
|
||||
'conditions' : ['ENABLE_VARLINK_SFTP'],
|
||||
},
|
||||
{
|
||||
'file' : 'varlink-sftp-root.socket.in',
|
||||
'conditions' : ['ENABLE_VARLINK_SFTP'],
|
||||
},
|
||||
{
|
||||
'file' : 'varlink-sftp-root-ro.service.in',
|
||||
'conditions' : ['ENABLE_VARLINK_SFTP'],
|
||||
},
|
||||
{
|
||||
'file' : 'varlink-sftp-root-ro.socket.in',
|
||||
'conditions' : ['ENABLE_VARLINK_SFTP'],
|
||||
},
|
||||
{
|
||||
'file' : 'veritysetup-pre.target',
|
||||
'conditions' : ['ENABLE_VERITYSETUP'],
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Varlink SFTP Server (root, read-only)
|
||||
Documentation=man:varlinkctl(1)
|
||||
Requires=varlink-sftp-root-ro.socket
|
||||
ConditionPathExists={{SFTP_SERVER}}
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
# -R places sftp-server in read-only mode
|
||||
# -e makes it log to stderr (which ends up in the journal)
|
||||
ExecStart=varlinkctl serve --polkit-action=io.systemd.sftp.root-read-only io.systemd.Sftp.RootReadOnly.Connect -- {{SFTP_SERVER}} -e -R
|
||||
|
||||
# Avoid settings that hide parts of the file system (PrivateDevices=,
|
||||
# ProtectProc=, ...) so both flavours export the same view.
|
||||
NoNewPrivileges=yes
|
||||
PrivateNetwork=yes
|
||||
RestrictAddressFamilies=AF_UNIX
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallArchitectures=native
|
||||
TasksMax=64
|
||||
# While -R already denies writes we still enforce it (because we can).
|
||||
ProtectSystem=strict
|
||||
ProtectHome=read-only
|
||||
RestrictSUIDSGID=yes
|
||||
SystemCallFilter=@system-service
|
||||
SystemCallFilter=~@privileged @resources
|
||||
SystemCallErrorNumber=EPERM
|
||||
|
||||
[Install]
|
||||
Also=varlink-sftp-root-ro.socket
|
||||
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Varlink SFTP Server Socket (root, read-only)
|
||||
Documentation=man:varlinkctl(1)
|
||||
ConditionPathExists={{SFTP_SERVER}}
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/io.systemd.Sftp.RootReadOnly
|
||||
Symlinks=/run/varlink/registry/io.systemd.Sftp.RootReadOnly
|
||||
SocketMode=0666
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
@@ -0,0 +1,32 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Varlink SFTP Server (root, read-write)
|
||||
Documentation=man:varlinkctl(1)
|
||||
Requires=varlink-sftp-root.socket
|
||||
ConditionPathExists={{SFTP_SERVER}}
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
# -e makes it log to stderr (which ends up in the journal)
|
||||
ExecStart=varlinkctl serve --polkit-action=io.systemd.sftp.root io.systemd.Sftp.Root.Connect -- {{SFTP_SERVER}} -e
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateNetwork=yes
|
||||
RestrictAddressFamilies=AF_UNIX
|
||||
RestrictNamespaces=yes
|
||||
RestrictRealtime=yes
|
||||
LockPersonality=yes
|
||||
MemoryDenyWriteExecute=yes
|
||||
SystemCallArchitectures=native
|
||||
TasksMax=64
|
||||
|
||||
[Install]
|
||||
Also=varlink-sftp-root.socket
|
||||
@@ -0,0 +1,24 @@
|
||||
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
#
|
||||
# This file is part of systemd.
|
||||
#
|
||||
# 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.
|
||||
|
||||
[Unit]
|
||||
Description=Varlink SFTP Server Socket (root, read-write)
|
||||
Documentation=man:varlinkctl(1)
|
||||
ConditionPathExists={{SFTP_SERVER}}
|
||||
|
||||
[Socket]
|
||||
ListenStream=/run/systemd/io.systemd.Sftp.Root
|
||||
Symlinks=/run/varlink/registry/io.systemd.Sftp.Root
|
||||
SocketMode=0666
|
||||
RemoveOnStop=yes
|
||||
XAttrEntryPoint=user.varlink=entrypoint
|
||||
XAttrListen=user.varlink=listen
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
Reference in new issue
Block a user