こんにちは。 インフラストラクチャー部の山下です。 最近、チームのリーダーになってからあまりターミナルを触っていなかったのですが、久々にがっつり触れたので記事にしてみました。 (この記事は 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アカウント管理は結構頭の痛い課題ですが、この記事が少しでもお役に立てば幸いです。