OpenLDAPでEC2のOSユーザ認証させてみた
こんにちは。 インフラストラクチャー部の山下です。
最近、チームのリーダーになってからあまりターミナルを触っていなかったのですが、久々にがっつり触れたので記事にしてみました。
(この記事は mediba Advent Calendar 2016 の15日目です。)
入退社や配属の変更などがあり、EC2のユーザアカウントの管理は頭が痛い問題です。
弊社ではお手製のツールを使って管理を行っていますが、今回はEC2上にLDAPサーバを立ててLDAP認証で各EC2にログインする検証を行ったのでご紹介します。
LDAPとは?
Lightweight Directory Access Protoclの略で、ディレクトリサービスにアクセスするためのプロトコルです。
LDAPサーバ内にあるデータベースでユーザなどを一元管理できます。
要件
- クライアントのEC2(以降クライアント)にはSSHの鍵もユーザも登録せず、LDAPのスキーマ内にあるSSH鍵を使用してログインする
- ユーザによって入れるインスタンスを制御する
- sudoが可能なグループもLDAPで管理する
構成
今回は検証目的なので冗長化などは考慮していません。
同一VPC内にLDAPサーバとクライアント3インスタンスがあるだけです。
各インスタンス同士でLDAPのポート(389/tcp)の通信を許可してあるものとします。
環境
- AmazonLinux 2016.09
- slapd 2.4.40
- OpenSSH_6.6.1p1
- Sudo version 1.8.6p3
構築
では、実際に構築していきましょう。
LDAPサーバ側
1. パッケージのインストール
$ sudo yum install openldap-servers openldap-clients pam_ldap openssh-ldap
2. デフォルトデータの削除
$ sudo rm -rf /etc/openldap/slapd.d/* /var/lib/ldap/*
3. slapd.confファイルの作成
OpenLDAP2.4以降slapd.confを使用した設定は非推奨となっており、OLC(On-Line Config)にて設定することが推奨となりました。
OLCを用いると、再起動なしでLDAPの設定変更が可能となりますが、スキーマの追加/修正など行う際にLDIFを作成してインポートする必要があり手間なので、
今回はslapd.confを使用して設定を行います。
$ sudo cp /usr/share/openldap-servers/slapd.conf.obsolete /etc/openldap/slapd.conf
修正した箇所は以下のとおりです。
$ diff -u /usr/share/openldap-servers/slapd.conf.obsolete /etc/openldap/slapd.conf
--- /usr/share/openldap-servers/slapd.conf.obsolete 2016-08-16 21:31:54.000000000 +0000
+++ /etc/openldap/slapd.conf 2016-12-09 06:19:30.004239349 +0000
@@ -15,6 +15,8 @@
include /etc/openldap/schema/openldap.schema
include /etc/openldap/schema/ppolicy.schema
include /etc/openldap/schema/collective.schema
+include /etc/openldap/schema/sudo.schema
+include /etc/openldap/schema/openssh-lpk-openldap.schema
# Allow LDAPv2 client connections. This is NOT the default.
allow bind_v2
@@ -77,6 +79,14 @@
# security ssf=1 update_ssf=112 simple_bind=64
# Sample access control policy:
+access to attrs=userPassword
+ by dn="cn=Manager,dc=example,dc=com" write
+ by self write
+ by anonymous auth
+ by * none
+access to *
+ by self write
+ by * read
# Root DSE: allow anyone to read it
# Subschema (sub)entry DSE: allow anyone to read it
# Other DSEs:
@@ -98,16 +108,16 @@
# rootdn can always read and write EVERYTHING!
# enable on-the-fly configuration (cn=config)
-database config
-access to *
- by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
- by * none
+#database config
+#access to *
+# by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" manage
+# by * none
# enable server status monitoring (cn=monitor)
database monitor
access to *
by dn.exact="gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth" read
- by dn.exact="cn=Manager,dc=my-domain,dc=com" read
+ by dn.exact="cn=Manager,dc=example,dc=com" read
by * none
#######################################################################
@@ -115,16 +125,16 @@
#######################################################################
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"
# Cleartext passwords, especially for the rootdn, should
# be avoided. See slappasswd(8) and slapd.conf(5) for details.
# Use of strong authentication encouraged.
# rootpw secret
-# rootpw {crypt}ijFYNcSNctBYg
+rootpw {SSHA}xxxxxxxxxxxxxxx
※ rootpwはslappasswdコマンドで生成したパスワードを記述します。
4. slapd.confを読み込むようにする
/etc/sysconfig/ldap
に以下の文字列を追加します。
SLAPD_OPTIONS="-f /etc/openldap/slapd.conf"
5. ldap.confの編集
以下の記述を行うことで、ldapaddやldapsearch時にホスト名の指定などが不要になります。/etc/openldap/ldap.conf
に以下を追加します。
BASE dc=example,dc=com
URI ldapi://xx.xx.xx.xx/
6. スキーマファイルの配置
スキーマを配置してincludeすることにより、SSHの鍵やsudoの属性などを登録する事ができるようになります。
$ sudo cp /usr/share/doc/openssh-ldap-6.6.1p1/openssh-lpk-openldap.schema /etc/openldap/schema/.
$ sudo cp /usr/share/doc/sudo-1.8.6p3/schema.OpenLDAP /etc/openldap/schema/sudo.schema
7. slapdの起動
$ sudo service slapd start
$ sudo chkconfig slapd on
8. 初期データの作成
$ vim init.ldif
# ベースドメイン
dn: dc=example,dc=com
dc: example
o: example
objectClass: dcObject
objectClass: organization
# User OU
dn: ou=Users,dc=example,dc=com
ou: Users
objectClass: organizationalUnit
# 管理者
dn: cn=Manager,dc=example,dc=com
objectClass: organizationalRole
objectClass: simpleSecurityObject
cn: Manager
userPassword: {SSHA}xxxxxxxxx
# Group OU
dn: ou=Group,dc=example,dc=com
objectClass: organizationalUnit
ou: Group
# admin Unix Group
dn: cn=admin,ou=Group,dc=example,dc=com
objectclass: posixGroup
cn: admin
gidNumber: 1000
# developer Unix Group
dn: cn=developer,ou=Group,dc=example,dc=com
objectclass: posixGroup
cn: developer
gidNumber: 1001
# sudo OU
dn: ou=SUDOers,dc=example,dc=com
objectClass: organizationalUnit
ou: SUDOers
# sudo defaults setting
dn: cn=defaults,ou=SUDOers,dc=example,dc=com
objectclass: top
objectclass: sudoRole
cn: defaults
sudoOption: !root_sudo
sudoOption: !lecture
sudoOption: log_host
sudoOption: log_year
sudoOption: syslog=local3
sudoOption: logfile=/var/log/sudo.log
sudoOption: ignore_dot
sudoOption: ignore_local_sudoers
sudoOption: timestamp_timeout=0
# %adminグループのsudo許可
dn: cn=%admin,ou=SUDOers,dc=example,dc=com
objectClass: top
objectClass: sudoRole
cn: %admin
sudoUser: %admin
sudoHost: ALL
sudoCommand: ALL
# yu-yamashita
dn: uid=yu-yamashita,ou=Users,dc=example,dc=com
objectClass: account
objectClass: posixAccount
objectClass: ldapPublickey
uid: yu-yamashita
cn: Yuki Yamashita
uidNumber: 1000
gidNumber: 1000
homeDirectory: /home/yu-yamashita
userPassword: {SSHA}xxxxxxxxx
sshPublicKey: ssh-rsa AAAABxxxxxxxxxxxxxxx
description: system_a_admin
description: system_b_admin
loginShell: /bin/bash
# numasawa
dn: uid=numasawa,ou=Users,dc=example,dc=com
objectClass: account
objectClass: posixAccount
objectClass: ldapPublickey
uid: numasawa
cn: numasawa
uidNumber: 1001
gidNumber: 1001
homeDirectory: /home/numasawa
userPassword: {SSHA}xxxxxxxxx
sshPublicKey: ssh-rsa AAAABxxxxxxxxxxxxxxx
description: system_a_admin
loginShell: /bin/bash
# r-adachi
dn: uid=r-adachi,ou=Users,dc=example,dc=com
objectClass: account
objectClass: posixAccount
objectClass: ldapPublickey
uid: r-adachi
cn: r-adachi
uidNumber: 1002
gidNumber: 1001
homeDirectory: /home/r-adachi
userPassword: {SSHA}xxxxxxxxx
sshPublicKey: ssh-rsa AAAABxxxxxxxxxxxxxxx
description: system_b_admin
loginShell: /bin/bash
このLDIFでは
- ベースドメイン(大元の入れ物)を作成
- Userという組織単位(OU)を作成
- LDAP管理者ユーザを作成
- Groupという組織単位(OU)を作成
- adminというUnixグループ
- developerというUnixグループ
- sudoという組織単位(OU)
- sudoの設定(ここではadminグループに属すユーザのみsudo可能)
- ついでにyu-yamashitaユーザも作成し、adminグループに所属させ、system_a_adminとsystem_b_adminというdescriptionを付与する
- ついでにnumasawaユーザも作成し、developerグループに所属させ、system_a_adminというdescriptionを付与する
- ついでにr-adachiユーザも作成し、developerグループに所属させ、system_b_adminというdescriptionを付与する
というデータを記述しています。
9. LDIFの投入
上記で作成したデータをLDAPに登録します。
$ sudo ldapadd -x -D "cn=Manager,dc=example,dc=com" -W -f init.ldif
10. データの確認
ゆとりなのでGUIから確認します。
Apache Directory Studioというツールをインストールし開きます。
SecurityGroupで389/tcpを開けるのをお忘れなく。
- File -> New を選択し、LDAP Connectionを選択しNextをクリック
- ConnectionNameは任意の値、HostNameにインスタンスのIPを入力しNextをクリック
- Bind DN or User に
cn=Manager,dc=example,dc=com
を入力 - Passwordはldifを作成する際に記述したパスワードを入力しFinishをクリック
RootDSE -> dc=example,dc=com
を辿っていくと、データが登録されていることがわかります。
以上でサーバ側の設定は完了です。
LDAPクライアント側
1. LDAPクライアントインストール
$ sudo yum install openldap-clients nss-pam-ldapd openssh-ldap
2. LDAP認証にする
$ sudo authconfig --enableldap --enableldapauth --ldapserver=xx.xx.xx.xx --ldapbasedn="dc=example,dc=com" --enablemkhomedir --update
3. Group情報をLDAPから取得するようにする
/etc/nslcd.conf
に以下を追加する
base group ou=Group,dc=example,dc=com
4. SSHの鍵をLDAPサーバから取得する
/etc/ssh/sshd_config
に以下の記述を行う
AuthorizedKeysCommand /usr/libexec/openssh/ssh-ldap-wrapper
AuthorizedKeysCommandUser root
5. sudoできるようにする
/etc/sudo-ldap.conf
に以下を追加する
uri ldap://xx.xx.xx.xx/
sudoers_base ou=SUDOers,dc=example,dc=com
bind_timelimit 120
host xx.xx.xx.xx
base dc=example,dc=com
次に /etc/pam.d/su
内の auth required pam_wheel.so use_uid
のコメントを外す。
最後に /etc/nsswitch.conf
に以下を追加する
sudoers: ldap files
6. SSHの認証にLDAPを使うようにする
$ vim /etc/ssh/ldap.conf
uri ldap://localhost/
base dc=example-dev,dc=com
ssl no
7. sshdの再起動
$ sudo service sshd restart
ここまで行えば、SSHの鍵もユーザもクライアントに作らずログイン出来るようになります。
また、adminグループに所属するユーザのみsudoが可能です。
ログインできるホストに制限をかける
次に、ユーザによってログインできるホストを分けてみたいと思います。
上述のLDIFではsystem_a_admin, system_b_adminという3種類のattributeを記述しています。
ここでは、以下のようなルールで制限を行います。
- system_a_admin: system_aのインスタンスのみログインできる
- system_b_admin: system_bのインスタンスのみログインできる
上記のルールの場合、先ほど作ったユーザは以下のようになります。
- yu-yamashita: 全てのインスタンスにログインできる
- numasawa: system_aのインスタンスのみログインできる
- r-adachi: system_bのインスタンスのみログインできる
以下はsystem_aのインスタンスの設定を行います。
system_bも同じ要領で設定が行なえます。
1. /etc/pam_ldap.confの編集
/etc/pam_ldap.conf
に以下を追記する
pam_filter description=system_a_admin
nss_base_passwd ou=Users,dc=example,dc=com?sub?description=system_a_admin
nss_base_shadow ou=Users,dc=example,dc=com?sub?description=system_a_admin
nss_base_group ou=Group,dc=example,dc=com?sub?objectClass=posixGroup
2. /etc/ssh/ldap.confの編集
/etc/ssh/ldap.conf
に以下を追記する
pam_filter description=system_a_admin
3. /etc/nslcd.confの編集
/etc/nslcd.conf
に以下を追記する
filter passwd (description=system_a_admin)
filter shadow (description=system_a_admin)
filter group (objectClass=posixGroup)
上記の設定を行うことで、yu-yamashita,numasawaはログイン可能、r-adachiはログイン不可能となります。
まとめ
むしろ構築手順のようになってしまいました…。
OSアカウント管理は結構頭の痛い課題ですが、この記事が少しでもお役に立てば幸いです。
