aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar steering72532026-05-18 06:12:21 -0600
committerGravatar steering72532026-05-18 06:12:21 -0600
commit4707d391430ebacd08d00bfcf36360fe982b195a (patch)
treefba7b778b0137c4c745d7dcb9c91556e97e39bbc
parentchange attempt format (diff)
add gpg auth
-rw-r--r--crond.autopeer2
-rwxr-xr-xgpg-verify57
-rwxr-xr-xinstall.sh3
-rwxr-xr-xnew_user_created.sh9
-rw-r--r--pam_autopeer/Makefile11
-rw-r--r--pam_autopeer/pam_autopeer.c87
-rw-r--r--pam_autopeer/pam_autopeer.obin0 -> 5128 bytes
-rwxr-xr-xpam_autopeer/pam_autopeer.sobin0 -> 16168 bytes
-rw-r--r--pamd.autopeer56
-rw-r--r--sshd_config8
10 files changed, 227 insertions, 6 deletions
diff --git a/crond.autopeer b/crond.autopeer
index e2e8da8..c582187 100644
--- a/crond.autopeer
+++ b/crond.autopeer
@@ -1,6 +1,6 @@
47 * * * * root git -c merge.verifysignatures=false -c core.sshcommand="ssh -i /opt/autopeer/id_autopeer" -C /opt/autopeer/dn42-registry pull --quiet
*/5 * * * * root /opt/autopeer/cronjob.py >/dev/null
-0 * * * * root git -C /opt/autopeer pull --quiet
+0 * * * * root git -C /opt/autopeer pull --quiet; cd /opt/autopeer/pam_autopeer; make
*/15 * * * * root curl -sfSLR -o /etc/bird/roa_dn42.conf -z /etc/bird/roa_dn42.conf https://dn42.burble.com/roa/dn42_roa_bird2_4.conf && /usr/sbin/birdc configure > /dev/null
*/15 * * * * root curl -sfSLR -o /etc/bird/roa_dn42_v6.conf -z /etc/bird/roa_dn42_v6.conf https://dn42.burble.com/roa/dn42_roa_bird2_6.conf && /usr/sbin/birdc configure > /dev/null
diff --git a/gpg-verify b/gpg-verify
new file mode 100755
index 0000000..615a499
--- /dev/null
+++ b/gpg-verify
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+set -o pipefail
+exec &>>/var/log/gpg-verify.log
+perl -MData::Dumper -e 'print Dumper(\@ARGV);' "$@"
+
+if [ $# -ne 4 ]; then
+ exit 2
+fi
+
+username="$1"
+nonce="$2"
+key="$3"
+sig="$4"
+
+keyring="$(mktemp)"
+curl -sL "$key" | gpg -o - --dearmor >"$keyring" || exit 7
+gpgv_out="$(gpgv --keyring "$keyring" <(echo "$sig" | sed 's/-----BEGIN PGP SIGNATURE-----/&\n\n/') <(echo "$nonce") 2>&1)"
+verified_key="$(echo "$gpgv_out" |& grep -oP 'gpgv:\s*using \S+ key \K.*')"
+
+echo "keyring: $keyring"
+echo "$gpgv_out"
+
+if ! echo "$gpgv_out" | grep -qP 'gpgv: Good signature from'; then
+ exit 3
+fi
+
+if [ -z "$verified_key" ]; then
+ exit 4
+fi
+
+if [ "$username" = "new" ]; then
+ if new_user="$(grep -l -s -r -P '^\s*auth:\s*pgp-fingerprint\s+\Q'"$verified_key"'\E(\s|$)' /opt/autopeer/dn42-registry/data/mntner/ | perl -ne 's@^.*/@@; s@-MNT$@@; print lc;' | head -1)"; then
+ echo "making $new_user"; exit 0
+ if getent passwd "$new_user" &>/dev/null; then
+ exit 0
+ else
+ echo "[autopeer $(hostname -f)] New user being created: $new_user from $key $connection" | socat stdio "$NOTIFY_TO"
+ /usr/sbin/adduser --disabled-password --quiet --comment "created at $(date +%s) by $key ${connection//:/_}" --ingroup autopeer "$new_user"
+ /usr/sbin/adduser "$new_user" bird
+ ( umask 0077; touch "/var/log/autopeer/$new_user".{tim,io}; )
+ chown "$new_user" "/var/log/autopeer/$new_user".{tim,io}
+ exit 0
+ fi
+ else
+ exit 5
+ fi
+else
+ if grep -q -s -P '^\s*auth:\s*pgp-fingerprint\s+\Q'"$verified_key"'\E(\s|$)' /opt/autopeer/dn42-registry/data/mntner/"$(echo "$username" | perl -ne 's@$@-MNT@; print uc;')"; then
+ exit 0
+ else
+ exit 6
+ fi
+fi
+
+
+exit 1
diff --git a/install.sh b/install.sh
index 2119e5b..de114dd 100755
--- a/install.sh
+++ b/install.sh
@@ -7,7 +7,7 @@ read -p "Press enter once you've done that..."
apt install -y git # you needed this to clone
apt install -y vim curl wget man-db whois bind9 bind9-dnsutils bird2 # suggestions
-apt install -y python3 wireguard-tools cron socat # dependencies
+apt install -y python3 wireguard-tools cron socat make libpam-dev # dependencies
ln -s /opt/autopeer/sshd_config /etc/ssh/sshd_config.d/autopeer.conf
systemctl reload ssh
addgroup autopeer
@@ -22,6 +22,7 @@ git -c core.sshcommand="ssh -i /opt/autopeer/id_autopeer" clone git@git.dn42.dev
git -C /opt/autopeer/dn42-registry config core.sshCommand "ssh -i /opt/autopeer/id_autopeer"
ln -s /opt/autopeer/crond.autopeer /etc/cron.d/autopeer
+ln -s /opt/autopeer/pamd.autopeer /etc/pam.d/autopeer
ln -s /opt/autopeer/sysctld.autopeer /etc/sysctl.d/99-dn42.conf
systemctl restart systemd-sysctl
diff --git a/new_user_created.sh b/new_user_created.sh
index e00a3bb..802d424 100755
--- a/new_user_created.sh
+++ b/new_user_created.sh
@@ -1,7 +1,12 @@
#!/bin/sh
-read method type key <$SSH_USER_AUTH
-user="$(grep -l -s -r -P '^\s*auth:\s*\Q'"$type"'\E\s+\Q'"$key"'\E(\s|$)' /opt/autopeer/dn42-registry/data/mntner/ | perl -ne 's@^.*/@@; s@-MNT$@@; print lc;' | head -1)"
+if [ -z "$NEW_USER" ]; then # ssh auth
+ read method type key <$SSH_USER_AUTH
+ user="$(grep -l -s -r -P '^\s*auth:\s*\Q'"$type"'\E\s+\Q'"$key"'\E(\s|$)' /opt/autopeer/dn42-registry/data/mntner/ | perl -ne 's@^.*/@@; s@-MNT$@@; print lc;' | head -1)"
+else # pgp auth
+ fpr="$(curl -s "$NEW_USER" | gpg --show-keys --with-colons | grep -Po 'fpr:*\K[^:]+' | head -1)"
+ user="$(grep -l -s -r -P '^\s*auth:\s*pgp-fingerprint\s+\Q'"$fpr"'\E(\s|$)' /opt/autopeer/dn42-registry/data/mntner/ | perl -ne 's@^.*/@@; s@-MNT$@@; print lc;' | head -1)"
+fi
if getent passwd "$user" >/dev/null 2>&1; then
echo "Your account has been created, go ahead and log in:"
diff --git a/pam_autopeer/Makefile b/pam_autopeer/Makefile
new file mode 100644
index 0000000..355ef35
--- /dev/null
+++ b/pam_autopeer/Makefile
@@ -0,0 +1,11 @@
+CFLAGS = -fPIC -Wall -Wextra -Wno-unused-parameter
+LDFLAGS = -shared
+
+all: pam_autopeer.so
+
+pam_autopeer.so: pam_autopeer.o
+ $(CC) $(LDFLAGS) -o pam_autopeer.so pam_autopeer.c
+
+
+clean:
+ rm -f pam_autopeer.so pam_autopeer.o
diff --git a/pam_autopeer/pam_autopeer.c b/pam_autopeer/pam_autopeer.c
new file mode 100644
index 0000000..cf3ee11
--- /dev/null
+++ b/pam_autopeer/pam_autopeer.c
@@ -0,0 +1,87 @@
+/*
+gcc -fPIC -c pam_module.c
+gcc -shared -o pam_module.so pam_module.o -lpam
+*/
+
+//#include <security/pam_appl.h>
+#include <security/pam_modules.h>
+#include <security/pam_ext.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <openssl/rand.h>
+
+int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return PAM_SUCCESS;
+}
+/*
+int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ return PAM_IGNORE;
+}
+*/
+
+static const char *errors[] = {
+ NULL,
+ "internal error (fell through)",
+ "internal error (wrong # args)",
+ "no good signature foud in gpgv output",
+ "no verified key found in gpgv output",
+ "new user: key not found in registry",
+ "existing user: key not found in this mntner",
+ "downloading public key failed",
+};
+
+
+int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) {
+ const char *username;
+ int retval = pam_get_user(pamh, &username, "Username: ");
+ if (retval != PAM_SUCCESS)
+ return PAM_SYSTEM_ERR;
+
+ int err;
+ unsigned char nonce[10];
+ RAND_bytes(nonce, 10);
+ char s_nonce[21];
+ sprintf(s_nonce, "%x%x%x%x%x%x%x%x%x%x", nonce[0], nonce[1], nonce[2], nonce[3], nonce[4], nonce[5], nonce[6], nonce[7], nonce[8], nonce[9]);
+ char *key, *sig;
+ pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &key, "Please enter a URL to download your ASCII-armored key:\n");
+ pam_prompt(pamh, PAM_PROMPT_ECHO_ON, &sig, "Please enter a ASCII-armored detached signature of the nonce '%s\\n', without newlines, i.e. `gpg -a --detach-sign -o - -u $YOUR_KEY <(echo %s) | tr -d '\\n'; echo`:\n", s_nonce, s_nonce);
+
+ int pid;
+ switch (pid = fork()) {
+ case -1: // error
+ err = errno;
+ pam_syslog(pamh, LOG_ERR, "Error fork: %s", strerror(err));
+ pam_error(pamh, "Error fork: %s", strerror(err));
+ return PAM_SYSTEM_ERR;
+ case 0: // child
+ execl("/opt/autopeer/gpg-verify", "/opt/autopeer/gpg-verify", username, s_nonce, key, sig, (char *)NULL);
+ // if (execl == -1):
+ err = errno;
+ pam_syslog(pamh, LOG_ERR, "Error execl: %s", strerror(err));
+ pam_error(pamh, "Error execl: %s", strerror(err));
+ _exit(PAM_SYSTEM_ERR);
+ default: // parent
+ int wstatus;
+ if (waitpid(pid, &wstatus, 0) == -1) {
+ err = errno;
+ pam_syslog(pamh, LOG_ERR, "Error waitpid: %s", strerror(err));
+ pam_error(pamh, "Error waitpid: %s", strerror(err));
+ }
+ if (WIFEXITED(wstatus)) {
+ if (!WEXITSTATUS(wstatus)) {
+ char newu[100];
+ snprintf(newu, sizeof(newu), "NEW_USER=%s", key);
+ pam_putenv(pamh, newu);
+ return PAM_SUCCESS;
+ } else {
+ pam_syslog(pamh, LOG_ERR, "Error auth: %s (%d)", WEXITSTATUS(wstatus) < sizeof(errors)/sizeof(errors[0]) ? errors[WEXITSTATUS(wstatus)] : "", WEXITSTATUS(wstatus));
+ pam_error(pamh, "Error auth: %s (%d)", WEXITSTATUS(wstatus) < sizeof(errors)/sizeof(errors[0]) ? errors[WEXITSTATUS(wstatus)] : "", WEXITSTATUS(wstatus));
+ }
+ }
+ return PAM_SYSTEM_ERR;
+ }
+ return PAM_SYSTEM_ERR;
+}
diff --git a/pam_autopeer/pam_autopeer.o b/pam_autopeer/pam_autopeer.o
new file mode 100644
index 0000000..bb65a05
--- /dev/null
+++ b/pam_autopeer/pam_autopeer.o
Binary files differ
diff --git a/pam_autopeer/pam_autopeer.so b/pam_autopeer/pam_autopeer.so
new file mode 100755
index 0000000..ec5f12a
--- /dev/null
+++ b/pam_autopeer/pam_autopeer.so
Binary files differ
diff --git a/pamd.autopeer b/pamd.autopeer
new file mode 100644
index 0000000..2a0c5eb
--- /dev/null
+++ b/pamd.autopeer
@@ -0,0 +1,56 @@
+# PAM configuration for the Secure Shell service
+
+# Standard Un*x authentication.
+auth required /opt/autopeer/pam_autopeer/pam_autopeer.so
+#@include common-auth
+
+# Disallow non-root logins when /etc/nologin exists.
+account required pam_nologin.so
+
+# Uncomment and edit /etc/security/access.conf if you need to set complex
+# access limits that are hard to express in sshd_config.
+# account required pam_access.so
+
+# Standard Un*x authorization.
+@include common-account
+
+# SELinux needs to be the first session rule. This ensures that any
+# lingering context has been cleared. Without this it is possible that a
+# module could execute code in the wrong domain.
+session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close
+
+# Set the loginuid process attribute.
+session required pam_loginuid.so
+
+# Create a new session keyring.
+session optional pam_keyinit.so force revoke
+
+# Standard Un*x session setup and teardown.
+@include common-session
+
+# Print the message of the day upon successful login.
+# This includes a dynamically generated part from /run/motd.dynamic
+# and a static (admin-editable) part from /etc/motd.
+session optional pam_motd.so motd=/run/motd.dynamic
+session optional pam_motd.so noupdate
+
+# Print the status of the user's mailbox upon successful login.
+session optional pam_mail.so standard noenv # [1]
+
+# Set up user limits from /etc/security/limits.conf.
+session required pam_limits.so
+
+# Read environment variables from /etc/environment and
+# /etc/security/pam_env.conf.
+session required pam_env.so # [1]
+# In Debian 4.0 (etch), locale-related environment variables were moved to
+# /etc/default/locale, so read that as well.
+session required pam_env.so envfile=/etc/default/locale
+
+# SELinux needs to intervene at login time to ensure that the process starts
+# in the proper default security context. Only sessions which are intended
+# to run in the user's context should be run after this.
+session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open
+
+# Standard Un*x password updating.
+@include common-password
diff --git a/sshd_config b/sshd_config
index 1afda01..0ca90dc 100644
--- a/sshd_config
+++ b/sshd_config
@@ -1,7 +1,9 @@
Match user new
+ PAMServiceName autopeer
+ KbdInteractiveAuthentication yes
AuthorizedKeysCommand /opt/autopeer/authorized_keys.sh %u %t %k %C
AuthorizedKeysCommandUser root
- AuthenticationMethods publickey
+ AuthenticationMethods publickey keyboard-interactive
AllowAgentForwarding no
AllowStreamLocalForwarding no
AllowTcpForwarding no
@@ -12,9 +14,11 @@ Match user new
ForceCommand /opt/autopeer/new_user_created.sh
Match group autopeer
+ PAMServiceName autopeer
+ KbdInteractiveAuthentication yes
AuthorizedKeysCommand /opt/autopeer/authorized_keys.sh %u %t %k %C
AuthorizedKeysCommandUser root
- AuthenticationMethods publickey
+ AuthenticationMethods publickey keyboard-interactive
AllowAgentForwarding no
AllowStreamLocalForwarding no
AllowTcpForwarding no