先日、SSSD+LDAPネタを連投しましたが、ローカルアカウントとLDAPの2つのデータにおいて、少々変わった挙動がありましたのでメモっておきます。
今回の話はローカルユーザー/ローカルグループどちらも対象なのですが、発見時の操作がグループだったのと、作成・削除がよりシンプルなのでグループメインで説明していきます。OSはCentOS7です。
問題点
とある環境でGIDを指定せずに groupadd した時、新規グループのGIDは、ローカルグループ・LDAPグループの両方の中から、利用中の最大GID+1 で登録されました。別の環境で同様に groupadd すると、GIDは 1000番で登録されました。
この違いを探るのに最初はディストリビューションの違いを疑いましたが、軽く調べているうちに nslcd の場合は最大GID+1 を、SSSD+LDAPの場合は 1000番以降で使われていない最も若いIDを使うことがわかったので、groupadd について潜っていくことにしました。
ローカルアカウントのIDについて復習
と、その前にIDのルールについて復習しておきます。数値はディストリビューションによって異なりますが、/etc/login.defs に使用可能なIDの範囲が記述されています。
1 2 3 4 5 6 7 8 |
UID_MIN 1000 UID_MAX 60000 GID_MIN 1000 GID_MAX 60000 SYS_UID_MIN 201 SYS_UID_MAX 999 SYS_GID_MIN 201 SYS_GID_MAX 999 |
この場合、一般ユーザーは 1000~60000 のIDを使い、システムユーザーは 201~999 を。0~200 はOSの予約済みIDってことで、予約済みIDはこちらで確認できます。
1 2 3 4 5 6 7 8 9 10 11 |
NAME UID GID HOME SHELL PACKAGES root 0 0 /root /bin/bash setup bin 1 1 /bin /sbin/nologin setup daemon 2 2 /sbin /sbin/nologin setup ~snip~ systemd-journal-gateway 191 191 / /sbin/nologin systemd gnats ? ? ? ? gnats, gnats-db listar ? ? ? ? listar nfsnobody 65534 65534 /var/lib/nfs /sbin/nologin nfs-utils # Note: nfsnobody is 4294967294 on 64-bit platforms (-2) |
システムユーザーは useradd/groupadd の時に -r (–system) オプションを付与することで指定できるのですが、何故か man useradd, man groupadd に載っていなく、不便な感じです。-r オプションについてはこちらに載っています。
一般ユーザーは 1000 から始まって 1001, 1002 と使用され、システムIDは 999 から使用され、998, 997 と下っていくことになります。
SSSDでLDAPデータの最終IDを考慮していない原因を探る
こんな時はソースを見るのが手っ取り早いので、groupadd コマンド周辺を追っていきます。まずは、ソースを取得します。groupadd は shadow-utils パッケージに入っています。
1 2 3 |
yum install yum-utils cd /usr/local/src yumdownloader --source shadow-utils |
次に解凍します。
1 2 3 4 5 |
rpm -i shadow-utils-4.1.5.1-18.el7.src.rpm cd ~/rpmbuild/SOURCES/ yum install bzip2 tar xf shadow-4.1.5.1.tar.bz2 cd shadow-4.1.5.1 |
src/gruopadd.c を見ると、find_new_gid で新GIDを取得しています。
611 612 613 614 615 |
if (!gflg) { if (find_new_gid (rflg, &group_id, NULL) < 0) { exit (E_GID_IN_USE); } } |
次に find_new_gid を見ると、システムグループか一般グループかで分けて処理されていますが、一般の方ではこんな感じで使用中IDを整理するついでに group_id を決定し、
138 139 140 141 142 143 144 145 146 147 148 149 150 |
} else { group_id = gid_min; setgrent (); while ((grp = getgrent ()) != NULL) { if ((grp->gr_gid >= group_id) && (grp->gr_gid <= gid_max)) { group_id = grp->gr_gid + 1; } /* create index of used GIDs */ if (grp->gr_gid <= gid_max) { used_gids[grp->gr_gid] = true; } } endgrent (); |
その後で、GID_MAXを超えていたら再度範囲内に空きがないかチェックして確定する、といった流れになっています。
普通に考えて、GID群を取得している getgrent (Man page of GETGRENT) に問題があるのかな?と調べてみたらすぐ見つかりました。
この説明では Red Hat Enterprise Linux 5/6 での SSSD となっていますが、CentOS7 も対象ということになります。ということで、原因はわかりました。useradd の場合は getpwent で取得していて、こちらも同様に SSSD ではLDAPから取得してくれないようです。
そのため、getent passwd コマンドでユーザー一覧を取得してみると、nslcd ならLDAPユーザーもリストされるのに、SSSD だとローカルユーザーだけになります。
しかし、作成の際の既存チェックはしっかりされていて、LDAPに既存の名前やIDで作成しようとするとエラーになります。これは、既存チェックが getgrnam と getgrgid でされているからで、こちらはSSSDの場合もLDAPデータを見てくれる、ということなのでしょう。
運用にあたって
この問題による影響範囲がどのくらいかはわかりませんが、useradd/groupadd 周りだけでいえば、結局どちらの挙動が良いかというと、実はSSSD+LDAPのような LDAPデータを考慮しないMaxIDの取り方 が使いやすいと思っています。なぜなら、LDAPデータは日々追加される可能性が高いものであり、ローカルに末尾IDで作成した、その近い未来にLDAP管理画面などで追加されると、IDが被ってしまうからです。そうなると、所属グループの表示が意図しないモノになったり、ファイルシステムに対する動作にも不安要素ができてしまいます。
だからというわけではないですが、設定の利便性も相まって気分的には SSSD+LDAP がより優位になったのですが、わりと信頼していた /etc/nsswitch.conf も結局はカーネルのシステムコールなどの周辺システム次第なんだなと実感したのが、今回の大きな収穫であり、
エンジニアサイクルの1つとして、たまには普段関わらないソースを読み込むというのが、良いエンジニアストレッチになるなと感じた次第であります。