google
SSH
MFA
Authetication

ssh password authentication + Google Authenticator

More than 1 year has passed since last update.

sshのパスワード認証(公開鍵認証ではない) + MFAtoken によるログインの設定方法です。
やりたいことは、クラメソさんのこの記事のパスワード認証版です。

  • 検証には、AWSさんから、Amazon Linux AMI 2017.03.1 (HVM), SSD Volume Type (ami-3bd3c45c) を利用しました。
  • MFAtoken は Google Authenticator です。

[Note] 本ドキュメントの command枠 のコピペ作業で、動作できるように作っています。(ミスを避けるため vi による編集を避けた)

1. pre-conditions

1.1 Login to the server

AWSの操作は省略します。下記のAMIをつかってサーバを起動しました。

  • Amazon Linux AMI 2017.03.1 (ami-3bd3c45c) をつかってEC2を起動します。
  • 起動後、サーバーへログインします。

1.2 Chenge timezone (追記: たぶん変更しなくても大丈夫)

Timezoneの設定はこちらを参考にしました。

timezoneファイル差し替え

command
sudo cp /etc/localtime /etc/localtime.org
sudo ln -sf  /usr/share/zoneinfo/Asia/Tokyo /etc/localtime \
&& cat /etc/localtime

reboot後変更したタイムゾーンがUTCにもどらないための設定

command
sudo cp /etc/sysconfig/clock /etc/sysconfig/clock.org
sudo sh -c "echo -e 'ZONE=\"Asia/Tokyo\"\nUTC=false' > /etc/sysconfig/clock" \
&& cat /etc/sysconfig/clock

1.3 Update packages

command
sudo yum update

1.4 Installing google/google-authenticator

command
sudo yum install google-authenticator qrencode-libs -y

2. How to setup

2.1 Editing sshd_config

/etc/ssh/sshd_config の編集

編集するsshd_configのバックアップをとります.

commnad
cp /etc/ssh/sshd_config ${HOME}/_bak_sshd_config

sshd_configが下記になるよう編集します。

PasswordAuthentication は yes でも no でもどちらでも大丈夫です。
/etc/pam.d/sshdpassword-auth を有効にすることでこの設定は関係なく上手く動くようです。

  • ChallengeResponseAuthentication yes
  • UsePAM yes

現状を確認します。

command
cat /etc/ssh/sshd_config \
| grep -e ^PasswordAuthentication \
       -e ^ChallengeResponseAuthentication \
       -e ^UsePAM
response
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes

ここでは ChallengeResponseAuthenticationyes になるよう編集する必要があることがわかりましたので、修正します。

command
BEFORE="ChallengeResponseAuthentication no"
AFTER="ChallengeResponseAuthentication yes"
sudo sed -i s/"${BEFORE}"/"${AFTER}"/g /etc/ssh/sshd_config

再度内容を確認します。

command
cat /etc/ssh/sshd_config \
| grep -e ^ChallengeResponseAuthentication
response
ChallengeResponseAuthentication yes

ec2-userは今まで通り公開鍵認証のログインのままとします。

command
sudo sh -c "cat << EOF >> /etc/ssh/sshd_config

# -- EC2 USER Setting --
Match User ec2-user
   AuthenticationMethods publickey
   PubkeyAuthentication yes
   PasswordAuthentication no
EOF" && tail /etc/ssh/sshd_config
response
#   X11Forwarding no
#   AllowTcpForwarding no
#   PermitTTY no
#   ForceCommand cvs serverMatch User ec2-user

# -- EC2 USER Setting --
Match User ec2-user
   AuthenticationMethods publickey
   PubkeyAuthentication yes
   PasswordAuthentication no

sshdの再起動

sshdの再起動します。

command
sudo service sshd restart
response
Stopping sshd:                                             [  OK  ]
Starting sshd:                                             [  OK  ]

2.2 Editing PAM sshd

次にPAMの設定を修正し、sshログイン認証時にGoogle Authenticatorが使われるようにします。

/etc/pam.d/google-authの作成

同じファイル名のファイルがないことを確認します。

command
ls -l /etc/pam.d/google-auth
response
ls: cannot access /etc/pam.d/google-auth: No such file or directory

google-authという名前のファイルを作成します。これは /etc/pam.d/sshd から読み込みます。

command
sudo sh -c "cat << EOF > /etc/pam.d/google-auth
#%PAM-1.0
auth        required      pam_env.so
auth        sufficient    pam_google_authenticator.so nullok
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        required      pam_deny.so
EOF" && cat /etc/pam.d/google-auth

