Voici une solution qui fonctionne pour moi. Mon /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
Et /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; }'
J'obtiens ce comportement :
$ 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 première ligne ajoutée au pam.d/sudo
par défaut appelle le pam_exec
et, s'il réussit, saute l'entrée suivante. La deuxième ligne refuse simplement l'accès sans condition.
En /tmp/test-pam
J'appelle last
pour obtenir l'adresse IP associée au téléimprimeur à partir duquel pam a été appelé. ${PAM_TTY#/dev/}
supprime /dev/
du devant de la valeur, car last
ne reconnaît pas le chemin complet du périphérique. Le -i
le drapeau fait last
afficher soit l'adresse IP soit l'espace réservé 0.0.0.0
s'il n'y a pas d'adresse IP ; par défaut, il affiche une chaîne d'informations beaucoup plus difficile à vérifier. C'est aussi pourquoi j'ai utilisé last
au lieu de who
ou w
; ceux-ci n'ont pas une option similaire. Le -p now
l'option n'est pas strictement nécessaire, comme nous le verrons awk
ne vérifie que la première ligne de sortie, mais il limite last
pour afficher uniquement les utilisateurs actuellement connectés.
Le awk
commande vérifie simplement la première ligne, et si le troisième champ n'est pas 0.0.0.0
il sort avec une erreur. Puisqu'il s'agit de la dernière commande dans /tmp/test-pam
, le code de sortie d'awk devient le code de sortie du script.
Sur mon système, aucun des tests que vous essayiez dans votre deny-ssh-user.sh
travaillerait. Si vous mettez env > /tmp/test-pam.log
en haut de votre script, vous verrez que l'environnement a été supprimé, donc aucune de vos variables SSH_FOO ne sera définie. Et $PPID pourrait pointer vers n'importe quel nombre de processus. Par exemple, exécutez perl -e 'system("sudo cat /etc/passwd")'
et voyez que $PPID fait référence au perl
processus.
Ceci est Arch Linux, noyau 4.16.11-1-ARCH
, au cas où ça compte. Je ne pense pas que cela devrait, cependant.
Eh bien, il s'avère que je suis en fait un idiot, le pam_exec.so
module est parfaitement adapté à la création de conditions PAM.
Tim Smith avait raison d'évaluer que les deux tests dans mon /etc/security/deny-ssh-user.sh
le script ne définissait JAMAIS la variable SSH_SESSION
à vrai. Je n'ai pas pris cela en considération car le script fonctionne dans un shell normal, mais le contexte de l'environnement est supprimé lorsqu'il est exécuté par pam_exec.so
.
J'ai fini par réécrire le script pour utiliser le last
utilitaire tout comme son exemple, mais j'ai dû en changer certains car les commutateurs pour last
diffèrent d'Arch Linux à RedHat.
Voici le script révisé dans /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
Contenu de /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
....