LoginSignup
15
17

More than 5 years have passed since last update.

Kerberos認証を使ってみる (OpenSSH)

Last updated at Posted at 2019-01-25

前回記事の続きです。

モチベーション

前回はサーバーのセットアップまで完了しました。ここでは、その環境を使ってSSO(Single Sign On)を実現していきます。

完成予定図です。

大量に作ったVMをSSOを使って簡単に管理しようという魂胆です。

環境は、KDCとVM達はGoogle Cloud Platform上のDebian GNU/Linux 9 (stretch)を使っています。また、ClientはMacOS High Sierraです。

Kerberosについての簡単な説明

前回の記事では環境構築をひたすらやったので、それぞれのサーバーの役割をあまり説明しませんでした。なので、ここでごく簡単な説明を入れます。

先ほどの図で、KDCやticket、keytabといった見慣れない単語が出てきたと思います。これらはKerberos認証と呼ばれる認証方式を実現するのに必要なものです。

Kerberos認証は双方向認証(クライアント認証+サーバー認証)です。かの有名なActiveDirectoryでもKerberos認証を使うことができます。Kerberosは認証しかしないので、認証対象のユーザーの情報の保存は別に考えなくてはいけません。

KDCはKey Distribution Centerの略で、AS(Authentication Server)とTGS(Ticket Granting Server)から成ります。クライアントはASに認証してもらった後、TGSからticketを受け取り、それを相手に提示することで自分の身分を証明します。keytabは、逆に相手が身分を証明するために用いるものです。このようにして、お互いに身分を証明し合うことにより、安心して通信を行うことができます。

サーバーにKerberos認証を使ってアクセスする際の、認証の詳しい手順は以下の通りです。

暗号鍵の生成

パスワードを使う場合

  1. ユーザーがユーザー名と所属realm、パスワードを入力します。
  2. クライアントプログラムがそれらの情報(credential)から共通鍵を生成します。

keytabを使う場合

  1. ユーザーはあらかじめcredentialをクライアントプログラムに登録しておきます。クライアントプログラムはそこから共通鍵を生成し、keytabというファイルに保持します。keytabからcredentialを復元することはできません。

この時点で、クライアントはASとの通信に使う共通鍵を持っています。

クライアント認証

ASがクライアントが本人であるか確認します。

  1. クライアントはASにユーザーIDを送り、認証を要求します。
  2. ASはデータベース上に送られてきたユーザーIDに対応するユーザーがいることを確認し、データベース上からcredentialを取ってきて共通鍵を生成します。そして、以下の二つのメッセージをクライアントに返します。
    • A: Client/TGS Session Key [共通鍵で暗号化]
    • B: TGT(Ticket Getting Ticket) [TGSの暗号鍵で暗号化]
    TGTにはユーザーID、ユーザーのネットワークアドレス、チケットの有効期間、Client/TGS Session Keyが含まれます。
  3. クライアントはメッセージAを共通鍵で復号し、Client/TGS Session Keyを取り出します。

この時点で、クライアントは以下の二つの情報を持っています。

  • Client/TGS Session Key
  • TGT [TGSの暗号鍵で暗号化]

クライアント認可

TGSがクライアントにサーバーへのアクセス権(券)を与えます。

  1. クライアントはTGSにサーバーへのアクセス権を要求します。そのさい、以下の二つをTGSに渡します。

    • メッセージC: TGSの暗号鍵で暗号化されたTGTおよびアクセスを要求するサービスのID
    • メッセージD: ユーザーIDおよびタイムスタンプ [Client/TGS Session Keyで暗号化]
  2. TGSはメッセージCからTGSの暗号鍵で暗号化されたTGTを取り出して復号し、TGTを得ます。そしてTGSから、Client/TGS Session Keyを取り出します。この鍵を使って、メッセージDを復号し、そこに含まれるユーザーIDとTGTから得られるユーザーIDが一致するか確認します。一致した場合、次の二つのメッセージを返します。

    • メッセージE: ticket [サーバーの暗号鍵で暗号化]
    • メッセージF: Client/Server Session Key [Client/TGS Session Keyで暗号化]
    ticketには、ユーザーID、ユーザーネットワークアドレス、有効期間、そしてClient/Server Session Keyが含まれます。
  3. クライアントはメッセージFを復号して、Client/Server Session Keyを得ます。

この時点でクライアントは、以下の二つの情報を持っています。

  • ticket [サーバーの暗号鍵で暗号化]
  • Client/Server Session Key

