ICTSC2018 本戦 問題解説: LDAPが動かない
皆さんはLDAPを使ったことがありますか?
LDAPとは Lightweight Directory Access Protocol の略で、ディレクトリサービスのためのプロトコルです。ディレクトリサービスというのはその名前の通りディレクトリのようなツリー状の情報を管理するサービスで、その特徴からリソースの場所・設定などの情報を一元管理するのに用いられています。
とりわけ大きな組織においてはこのような情報を管理することにおいて大きなメリットがあります。管理すべき機器やユーザの数が膨大になってきたとき、必要に応じて素早く情報にアクセスできることが要求されるためです。
多くの企業ではディレクトリサービスを用いて情報を管理しており、ディレクトリサービスを提供するためのソフトウェアとしてはActive Directoryが代表的です。優れたGUIと長年Microsoftがメンテナンスしてきたことによる実績があり、何よりWindowsマシンを管理するのに長けているからです。
一方で、昔ながらのOpenLDAPを用いる場合もあります。OpenLDAPはLDAPのオープンソース実装の一つであり、高機能なActive Directoryなどに比べて軽量・高速に動作します。先に挙げたActive DirectoryもLDAPをベースに実装されており、LDAPへの理解を深めることはディレクトリサービスを知る上で欠かせないと言えます。
今回出題したトラブルは、LDAPに関する基本的な操作・知識を問う問題でした。LDAPを運用した経験がないと時間内に解くのはなかなか難しかったのではないかと思います。
問題文
最近LDAPを導入して、GitLabの認証とサーバへのSSHをLDAP経由で行えるように設定したのだがどうもうまく動いていないようだ。 原因を突き止めて、GitLabにログインできるようにしてほしい。必要に応じてサーバの設定を変えても構わないが、なるべくセキュリティレベルが下がらないようにしてほしい。また、admin (cn=admin,dc=finals,dc=ictsc) とoperator (cn=operator,ou=users,dc=finals,dc=ictsc) のパスワードが脆弱なので、可能であれば指定したものに変更してほしい。
なお、すべてのサーバはLDAPに登録してあるSSH鍵で入れるようになっているはずだが、こちらも同様に動いてないのでLDAPサーバのみにログインできるアカウントとLDAPに登録したSSH秘密鍵を用意した。必要に応じて使ってほしい。
問題のゴール状態
GitLabにLDAPユーザのoperator (パスワード変更済み) でログインに成功する
解説
今回の問題に登場するサーバは全部で2つです。
- LDAPサーバ (dc.finals.ictsc)
- GitLabサーバ (gitlab.finals.ictsc)
それぞれにSSSDがインストールされており、sss_ssh_authorizedkeys
コマンドを用いてLDAPに登録されている公開鍵でログインできるようになっているはずが、SSHログインできなくなっている状態がスタートです。
また、GitLabのLDAPログインも同様に動かない (SSL errorが発生する) ので、これらを直してほしいという内容です。
初期状態をまとめると以下の図のようになります。
図で×印がついている部分がLDAPで認証を行う部分であり、これらをすべて復旧するのがゴールです。
問題のゴール自体はGitLabでLDAPログインできるようになることとなっていますが、実際には認証を直してGitLabサーバへのSSHログインができるようにならないとトラブルシューティングが行えないため、まずはSSHログインを復活させるのが1つ目のゴールになります。
SSL証明書の有効化
GitLabでLDAPログインを試みると以下のようなエラーメッセージが出ます。
Could not authenticate you from Ldapmain because “Ssl connect syscall returned=5 errno=0 state=sslv2/v3 read server hello a”.
これはそもそもSSL接続が確立できていないということなので、opensslで確認してみます。
~$ openssl s_client -connect 192.168.2.10:636 CONNECTED(00000003) 140350580467344:error:140790E5:SSL routines:SSL23_WRITE:ssl handshake failure:s23_lib.c:177: --- no peer certificate available --- No client certificate CA names sent --- SSL handshake has read 0 bytes and written 289 bytes --- New, (NONE), Cipher is (NONE) Secure Renegotiation IS NOT supported Compression: NONE Expansion: NONE SSL-Session: Protocol : TLSv1.2 Cipher : 0000 Session-ID: Session-ID-ctx: Master-Key: Key-Arg : None PSK identity: None PSK identity hint: None SRP username: None Start Time: 1552100789 Timeout : 300 (sec) Verify return code: 0 (ok) ---
これを見ればわかるとおり、そもそも証明書が設定されていないことがわかります。
しかし /etc/ldap/slapd.conf
を見ると TLSCACertificateFile=/etc/ldap/ssl/ca.crt
などの設定がされているため本来は有効化されているはずですが、これらのパラメータは少なくともdebian系のslapdでは正しく動きません。よって適切なLDIFを書いて、手動で有効化してやる必要があります。
dn: cn=config changetype: modify add: olcTLSCACertificateFile olcTLSCACertificateFile: /etc/ldap/ssl/ca.crt - add: olcTLSCertificateKeyFile olcTLSCertificateKeyFile: /etc/ldap/ssl/dc.finals.ictsc.key - add: olcTLSCertficateFile olcTLSCertficateFile: /etc/ldap/ssl/dc.finals.ictsc.crt
SSL証明書のパーミッションも正しくしておきましょう。
~$ ls -lhat /etc/ldap/ssl/ total 24K drwxr-xr-x 2 root root 4.0K Feb 17 02:07 . -rw-r--r-- 1 root root 2.0K Feb 17 02:07 ca.crt -rw-r--r-- 1 root root 5.9K Feb 17 02:07 dc.finals.ictsc.crt -rw------- 1 root root 1.7K Feb 17 02:07 dc.finals.ictsc.key drwxr-xr-x 7 root root 4.0K Feb 17 02:07 .. ... ~$ ls -lhat /etc/ldap/ssl/ total 24K drwxr-xr-x 2 root root 4.0K Feb 17 02:07 . -rw-r--r-- 1 root openldap 2.0K Feb 17 02:07 ca.crt -rw-r--r-- 1 root openldap 5.9K Feb 17 02:07 dc.finals.ictsc.crt -rw-r----- 1 root openldap 1.7K Feb 17 02:07 dc.finals.ictsc.key drwxr-xr-x 7 root root 4.0K Feb 17 02:07 ..
先ほどのLDIFを olcSSL.ldif
という名前で保存し、以下のコマンドで適用します。
sudo ldapadd -Y EXTERNAL -H ldapi:/// -f olcSSL.ldif
SSSDで公開鍵が取得できない
サーバの/etc/ssh/sshd_config
を見ると、AuthorizedKeysCommandディレクティブにsss_ssh_authorizedkeys
が設定されていることがわかります。しかし試しにこれを実行してみても公開鍵が出力される様子がありません。
~$ /usr/bin/sss_ssh_authorizedkeys operator ~$
これが原因でSSHログインができないことがわかりますが、なぜ公開鍵が取得できないのでしょうか。LDAPの設定を見直しても公開鍵はちゃんと登録されています。
sss_ssh_authorizedkeys
コマンドには実はデバッグオプションが存在します(これはhelpでも出てきません)。--debug 10
という引数をつけてコマンドを呼び出すと、詳しいエラーメッセージを見ることができます。
~$ ~$ sss_ssh_authorizedkeys operator --debug 10 (Wed Mar 27 20:25:46:414921 2019) [sss_ssh_authorizedkeys] [main] (0x0040): sss_ssh_format_pubkey() failed (22): Invalid argument
このメッセージから、登録されている公開鍵のフォーマットが正しくないと予想できます。もう一度LDAPに登録されている鍵をみてみましょう。
~$ ldapvi -D 'cn=admin,dc=finals,dc=ictsc' -b 'dc=finals,dc=ictsc' ... loginShell: /bin/bash sshPublicKey:; ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJodlzzbHLCCfldHfG7xKlA4tl6t118hAdjbzuZIYCJELLFTwctlFVOBgZHs4JkT5Cgm7eK1VXL99w7SapNzhMs= operator@dc^M\
よく注意して見ると、公開鍵の末尾に改行が含まれていることがわかります。試しに改行を消してみるとどうなるでしょうか。
~$ sss_ssh_authorizedkeys operator ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJodlzzbHLCCfldHfG7xKlA4tl6t118hAdjbzuZIYCJELLFTwctlFVOBgZHs4JkT5Cgm7eK1VXL99w7SapNzhMs= operator@dc
ちゃんと取得することができました。秘密鍵でSSHログインが可能なことも確認できます。
~$ ssh -i files/id_ecdsa [email protected] Last login: Sun Feb 17 03:38:42 2019 from 192.168.2.1 Could not chdir to home directory /home/operator: No such file or directory operator@dc:/$
これで登録してある公開鍵でログインができるようになりました。
パスワードの変更
GitLabのトラブルシューティングに移る前にパスワードの変更をしておきます。
ldapviを用いてパスワードを変更しようとするとエラーが発生し変更できなかったり、phpLDAPadminにおいてはエラーすら出なかったりします。(この場合も依然として変更はできていません)
このような場合はアクセスコントロールを疑います。/etc/ldap/ldif/olcAccess.ldif
が置いてあるのでこの中身をみてみましょう。
dn: olcDatabase={1}mdb,cn=config changetype: modify replace: olcAccess # password should not be readable and only user can update olcAccess: to attrs=userPassword by self =sw # uid, uidNumber, gidNumber should not be updated olcAccess: to attrs=uid,uidNumber,gidNumber by self read by users read # for ssh login olcAccess: to attrs=sshPublicKey by self write by group.exact="cn=servers,ou=sgroups,dc=finals,dc=ictsc" read # for sudo olcAccess: to dn.children="ou=sudoers,dc=finals,dc=ictsc" by group.exact="cn=servers,ou=sgroups,dc=finals,dc=ictsc" read # not matched olcAccess: to * by self read by users read by anonymous auth by * none
注目すべきはuserPassword
に対して設定してある項目です。by self =sw
はsearchとwriteをユーザー自身に許可する設定なので大丈夫なはずですが、by anonymous auth
がついていないのでbindができないようになっています。
よってolcAccess.ldif
のuserPassword
にby anonymous auth
を追加してあげるとパスワードの変更が可能になります。
GitLabの証明書
SSSDのエラーを修正したことでGitLabにログインが可能になりますが、GitLabのログインページは依然としてエラーを出します。エラーメッセージを読むと証明書周りのエラーということがわかるので、/etc/gitlab/gitlab.rb
の中身を確認します。
LDAPの設定部分にverify_certificates: true
とあるので、証明書がチェックに引っかかっていることが原因のようです。これを回避するためには証明書のチェックを無効にするのが一つの手ですが、セキュリティの観点からはあまり好ましい対策とは言えません。証明書 (dc.finals.ictsc) をシステムのチェーンに登録して有効な証明書とするのがベターです。
~$ sudo -s ~# cd /usr/share/ca-certificates/ ~# openssl s_client -showcerts -connect dc.finals.ictsc:636 2>/dev/null | openssl x509 > rootCA.crt ~# echo "rootCA.crt" >> /etc/ca-certificates.conf ~# update-ca-certificates
GitLab上のユーザブロック解除
証明書のエラーを解決するとようやくLDAP認証が動くようになります。しかしoperatorユーザはなぜかログインできず、ブロックされている旨のメッセージが出ます。
これはすでにローカルでoperatorという名前のユーザが登録されていたことが原因ですが、解除するためにはadminアカウントが必要になります。operatorユーザでSSHログインするとsudoでrootになることができるので、GitLabのrails consoleに直接接続してパスワードを書き換えることができます。
~# gitlab-rails console production user = User.where(id: 1).first user.password = 'strongpassword' user.password_confimation = 'strongpassword' user.save!
これでadminとしてログインして、
admin area
にあるユーザの設定でLDAPのoperatorユーザを有効にするとログインできるようになります。採点基準
GitLabサーバにログインできるようになるのが一つ目のゴールなので、ここまで到達した場合は満点の50%が得られます。
- LDAPユーザのパスワードを変更する (20%)
- GitLabサーバにログインできるようになる (30%)
- GitLabでLDAP認証を使えるようにする
- LDAPをTLS通信に対応させる (30%)
- operatorユーザのブロックを解除する (20%)
講評
今回の問題の一番のポイントはLDAPそのもではなく、Undocumentedな仕様を適切に探し出すことができるかという点にありました。OpenLDAPのslapd.conf
が意図した通りに動かない、SSSDが解釈できる公開鍵のフォーマットに隠された仕様があるなど、ソフトウェアは思わぬところで予想に反する挙動を示すことがあります。事前に知識を持っている場合はすぐに気づくことができますが、多くの場合はそうではありません。今回はLDAP+SSSD+GitLabという、意外と一般的だが普段は使ってなさそうな技術を意図的に選びました。
もっとも高得点を獲得したのはLDAPのSSL証明書の設定を解決したチームでした。残念ながらGitLabサーバへSSHできるところまでたどり着くチームはいませんでしたが、コンテスト時間がこれだけ短く、問題数が多いことを考えると仕方ないのかなと思います。また、得点の高さをみて手をつけなかったチームも結構いたのではないでしょうか。トラコンでは必ずしも問題を解ききる必要はないので、部分点解法を大量に稼いでいくのも一つの戦略です。