如何配置 PAM 以使用(密码和 OTP)或(公钥和 OTP)验证 SSH 登录

如何配置 PAM 以使用(密码和 OTP)或(公钥和 OTP)验证 SSH 登录

(public key and OTP) or (password and OTP)我的目标是开发一个 ansible playbook,以在 Ubuntu Server 18.04 主机上部署此类多因素 ssh 登录。

我遵循了指南这里并且它基本上有效。我遇到的唯一问题是需要添加auth required pam_permit.so到末尾,/etc/pam.d/sshd以便绕过没有~/.google_authenticator文件的用户的 OTP 检查。

本指南设置为使用AuthenticationMethods publickey,password publickey,keyboard-interactive,其中/etc/ssh/sshd_config要求所有登录都使用公钥,而我尝试实施的 MFA 方案允许使用 OTP 密码。但是,任何其他排列AuthenticationMethods似乎都会完全破坏身份验证。

例如,只有连续两次输入正确的 OTP 才AuthenticationMethods password,keyboard-interface允许有文件的用户登录,而没有文件的用户则可以使用任意字符串作为密码登录。~/.google_authenticator~/.google_authenticator

我尝试了很多选项,但我开始认为这种配置是不可能的(参见类似的帖子这里)。为什么会这样?我越来越相信 PAM 是罪魁祸首,但我不明白为什么它AuthenticationMethods publickey,keyboard-interface password,keyboard-interface不能开箱即用

这是我的/etc/pam.d/sshd

# PAM configuration for the Secure Shell service

# Standard Un*x authentication.
# Commented out by me @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_s$

# 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 user_readenv=1 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

### Added by me ###
auth required pam_google_authenticator.so nullok
auth required pam_permit.so

和我的/etc/ssh/sshd_config

#       $OpenBSD: sshd_config,v 1.101 2017/03/14 07:19:07 djm Exp $

# This is the sshd server system-wide configuration file.  See
# sshd_config(5) for more information.

# This sshd was compiled with PATH=/usr/bin:/bin:/usr/sbin:/sbin

# The strategy used for options in the default sshd_config shipped with
# OpenSSH is to specify options with their default value where
# possible, but leave them commented.  Uncommented options override the
# default value.

#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

#HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_ecdsa_key
#HostKey /etc/ssh/ssh_host_ed25519_key

# Ciphers and keying
#RekeyLimit default none

# Logging
#SyslogFacility AUTH
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin prohibit-password
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#PubkeyAuthentication yes

# Expect .ssh/authorized_keys2 to be disregarded by default in future.
#AuthorizedKeysFile     .ssh/authorized_keys .ssh/authorized_keys2

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# HostbasedAuthentication
#IgnoreUserKnownHosts no
# Don't read the user's ~/.rhosts and ~/.shosts files
#IgnoreRhosts yes

# To disable tunneled clear text passwords, change to no here!
#PasswordAuthentication yes
#PermitEmptyPasswords no

# Change to yes to enable challenge-response passwords (beware issues with
# some PAM modules and threads)

### changed by me ###----------------------------------------------------------------
ChallengeResponseAuthentication yes

# Kerberos options
#KerberosAuthentication no
#KerberosOrLocalPasswd yes
#KerberosTicketCleanup yes
#KerberosGetAFSToken no

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no

# Set this to 'yes' to enable PAM authentication, account processing,
# and session processing. If this is enabled, PAM authentication will
# be allowed through the ChallengeResponseAuthentication and
# PasswordAuthentication.  Depending on your PAM configuration,
# PAM authentication via ChallengeResponseAuthentication may bypass
# the setting of "PermitRootLogin without-password".
# If you just want the PAM account and session checks to run without
# PAM authentication, then enable this but set PasswordAuthentication
# and ChallengeResponseAuthentication to 'no'.
UsePAM yes

### Added by me ###------------------------------------------------------------------
AuthenticationMethods publickey,keyboard-interactive password,keyboard-interactive

#AllowAgentForwarding yes
#AllowTcpForwarding yes
#GatewayPorts no
X11Forwarding yes
#X11DisplayOffset 10
#X11UseLocalhost yes
#PermitTTY yes
PrintMotd no
#PrintLastLog yes
#TCPKeepAlive yes
#UseLogin no
#PermitUserEnvironment no
#Compression delayed
#ClientAliveInterval 0
#ClientAliveCountMax 3
#UseDNS no
#PidFile /var/run/sshd.pid
#MaxStartups 10:30:100
#PermitTunnel no
#ChrootDirectory none
#VersionAddendum none

