Ecco una soluzione che funziona per me. Il mio /etc/pam.d/sudo
:
#%PAM-1.0
auth [success=1] pam_exec.so /tmp/test-pam
auth required pam_deny.so
auth include system-auth
account include system-auth
session include system-auth
E /tmp/test-pam
:
#! /bin/bash
/bin/last -i -p now ${PAM_TTY#/dev/} | \
/bin/awk 'NR==1 { if ($3 != "0.0.0.0") exit 9; exit 0; }'
Ottengo questo comportamento:
$ sudo date
[sudo] password for jdoe:
Thu Jun 28 23:51:58 MDT 2018
$ ssh localhost
Last login: Thu Jun 28 23:40:23 2018 from ::1
valli$ sudo date
/tmp/test-pam failed: exit code 9
[sudo] password for jdoe:
sudo: PAM authentication error: System error
valli$
La prima riga aggiunta al pam.d/sudo
predefinito chiama pam_exec
e, se ha successo, salta la voce successiva. La seconda riga nega l'accesso incondizionatamente.
In /tmp/test-pam
Chiamo last
per ottenere l'indirizzo IP associato al TTY pam da cui è stato richiamato. ${PAM_TTY#/dev/}
rimuove /dev/
dalla parte anteriore del valore, perché last
non riconosce il percorso completo del dispositivo. Il -i
flag rende last
mostra l'indirizzo IP o il segnaposto 0.0.0.0
se non c'è un indirizzo IP; per impostazione predefinita mostra una stringa di informazioni che è molto più difficile da controllare. Questo è anche il motivo per cui ho usato last
invece di who
o w
; quelli non hanno un'opzione simile. Il -p now
l'opzione non è strettamente necessaria, come vedremo awk
controlla solo la prima riga dell'output, ma limita last
per mostrare solo gli utenti che sono attualmente connessi.
Il awk
command controlla solo la prima riga e se il terzo campo non è 0.0.0.0
esce con un errore. Poiché questo è l'ultimo comando in /tmp/test-pam
, il codice di uscita di awk diventa il codice di uscita per lo script.
Sul mio sistema, nessuno dei test che stavi provando nel tuo deny-ssh-user.sh
funzionerebbe. Se inserisci env > /tmp/test-pam.log
nella parte superiore del tuo script, vedrai che l'ambiente è stato rimosso, quindi nessuna delle tue variabili SSH_FOO verrà impostata. E $PPID potrebbe puntare a qualsiasi numero di processi. Ad esempio, esegui perl -e 'system("sudo cat /etc/passwd")'
e vedi che $PPID fa riferimento a perl
processo.
Questo è Arch Linux, kernel 4.16.11-1-ARCH
, nel caso sia importante. Non penso che dovrebbe, però.
Beh, si scopre che in realtà sono un idiota, il pam_exec.so
module va benissimo per creare condizionali PAM.
Tim Smith aveva ragione nel valutare che entrambi i test nel mio /etc/security/deny-ssh-user.sh
script non impostavano MAI la variabile SSH_SESSION
a vero. Non l'ho preso in considerazione perché lo script funziona in una shell normale, ma il contesto dell'ambiente viene rimosso quando viene eseguito da pam_exec.so
.
Ho finito per riscrivere lo script per usare il last
proprio come il suo esempio, tuttavia ho dovuto modificarne alcune perché le opzioni per last
differiscono da Arch Linux a RedHat.
Ecco lo script rivisto in /etc/security/deny-ssh-user.sh:
#!/bin/bash
# Returns 1 if the user is logged in through SSH
# Returns 0 if the user is not logged in through SSH
SSH_SESSION=false
function isSshSession {
local terminal="${1}"
if $(/usr/bin/last -i |
/usr/bin/grep "${terminal}" |
/usr/bin/grep 'still logged in' |
/usr/bin/awk '{print $3}' |
/usr/bin/grep -q --invert-match '0\.0\.0\.0'); then
echo true
else
echo false
fi
}
function stripTerminal {
local terminal="${1}"
# PAM_TTY is in the form /dev/pts/X
# Last utility displays TTY in the form pts/x
# Returns the first five characters stripped from TTY
echo "${terminal:5}"
}
lastTerminal=$( stripTerminal "${PAM_TTY}")
SSH_SESSION=$(isSshSession "${lastTerminal}")
if "${SSH_SESSION}"; then
exit 1
else
exit 0
fi
Contenuto di /etc/pam.d/sudo
....
auth [success=ok default=1] pam_exec.so /etc/security/deny-ssh-user.sh
auth sufficient pam_module_to_skip.so
....