/etc/pam.d/sshdの編集

編集する /etc/pam.d/sshd のバックアップをとります.

command
cp /etc/pam.d/sshd ${HOME}/_bak_pam_sshd

現在の変数の状況を確認します。

command
cat /etc/pam.d/sshd | grep ^auth
response
auth       required pam_sepermit.so
auth       substack     password-auth
auth       include      postlogi

google authentication を有効にするためこのファイルを編集します。

command
BEFORE="auth       substack     password-auth"
AFTER="auth       substack     password-auth\nauth       substack     google-auth"
sudo sed -i s/"${BEFORE}"/"${AFTER}"/g /etc/pam.d/sshd

もう一度ファイルを確認します。

command
cat /etc/pam.d/sshd | grep "^auth"
response
auth       required pam_sepermit.so
auth       substack     password-auth
auth       substack     google-auth
auth       include      postlogin

もう一度サービスを再起動します(いらないかも...)

command
sudo service sshd restart
Stopping sshd:                                             [  OK  ]
Starting sshd:                                             [  OK  ]

2.3 Creating ".google_authenticator" at first user's login

/etc/profile.d/google-authenticator.shを作成し、root と ec2-user以外のユーザがはじめてログインしたとき、google_authenticatorの初期設定がなされ、ユーザの ${HOME}/.google_authenticator が強制的に作成されるよう設定します。

同名のファイルがないかを確認します。

command
ls -l /etc/profile.d/google-authenticator.sh
response
ls: cannot access /etc/profile.d/google-authenticator.sh: No such file or directory

/etc/profile.d/google-authenticator.sh を作成します

command
cat << EOF > google-authenticator.sh
#!/bin/sh

trap 'exit' SIGINT

if [ "\$USER" != "root" -a "\$USER" != "ec2-user" ]; then
  if [ ! -f "\$HOME/.google_authenticator" ]; then
    echo "setup google-authenticator..."
    /usr/bin/google-authenticator -t -d -w 17 -u -f
  fi
fi
EOF
sudo cp google-authenticator.sh /etc/profile.d/google-authenticator.sh
sudo chmod +x /etc/profile.d/google-authenticator.sh
cat /etc/profile.d/google-authenticator.sh

-W 17 はもし、/usr/bin/google-authenticator を直だたきしたあと、4つめの "window" に関する質問 y で答えると、設定される値です。ここを調整しないとなぜかログインできなかったです。サーバとTokenデバイス間の Time-skew?

3. Test

3.1 Creating Test User

サーバー側へのログイン用のユーザを作ります。
/etc/pam.d/google-authの設定により、UIDは必ず500以上にする必要があります。

ユーザ名を決定します。

command
NEWUSER=<'任意のユーザ名'>

ユーザを作成します。

command
sudo useradd ${NEWUSER}

ユーザの状態を確認します。

command
id -a ${NEWUSER}
response
uid=501(<'任意のユーザ名'>) gid=501(<'任意のユーザ名'>) groups=501(<'任意のユーザ名'>)

初期パスワードを設定します。

command
sudo passwd ${NEWUSER}

3.2 Test

初回ログイン

テストを実施します。

command
ssh <'作成したユーザ'>@<'サーバー名'>

下記スクリーンショットの1行目の "Password" で Unixパスワードを入力しました。
1回目は、MFATokenの設定がされていないので、そのままログインできます。

Kobito.o4XnfV.png

2回目以降

サーバにログインします。

command
ssh <'作成したユーザ'>@<'サーバー名'>

PasswordでUnixパスワードを入力し、Verification code でMFATokenを入力します。

response
Password: 
Verification code: 
Last login: Mon Aug  7 22:18:47 2017 from aa20111001946f573a19.userreverse.dion.ne.jp

       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|

https://aws.amazon.com/amazon-linux-ami/2017.03-release-notes/

4. Refereces

5. Appendix

/etc/ssh/sshd_config
#   $OpenBSD: sshd_config,v 1.93 2014/01/10 05:59:19 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/local/bin:/bin:/usr/bin

# 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.

# If you want to change the port on a SELinux system, you have to tell
# SELinux about this change.
# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER
#
#Port 22
#AddressFamily any
#ListenAddress 0.0.0.0
#ListenAddress ::

# The default requires explicit activation of protocol 1
#Protocol 2

