OpenLDAPとSSSDを利用したユーザー認証


# これはなに

[OSSTech Advent Calendar 2019](https://qiita.com/advent-calendar/2019/osstech)の5日目の記事です。

普段は自分が使っている技術の備忘録のような記事しか書いてないのですが、
一応認証に関する記事になるつもりです。


嘘ですまた備忘録です。


今回は`CentOS 8`をインストールした後、ユーザー認証をOpenLDAPと連携したSSSDで行うことを目標にします。
主にSSSDのお話です。

# SSSDのインストール

``` console
# dnf install sssd sssd-tools sssd-dbus
```

SSSDを動かすだけなら`sssd`だけで大丈夫です。
`sssd-tools`と`sssd-dbus`をインストールすると、`sssctl`というコマンドが利用できるようになり、
SSSDの設定を確認する時などに使えます。

`sssctl`は偶然見つけて使ってみたのですがわりと良い感じ。

# 連携先となるOpenLDAPの準備

OpenLDAPを準備する方法はなんでもいいんですが、気をつけることがいくつかあります。

* LDAPS通信が必須

  SSSDのプロバイダとしてLDAPを利用するとき、LDAPS通信が必須になります。

* sudo設定用のスキーマを追加する

  sudo設定は`sudoRole`というobjectClass内で定義されている属性から参照します。
  LDAPで`objectClass: sudoRole`が利用出来るようにしておいてください。

特に`sudoRole`のスキーマを追加するのを見落としていて、
しばらく「ログイン認証は通るけどsudoコマンドを使うとエラーになる」という状態でハマっていました。

`sudoRole`用のスキーマは`sudo`パッケージの中に同梱されていました。(これがなかなか見つからない...)

``` console
$ rpm -ql sudo | grep OpenLDAP
/usr/share/doc/sudo/schema.OpenLDAP
```

コピーするなりしてOpenLDAPにこのスキーマを適用してください。

# LDAPのツリー

今回用意したLDAPのツリーは以下の通りです。

```
dc=example,dc=com
├── cn=sssd
├── ou=Users
│   └── uid=user1
│       ├── objectClass=person
│       ├── objectClass=posixAccount
│       ├── objectClass=top
│       ├── cn=user1
│       ├── sn=user
│       ├── givenName=test
│       ├── uidNumber=10000
│       └── gidNumber=10000
├── ou=Groups
│   └── cn=group1
│       ├── objectClass=posixGroup
│       ├── cn=group1
│       └── gidNumber=10000
└── ou=SUDOers
    └── cn=%group1
        ├── objectClass=top
        ├── objectClass=sudoRole
        ├── cn=%group1
        ├── sudoCommand=ALL
        ├── sudoHost=ALL
        └── sudoUser=%group1
```

<details><summary>LDIFファイルのサンプル</summary><div>

```
dn: dc=example,dc=com
objectClass: domain
dc: example

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

dn: ou=Groups,dc=example,dc=com
objectClass: organizationalUnit
ou: Groups

dn: ou=SUDOers,dc=example,dc=com
objectClass: organizationalUnit
ou: SUDOers

dn: cn=sssd,dc=example,dc=com
objectClass: applicationProcess
objectClass: simpleSecurityObject
cn: sssd
userPassword: password

dn: uid=user1,ou=Users,dc=example,dc=com
objectClass: top
objectClass: person
objectClass: inetOrgPerson
objectClass: organizationalPerson
objectClass: posixAccount
uid: user1
cn: user1
sn: user1
uidNumber: 10000
gidNumber: 10000
givenName: test
homeDirectory: /home/user1
loginShell: /bin/zsh
userPassword: password
description: Test Account

dn: cn=group1,ou=Groups,dc=example,dc=com
objectClass: top
objectClass: posixGroup
cn: group1
gidNumber: 10000
description: Test Group
memberUid: group1

dn: cn=%group1,ou=SUDOers,dc=example,dc=com
objectClass: top
objectClass: sudoRole
cn: %group1
sudoUser: %group1
sudoHost: ALL
sudoCommand: ALL
```

</div></details>

あくまで必要最小限のLDIFですので、必要に応じてエントリーや属性を追加・変更してください。

これでLDAP側の準備は完了です。
気をつけることとして思いつくのは以下の通りです。

* cn=sssdのエントリーにアクセス権が必要
* ユーザーエントリーは`posixAccount`(別のクラスでも出来るが面倒くさい)
* グループエントリーは`posixGroup`(別のクラスでも出来るが略)
* sudo設定用のエントリーは`sudoRole`

# SSSDの設定

ここまでで連携先のLDAPサーバーの準備が出来たと思うので、いよいよSSSDの設定を行います。

SSSDの設定ファイルは`/etc/sssd/`配下に保存します。今回は`/etc/sssd/sssd.conf`というファイルを編集して、SSSDの設定をおこなって行こうと思います。このファイルは初期インストール時には存在しないので、エディタなどで適宜作成してください。
権限が適切でないと起動に失敗するので、ファイルの作成後に以下のコマンドを実行してください。

```
# chown root:root /etc/sssd/sssd.conf
# chmod 600 /etc/sssd/sssd.conf
```

## ユーザー認証が出来るようになるまで

`/etc/sssd/sssd.conf`を以下の用に編集します。

``` INI:/etc/sssd/sssd.conf
[sssd]
debug_level = 4
services = nss, pam, ifp
domains = mydomain

[nss]
filter_groups = root
filter_users = root

[pam]

[domain/mydomain]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap

enumerate = True
cache_credentials = True
case_sensitive = false

ldap_uri = ldap://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_default_bind_dn = cn=sssd,dc=example,dc=com

ldap_tls_reqcert = never
ldap_id_use_start_tls = True
```

LDAPサーバーへの接続設定は`[domain/mydomain]`のセクションに記述します。この`mydomain`の部分は任意の名前に変更可能です。実際にSSSDが利用するドメインを`[sssd]`セクション内の`domains`で指定します。

今回は自己証明書のLDAPS通信を利用しているので、`ldap_tls_reqcert`を`never`にしていますが、
基本的にはこの項目は設定せず、証明書が正しいことを検証するべきです。

このままでは`cn=sssd`のバインドパスワードが無いのでSSSDは利用出来ません。
`ldap_default_authtok`というパラメータを追記し、パスワードをそのままいれれば動きますがよろしくありません。

そこで`sss_obfuscate`というコマンドを利用しましょう。
このコマンドは`sssd-tools`パッケージに含まれています。
以下のコマンドを実行してください。

``` console
# sss_obfuscate -d mydomain
Enter password:  <- cn=sssdのパスワードを入力
Re-enter password:
```

そうすると`sssd.conf`に自動的に難読化されたパスワードが設定されます。

``` INI:/etc/sssd/sssd.conf
[sssd]
debug_level = 4
services = nss, pam, ifp
domains = mydomain

[nss]
filter_groups = root
filter_users = root

[pam]

[domain/mydomain]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap

enumerate = True
cache_credentials = True
case_sensitive = false

ldap_uri = ldap://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_default_bind_dn = cn=sssd,dc=example,dc=com

ldap_tls_reqcert = never
ldap_id_use_start_tls = True
ldap_default_authtok_type = obfuscated_password
ldap_default_authtok = xxxxxxxxxxx難読化済みのパスワードxxxxxxxxxxxxxx
```

ネームサービスと認証サービスにSSSDを利用するようにします。
`CentOS 8`では`authselect`というコマンドを利用します。
以下のコマンドを実行してください。

``` console
# authselect select sssd with-sudo --force
```

この状態でSSSDを再起動します。

```console
# systemctl restart sssd
```

`sssctl`コマンドなどでLDAPサーバーへの接続が出来ているか確認しましょう。

``` console
# sssctl domain-status mydomain
Online status: Online

```

`getent`コマンドや`id`コマンドでLDAPサーバー上のユーザー情報が正常に取れているか確認しましょう。

```
# getent passwd user1
user1:*:10000:10000:Test Account:/home/user1:/bin/zsh
# id user1
uid=10000(user1) gid=10000(group1) groups=10000(group1)
```

これでログイン時などパスワード認証にLDAPサーバーで設定したパスワードが利用できれば、ユーザー認証の設定は完成です。

## sudoが出来るようになるまで

上記の設定のままだと、user1でログインしたあと、`sudo`コマンドが利用できません。
これでは不便なので`sudo`の設定も追加しましょう。

`sudo`の設定を追記した`sssd.conf`が以下になります。


``` INI:/etc/sssd/sssd.conf
[sssd]
debug_level = 4
services = nss, pam, ifp, sudo
domains = mydomain

[sudo]

[nss]
filter_groups = root
filter_users = root

[pam]

[domain/mydomain]
id_provider = ldap
auth_provider = ldap
chpass_provider = ldap
sudo_provider = ldap

enumerate = True
cache_credentials = True
case_sensitive = false

ldap_uri = ldap://ldap.example.com
ldap_search_base = dc=example,dc=com
ldap_default_bind_dn = cn=sssd,dc=example,dc=com

ldap_tls_reqcert = never
ldap_id_use_start_tls = True
ldap_default_authtok_type = obfuscated_password
ldap_default_authtok = xxxxxxxxxxx難読化済みのパスワードxxxxxxxxxxxxxx
```

`sudo`の設定エントリーはデフォルトで`ou=sudoers,<ldap_search_base>`を検索するので、[LDAPのツリー](#LDAPのツリー)で作成したツリーを利用している場合は特に設定する項目はありません。
別のツリーを指定したい場合は`ldap_sudo_search_base`というパラメータで指定してください。

SSSDを再起動します。

``` console
# systemctl restart sssd
```

LDAPサーバー上のユーザーでログインした後、`sudo -i`などを実行して正常に`sudo`コマンドが実行できれば完了です。

# おわり

以前触ってみた時は`sudo`が出来ずに挫折したのですが、今回なんとか目的の設定ができました。

`authconfig`で全部コマンドで設定していたときと比べると設定ファイルを書く手間がありますが、
一度使った設定ファイルを使い回せば新しい環境でもすぐに使えるようになるので、結果的に楽でした。

今後はこのファイルを使いまわす予定なので来年くらいにはまた忘れているんだろうな...