2011年12月12日月曜日

RHEL6.1~系の OpenLDAP (slapd.conf) で SSH 公開鍵配布

お世話になっております、SIOS 那賀です。

先日掲載した「SIOS OSS Tech: RHEL6 系での OpenLDAP サーバと PAM 認証」で、slapd.conf 型から cn=config 型への移行を頑張ろうと決意したのですが、「Convert schema files for import - OpenLdap: Switch to dynamic config backend (cn=config) - Zarafa wiki」を読んでアホらしくなってしまい、slapd.conf へ戻すことにしました。今回の主題は OpenLDAP を使った SSH キーの配布なのですが、その前段として、前回の手順一通りを、slapd.conf を用いるものに書き換えて再掲しようと思います。cn=config 方式にも有用な場面は(多分)あるかと思われますので、既存の記事も、そのまま残しておきます。

ディレクトリ・サーバの設定

まずはいつものように、SELinux と iptables の無効化です。実運用のサーバでは、運用ポリシーに従って、それぞれ適切に設定してください。

[root@dirserv ~]# setenforce permissive
[root@dirserv ~]# getenforce
Permissive
[root@dirserv ~]# service iptables stop
iptables: ファイアウォールルールを消去中:                  [  OK  ]
iptables: チェインをポリシー ACCEPT へ設定中filter         [  OK  ]
iptables: モジュールを取り外し中:                          [  OK  ]
[root@dirserv ~]#

サーバと、操作用のクライアントをインストールします。

[root@dirserv ~]# yum install -y openldap-clients openldap-servers

もし動いているようであれば、slapd サービスを停止させてから、slapd.d/ ディレクトリを削除します。これで、slapd は slapd.conf から設定を読むようになります。

[root@dirserv ~]# service slapd status
slapd は停止しています
[root@dirserv ~]# rm -fr /etc/openldap/slapd.d/ /var/lib/ldap/*

"Obsolete" のファイルを元に、slapd.conf を作成します。

[root@dirserv ~]# slappasswd -s secret2
{SSHA}Z42Qdb/r/YAw0QRD8vI/SYcLgoxtzF4l
[root@dirserv ~]# cp \
 /usr/share/openldap-servers/slapd.conf.obsolete \
 /etc/openldap/slapd.conf
[root@dirserv ~]# chown ldap.ldap /etc/openldap/slapd.conf
[root@dirserv ~]# chmod 0600 /etc/openldap/slapd.conf
[root@dirserv ~]# vi /etc/openldap/slapd.conf
[root@dirserv ~]# diff -uNr \
 /usr/share/openldap-servers/slapd.conf.obsolete \
 /etc/openldap/slapd.conf
--- /usr/share/openldap-servers/slapd.conf.obsolete
+++ /etc/openldap/slapd.conf
@@ -64,9 +64,9 @@
 # /etc/pki/tls/certs, running "make slapd.pem", and fixing permissions on
 # slapd.pem so that the ldap user or group can read it.  Your client software
 # may balk at self-signed certificates, however.
-# TLSCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
-# TLSCertificateFile /etc/pki/tls/certs/slapd.pem
-# TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem
+TLSCACertificateFile /etc/pki/tls/certs/ca-bundle.crt
+TLSCertificateFile /etc/pki/tls/certs/slapd.pem
+TLSCertificateKeyFile /etc/pki/tls/certs/slapd.pem

 # Sample security restrictions
 #      Require integrity protection (prevent hijacking)
@@ -95,14 +95,26 @@
 #
 # rootdn can always read and write EVERYTHING!

+access to attrs=userPassword
+  by dn="cn=Manager,dc=example,dc=com" write
+  by self write
+  by anonymous auth
+  by * none
+
+access to *
+  by dn.exact="cn=Manager,dc=example,dc=com" write
+  by self write
+  by * read
+
 #######################################################################
 # ldbm and/or bdb database definitions
 #######################################################################

 database       bdb
