ppp-project / ppp

Paul's PPP Package: PPP daemon and associated utilities | Official GitHub repo: https://github.com/ppp-project/ppp
https://github.com/ppp-project/ppp
Other
384 stars 228 forks source link

[RFE] Option to use SystemdCreds to read user+password #459

Open TriMoon opened 10 months ago

TriMoon commented 10 months ago

To enhance the systemd integration (see #79 and https://github.com/systemd/systemd/issues/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.

When this option is used, pppd should automatically read and use:

  1. The value for user from the SystemdCreds, fe. from the PPPoE-username credential. (the exact naming is up to you)
  2. The value for password from the SystemdCreds, fe. from the PPPoE-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 ```ini # /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 ```ini # /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 ```ini # /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 ```ini # /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 ```ini # /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 ```ini # /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 ```ini # /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 ```ini # /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 ```bash #!/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##*/} " 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 :wink:


[!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 ```ini # /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 ```bash #!/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 ```bash #!/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. :handshake:


And i use these systemd-networked files for the connection configs: (My onboard Ethernet connection is renamed to utp)

21-pppoe-vlan35.netdev ```ini # /.21-pppoe-vlan35.netdev [NetDev] Description=Internet VLAN of ISP. Name=vlan35 Kind=vlan [VLAN] Id=35 GVRP=yes MVRP=yes ReorderHeader=yes # /.21-pppoe-vlan35.netdev.d/00-Match: KernelCommandLine=pppoe.conf [Match] KernelCommandLine=pppoe ```
21-pppoe-vlan35.network ```ini # /.21-pppoe-vlan35.network [Match] Name=vlan35 [Network] Description=Internet VLAN of ISP. # /.21-pppoe-vlan35.network.d/00-Match: KernelCommandLine=pppoe.conf [Match] KernelCommandLine=pppoe # /.21-pppoe-vlan35.network.d/01-Link: ARP=yes.conf [Link] ARP=yes # /.21-pppoe-vlan35.network.d/01-Link: AllMulticast=yes.conf [Link] AllMulticast=yes # /.21-pppoe-vlan35.network.d/01-Link: Group=1.conf [Link] Group=1 # /.21-pppoe-vlan35.network.d/01-Link: Multicast=yes.conf [Link] Multicast=yes # /.21-pppoe-vlan35.network.d/01-Link: RequiredForOnline=carrier.conf [Link] RequiredForOnline=carrier # /.21-pppoe-vlan35.network.d/02-Network: BindCarrier=utp.conf [Network] BindCarrier=utp # /.21-pppoe-vlan35.network.d/02-Network: LinkLocalAddressing=no.conf [Network] LinkLocalAddressing=no ```
30-pppoe-TurkNet.network ```ini # /.30-pppoe-TurkNet.network [Match] Name=TurkNet Type=ppp # /.30-pppoe-TurkNet.network.d/00-Match: KernelCommandLine=pppoe.conf [Match] KernelCommandLine=pppoe # /.30-pppoe-TurkNet.network.d/02-Network: BindCarrier=vlan35.conf [Network] BindCarrier=vlan35 # /.30-pppoe-TurkNet.network.d/02-Network: DHCP=yes.conf [Network] DHCP=yes # /.30-pppoe-TurkNet.network.d/04-DHCPv4.conf [DHCPv4] Hostname=Linux UseDNS=no UseNTP=no UseMTU=yes UseDomains=route SendDecline=yes UseHostname=no # /.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 :wink: https://gitlab.com/trimoon-inc/system/systemd-PPPoE

paulusmack commented 10 months ago

This all sounds reasonable at first glance. What functions would pppd need to call to read the user and password values?

TriMoon commented 10 months ago

@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:

But as said you're free to choose other names, or even let the user choose which names to use :wink:

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:

TriMoon commented 7 months ago

@paulusmack a followup from your side would be appreciated :wink:

Neustradamus commented 7 months ago

@paulusmack: Have you seen latest @TriMoon comments?

Neustradamus commented 4 weeks ago

@paulusmack: Have you seen latest @TriMoon comments?

@enaess, @jkroonza, @sthibaul, @yarda: What do you think?

enaess commented 3 weeks ago

Definitely not a 2.5.1 feature. I think it would be awesome to provide tighter integration with systemd in a few different ways, e.g. networking, dns and credentials. Won't have the time to look into this anytime soon.

paulusmack commented 3 weeks ago

There is already the +ua option, but it takes both username and password from the same file. If it really is necessary to have the username and password in separate files, it would be possible to add an option that provides a prefix to which \"username\" and \"password\" are appended to get the names of two files, from which to read the username and password. You could then do something like userpass-prefix %d/PPPoE- or similar on the pppd command line. Would that suit?