ssh password authentication + Google Authenticator



sshのパスワード認証(公開鍵認証ではない) + MFAtoken によるログインの設定方法です。
やりたいことは、[クラメソさんのこの記事](http://dev.classmethod.jp/cloud/aws/amazon-linux-ssh-two-step-authentication/)のパスワード認証版です。

- 検証には、AWSさんから、Amazon Linux AMI 2017.03.1 (HVM), SSD Volume Type (ami-3bd3c45c) を利用しました。
- MFAtoken は [Google Authenticator](https://github.com/google/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の設定は[こちら](http://qiita.com/azusanakano/items/b39bd22504313884a7c3)を参考にしました。


timezoneファイル差し替え

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

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

```bash: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
------------------------


```bash:command
sudo yum update
```

1.4 Installing google/google-authenticator
------------------------------------------------

```bash:command
sudo yum install google-authenticator qrencode-libs -y
```

2. How to setup
===================================================

2.1 Editing sshd_config
---------------------------

### /etc/ssh/sshd_config の編集

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

```bash:commnad
cp /etc/ssh/sshd_config ${HOME}/_bak_sshd_config
```

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

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

- ChallengeResponseAuthentication `yes`
- UsePAM `yes`

現状を確認します。

```bash:command
cat /etc/ssh/sshd_config \
| grep -e ^PasswordAuthentication \
       -e ^ChallengeResponseAuthentication \
       -e ^UsePAM
```

```bash:response
PasswordAuthentication no
ChallengeResponseAuthentication no
UsePAM yes
```

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

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

再度内容を確認します。

```bash:command
cat /etc/ssh/sshd_config \
| grep -e ^ChallengeResponseAuthentication
```

```bash:response
ChallengeResponseAuthentication yes
```

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

```bash: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
```

```bash: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の再起動します。

```bash:command
sudo service sshd restart
```

```bash:response
Stopping sshd:                                             [  OK  ]
Starting sshd:                                             [  OK  ]
```

2.2 Editing PAM sshd
---------------------------

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

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

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

```bash:command
ls -l /etc/pam.d/google-auth
```

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

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

```bash: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` のバックアップをとります.

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

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

```bash:command
cat /etc/pam.d/sshd | grep ^auth
```

```bash:response
auth	   required	pam_sepermit.so
auth       substack     password-auth
auth       include      postlogi
```

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

```bash: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
```

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

```bash:command
cat /etc/pam.d/sshd | grep "^auth"
```

```bash:response
auth	   required	pam_sepermit.so
auth       substack     password-auth
auth       substack     google-auth
auth       include      postlogin
```

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

```bash: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` が強制的に作成されるよう設定します。


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

```bash:command
ls -l /etc/profile.d/google-authenticator.sh
```

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

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

```bash: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以上にする必要があります。

ユーザ名を決定します。

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

ユーザを作成します。

```bash:command
sudo useradd ${NEWUSER}
```

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

```bash:command
id -a ${NEWUSER}
```

```bash:response
uid=501(<'任意のユーザ名'>) gid=501(<'任意のユーザ名'>) groups=501(<'任意のユーザ名'>)
```

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

```bash:command
sudo passwd ${NEWUSER}
```


3.2 Test
--------------------------

### 初回ログイン

テストを実施します。

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

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

![Kobito.o4XnfV.png](https://qiita-image-store.s3.amazonaws.com/0/73207/4f2cf576-dd00-7830-875e-489b6afaae76.png "Kobito.o4XnfV.png")

### 2回目以降

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

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

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

```bash: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
==================

- http://dev.classmethod.jp/cloud/aws/amazon-linux-ssh-two-step-authentication/
- http://blog.kenichimaehashi.com/?article=13076317960
- http://qiita.com/azusanakano/items/b39bd22504313884a7c3
- http://dev.classmethod.jp/cloud/aws/amazon-linux-ssh-two-step-authentication/



5. Appendix
==================

```bash:/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
```

```bash:/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
```

```bash:/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
```

```bash:/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は必要ありません。なぜかは分からないけど、いろいろ触っていたらできた。
ようわからん。。。

```bash:/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
```