-suffix         "dc=my-domain,dc=com"
+suffix         "dc=example,dc=com"
 checkpoint     1024 15
-rootdn         "cn=Manager,dc=my-domain,dc=com"
+rootdn         "cn=Manager,dc=example,dc=com"
+rootpw         {SSHA}Z42Qdb/r/YAw0QRD8vI/SYcLgoxtzF4l
 # Cleartext passwords, especially for the rootdn, should
 # be avoided.  See slappasswd(8) and slapd.conf(5) for details.
 # Use of strong authentication encouraged.

起動します。今回はテストですので、インデックスも BDB の設定も、そのまま放置します。実運用では、パフォーマンスやバックアップにも気をつかってあげてください。

[root@dirserv ~]# service slapd start
slapd を起動中:                                            [  OK  ]

以下は任意ですが、サーバの挙動が分かりやすいように、ログの出力も設定しておきます。slapd は local4 のファシリティで syslog 出力をするのですが、せっかくの RHEL6 系の rsyslog ですので、アプリケーション名 ("slapd") でログを振り分けるようにしてみます。実運用では、ログが溢れないように、ローテートの設定もしておいてください。

[root@dirserv ~]# cp /etc/rsyslog.conf /etc/rsyslog.conf.orig
[root@dirserv ~]# vi /etc/rsyslog.conf
[root@dirserv ~]# diff -uNr /etc/rsyslog.conf.orig /etc/rsyslog.conf
--- /etc/rsyslog.conf.orig
+++ /etc/rsyslog.conf
@@ -76,3 +76,6 @@
 # remote host is: name/ip:port, e.g. 192.168.0.1:514, port optional
 #*.* @@remote-host:514
 # ### end of the forwarding rule ###
+
+!slapd
+*.* /var/log/slapd.log
[root@dirserv ~]# service rsyslog reload
Reloading system logger...                                 [  OK  ]

アクセスできるかどうか、試してみます。

[root@dirserv ~]# ldapsearch -x -D "cn=Manager,dc=example,dc=com" \
 -b "dc=example,dc=com" -w secret2
# extended LDIF
#
# LDAPv3
# base  with scope subtree
# filter: (objectclass=*)
# requesting: ALL
#

# search result
search: 2
result: 32 No such object

# numResponses: 1

良いようであれば、認証用のデータを入れて行きます。根元から作って行きます。

