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アカウント管理は結構頭の痛い課題ですが、この記事が少しでもお役に立てば幸いです。