サーバーへの接続

  1. クライアントはサーバーに接続を要求します。そのさい、以下の二つのメッセージを渡します。

    • メッセージE: ticket [サーバーの暗号鍵で暗号化]
    • メッセージG: ユーザーIDとタイムスタンプ [Client/Server Session Keyで暗号化]
  2. サーバーは事前に用意されたkeytabから自身の暗号鍵を取り出し、メッセージEを復号して、ticketを得る。さらに、ticketからClient/Server Session Keyを取り出し、メッセージGを復号することで、ユーザーIDを得る。このユーザーIDとticketに含まれるユーザーIDが一致するか調べ、一致する場合は次のメッセージを返す。

    • メッセージH: メッセージGから取り出したタイムスタンプ [Client/Server Session Keyで暗号化]
  3. クライアントはメッセージHを復号し、タイムスタンプが送ったものと一致するか確認する。一致した場合は、サーバーに接続する。

以上です。この内容は、wikipediaに書いてあります。

クライアントがどうして偽れないのか、サーバーがどうして偽れないのかを考えてみると良いかと思います。

前提

前回記事でやったことを前提とします。

  • krb5-admin-server(adminサーバー)が動いている
  • krb5-kdc(master KDCサーバー)が動いている
  • 管理者用principalがすでに存在する
  • firewallが適切に設定されている

前回はKerberosのバックグラウンドにOpenLDAPを使いましたが、今回の内容はバックグラウンドに関わらず使えると思います。

principalの追加

前回の記事では、管理者principalの追加までやったので、これからはkadmin.localではなくkadminを使っていきましょう。

一般ユーザー用principal(usagi)を追加します。名前のつけ方にはルールがあって、サービスの場合は、<service-name>/<hostname>@<realm>に従ってください。ユーザー用principalの場合は、<user-name>@<realm>です。@<realm>は省略可能で、省略した場合、デフォルトrealmが使われます。

sudo kadmin -p ${admin}/admin -q 'addprinc usagi'

これまではkadminを対話モードで使っていましたが、このように対話モードに入らなくても設定ができます。

サービス用principal(http/www)を追加します。これは、サービスをホストする機器で行います。当然、その機器にはkadminが適切にインストールされている必要があります。前回の記事が参考になるかもしれません。

sudo kadmin -p ${admin}/admin
kadmin: addprinc http/www -randkey
kadmin: ktadd -k ${service keytab file} usagi # keytabを作る
kadmin: exit

一般ユーザーのkeytabも作りたくなるかもしれませんが、ktaddで作ることはできません。ktaddでkeytabを作ると、以前のパスワードは使えなくなるからです。

