LDAP のようなディレクトリサービスは一旦忘れて、大昔からあるローカルシステムで閉じたユーザのアカウント・パスワード管理についてメモ。
expire/lock 周りの細かい仕様を知る必要があったので。
検証には CentOS 6 を利用。
アカウント・パスワードは主に以下の2ファイルで管理。
- /etc/passwd アカウント情報を管理。
- /etc/shadow アカウントのパスワードを暗号化して管理。
まずはパスワードを設定せずに新規ユーザーを追加
# useradd foo
/etc/passwd
を見る
/etc/passwd
ファイルにあるアカウント情報を確認
/etc/passwd
のパーミッションは -rw-r--r--
なのでだれでも read 可能。
# getent passwd foo ($ grep ^foo /etc/passwd でも OK) foo:x:504:505::/home/foo:/bin/bash
1行1アカウントで、: 区切りでアカウント情報が並んでいる。
- 1カラム目がユーザ名。
- 2カラム目がパスワード。
牧歌的な時代はここに暗号化されたパスワードが記載されていたが、近代ではパスワードは shadow ファイルで管理している。
PASSWD(5)から引用
These days many people run some version of the shadow password suite, where /etc/passwd has an ‘x’ character in the password field, and the encrypted passwords are in /etc/shadow, which is readable by the superuser only.
/etc/shadow
を見る
/etc/shadow
ファイルにあるパスワード情報を確認
/etc/shadow
のパーミッションは ----------
なのでスーパーユーザーだけが read/write 可能。
# getent shadow foo foo:!!:16195:0:99999:7:::
- 1カラム目がユーザ名。
- 2カラム目がパスワード。
RedHat 系ではパスワードが設定されていない場合、パスワード欄が "!!"
となる。
パスワードを設定して、変化を確認
# passwd foo Changing password for user foo. New password: Retype new password: passwd: all authentication tokens updated successfully. # getent shadow foo foo:$1$Ml1TwfGg$olbOXvZ1fdk9LVmpG.LIe1:16195:0:99999:7:::
2カラム目が $1$Ml1TwfGg$olbOXvZ1fdk9LVmpG.LIe1
となった。
このカラムは $ で区切って情報が入っていて
1つめ : ハッシュ方式。$1 は md5。$2a だと bcrypt, $5 だと SHA-256, $6 だと SHA-512 となる。
2つめ : salt。今回の例では “Ml1TwfGg”
3つめ : CRYPT(3)にsaltとパスワードを入力したハッシュ値。
openssl コマンド経由で実際にハッシュ値を確認してみる
# openssl passwd --help Usage: passwd [options] [passwords] where options are -crypt standard Unix password algorithm (default) -1 MD5-based password algorithm -apr1 MD5-based password algorithm, Apache variant -salt string use provided salt -in file read passwords from file -stdin read passwords from stdin -noverify never verify when reading password from terminal -quiet no warnings -table format output as table -reverse switch table columns # openssl passwd -1 -salt 'Ml1TwfGg' Password: $1$Ml1TwfGg$olbOXvZ1fdk9LVmpG.LIe1
ということで /etc/shadow
と一致している。
アカウントを lock
アカウントを lock すると、パスワード認証が無効化される。
ただし、鍵認証は通る。
ロック前
# passwd -S test test PS 2014-05-05 0 99999 7 -1 (Password set, MD5 crypt.) # getent shadow test test:$1$hZosduXo$vH4KgbuC806dmcvUYbrWc1:16195:0:99999:7:::
アカウントをロック
# usermod --lock test
ロック後
# passwd -S test test LK 2014-05-05 0 99999 7 -1 (Password locked.) # getent shadow test test:!$1$hZosduXo$vH4KgbuC806dmcvUYbrWc1:16195:0:99999:7:::
"passwd -S"
の2カラム目が PS(has a passwrd) から LK(cloked)にかわっている。
The status is “PS” if the user has a password, “LK” if the user has an
administrative lock, or “NP” if the user has no password.
http://h50146.www5.hp.com/products/software/oe/tru64unix/manual/v51a_ref/HTML/MAN/MAN1/0275____.HTM
また、 /etc/shadow
ファイルの2カラム目が "!"
で始まるようになっている。
crypt のハッシュ値に “!” は含まれないのでパスワード認証を無効化している。
$ ssh test@localhost
としても、パスワード認証に失敗する。
SHADOW(5) から
If the password field contains some string that is not a valid result of crypt(3), for instance ! or *, the user will not be able to use a unix password to log in (but the user may log in the system by other means).
ロック解除
# usermod --unlock test
アンロック後
# passwd -S test test PS 2014-05-05 0 99999 7 -1 (Password set, MD5 crypt.) # getent shadow test test:$1$hZosduXo$vH4KgbuC806dmcvUYbrWc1:16195:0:99999:7:::
アカウントを expire
アカウントが expire すると、鍵認証もできなくなる。
# usermod --expiredate 2014-05-01 test # getent shadow test test:$1$wUWokF2k$DaUHD/FAR4MA/b28VSAJa/:16195:0:99999:7::16191: # chage -l test Last password change : May 05, 2014 Password expires : never Password inactive : never Account expires : May 01, 2014 Minimum number of days between password change : 0 Maximum number of days between password change : 99999 Number of days of warning before password expires : 7
実際に ssh してみると
# ssh -i ~/.ssh/id_rsa test@localhost Your account has expired; please contact your system administrator Connection closed by ::1
というように鍵認証でも弾かれる。
accout の expire を never に戻す
# usermod -L -e '' test (OR # chage -E -1 test) # chage -l test Last password change : May 05, 2014 Password expires : never Password inactive : never Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 99999 Number of days of warning before password expires : 7
パスワードを expire させる。
アカウントではなくパスワードを expire させることもできる。
(効果があるかはともかく)運用ポリシーで定期的にパスワードを変更させる場合に利用。
パスワードの最終更新日(-d)が 2014/04/01, 30日でパスワードを切れさせ(-M)、パスワードが切れる7日前から警告メッセージを表示させたい(-W)場合
# chage -d '2014/04/10' -M 30 -W 7 test # chage -l test Last password change : Apr 10, 2014 Password expires : May 10, 2014 Password inactive : never Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 30 Number of days of warning before password expires : 7
ssh でパスワードログインしてみる
# ssh test@localhost test@localhost's password: Warning: your password will expire in 5 days Last login: Mon May 5 08:26:33 2014 from localhost Welcome to your Vagrant-built virtual machine. $
ログイン成功後、 “Warning: your password will expire in 5 days” というような警告メッセージが表示される。
有効期限はパスワードに対するものなので、鍵認証の場合はこの影響を受けない。
即座にパスワードを無効化
passwd -e
で特定のアカウントのパスワードを即座に無効化できる。
# passwd -e test Expiring password for user test. passwd: Success # chage -l test Last password change : password must be changed Password expires : password must be changed Password inactive : password must be changed Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 30 Number of days of warning before password expires : 7
パスワード認証でログインすると、パスワードが切れているので再設定するように促される。
# ssh test@localhost test@localhost's password: You are required to change your password immediately (root enforced) Last login: Mon May 5 08:30:57 2014 from localhost Welcome to your Vagrant-built virtual machine. WARNING: Your password has expired. You must change your password now and login again! Changing password for user test. Changing password for test. (current) UNIX password: New password: Retype new password: passwd: all authentication tokens updated successfully. Connection to localhost closed.
古いパスワードで認証後、パスワードの更新だけが行われ、ログインはしない。
# chage -l test Last password change : May 05, 2014 Password expires : Jun 04, 2014 Password inactive : never Account expires : never Minimum number of days between password change : 0 Maximum number of days between password change : 30 Number of days of warning before password expires : 7
“Password expires” が更新されている。
暗号方式を変える
パスワード暗号化のハッシュ関数は authconfig
コマンドで確認できる。
# authconfig --test | grep password shadow passwords are enabled password hashing algorithm is md5
この出力から /etc/shadow
ファイルを利用し、 md5 でハッシュ化していることがわかる。
このハッシュ関数をよりセキュアな sha512 に変更してみる
# authconfig --passalgo=sha512 --update # authconfig --test | grep password shadow passwords are enabled password hashing algorithm is sha512
ハッシュ関数が sha512 になったところでパスワード(“asdfqwer1234)を再設定
# passwd foo Changing password for user foo. New password: Retype new password: passwd: all authentication tokens updated successfully. # getent shadow foo foo:$6$.TyxHKUj$d5.JkU6dmR1mhr4Y3iHsuzUVi/c4klAeqQvnxHWOUBAqtpx.Ss9/DsSyzxN2ASZzJ2OsHWL8E9DpLn8fAxI.f.:16195:0:30:7:::
暗号方式が $6 となっているので sha512 とわかる。
また、ハッシュ文字列も md5 に比べてはるかに長い。
Python の標準ライブラリーの crypt からも確認。
crypt
関数のsalt
にはハッシュ方式($6$)も含める
# python -c "import crypt;print crypt.crypt('asdfqwer1234', '$6$.TyxHKUj')" $6$.TyxHKUj$d5.JkU6dmR1mhr4Y3iHsuzUVi/c4klAeqQvnxHWOUBAqtpx.Ss9/DsSyzxN2ASZzJ2OsHWL8E9DpLn8fAxI.f.
ということで /etc/passwd
と一致している。
お遊びで、ハッシュを DES にし、shadow
ファイルも無効化してみる。
# authconfig --disableshadow --passalgo=descrypt --update
パスワードを “asdfqwer1234” に設定。
# passwd foo Changing password for user foo. New password: Retype new password: passwd: all authentication tokens updated successfully.
getent
で /etc/passwd
と /etc/shadow
を確認
# getent passwd foo foo:/ANI6mKDVNsRg:502:503::/home/foo:/bin/bash # getent shadow foo # passwd -S foo foo PS (Password set, DES crypt.)
shadow
ファイルは空なので出力がない。
次に openssl コマンドでハッシュを確認。
crypt 関数の仕様で、DES の場合、2カラム目(“/ANI6mKDVNsRg”)の先頭2文字を salt
として使い、crypt
に渡すキーはパスワードの先頭8文字を利用する。
# openssl passwd -crypt -salt '/A' Password: <- type "asdfqwer" /ANI6mKDVNsRg
Python の標準ライブラリーの crypt からも確認。
# python -c "import crypt;print crypt.crypt('asdfqwer', '/A')" /ANI6mKDVNsRg
ということで /etc/passwd
と一致している。
References
- http://man7.org/linux/man-pages/man1/getent.1.html
- http://man7.org/linux/man-pages/man5/passwd.5.html
- http://www.gnu.org/software/libc/manual/html_node/crypt.html
- http://en.wikipedia.org/wiki/Crypt_(C)
- https://help.ubuntu.com/14.04/serverguide/user-management.html
- http://www.cyberciti.biz/faq/understanding-etcshadow-file/
- http://en.wikipedia.org/wiki/Passwd
/etc/shadow の仕様(生成方法)について知らず、
Ansible から任意のパスワード・リストを基に
自動的にユーザ・アカウントを登録する処理を
作る際に役立ちました。ありがとうございます。
(公式のガイドにも、やり方は書かれているのですが、
これは何かという基本的な説明は省かれていたので。)