[RFE] Option to use SystemdCreds to read user+password
TriMoon opened this issue · comments
To enhance the systemd
integration (see #79 and systemd/systemd#481) even more, i suggest to add an option to allow automatic reading of System and Service Credentials or SystemdCreds
as i like to name them.
- An option named
sd_creds
for example, the exact naming is up to you.
When this option is used, pppd
should automatically read and use:
- The value for
user
from the SystemdCreds, fe. from thePPPoE-username
credential. (the exact naming is up to you) - The value for
password
from the SystemdCreds, fe. from thePPPoE-password
credential. (the exact naming is up to you)
To illustrate the usage and workaround until this functionality is implemented
I'm currently using the below self-made scripts and configs in my System, which i post here for others to use till then:
(Still a W.I.P. but it already works flawlessly)
Click the arrowed sections to expand and view (and be able to copy them)...
PPPoE@.target
# <configdir>/PPPoE@.target
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# You should enable this unit with the interface as instance name.
# eg. PPPoE@eth0.target / PPPoE@vlan35.target
# You can disregard the warning about the adition as a dependency to
# a non-existant unit 'sys-subsystem-net-devices-xxxx.device',
# this is expected behaviour...
#
# VLAN etc usage:
# Your normal network config should create the virtual network device,
# so this service automatically gets started after it.
# eg. a "VLAN=VlanID" inside eth0.network, with a corrosponding
# "VlanID.netdev" (and "VlanID.network" if needed)
[Unit]
Description=Target for %p connections on %I
Documentation=man:systemd.unit(5)#Specifiers
Documentation=man:systemd.target
# This target is NOT a "SysV run-level" like target.
AllowIsolate=no
# See: https://systemd.io/NETWORK_ONLINE/
After=network.target
BindsTo=sys-subsystem-net-devices-%i.device
After=sys-subsystem-net-devices-%i.device
PartOf=sys-subsystem-net-devices-%i.device
# avoid race waiting for systemd-networkd to configure interface
# https://github.com/systemd/systemd/issues/481#issuecomment-1010092917
# systemd guarentees MTU is set before activating (carrier) link
# https://github.com/systemd/systemd/issues/481#issuecomment-1010159176
After=systemd-networkd-wait-online@%i.service
[Install]
WantedBy=sys-subsystem-net-devices-%i.device
# WantedBy=sys-devices-virtual-net-%i.device
PPPoE@.target.d/DefaultInstance.conf
# <configdir>/PPPoE@.target.d/DefaultInstance.conf
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# Optional: Default instance to enable when the template is attempted
# to be enabled without an instance name, which isn't allowed.
[Install]
DefaultInstance=vlan35
PPPoE@.target.d/KernelCommandLine.conf
# <configdir>/PPPoE@.target.d/KernelCommandLine.conf
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# Optional: Only allow activation when a special KernelCommandLine option is present.
[Unit]
ConditionKernelCommandLine=pppoe
PPPoE@vlan35.target.d/TurkNet.conf
# <configdir>/PPPoE@vlan35.target.d/TurkNet.conf
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# Auto start/restart the instance unit below.
# In this case '%j-%i' will be: 'PPPoE-vlan35'
[Unit]
Upholds=%j-%i@TurkNet.service
PPPoE-vlan35@.service
# <configdir>/PPPoE-vlan35@.service
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# You should make a copy of this template with the interface name as
# the final component of the prefix as the unit name.
# This final component of the prefix is used as the interface to run the PPPoE
# connection over.
# Eg. to use "vlan35" as interface name to run the PPPoE over you should name
# the template: 'PPPoE-vlan35@.service'
#
# To view journal logs of your instance unit:
# journalctl -u PPPoE-vlan35@MyISP
#
[Unit]
Description=%I connection on PPPoE@%j
Documentation=man:pppd(8)
Documentation=https://github.com/ppp-project/ppp/issues/459
Documentation=https://github.com/systemd/systemd/issues/481#issuecomment-544337575
Documentation=https://github.com/jimdigriz/debian-clearfog-gt-8k#libsystemdsystempppdservice
# Refuse to start without a corrosponding peer file !
# AssertPathExists=/etc/ppp/peers/%I
BindsTo=PPPoE@%j.target
After=PPPoE@%j.target
PartOf=PPPoE@%j.target
# [Install]
# WantedBy=%p.target
[Service]
# https://github.com/ppp-project/ppp/commit/d34159f417620eb7c481bf53f29fe04c86ccd223
# otherwsise you can use 'forking' and replace 'up_sdnotify' with 'updetach'
Type=notify
# Type=oneshot
IPAccounting=yes
# StandardOutput=null
# Environment="pppd_opts0=plugin rp-pppoe.so nic-%J linkname %I ifname %I call %I up_sdnotify"
# Environment="pppd_opts1=persist default-mru nolog noauth debug pppoe-verbose 1 holdoff 5 lcp-echo-adaptive"
Environment="pppd_opts1=default-mru debug pppoe-verbose 1 holdoff 5"
# Maybe add 'noipdefault' but seems NOT needed...
# Environment="pppd_opts2=+ipv6 ipv6cp-use-persistent"
# Optional:
# Environment="pppd_opts3=defaultroute defaultroute6 replacedefaultroute"
# IPv4 default route is needed, IPv6 doesn't seem to need...
Environment="pppd_opts3=defaultroute"
ExecStart=/usr/sbin/pppd $pppd_opts0 $pppd_opts1 $pppd_opts2 $pppd_opts3
ExecReload=/bin/kill -HUP $MAINPID
KillSignal=SIGINT
RestartKillSignal=SIGHUP
RestartSec=5s
TimeoutStopSec=5s
Restart=on-failure
#
# # https://github.com/systemd/systemd/issues/481#issuecomment-544341423
# # Restart=always
# Pppd terminated because it was sent a SIGINT, SIGTERM or SIGHUP signal.
SuccessExitStatus=5
# The link was established successfully and terminated because it was idle.
SuccessExitStatus=12
# The link was established successfully and terminated because the connect time limit was reached.
SuccessExitStatus=13
# Callback was negotiated and an incoming call should arrive shortly.
# SuccessExitStatus=14
# An error was detected in processing the options given, such as two mutually exclusive options being used.
RestartPreventExitStatus=2
# The kernel does not support PPP, for example, the PPP kernel driver is not included or cannot be loaded.
RestartPreventExitStatus=4
# The connect script failed (returned a non-zero exit status).
RestartPreventExitStatus=8
# The PPP negotiation failed, that is, it didn't reach the point where at least one network protocol (e.g. IP) was running.
# RestartPreventExitStatus=10
# The peer system failed (or refused) to authenticate itself.
RestartPreventExitStatus=11
# The init script failed (returned a non-zero exit status).
RestartPreventExitStatus=18
# The PPP negotiation failed, that is, it didn't reach the point where at least one network protocol (e.g. IP) was running.
RestartForceExitStatus=10
# Callback was negotiated and an incoming call should arrive shortly.
RestartForceExitStatus=14
# The link was terminated because the peer is not responding to echo requests.
RestartForceExitStatus=15
# The link was terminated by the modem hanging up.
RestartForceExitStatus=16
# We failed to authenticate ourselves to the peer.
RestartForceExitStatus=19
# HARDENING
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
# allow /etc/ppp/resolv.conf to be written when using 'usepeerdns'
ReadWritePaths=/run/ /etc/ppp/
# https://github.com/systemd/systemd/issues/481#issuecomment-610951209
#ProtectKernelTunables=yes
ProtectControlGroups=yes
SystemCallFilter=~@mount
SystemCallArchitectures=native
LockPersonality=yes
MemoryDenyWriteExecute=yes
RestrictRealtime=yes
PPPoE-vlan35@.service.d/KernelCommandLine.conf
# <configdir>/PPPoE-vlan35@.service.d/KernelCommandLine.conf
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# Optional: Only allow activation when a special KernelCommandLine option is present.
[Unit]
ConditionKernelCommandLine=pppoe
PPPoE-vlan35@TurkNet.service.d/Credentials.conf
# <configdir>/PPPoE-vlan35@TurkNet.service.d/Credentials.conf
# PPPoE-Credentials for TurkNet@vlan35
[Service]
SetCredentialEncrypted=PPPoE-username: \
Whxqht+dQJax1aZeCGLxmiAAAAABAAAADAAAABAAAADY5KqFUJ+YZhTQOOoAAAAAClClv \
M7s8F3TQIm0e7+0LufdC+6eFdTvtpDSi9ecJUG1FJivZteUua52jVaZ1PuGL8DoqeVFYQ \
pL9A2kXo5zduY7QUO10auWbR6B4Q==
SetCredentialEncrypted=PPPoE-password: \
Whxqht+dQJax1aZeCGLxmiAAAAABAAAADAAAABAAAADEUFVtTvt2IqKxipQAAAAA/IGhP \
x+PfeA5OiDE/I/O7ARi8X7MHGocMrB216kRlaAX37JSXsU4+hVJFBgfH8MN7VcA6/mThc \
6BIR09VLIbiaiY
PPPoE-@.service.d/UseCredentials.conf
# <configdir>/PPPoE-@.service.d/UseCredentials.conf
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
[Unit]
Documentation=https://systemd.io/CREDENTIALS
# NOTE: Needed because pppd still lacks ability to read Systemd-Credentials...
[Service]
ExecStartPre= @bash 'AutoConfigGenerator_%p' -c \n"\
printf \"%%s\\t%%s\\n\" \\\n\
\"user\" \"$$(<%d/PPPoE-username)\" \\\n\
\"password\" \"$$(<%d/PPPoE-password)\" \\\n\
\"plugin\" \"rp-pppoe.so\" \\\n\
\"nic-%J\" \"\" \\\n\
\"linkname\" \"%I\" \\\n\
\"ifname\" \"%I\" \\\n\
\"up_sdnotify\" \"\" \\\n\
\"persist\" \"\" \\\n\
\"nolog\" \"\" \\\n\
\"noauth\" \"\" \\\n\
\"lcp-echo-adaptive\" \"\" \\\n\
\"+ipv6\" \"\" \\\n\
\"ipv6cp-use-persistent\" \"\" \\\n\
>/tmp/AutoConfig_%p_%I\n\
"
# ExecStartPre=cat /tmp/AutoConfig_%p_%I
ExecStart=
ExecStart= /usr/sbin/pppd file /tmp/AutoConfig_%p_%I $pppd_opts1 $pppd_opts2 $pppd_opts3
<bin path>/createSystemdCreds-PPPoE
#!/usr/bin/env bash
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# To use the creds you could use one of:
# 1.
# Environment=CRED_USERNAME=%d/PPPoE-username
# Environment=CRED_PASSWORD=%d/PPPoE-password
# cat $CRED_USERNAME
# cat $CRED_PASSWORD
# 2.
# cat %d/PPPoE-username
# cat %d/PPPoE-password
# See: man systemd-creds
# NOTE: The example in the man-page has a bug !
# it doesn't output the section header, so we need to !
#
# $1 = username
# $2 = password
function genSystemdCred () {
local -a opts
local \
credName \
credVal
# Output the header and section name at start
printf "%s\n" \
"# PPPoE-Credentials for ${connection}@${interface}" \
"[Service]"
# Output the creds lines
for credName in username password; do
case "${credName}" in
username) credVal="$1" ;;
password) credVal="$2" ;;
*) ;; # No other posibilities.
esac
opts=(
--pretty
--name="PPPoE-${credName}"
encrypt
# Input = stdin
-
# Output = stdout
-
)
# shellcheck disable=2312
printf "%s" "${credVal}" \
| systemd-creds "${opts[@]}" \
| sed -E 's/\s{2,}/\t/g' # Convert multiple white-space by a single tab.
done
}
function main () {
local -a opts
local \
username \
password \
connection \
interface \
dropInDir \
credName
username="$1"
password="$2"
connection="$3"
interface="$4"
if test -z "${username}" \
-o -z "${password}" \
-o -z "${connection}" \
-o -z "${interface}"
then
printf "%s\n" \
"Missing arguments !" \
"Usage: ${0##*/} <username> <password> <connection> <interface>"
exit 64 # EX_USAGE - Command line usage error
fi
# Generate the drop-in dir
printf -v dropInDir "PPPoE-%s@%s.service.d" \
"${interface}" \
"${connection}"
printf "%s\n" \
"username: ${username}" \
"password: ${password}" \
"connection: ${connection}" \
"interface: ${interface}" \
"drop-in dir: ${dropInDir}" \
""
# Create the drop-in dir if non-existant.
if test ! -d "${dropInDir}"; then
opts=(
--verbose
--directory
--mode=2750
--group=systemd-network
)
install "${opts[@]}" "${dropInDir}"
# Give access to admins.
setfacl --modify g:adm:rwX,g:sudo:rwX,d:g:adm:rwX,d:g:sudo:rwX "${dropInDir}"
fi
genSystemdCred \
"${username}" \
"${password}" \
>"${dropInDir}/Credentials.conf"
}
# We need to run as ROOT !
# shellcheck disable=2312
if test "$(id -u)" -ne 0; then
exec sudo "$0" "$@"
else
main "$@"
fi
The last three are the SystemdCreds
specific parts obviously 😉
- The
PPPoE-vlan35@TurkNet.service.d/Credentials.conf
dir+file was auto-generated using thecreateSystemdCreds-PPPoE
script...
⚠️ It will NOT work for you as-is, so you need to generate your own ! - The
PPPoE-@.service.d/UseCredentials.conf
drop-in overrides theExecStart
of the main template to read a config file that is auto-generated to implement the automatic reading and usage of theSystemdCreds
.
As you can see the is FAR from optimal because it uses a temporary file which can be eliminated if the functionality asked-for is implemented.
(It is still relatively safe to use, because of the private tmp used in the hardening this file is only readable by ROOT...)
Note
I use the below parts for tearing down DynNS config.
If you don't make/need use of that functionality; you can safely disregard these parts, and you have no need to install the
/etc/ppp/ip-pre-down
and /etc/ppp/ipv6-pre-down
scripts with accompanying directories they use...
PPPoE-@.service.d/DynNS-TearDown.conf
# <configdir>/PPPoE-@.service.d/DynNS-TearDown.conf
# SPDX-License-Identifier: CC-BY-NC-SA-4.0
#
# For DynNS teardown BEFORE the connection gets terminated...
[Service]
# ExecStop= -run-parts /etc/ppp/ip-pre-down.d
ExecStop= -/etc/ppp/ip-pre-down %I
# ExecStop= -run-parts /etc/ppp/ipv6-pre-down.d
ExecStop= -/etc/ppp/ipv6-pre-down %I
# ExecStop=/bin/kill $MAINPID
/etc/ppp/ip-pre-down
#!/bin/sh
# The environment is cleared before executing this script
# so the path must be reset.
PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin
export PATH
# These variables are for the use of the scripts run by run-parts
PPP_IFACE="$1"
export PPP_IFACE
# If /var/log/ppp-ipupdown.log exists use it for logging.
if [ -e /var/log/ppp-ipupdown.log ]; then
exec >> /var/log/ppp-ipupdown.log 2>&1
echo "$0" "$@"
echo
fi
# This script can be used to override the .d files supplied by other packages.
if [ -x /etc/ppp/ip-pre-down.local ]; then
exec /etc/ppp/ip-pre-down.local "$@"
fi
run-parts /etc/ppp/ip-pre-down.d \
--arg="$1"
/etc/ppp/ipv6-pre-down
#!/bin/sh
# The environment is cleared before executing this script
# so the path must be reset.
PATH=/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin
export PATH
# These variables are for the use of the scripts run by run-parts
PPP_IFACE="$1"
export PPP_IFACE
# If /var/log/ppp-ipupdown.log exists use it for logging.
if [ -e /var/log/ppp-ipupdown.log ]; then
exec >> /var/log/ppp-ipupdown.log 2>&1
echo "$0" "$@"
echo
fi
# This script can be used to override the .d files supplied by other packages.
if [ -x /etc/ppp/ipv6-pre-down.local ]; then
exec /etc/ppp/ipv6-pre-down.local "$@"
fi
run-parts /etc/ppp/ipv6-pre-down.d \
--arg="$1"
Note
To @ppp-project members:
You might consider adding this pre-down
functionality to pppd
itself, so they get automatically called BEFORE the connection gets teared down, if wanted by the user. 🤝
And i use these systemd-networked
files for the connection configs:
(My onboard Ethernet connection is renamed to utp
)
21-pppoe-vlan35.netdev
# <includedir>/.21-pppoe-vlan35.netdev
[NetDev]
Description=Internet VLAN of ISP.
Name=vlan35
Kind=vlan
[VLAN]
Id=35
GVRP=yes
MVRP=yes
ReorderHeader=yes
# <includedir>/.21-pppoe-vlan35.netdev.d/00-Match: KernelCommandLine=pppoe.conf
[Match]
KernelCommandLine=pppoe
21-pppoe-vlan35.network
# <includedir>/.21-pppoe-vlan35.network
[Match]
Name=vlan35
[Network]
Description=Internet VLAN of ISP.
# <includedir>/.21-pppoe-vlan35.network.d/00-Match: KernelCommandLine=pppoe.conf
[Match]
KernelCommandLine=pppoe
# <includedir>/.21-pppoe-vlan35.network.d/01-Link: ARP=yes.conf
[Link]
ARP=yes
# <includedir>/.21-pppoe-vlan35.network.d/01-Link: AllMulticast=yes.conf
[Link]
AllMulticast=yes
# <includedir>/.21-pppoe-vlan35.network.d/01-Link: Group=1.conf
[Link]
Group=1
# <includedir>/.21-pppoe-vlan35.network.d/01-Link: Multicast=yes.conf
[Link]
Multicast=yes
# <includedir>/.21-pppoe-vlan35.network.d/01-Link: RequiredForOnline=carrier.conf
[Link]
RequiredForOnline=carrier
# <includedir>/.21-pppoe-vlan35.network.d/02-Network: BindCarrier=utp.conf
[Network]
BindCarrier=utp
# <includedir>/.21-pppoe-vlan35.network.d/02-Network: LinkLocalAddressing=no.conf
[Network]
LinkLocalAddressing=no
30-pppoe-TurkNet.network
# <includedir>/.30-pppoe-TurkNet.network
[Match]
Name=TurkNet
Type=ppp
# <includedir>/.30-pppoe-TurkNet.network.d/00-Match: KernelCommandLine=pppoe.conf
[Match]
KernelCommandLine=pppoe
# <includedir>/.30-pppoe-TurkNet.network.d/02-Network: BindCarrier=vlan35.conf
[Network]
BindCarrier=vlan35
# <includedir>/.30-pppoe-TurkNet.network.d/02-Network: DHCP=yes.conf
[Network]
DHCP=yes
# <includedir>/.30-pppoe-TurkNet.network.d/04-DHCPv4.conf
[DHCPv4]
Hostname=Linux
UseDNS=no
UseNTP=no
UseMTU=yes
UseDomains=route
SendDecline=yes
UseHostname=no
# <includedir>/.30-pppoe-TurkNet.network.d/04-DHCPv6-uplink.conf
[Network]
IPv6AcceptRA=yes
IPv6PrivacyExtensions=prefer-public
IPv6SendRA=no
DHCPPrefixDelegation=yes
[DHCPv6]
UseHostname=no
UseDNS=no
UseNTP=no
[DHCPPrefixDelegation]
UplinkInterface=:self
SubnetId=::1
Announce=no
Assign=yes
Token=static:::1
[IPv6AcceptRA]
UseDNS=no
UseDomains=route
Note
This config does NOT make use of the DNS/NTP/etc setting provided by the DHCP-Server from the ISP, if you need to make use of those you can tweak the systemd-networkd config options inside.
I use my own settings on my system, that's why i disabled usage of those.
Update:
For easier testing etc of the posted files, i have created a public repo where they can be found.
It will also function as a backup for my own setup 😉
https://gitlab.com/trimoon-inc/system/systemd-PPPoE
This all sounds reasonable at first glance. What functions would pppd need to call to read the user and password values?
@paulusmack
It just needs to read the contents of files inside the $CREDENTIALS_DIRECTORY
directory.
See: https://systemd.io/CREDENTIALS/#programming-interface-from-service-code
Note
$CREDENTIALS_DIRECTORY
is an environment variable provided for processes executed by the service.
$CREDENTIALS_DIRECTORY
= %d
in unit files...
If you use the names i used in my examples those files would be:
${CREDENTIALS_DIRECTORY}/PPPoE-username
(The contents will already be the decrypted value to be used)${CREDENTIALS_DIRECTORY}/PPPoE-password
(The contents will already be the decrypted value to be used)
But as said you're free to choose other names, or even let the user choose which names to use 😉
It basically boils down to automatically read the contents of those files and use them "as-if" the user provided those options with their values....
That is what i am actually doing in these lines:
PPPoE-@.service.d/UseCredentials.conf
\"user\" \"$$(<%d/PPPoE-username)\" \\\n\ \"password\" \"$$(<%d/PPPoE-password)\" \\\n\
@paulusmack a followup from your side would be appreciated 😉
@paulusmack: Have you seen latest @TriMoon comments?