Each principal’s keys are randomized in the process.
(https://web.mit.edu/kerberos/krb5-1.12/doc/admin/admin_commands/kadmin_local.html より)

その代わり、ktutilというコマンドで作ります。

sudo ktutil
ktutil: read_kt /etc/krb5.keytab
ktutil: add_entry add_entry -password -p ${princ} -k ${kvn} -e ${encry}
ktutil: write_kt /etc/krb5.keytab

/etc/krb5.keytabというのはデフォルトのkeytabファイルで、kadmin -kで使われるものです。もちろん、他のファイルを指定しても構いません。

principalのパスワードの変え方

これはmiscですが、大事なので書いておきます。パスワードの変更は、

kpasswd ${principal}

で出来ます。ただ、この方法は変更前のパスワードを知っている必要があるので、パスワードを忘れた場合には使えません。そのような時は、

kadmin -p ${admin}/admin -q ${princ}

を使います。元のパスワードを知らなくても変更できるので、忘れた時などにも使えます。

SSHでKerberos認証を使う

では最後に、実際にkerberos認証を使ってみます。

sshd_configの設定

まずは、sshサーバーのdaemonであるsshdの設定ファイルsshd_configを変更します。

sshd_config
...
PasswordAuthentication no
...
GSSAPIAuthentication yes
...

としてください。

systemctl restart sshd

でdaemonの再起動を行います。

SSHサーバー用principalの作成

kadmin -p ${admin}/admin@${REALM}
# sshログインユーザー用principal
kadmin: add_principal ${username}@${REALM}
# sshサーバー用principal
kadmin: add_principal -nokey host/${FQDN}@${REALM}

サーバー用principalについては、あとでktaddによって暗号鍵を生成するので、ここではnokeyとしています。

ここで、注意しないといけないのが、sshサーバー用principalの名前です。

上で見た通り、sshクライアントはticketをもらう時に、sshサーバーprincipalを知っておく必要があります。sshクライアントは、おそらく、

ssh ${FQDN}

と要求された場合、sshサーバーのprincipalとしてhost/\${FQDN}@\${default realm}を想定するのでしょう。そのため、sshサーバー用principalの名前は自由に決められません。(要検証)

また\${FQDN}は、SSHサーバー上で

hostname --fqdn

を実行したときの結果と一致している必要があります。サーバーのFQDNを変更するには、/etc/hostsファイルを変更すれば良いです。詳しいやり方はman hostnameを見るなり、ググるなりして、調べて見てください。

蛇足ですが、sshサーバーがどうやってログインユーザーを決めているかというと、ticketにユーザーIDが含まれるので、そのIDを用いてログインユーザーを決定しているのでしょう。(要検証) ただ、ユーザーIDとuidのバインディングを設定するファイルは見当たりませんでした。どうやって、バインディングしているのかは不明です(ユーザー名?)。ご存知の方はコメントをお願いします。

keytabの登録

SSHサーバーで、

kadmin -p ${admin}/admin@${REALM} -q ktadd host/${FQDN}@${REALM}

を実行してください。サーバーにkeytabが登録されます。

SSHクライアントの設定

~/.ssh/configファイルに、以下を追記します。

~/.ssh/config
Host ${target name}
    HostName ${FQDN}
    User ${login name}
    KerberosAuthentication yes
    KerberosTicketCleanup yes
    GSSAPIAuthentication yes
    GSSAPICleanupCredentials yes

ここで、principalの設定がないことに気づきます。が、これできちんと動きます。理由は、余談 複数のprincipalを使い分けるを見てください。

TGT(Ticket Granting Ticket)の取得

TGTを取得するには、以下のコマンドを実行してください。

kinit ${princ name}@${REALM}

取得したTGT(とその他のticket)は、

klist

で確認できます。TGTは、

krbtgt/${REALM}@${REALM}

と表示されるはずです。

~/.k5loginの編集

名前が\${login name}@\${default realm}のprincipalは特別な設定なくログインできます。そうでない場合(以下の条件を満たす場合)は設定が必要です。

  • kinitで指定した\${princ name}が~/.ssh/confで設定した\${login name}と一致しない場合
  • kinitで指定した\${REALM}がsshサーバーのデフォルトrealmでない場合

この二つの条件のうち一つでも当てはまる場合は、sshサーバー上のユーザー\${login name}のホームディレクトリに.k5loginファイルを作り、以下の内容を書き込んでください。

~/.k5login
${princ name}@{REALM}

また、.k5loginファイルを作成した場合、デフォルトの設定が無効となりますので、名前が\${login name}@\${default realm}のprincipalも、そのprincipalでログインしたいならば.k5loginファイルに書き込む必要があります。

With no .k5login file, or with k5login_authoritative* set to false, a default rule would permit the principal alice [\${princ name}] in the machine’s default realm to access the alice [\${login name}] account.

* k5login_authoritativeはkrb5.confファイルで設定できます。
(http://web.mit.edu/Kerberos/krb5-devel/doc/user/user_config/k5login.html より引用)

接続確認

ssh ${target name}

パスワードを要求されずに接続できれば成功です。

余談

複数のprincipalを使い分ける

あるprincipalのTGTをもらうには、

kinit ${princ}

とします。この時、別のprincipalのTGTを

kinit ${another princ}

で要求すると、先ほどもらったTGTが上書きされます。このことは、

klist

が常に最大一つまでのkrb/\${REALM}@\${REALM}チケットしか持たないことから推察できます。

よって、一人のユーザーが持ちうるTGTは(kinitを使う限りは)一つだけであり、そのため、ssh接続するときに使用するprincipalを指定する必要がないのです。

SSHの再インストール (SSH1を使いたい人向け)

<重要> ssh kerberosと検索したときに上位に出てくるhttps://www.oreilly.com/library/view/linux-security-cookbook/0596003919/ch04s14.htmlhttps://nnc3.com/mags/Networking2/ssh/ch11_04.htm には、SSHを--with-kerberos5付きでコンパイルするよう書いてありますが、不要です。SSH1でKerberos認証を行うときには、サーバーで

/etc/sshd_conf
KerberosAuthentication yes

という設定をします。この設定を使うには--with-kerberos5が必要なのでしょうが、SSH2ではその代わりに

/etc/sshd_conf
GSSAPIAuthentication yes

という設定が使えます。そして、これでもKerberos(を使った)認証ができます。この方法は、少なくとも僕の環境ではデフォルトで使えました。

とはいえ、僕はそれに気付かずにSSHの再インストールを行なったのでその時のメモは以下に残しておきます。

標準でついてくるsshやsshdはKerberos付きでコンパイルされていないことが多いため、自分でコンパイルしたものを代わりに使う必要があります。

# 依存するライブラリ等のインストール
sudo apt install gcc zlib1g-dev libssl-dev libpam0g-dev libkrb5-dev
# ソースコードのダウンロード
wget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/openssh-7.9p1.tar.gz
# 解凍
tar xf openssh-7.9p1.tar.gz

最新のソースコードは、https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/ とかで配布されています。必ずportableの物を使ってください。

コンパイルして、インストールします。

cd openssh-7.9p1
# autoconf
./configure --with-kerberos5 --with-pam
make

# 既存のプログラムを必要なら消す
sudo rm /usr/bin/ssh* /usr/sbin/sshd

sudo make install

# systemd unitファイルの書き換え
sudo -e /lib/systemd/system/ssh.service
15
17
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
15
17