公開鍵認証でのユーザー追加
クラウド上のサーバなどを設置した際に、ユーザー認証は公開鍵認証のみを許可している場合も多いと思います。というかそうした方がよいです。
ただ、多人数が使う場合に公開鍵設置は結構面倒な作業になるのでスクリプト作りたいなぁと思っていました。で、作りました。
本当にちゃんと運用する場合はLDAPとかを考えた方が良いです。
仕組み
Ubuntuだとユーザ追加のコマンドとしてadduser
が用意されていて対話形式で追加できますが、この場合だと公開鍵を手動で置く必要があります。
一方でLinuxで用意されているuseradd
コマンドであればオプションを組み合わせていろいろできます。
ぶっちゃけadduser
コマンドでもいろいろできそうですけど、useradd
で作ったのでadduser
のことは忘れてください。
公開鍵の設置
基本的な話ですが、公開鍵(id_rsa.pub
)を置く場所は~/.ssh/authorized_keys
です。
新しいユーザを作ったら、ホームディレクトリの下に.ssh
ディレクトリを作って、その下にauthorized_keys
の名前でid_rsa.pub
をコピーすれば良いです。面倒ですね。
useraddコマンドにはskellという機能があります。
skell機能を使う
skell機能は、簡単に言うと新ユーザのホームディレクトリをコピーして作ってくれる機能です。
-k
でディレクトリを指定します。そうするとそのディレクトリのファイルをコピーしてくれるのです。
つまり、useradd
する前に公開鍵をskellディレクトリへコピーしておいてuseradd
すればいいのです。
結局コピーするなら、useradd
した後にシェルスクリプトで追加しても一緒って思いがちですが、ファイルの権限とか所有者とか考えるとskell使った方が良いです。
スクリプト第一弾
てことでまずはこんなシェルスクリプトを考えてみました。
# !/bin/sh
if [ $# != 2 ]; then
echo Usage: $0 username pubkey
exit 1
fi
cp $2 skell/.ssh/authorized_keys || exit 1
sudo useradd -m -k skell $1
使い方は第一引数がユーザー名、第二引数が公開鍵ファイルパスになります。
実行する場所のskell/.ssh
ディレクトリが作られていることが前提です。
ファイルが無いなどのエラーの場合は末尾の|| exit 1
が働いて強制終了します。
この書き方便利なので重宝します。
これで目的の8割は達成しましたが、もうちょっと運用考えます。
ユーザー追加フロー
このスクリプトだとユーザー名と公開鍵を貰って、スクリプトを叩く必要がありますが、容易に想像できるのがみんなid_rsa.pub
のファイルを送ってきて、それを取り違えてユーザー作ってしまうという運用ミスです。
ファイルを間違えて上書きする危険もあれば、コマンドを叩くときに間違える可能性もあります。
この危険性をなくすためにやることは1つです。
- 公開鍵は希望するユーザー名で送って貰う
hogeというユーザ名を希望するなら、公開鍵をhogeという名前に変えてから送って貰います。
hogeというファイルはpubs
とかのディレクトリに格納して、それを指定することでユーザ追加するようにしましょう。
スクリプト第2弾
さて、こんな感じになります。
# !/bin/sh
if [ $# != 1 ];then
echo Usage: $0 username_pubkey_path
exit 1
fi
username=`basename $1`
cp $1 skell/.ssh/authorized_keys || exit 1
sudo useradd -m -k skell ${username}
使うときは./add.sh pubs/hoge
といった形で指定します。
basename
でファイル名を取得できるのでそれをそのままユーザー名にします。
もうちょっとだけ
もうほぼ目的は達成しましたが、この方法で追加したユーザーにはパスワードが設定されていません。
公開鍵でssh認証のみを指定している場合はパスワードを使う場面がない・・・と思いきや、例えばchshするときとか、権限を貰ってsudoするときとかに困ります。
useraddにはパスワードを引数で指定できますが、cryptのハッシュ値を入れないといけないのでちょっと面倒です。
パスワードを作る
平文のパスワードを、ハッシュ化された文字列に変換しましょう。
ハッシュ化に必要なのは元の平文と、ソルトの値になります。
ソルトを指定しないでみんながハッシュ値を使っていると、ハッシュ値を比較しただけで「こいつ、ここのパスワードとここのパスワード一緒やん!」ってなるのでソルトが必要です。
ただ、ランダムでいいのでランダム文字列を生成します。
salt=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -1`
これでソルトが作れます。
パスワードは下記のようにして作れます。
cmd="perl -e 'print crypt(\"${username}\", \"\\\$6\\\$${salt}\");'"
pass=`eval ${cmd}`
コマンド内に引数埋め込むのどうすんだっけ?っていうのを忘れたのでevalしました。1行で書けると思います。そのうち直します。
これをuseradd
の-p
オプションで指定すれば完成です。
シェルスクリプト:最終形態
ということで下記のようになります。
# !/bin/sh
if [ $# != 1 ];then
echo Usage: $0 username_pubkey_path
exit 1
fi
username=`basename $1`
salt=`cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 8 | head -1`
cmd="perl -e 'print crypt(\"${username}\", \"\\\$6\\\$${salt}\");'"
pass=`eval ${cmd}`
cp $1 skell/.ssh/authorized_keys || exit 1
sudo useradd -m -k skell -p ${pass} ${username}
他にも最初から設置したいファイルがあればskellの下に置けば良いので、適宜アレンジしてみてください。