# no default banner path
#Banner none

# Allow client to pass locale environment variables
AcceptEnv LANG LC_*

# override default of no subsystems
Subsystem       sftp    /usr/lib/openssh/sftp-server

# Example of overriding settings on a per-user basis
#Match User anoncvs
#       X11Forwarding no
#       AllowTcpForwarding no
#       PermitTTY no
#       ForceCommand cvs server
PasswordAuthentication yes

答案1

问题源于 PAM,它几乎使得这变得不可能。

核心问题是公钥部分由 ssh 守护进程处理,而键盘交互始终由 PAM 处理(并且密码也经常由 PAM 处理,具体取决于配置)。因此,PAM 无法知道是否已设置公钥,并且无法将 ssh 配置为根据是否使用公钥来随意选择不同的 AuthenticationMethods。

但是 ssh当公钥已设置并验证为已批准时,设置环境变量 SSH_AUTH_INFO_0,PAM 可以看到此变量。因此,您要做的就是编写一个 pam_exec 脚本,该脚本根据 SSH_AUTH_INFO_0 是否设置为某项而以不同的方式退出。并且 pam_exec 可以使用 success=1 跳过一行(如果需要,可以跳过多行,在我修改它之前,RHEL8 上的代码已经是意大利面条式代码了,这确实使事情变得更加复杂)。

我使用了一个 perl 脚本:

#!/usr/bin/perl

if ($ENV{'SSH_AUTH_INFO_0'} =~ /^publickey /) {
  exit(0);
} else {
  exit(1);
}

并且它使用此行调用(success=2 是因为预先存在的代码,我不得不跳过 pam_sss.so 后面的 pam_deny(它是“足够的”并且没有遇到拒绝)。是否使用“ignore”或“ok”取决于 sshd 的其余 PAM 堆栈。(并且在某些情况下,如果您在输入密码之前提示输入 OTP,您可能能够使用“success=done”,尽管当我认为它应该工作时,我无法让它工作。)

auth        [success=2 default=ignore]                       pam_exec.so quiet /usr/local/sbin/ssh-pubkey-check

您还需要在 sshd_config 中设置这行有点奇怪的内容:

AuthenticationMethods publickey,keyboard-interactive keyboard-interactive

它首先尝试公钥和键盘交互。如果成功,PAM 将从环境变量中得知它可以完全跳过密码提示。如果失败(因为没有提供公钥),它接下来只尝试键盘交互,但在这种情况下,PAM 将提示输入 OTP(留给学生练习)和密码提示。

还有更多内容,但这一切都取决于您现有的 PAM 设置是什么样的,以及您要根据什么进行身份验证(本地密码文件、LDAP、NIS 等等),所以我无法提供一刀切的解决方案。

还要注意,使用环境变量来控制身份验证步骤感觉不太好,理论上可能会导致只需要 OTP 而不需要其他令牌的漏洞。尽管在我能想到的每种情况下,如果有人可以远程或本地设置 PAM 从 sshd 调用时看到的环境变量,那么您的系统已经受到攻击,这不是额外的风险。

答案2

请注意,“pam_permit.so”是一个危险的模块。

我认为你的说法与你的两条规则相矛盾:

身份验证需要 pam_google_authenticator.so nullok

相对

身份验证需要 pam_permit.so

使用“nullok”关键字应该足以实现您想要做的事情。没有设置 google-authentication 的用户也可以登录,而其他用户则需要输入验证码。 身份验证方法公钥、键盘交互/etc/ssh/sshd_config 文件里面就可以了。

此外,您还有两个选项可以尝试:在文件中列出用户:/etc/authusers 稍后您可以参考sshd 配置文件

身份验证充分 pam_securid.so

身份验证需要 pam_deny.so

auth requisite pam_listfile.so item=用户感知=允许文件=/etc/authusers

...或使用用户组:

sshd 配置文件

身份验证 [成功 = 完成 默认 = 忽略] pam_succeed_if.so 用户组“groupname_here”

相关内容