Aquí hay una solución que funciona para mí. Mi /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
Y /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; }'
Obtengo este comportamiento:
$ 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 primera línea agregada al pam.d/sudo
predeterminado llamadas pam_exec
y, si tiene éxito, salta la siguiente entrada. La segunda línea simplemente niega el acceso incondicionalmente.
En /tmp/test-pam
Llamo a last
para obtener la dirección IP asociada con el TTY desde el que se invocó pam. ${PAM_TTY#/dev/}
elimina /dev/
desde el frente del valor, porque last
no reconoce la ruta completa del dispositivo. El -i
bandera hace last
muestra la dirección IP o el marcador de posición 0.0.0.0
si no hay dirección IP; de forma predeterminada, muestra una cadena de información que es mucho más difícil de verificar. Esta es también la razón por la que usé last
en lugar de who
o w
; esos no tienen una opción similar. El -p now
la opción no es estrictamente necesaria, como veremos awk
solo está comprobando la primera línea de salida, pero restringe last
para mostrar solo los usuarios que actualmente están conectados.
El awk
el comando solo verifica la primera línea, y si el tercer campo no es 0.0.0.0
sale con un error. Dado que este es el último comando en /tmp/test-pam
, el código de salida de awk se convierte en el código de salida del script.
En mi sistema, ninguna de las pruebas que intentabas en tu deny-ssh-user.sh
trabajaría. Si pones env > /tmp/test-pam.log
en la parte superior de su secuencia de comandos, verá que se ha eliminado el entorno, por lo que no se establecerá ninguna de sus variables SSH_FOO. Y $PPID podría apuntar a cualquier número de procesos. Por ejemplo, ejecute perl -e 'system("sudo cat /etc/passwd")'
y vea que $PPID se refiere al perl
proceso.
Esto es Arch Linux, núcleo 4.16.11-1-ARCH
, en caso de que importe. Sin embargo, no creo que deba hacerlo.
Bueno, resulta que en realidad soy un idiota, el pam_exec.so
El módulo está perfectamente bien para crear condicionales PAM.
Tim Smith tenía razón al evaluar que ambas pruebas en mi /etc/security/deny-ssh-user.sh
el script NUNCA estableció la variable SSH_SESSION
a la verdad No tomé eso en consideración porque el script funciona en un shell normal, pero el contexto del entorno se elimina cuando lo ejecuta pam_exec.so
.
Terminé reescribiendo el script para usar el last
utilidad como su ejemplo, sin embargo, tuve que cambiar algo porque los interruptores para last
difieren de Arch Linux a RedHat.
Aquí está el script revisado en /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
Contenido 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
....