(ヽ´ω`) < OpenLDAP + SSSDでLinuxログインアカウント一元管理がさっぱりわからん - 2. ユーザエントリとか作成 -
(ヽ´ω`) < ユーザとグループとsudoers設定
前回のエントリでOpenLDAPの初期設定が完了し、 dc=tsugihagi,dc=local
に対してデータを挿入できるようになったので、実際にログイン可能なユーザと、ユーザをまとめるグループ、さらにsudoersに該当するエントリを作成していく。
どのようにOUを設計していくかは、組織構成によって色んなパターンがあると思うので、ここではすごく簡単に下記のように作成していく。
- ユーザは
ou=Users,dc=tsugihagi,dc=local
配下にフラットに作成していく。 (例cn=sre-user1,ou=Users,dc=tsugihagi,dc=local
) - グループは
ou=Groups,dc=tsugihagi,dc=local
配下にフラットに作成していく。 (例 `cn=SRE,ou=Groups,dc=tsugihagi,dc=local' , 'cn=Dev,ou=Groups,dc=tsugihagi,dc=local ) - sudoersは
ou=Sudoers,dc=tsugihagi,dc=local
配下に〜
(ヽ´ω`) < 想定するサーバ構成とユーザ情報、アクセス権
ここでは下記の条件を想定してユーザ情報を作成していく。
- サーバは3台 Produciton, Staging, Development
- グループは3つ managers, sre, dev
- ユーザは4人 sre-manager, sre-senior-member, sre-member, dev-member
- それぞれのユーザのアクセス権は下記の通り
- ◎ ログイン可 sudo可
- ○ ログイン可 sudo不可
- ☓ ログイン不可
ユーザ | グループ | Production | Staging | Development |
---|---|---|---|---|
sre-manager | managers, sre | ◎ | ◎ | ◎ |
sre-senior-member | sre | ○ | ◎ | ◎ |
sre-member | sre | ○ | ○ | ◎ |
dev-member | dev | ☓ | ○ | ◎ |
上記の表より、下記の通りのポリシーを設定する。
- managersグループは全てのサーバに対してログイン/sudo可
- sreグループは全てのサーバに対してログイン可、加えてDevelopmentサーバにsudo可
- devグループはStagingサーバに対してログインのみ可、Developmentサーバに対してログイン/sudo可
- sre-senior-memberはsreグループの権限に加え、ユーザ個人に対してStagingサーバに対してsudo可を追加
(ヽ´ω`) < ベースドメインとOU
まずはベースDNの設定とOUの作成。
ベースDNは前回設定した dc=tsugihagi,dc=local
を使用する。(base.ldif)
dn: dc=tsugihagi,dc=local objectClass: dcObject objectClass: organization dc: tsugihagi o: tsugihagi dn: ou=Users,dc=tsugihagi,dc=local objectClass: organizationalUnit ou: Users dn: ou=Groups,dc=tsugihagi,dc=local objectClass: organizationalUnit ou: Groups dn: ou=Sudoers,dc=tsugihagi,dc=local objectClass: organizationalUnit ou: Sudoers
(ヽ´ω`) < ユーザ
自分が一番最初にOpenLDAPでユーザの一元管理を試してみたとき(もう10年近く前かな…)に感じた疑問が 「(ヽ´ω`) < そもそもLDAPのエントリは任意の場所(DITツリーでの位置)に任意の形式で作成できるんだけど、その中で何をもってユーザ情報と見做すの?」 だった記憶。
そんな昔の自分への回答。
ユーザは posixAccount
, inetOrgPerson
, ldapPublicKey
の3つのオブジェクトクラスをもつエントリとなる。場所についてはどこでもOK。(今回の場合は認証情報を見に来るサーバ側で検索する位置・範囲を設定するため)
それぞれに必要な属性を、各オブジェクトクラスのスキーマ定義を見て確認していく。
posixAccount
のスキーマ定義は下記の通り。
objectclass ( 1.3.6.1.1.1.2.0 NAME 'posixAccount' DESC 'Abstraction of an account with POSIX attributes' SUP top AUXILIARY MUST ( cn $ uid $ uidNumber $ gidNumber $ homeDirectory ) MAY ( userPassword $ loginShell $ gecos $ description ) )
MUSTなのが cn
, uid
, uidNumber
, gidNumber
, homeDirectory
なのでこれは必須。
SSSDでは loginShell
もうまいこと扱ってくれるので、こちらも入力しておく。 userPassword
は公開鍵認証を使用するので不要。 gecos
と descrption
は、まぁ入れておいてもいいかな。
続いて inetOrgPerson
のスキーマ定義。
# inetOrgPerson # The inetOrgPerson represents people who are associated with an # organization in some way. It is a structural class and is derived # from the organizationalPerson which is defined in X.521 [X521]. objectclass ( 2.16.840.1.113730.3.2.2 NAME 'inetOrgPerson' DESC 'RFC2798: Internet Organizational Person' SUP organizationalPerson STRUCTURAL MAY ( audio $ businessCategory $ carLicense $ departmentNumber $ displayName $ employeeNumber $ employeeType $ givenName $ homePhone $ homePostalAddress $ initials $ jpegPhoto $ labeledURI $ mail $ manager $ mobile $ o $ pager $ photo $ roomNumber $ secretary $ uid $ userCertificate $ x500uniqueIdentifier $ preferredLanguage $ userSMIMECertificate $ userPKCS12 ) )
実は、見ての通り全て属性がMAYなので inetOrgPerson
は必須ではない。が、ユーザ管理という面から便利な属性を持っているので、オブジェクトクラスとして追加する。
今回は例として mail
を入力しておく。
また上位のオブジェクトクラスであるorganizationalPersonのMUST属性として sn
が指定されているので、こちらについても必須となる。
ここまでは、いわゆる一般的なOpenLDAPでユーザ情報を保持する属性の定義。
今回はパスワード認証ではなく、公開鍵認証のための公開鍵をOpenLDAP側で保持する。そのための属性が sshPublicKey
で、その値を保持するオブジェクトクラスが ldapPublicKey
となる。
# octetString SYNTAX attributetype ( 1.3.6.1.4.1.24552.500.1.1.1.13 NAME 'sshPublicKey' DESC 'MANDATORY: OpenSSH Public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 ) # printableString SYNTAX yes|no objectclass ( 1.3.6.1.4.1.24552.500.1.1.2.0 NAME 'ldapPublicKey' SUP top AUXILIARY DESC 'MANDATORY: OpenSSH LPK objectclass' MUST ( sshPublicKey $ uid ) )
必要な属性が設定され、作成したユーザ追加用LDIFはこんな感じ。(users.ldif)
dn: cn=sre-manager,ou=Users,dc=tsugihagi,dc=local objectClass: inetOrgPerson objectClass: posixAccount objectClass: ldapPublicKey uid: sre-manager uidNumber: 10001 gidNumber: 10000 homeDirectory: /home/user1 loginShell: /bin/bash mail: sre-manager@tsugihagi.local sn: sre gecos: sre manager sshPublicKey: ssh-rsa 11111 sshPublicKey: ssh-rsa 22222 sshPublicKey: ssh-rsa 33333 dn: cn=sre-senior-member,ou=Users,dc=tsugihagi,dc=local objectClass: inetOrgPerson objectClass: posixAccount objectClass: ldapPublicKey uid: sre-senior-member uidNumber: 10002 gidNumber: 10001 homeDirectory: /home/sre-senior-member loginShell: /bin/bash mail: sre-senior-member@tsugihagi.local sn: sre gecos: sre-senior-member sshPublicKey: ssh-rsa 44444 dn: cn=sre-member,ou=Users,dc=tsugihagi,dc=local objectClass: inetOrgPerson objectClass: posixAccount objectClass: ldapPublicKey uid: sre-member uidNumber: 10003 gidNumber: 10001 homeDirectory: /home/sre-member loginShell: /bin/bash mail: sre-member@tsugihagi.local sn: sre gecos: sre-member sshPublicKey: ssh-rsa 55555 dn: cn=dev-member,ou=Users,dc=tsugihagi,dc=local objectClass: inetOrgPerson objectClass: posixAccount objectClass: ldapPublicKey uid: dev-member uidNumber: 10004 gidNumber: 10002 homeDirectory: /home/dev-member loginShell: /bin/bash mail: dev-member@tsugihagi.local sn: dev gecos: dev-member sshPublicKey: ssh-rsa 66666
(ヽ´ω`) < グループ
グループについては posixGroup
オブジェクトクラスを持つエントリを作成する。
objectclass ( 1.3.6.1.1.1.2.2 NAME 'posixGroup' DESC 'Abstraction of a group of accounts' SUP top STRUCTURAL MUST ( cn $ gidNumber ) MAY ( userPassword $ memberUid $ description ) )
cn
, gidNumber
は必須なので入力。
memberUid
もMAYとなっているが所属しているユーザを指定するために必要なので、ユーザが存在しないグループを定義する場合以外は設定が必要。
ということで実際のエントリはこんな感じ。(groups.ldif)
dn: cn=sre,ou=Groups,dc=tsugihagi,dc=local objectClass: posixGroup gidNumber: 10001 cn: sre memberUid: sre-manager memberUid: sre-senior-member memberUid: sre-member dn: cn=dev,ou=Groups,dc=tsugihagi,dc=local objectClass: posixGroup cn: dev gidNumber: 10002 memberUid: dev-member dn: cn=managers,ou=Groups,dc=tsugihagi,dc=local objectClass: posixGroup cn: managers gidNumber: 10003 memberUid: sre-manager
実はLDAPでグループを扱う際には"RFC2307 or RFC2307bis"というテーマについて考えないといけないのだが、これは独立したエントリで扱わないと長くなりそうなので… とりあえずここでは 「グループはposixGroupオブジェクトクラスを持ったエントリ内に、ユーザ名(memberUid)が列挙されて定義される」 と覚えておく。
(ヽ´ω`) < sudoers
sudoersはダウンロードした sudo.schema
に定義されている sudoRole
オブジェクトクラスを持つエントリを作成。
objectclass ( 1.3.6.1.4.1.15953.9.2.1 NAME 'sudoRole' SUP top STRUCTURAL DESC 'Sudoer Entries' MUST ( cn ) MAY ( sudoUser $ sudoHost $ sudoCommand $ sudoRunAs $ sudoRunAsUser $ sudoRunAsGroup $ sudoOption $ sudoOrder $ sudoNotBefore $ sudoNotAfter $ description ) )
cn
のみがMUSTとなっているが、sudoersファイルに記載されるような、こんな内容
## Allows people in group wheel to run all commands %wheel ALL=(ALL) NOPASSWD: ALL
を実現するには sudoHost
, sudoCommand
, sudoOption
のような他の属性を入力していく必要がある。
ここで NOPASSWD: ALL
を実現するためには
sudoOption: NOPASSWD
と書けば良さそうに思えるが、これはNG。正しくは
sudoOption: !authenticate
と書く必要がある。
困ったことに、この設定は sudoers.ldap
のmanにも記載がなかったので、どうしたものか戸惑ったのだが、下記のページがすごく参考になった。
このページに下記のような記載があったので、
The sudoers package contains a perl-script called sudoers2ldif, this script is provided in the /usr/share/doc/sudoers/ directory.
早速使ってみようと思って調べてみたが見当たらない。
sudo
パッケージの内容を見てみると、 cvtsudoers
と何やらそれらしい名前が。
# rpm -ql sudo <-- snip --> /usr/bin/cvtsudoers <-- snip --> /usr/share/man/man1/cvtsudoers.1.gz <-- snip -->
man cvtsudoers
してみるとDescriptionには
cvtsudoers can be used to convert between sudoers security policy file formats. The default input format is sudoers. The default output format is LDIF. It is only possible to convert a sudoers file that is syntactically correct.
とのことなので、早速試してみる。
-b
オプションでベースとなるDNを指定してやる。ここでは ou=Sudoers,dc=tsugihagi,dc=local
の配下にエントリを作成していくので、こんな感じ。
# cvtsudoers -b "ou=Sudoers,dc=tsugihagi,dc=local" /etc/sudoers dn: cn=defaults,ou=Sudoers,dc=tsugihagi,dc=local objectClass: top objectClass: sudoRole cn: defaults description: Default sudoOption's go here sudoOption: !visiblepw sudoOption: always_set_home sudoOption: match_group_by_gid sudoOption: always_query_group_plugin sudoOption: env_reset sudoOption: env_keep=COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS sudoOption: env_keep+=MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE sudoOption: env_keep+=LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES sudoOption: env_keep+=LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE sudoOption: env_keep+=LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY sudoOption: secure_path=/sbin:/bin:/usr/sbin:/usr/bin dn: cn=root,ou=Sudoers,dc=tsugihagi,dc=local objectClass: top objectClass: sudoRole cn: root sudoUser: root sudoHost: ALL sudoRunAsUser: ALL sudoCommand: ALL sudoOrder: 1 dn: cn=%wheel,ou=Sudoers,dc=tsugihagi,dc=local objectClass: top objectClass: sudoRole cn: %wheel sudoUser: %wheel sudoHost: ALL sudoRunAsUser: ALL sudoCommand: ALL sudoOrder: 2 dn: cn=ec2-user,ou=Sudoers,dc=tsugihagi,dc=local objectClass: top objectClass: sudoRole cn: ec2-user sudoUser: ec2-user sudoHost: ALL sudoRunAsUser: ALL sudoOption: !authenticate sudoCommand: ALL sudoOrder: 3
3つ目のエントリを見て分かる通り、sudoersファイルでの指定と同様に %
をプレフィックスとして付与することで、グループに対してsudo権を設定することができる。
ここで指定するグループは /etc/group
で管理されるグループは当然として、前項で定義したOpenLDAP上のグループに対しても適用が可能。
cvtsudoers
の出力は上記のとおりだが、ローカルで定義されているユーザ・グループ対するsudoersはローカルのファイルで定義すべきで、ここでは実際に追加するエントリは下記の通りとなる。
dn: cn=production-server,ou=Sudoers,dc=tsugihagi,dc=local objectClass: sudoRole cn: production-server sudoHost: ALL sudoCommand: ALL sudoUser: %managers dn: cn=staging-server,ou=Sudoers,dc=tsugihagi,dc=local objectClass: sudoRole cn: staging-server sudoHost: ALL sudoCommand: ALL sudoUser: %managers sudoUser: sre-senior-member dn: cn=dev-server,ou=Sudoers,dc=tsugihagi,dc=local objectClass: sudoRole cn: dev-server sudoHost: ALL sudoCommand: ALL sudoUser: %managers sudoUser: %sre sudoUser: dev-member
cnの値がサーバ名になっていることがわかると思う。
権限をどの単位で管理するかは既存のポリシーや組織によってまちまちだと思うが、ここではファイルによるsudoers管理と同様に、サーバからみてどのユーザ・グループに権限を付与するか、という視点で設定する。
(ヽ´ω`) < LDIF流し込み
作成された base.ldif
, users.ldif
, groups.ldif
, sudoers.ldif
をOpenLDAPに流し込んでいく。
ここで注意、改めて olcDatabase={2}hdb,cn=config
の設定を見てもわかるとおり、 olcAccess
属性が設定されていない。
[root@ip-172-16-1-209 ~]# ldapsearch -Y EXTERNAL -H ldapi:/// -b "olcDatabase={2}hdb,cn=config" SASL/EXTERNAL authentication started SASL username: gidNumber=0+uidNumber=0,cn=peercred,cn=external,cn=auth SASL SSF: 0 # extended LDIF # # LDAPv3 # base <olcDatabase={2}hdb,cn=config> with scope subtree # filter: (objectclass=*) # requesting: ALL # # {2}hdb, config dn: olcDatabase={2}hdb,cn=config objectClass: olcDatabaseConfig objectClass: olcHdbConfig olcDatabase: {2}hdb olcDbDirectory: /var/lib/ldap olcDbIndex: objectClass eq,pres olcDbIndex: ou,cn,mail,surname,givenname eq,pres,sub olcSuffix: dc=tsugihagi,dc=local olcRootDN: cn=Manager,dc=tsugihagi,dc=local olcRootPW: {SSHA}SFlHUkAMFkH6xUPA61IEqDnTY2lN2YwF
この状況では、全てのユーザはread権限を持つが書き込み権限を持つのはRootDNのみとなる。
そのため書き込みのために発行する ldapadd に渡すオプションは下記のとおりとなる。
# ldapadd -D "cn=Manager,dc=tsugihagi,dc=local" -whogehoge -H ldap://127.0.0.1 -f base.ldif # ldapadd -D "cn=Manager,dc=tsugihagi,dc=local" -whogehoge -H ldap://127.0.0.1 -f users.ldif # ldapadd -D "cn=Manager,dc=tsugihagi,dc=local" -whogehoge -H ldap://127.0.0.1 -f groups.ldif # ldapadd -D "cn=Manager,dc=tsugihagi,dc=local" -whogehoge -H ldap://127.0.0.1 -f sudoers.ldif
実際にエントリが書き込まれたかを確認。
# ldapsearch -Y EXTERNAL -H ldapi:/// -b "dc=tsugihagi,dc=local"
ここまででOpenLDAP側の設定は完了。
続いて実際に認証が行われる側、SSHDが稼働しているサーバ側のSSSD設定を行っていく。