[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 <<EOF
dn: dc=example,dc=com
dc: example
objectClass: dcObject
objectClass: organization
o: Example Corp.

dn: cn=Manager,dc=example,dc=com
cn: Manager
objectClass: organizationalRole

dn: o=Users,dc=example,dc=com
o: Linux Users
objectClass: organization

dn: ou=People,o=Users,dc=example,dc=com
ou: People
objectClass: organizationalUnit

dn: ou=Group,o=Users,dc=example,dc=com
ou: Group
objectClass: organizationalUnit
EOF
adding new entry "dc=example,dc=com"

adding new entry "cn=Manager,dc=example,dc=com"

adding new entry "o=Users,dc=example,dc=com"

adding new entry "ou=People,o=Users,dc=example,dc=com"

adding new entry "ou=Group,o=Users,dc=example,dc=com"

ちょっと間違うと、途中まで作ったところでエラーになり、そこまでの分が半端に残ります。failure atomic ではありません…、トランザクションがあればいいのにね。今回はテストですので、そんな時は、以下のように根元からガバッと再帰で消してしまってください。

[root@dirserv ~]# ldapdelete -v -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 -r "dc=example,dc=com"

ユーザ (foo と bar) を追加します。OpenLDAP のクライアント側 (SSSD, pam_ldap, nss-pam-ldapd 等) は、文書にはあまりキチンと書かれていないのですが (せいぜい /usr/share/doc/nss-pam-ldapd-0.7.5/README くらいか)、デフォルトで、クラス "posixAccount" や "posixShadow" を持つノードを、ユーザ情報として取りにきます。ですので、実はツリー構造は割と恣意的に決めてしまっても問題ありません。

[root@dirserv ~]# pass=$(slappasswd -s secret3)
[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 <<EOF
dn: uid=foo,ou=People,o=Users,dc=example,dc=com
objectClass: top
objectClass: posixAccount
objectClass: account
gecos: ldap system users
cn: foo
uid: foo
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/foo
loginShell: /bin/bash
userPassword: $pass
EOF
adding new entry "uid=foo,ou=People,o=Users,dc=example,dc=com"

[root@dirserv ~]# pass=$(slappasswd -s secret4)
[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 <<EOF
dn: uid=bar,ou=People,o=Users,dc=example,dc=com
objectClass: top
objectClass: posixAccount
objectClass: account
gecos: ldap system users
cn: bar
uid: bar
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/bar
loginShell: /bin/bash
userPassword: $pass
EOF
adding new entry "uid=bar,ou=People,o=Users,dc=example,dc=com"

グループ (foo と bar) を追加します。こちらも同様に、クライアントはデフォルトで、クラス "posixGroup" を持つノードを探索に来ます。今回は、昨今の Linux ディストリビューションの構成と同様に、UPG (User Private Group) 式で、ユーザごとに個別に同名のグループを割り当てますが、LDAP 的には、共通のグループを割り当てる方が一般的かも知れません。

[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 <<EOF
dn: cn=foo,ou=Group,o=Users,dc=example,dc=com
cn: foo
objectClass: posixGroup
gidNumber: 1000
userPassword: {CRYPT}x
EOF
adding new entry "cn=foo,ou=Group,o=Users,dc=example,dc=com"

[root@dirserv ~]# ldapadd -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 <<EOF
dn: cn=bar,ou=Group,o=Users,dc=example,dc=com
cn: bar
objectClass: posixGroup
gidNumber: 1001
userPassword: {CRYPT}x
EOF
adding new entry "cn=bar,ou=Group,o=Users,dc=example,dc=com"

一般ユーザから、他人のパスワードが見えないことを確認しておきます。

[root@dirserv ~]# ldapsearch -x \
 -D "uid=foo,ou=People,o=Users,dc=example,dc=com" \
 -b "dc=example,dc=com" -w secret3 "(objectClass=posixAccount)"
[root@dirserv ~]# ldapsearch -x \
 -D "uid=bar,ou=People,o=Users,dc=example,dc=com" \
 -b "dc=example,dc=com" -w secret4 "(objectClass=posixAccount)"

認証依頼元の Linux ホストの設定

こちらも同様に、SELinux と iptables の無効化です。実運用のサー (以下テンプレ略)。

[root@linhost ~]# setenforce permissive
[root@linhost ~]# getenforce
Permissive
[root@linhost ~]# service iptables stop
iptables: ファイアウォールルールを消去中:                  [  OK  ]
iptables: チェインをポリシー ACCEPT へ設定中filter         [  OK  ]
iptables: モジュールを取り外し中:                          [  OK  ]
[root@linhost ~]#

RHEL5 系の nss_ldap から、RHEL6 系では PAM は pam_ldap に、NSS は nss_ldap からフォークした nss-pam-ldapd へと分離しており、また、それらを束ねる SSSD 抽象化レイヤ (詳細) が入っていたりと、設定ファイルが多岐に渡り、もはや手作業では手に負えなくなっているので、authconfig コマンドを使用します。

同時に、TLS (389 番ポートでの "STARTTLS" による接続) も設定します。authconfig では、認証局ナシでの TLS 設定ができないので、設定をした後で、ちょっとだけ手を入れる必要があります。

SSSD を用いる手順:

[root@linhost ~]# authconfig --disableforcelegacy --enablemkhomedir \
 --enableldap --enableldapauth --enableldaptls \
 --ldapserver=dirserv.vnat --ldapbasedn="dc=example,dc=com" \
 --update
sssd を起動中:                                             [  OK  ]
oddjobd を起動中:                                          [  OK  ]
[root@linhost ~]# cat <<EOF >> /etc/sssd/sssd.conf
ldap_tls_reqcert = never
EOF
[root@linhost ~]# cat <<EOF >> /etc/nslcd.conf
tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt
tls_checkpeer no
EOF
[root@linhost ~]# service sssd restart
sssd を停止中:                                             [  OK  ]
sssd を起動中:                                             [  OK  ]

旧来の (legacy な) 手順:

[root@linhost ~]# authconfig --enableforcelegacy --enablemkhomedir \
 --enableldap --enableldapauth --enableldaptls \
 --ldapserver=dirserv.vnat --ldapbasedn="dc=example,dc=com" \
 --update
nslcd を起動中:                                            [  OK  ]
oddjobd を起動中:                                          [  OK  ]
[root@linhost ~]# cat <<EOF >> /etc/nslcd.conf
tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt
tls_reqcert no
EOF
[root@linhost ~]# service nslcd restart
nslcd を停止中:                                            [  OK  ]
nslcd を起動中:                                            [  OK  ]
[root@linhost ~]# cat <<EOF >> /etc/pam_ldap.conf
tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt
tls_checkpeer no
EOF

では、動作を確認してみます。

[root@linhost ~]# id foo
uid=1000(foo) gid=1000(foo) 所属グループ=1000(foo)
[root@linhost ~]# su - foo
Creating home directory for foo.
[foo@linhost ~]$ su - bar
パスワード: secret4
Creating home directory for bar.
[bar@linhost ~]$ id
uid=1001(bar) gid=1001(bar) 所属グループ=1001(bar) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
[bar@linhost ~]$ 

良いようです。

SSH キー配布の設定

ディレクトリサーバに、SSH のキーを保持する LDAP スキーマが必要なので、oepnssh-ldap をインストールします。

[root@dirserv ~]# yum install -y openssh-ldap

slapd の設定を修正し、スキーマを追加します。キーへのアクセス権限の設定も追加しておきます。

[root@dirserv ~]# cp /etc/openldap/slapd.conf \
 /etc/openldap/slapd.conf.orig
[root@dirserv ~]# vi /etc/openldap/slapd.conf
[root@dirserv ~]# diff -uNr /etc/openldap/slapd.conf.orig \
 /etc/openldap/slapd.conf
--- /etc/openldap/slapd.conf.orig
+++ /etc/openldap/slapd.conf
@@ -15,6 +15,7 @@
 include                /etc/openldap/schema/openldap.schema
 include                /etc/openldap/schema/ppolicy.schema
 include                /etc/openldap/schema/collective.schema
+include                /usr/share/doc/openssh-ldap-5.3p1/openssh-lpk-openldap.schema

 # Allow LDAPv2 client connections.  This is NOT the default.
 allow bind_v2
@@ -101,6 +102,11 @@
   by anonymous auth
   by * none

+access to attrs=sshPublicKey
+  by dn="cn=Manager,dc=example,dc=com" write
+  by self write
+  by * none
+
 access to *
   by dn.exact="cn=Manager,dc=example,dc=com" write
   by self write
[root@dirserv ~]# service slapd restart
slapd を停止中:                                            [  OK  ]
slapd を起動中:

ここでキーを作成し、登録します。

[root@dirserv ~]# ssh-keygen -f ~/.ssh/id_rsa -N ""
Generating public/private rsa key pair.
Your identification has been saved in /root/.ssh/id_rsa.
Your public key has been saved in /root/.ssh/id_rsa.pub.
The key fingerprint is:
(中略)
[root@dirserv ~]# key=$(cat ~/.ssh/id_rsa.pub)
[root@dirserv ~]# ldapmodify -x -D "cn=Manager,dc=example,dc=com" \
 -w secret2 <<EOF
dn: uid=foo,ou=People,o=Users,dc=example,dc=com
changetype: modify
add: objectClass
objectClass: ldapPublicKey
-
add: sshPublicKey
sshPublicKey: $key
EOF

以上で、LDAP サーバ側は終了です。

sshd サーバの設定

sshd サーバが LDAP サーバから、対象ユーザの公開鍵を取得できるように設定します。まずは、sshd の外部プログラムを追加でインストールします。

[root@linhost ~]# yum install -y openssh-ldap

sshd が、外部プログラムからキーを取得するように設定します。上記パッケージに含まれる HOWTO.ldap-keys では "AuthorizedKeysCommand" のパラメータをダブルクォートでくくっていますが、そのように記述すると、パーサはこれを処理しないため、動きません。クォートせずに、"/" で始まる絶対パスを指定してください。なお、stat(2) でファイルの有無を確認しているので、行末にスペース等が入っていると、これまた動きません。総じて不親切です。あまり使われていないのか。

[root@linhost ~]# cp /etc/ssh/sshd_config /etc/ssh/sshd_config.orig
[root@linhost ~]# vi /etc/ssh/sshd_config
[root@linhost ~]# diff -uNr \
 /etc/ssh/sshd_config.orig \
 /etc/ssh/sshd_config
--- /etc/ssh/sshd_config.orig
+++ /etc/ssh/sshd_config
@@ -45,10 +45,10 @@
 #MaxSessions 10

 #RSAAuthentication yes
-#PubkeyAuthentication yes
+PubkeyAuthentication yes
 #AuthorizedKeysFile    .ssh/authorized_keys
-#AuthorizedKeysCommand none
-#AuthorizedKeysCommandRunAs nobody
+AuthorizedKeysCommand /usr/libexec/openssh/ssh-ldap-wrapper
+AuthorizedKeysCommandRunAs root

 # For this to work you will also need host keys in /etc/ssh/ssh_known_hosts
 #RhostsRSAAuthentication no

上記の外部プログラムが利用する設定を記述します。パスワードを含むので、パーミッションに注意してください。設定後、"/usr/libexec/openssh/ssh-ldap-wrapper foo" のように実行してキーが返って来ないようであれば、設定をしくじっています。(※注意: openssh-ldap-5.3p1-52 の ssh-ldap-wrapper には、設定ファイルのパーサにバグがあり、"tls_checkpeer" の設定が効きません。直すか、ちゃんとした証明書を用意するか、TLS をオフにするしかないようです。下記では、オフにしています)

# TLS が使えなくとも、公開鍵ならば平文で飛んでも問題ありませんから、なまじ rootdn のパスワードを指定するよりも、anonymous でキーを取得できるようにした方が良いかも知れません

[root@linhost ~]# cat > /etc/ssh/ldap.conf <<EOF
uri ldap://dirserv.vnat/
base dc=example,dc=com
binddn cn=Manager,dc=example,dc=com
bindpw secret2
host dirserv.vnat

ssl no
#ssl start_tls
tls_cacertdir /etc/openldap/cacerts
tls_cacertfile /etc/pki/tls/certs/ca-bundle.crt
tls_checkpeer no
EOF
[root@linhost ~]# chmod 600 /etc/ssh/ldap.conf

sshd を再起動します。

[root@linhost ~]# service sshd restart
sshd を停止中:                                             [  OK  ]
sshd を起動中:                                             [  OK  ]

以上です。試しに、~/.ssh/id_rsa.pub を持っているホストからログインしてみます。

[root@dirserv etc]# ssh -o RSAAuthentication=yes \
 -o PasswordAuthentication=no -l foo linhost.vnat
Last login: Thu Dec XX 15:54:49 2011 from dirserv.vnat
[foo@linhost ~]$

良いようですね。では。

0 件のコメント:

コメントを投稿