# HostKey for protocol version 1
#HostKey /etc/ssh/ssh_host_key
# HostKeys for protocol version 2
HostKey /etc/ssh/ssh_host_rsa_key
#HostKey /etc/ssh/ssh_host_dsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key

# Lifetime and size of ephemeral version 1 server key
#KeyRegenerationInterval 1h
#ServerKeyBits 1024

# Ciphers and keying
#RekeyLimit default none

# Logging
# obsoletes QuietMode and FascistLogging
#SyslogFacility AUTH
SyslogFacility AUTHPRIV
#LogLevel INFO

# Authentication:

#LoginGraceTime 2m
#PermitRootLogin yes
# Only allow root to run commands over ssh, no shell
PermitRootLogin forced-commands-only
#StrictModes yes
#MaxAuthTries 6
#MaxSessions 10

#RSAAuthentication yes
#PubkeyAuthentication yes

# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2
# but this is overridden so installations will only check .ssh/authorized_keys
AuthorizedKeysFile .ssh/authorized_keys

#AuthorizedPrincipalsFile none

#AuthorizedKeysCommand none
#AuthorizedKeysCommandUser nobody

# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
#RhostsRSAAuthentication no
# similar for protocol version 2
#HostbasedAuthentication no
# Change to yes if you don't trust ~/.ssh/known_hosts for
# RhostsRSAAuthentication and 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
# EC2 uses keys for remote access
PasswordAuthentication no

# Change to no to disable s/key passwords
#ChallengeResponseAuthentication yes
ChallengeResponseAuthentication yes

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

# GSSAPI options
#GSSAPIAuthentication no
#GSSAPICleanupCredentials yes
#GSSAPIStrictAcceptorCheck yes
#GSSAPIKeyExchange no
#GSSAPIEnablek5users 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'.
# WARNING: 'UsePAM no' is not supported in Amazon Linux AMI and may cause several
# problems.
# Leaving enabled as described so that account and session checks are run
UsePAM yes

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

# no default banner path
#Banner none

# Accept locale-related environment variables
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS

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

# Example of overriding settings on a per-user basis
#Match User anoncvs
#   X11Forwarding no
#   AllowTcpForwarding no
#   PermitTTY no
#   ForceCommand cvs serverMatch User ec2-user
Match User ec2-user
   AuthenticationMethods publickey
   PubkeyAuthentication yes
   PasswordAuthentication no
/etc/pam.d/google-auth
#%PAM-1.0
auth        required      pam_env.so
auth        sufficient    pam_google_authenticator.so nullok
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        required      pam_deny.so
/ssh/pam.d/sshd
#%PAM-1.0
auth       required pam_sepermit.so
auth       substack     password-auth
auth       substack     google-auth
auth       include      postlogin
# Used with polkit to reauthorize users in remote sessions
-auth      optional     pam_reauthorize.so prepare
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    required     pam_namespace.so
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin
# Used with polkit to reauthorize users in remote sessions
-session   optional     pam_reauthorize.so prepare
/etc/profile.d/google-authenticator.sh
#!/bin/sh

trap 'exit' SIGINT

if [ "$USER" != "root" -a "$USER" != "ec2-user" ]; then
  if [ ! -f "$HOME/.google_authenticator" ]; then
    echo "setup google-authenticator..."
    /usr/bin/google-authenticator -t -d -w 17 -u -f
  fi
fi

Memo: 1回目はパスワード、2回目はMFATokenのみの動作

1回目はパスワード、2回目はMFATokenのみのログインにしたい場合、/etc/pam.d/sshd を下記のようにします。またgoogle-authは必要ありません。なぜかは分からないけど、いろいろ触っていたらできた。
ようわからん。。。

/etc/pam.d/sshd
#%PAM-1.0
auth       required pam_sepermit.so
#auth       substack     password-auth
auth       sufficient    pam_google_authenticator.so 
auth       requisite     pam_succeed_if.so uid >= 500 quiet
auth       substack     password-auth
#auth       substack     google-auth
auth       include      postlogin
# Used with polkit to reauthorize users in remote sessions
-auth      optional     pam_reauthorize.so prepare
account    required     pam_nologin.so
account    include      password-auth
password   include      password-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    required     pam_loginuid.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open env_params
session    required     pam_namespace.so
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session    include      postlogin
# Used with polkit to reauthorize users in remote sessions
-session   optional     pam_reauthorize.so prepare