# PAM configuration for the Secure Shell service

# instantly fail if /etc/nologin exists and user is not root:
auth   requisite                                                pam_nologin.so


# --- PASSWORD CHECK SECTION
# proceed in this section ONLY if the user is in group mfa-password-configd, skip it entirely otherwise (5 lines -> next section)
auth   [success=ignore ignore=ignore default=5]                 pam_succeed_if.so   quiet user ingroup mfa-password-configd
# just display a nice message to the user:
auth   optional                                                 pam_echo.so         Your account has Multi-Factor Authentication enabled, an additional authentication factor is required (password).
# also display how many days remain before expiration of their password:
auth   optional                                                 pam_exec.so         quiet debug stdout /opt/bastion/bin/shell/pam_exec_pwd_info.sh
# lock account after 6 password failures, for 5 minutes. 'preauth' -> only fail if count is already reached:
auth   required                                                 pam_faillock.so     preauth deny=6 unlock_time=300
# now actually ask for the password, and skip the next line if it's correct:
auth   [success=1 new_authtok_reqd=1 ignore=ignore default=bad] pam_unix.so
# this line is only called if password failed. 'authfail' -> we increment the fail counter and abort auth:
auth   [default=die]                                            pam_faillock.so     authfail deny=6 unlock_time=300


# --- TOTP CHECK SECTION
# if root is logging in, enable TOTP check in all cases (nullok is used below, so if TOTP is not configured for root, it'll pass through):
auth   [success=ignore ignore=ignore default=1]                                           pam_succeed_if.so             quiet uid eq 0
# [success=ok new_authtok_reqd=ok ignore=ignore default=bad module_unknown=ignore] == required + module_unknown:ignore
# if you have a recent enough libpam-google-authenticator, you can customize the prompt with the following option: [authtok_prompt=Verification Code (OTP): ]
# you can also add "debug" for more verbose logs (requires a not too old version of the pam module)
# note the nullok parameter, instructing that if TOTP is not configured for root, it shouldn't be asked
# if you know you've configured TOTP for root and prefer blocking access if there is a problem with the secret file, remove `nullok'
auth   [success=ok new_authtok_reqd=ok ignore=ignore default=bad module_unknown=ignore]   pam_google_authenticator.so   nullok secret=/var/otp/root
# if root, TOTP check has already been done just above, so skip this subsection (3 lines -> next section):
auth   [success=3 ignore=ignore default=ignore]                                           pam_succeed_if.so             quiet uid eq 0
# else (if not root), proceed in this subsection ONLY if the user is in group mfa-totp-configd, skip it entirely otherwise (2 lines -> next section):
auth   [success=ignore ignore=ignore default=2]                                           pam_succeed_if.so             quiet user ingroup mfa-totp-configd
# just display a nice message to the user:
auth   optional                                                                           pam_echo.so                   Multi-Factor Authentication enabled, an additional authentication factor is required (OTP).
# now actually ask for the TOTP:
auth   [success=ok new_authtok_reqd=ok ignore=ignore default=bad module_unknown=ignore]   pam_google_authenticator.so   secret=~/.otp


# 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

# Disallow non-root logins when /etc/nologin exists.
account    required     pam_nologin.so

# Reset counter if auth succeeded
account    required     pam_faillock.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

# Standard Un*x session setup and teardown.
@include common-session


# Print the message of the day upon successful login.
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

# Set the loginuid process attribute.
session    required     pam_loginuid.so

# Create a new session keyring.
session    optional     pam_keyinit.so force revoke

# 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

# 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