ansible-ldap is very simple and useful¶
OpenLDAP と仲間たち Advent Calendar 2015 24日目、クリスマスイブですね。一昨日(12/22)にEngineer All Handsという社内のイベントでLTをすることになり、「LDAPと私」というネタで ansible-ldap というモジュールの話を軽くしました。ついでにブログにでもちゃんと書いておこうかなと思い、昨日アドベントカレンダーの予定を見てみたら、空いていたので参加してみました。
概要¶
この記事の要点としては以下のとおりです。これを読んで理解できる方はその後を読む必要はありません。モジュールのソースコード内のドキュメントを読みましょう。
ansible-ldapの
ldap_entry
およびldap_attr
モジュールだけで LDAPのエントリーの追加・削除、エントリーの属性の追加、削除、置換を冪等に行うことができるldapi://
にも対応しているので slapd-config(5)の設定もできるAnsible Galaxy には登録されてないのでパッチ書いてPR送った (mergeされるかは別の話)
あと、LDAPには直接関係ありませんが、
サンプルのPlaybookを作っている際、サンプルデータをYAMLで記述するのが面倒になって、 CSVから変数を読み込めるAnsibleモジュール を作った
ansible-ldapとは¶
django-otp 、 django-auth-ldap 、 mockldap などの作者の Peter Sagerson 氏が開発された Ansib用の LDAP モジュールです。django-auth-ldapやmockldapには前職でとてもお世話になってました。 1
ldap_entry
と ldap_attr
の2つのモジュールがあります。
前者はLDAPのDITに対し、entryを追加または削除を、後者はすでに存在するエントリーの属性を追加、削除、もしくは置き換えを行うためのモジュールです。
fumiyasuさん が一日目にslapd.conf(5)を使ったPlaybookの 記事 を書かれていますが、ansible-ldapモジュールを使う場合は、slapd-config(5)を使います。DebianやUbuntuのOpenLDAPサーバのパッケージである、slapdパッケージはslapd-config(5)がデフォルトです。slapd-config(5)絡みの記事は 以前書いたもの も ある ので、そちらもご参照ください。
使い方¶
これらのモジュールを使った サンプルのPlaybook を作ったのでそれを例にして説明します。
ldap_entry¶
まずはシンプルに memberof
モジュールを追加する例です。
- name: be sure memberof module
ldap_entry:
dn: cn=module{1},cn=config
state: present
objectClass: olcModuleList
olcModulePath: /usr/lib/ldap
olcModuleLoad: memberof.la
dn
必須項目です。エントリーの追加時、
{1}
とあるindex番号は必要に応じて自動的に付加されます。しかし、cn=module,cn=config
とcn=module{1},cn=config
は別のdnになります。そのためエントリー追加後、同様にindexを指定しないで属性の変更や、エントリーの削除を行おうとすると、エントリーが見つからないためエラーになります。変更するエントリーのindexを把握しておく必要があるのでサンプルで明示的に指定しています。state
デフォルトは
present
で存在しなければ作成し、存在すれば何もしません。削除するときはabsent
を使います。これはAnsibleの他モジュールと同じ挙動です。ldap_entry
には変更という操作はありません。つまりldapadd
コマンドとldapdelete
コマンドに相当する操作だけです。ldapmodify
コマンドに相当する操作はldap_attr
で行います。objectClass
及びその他の属性必要な場合は設定します。 slapd-config(5)の属性の名称はslapd.conf(5) の設定オプション名とは微妙に異なるので、
man slapd-config(5)
でGLOBAL CONFIGURATION OPTIONS
以降のセクションを参照しましょう。
次に、suffixが dc=example,dc=org
のLDAPディレクトリに対し、oganizational unitを追加する例を見てみます。
- ldap_entry:
dn: "ou={{ item }},{{ suffix }}"
objectClass: organizationalUnit
ou: "{{ item }}"
bind_dn: "cn=admin,{{ suffix }}"
bind_pw: "{{ admin_pw }}"
state: present
with_items:
- People
- Groups
- SUDOers
server_uri
この例では省略していますが、これはデフォルトでは
ldapi:///
になります。リモートホストのLDAPサーバを対象にする場合には、LDAPのURLを指定する必要があります。start_tls
StartTLS LDAPを使うときはこのオプションを
true
にします。デフォルトではfalse
です。bind_dn
とbind_pw
slapd-config(5) の設定の時は省略することで EXTERNAL mechanism でアクセスしますが、特定のDITの操作を行うには、デフォルトでは認証が必要になります。bind用のDNを
bind_dn
で、パスワードをbind_pw
で指定します。slapd自体の設定し、特定のsuffix のDITに対し変更を行う場合、つい忘れがちなので気をつけましょう。
ldap_attr¶
アクセス権限を設定する例を見てみます。
- name: olcAccess are absent.
ldap_attr:
dn: "olcDatabase={1}{{ backend | lower }},cn=config"
name: olcAccess
state: absent
values:
- '{0}to attrs=userPassword by self write by anonymous auth by * none'
- '{1}to attrs=shadowLastChange by self write by * read'
- '{2}to * by * read'
- name: olcAccess are present.
ldap_attr:
dn: "olcDatabase={1}{{ backend | lower }},cn=config"
name: olcAccess
state: present
values:
- '{0}to attrs=userPassword,shadowLastChange
by self write
by anonymous auth
by dn="cn=admin,{{ suffix }}" write
by * none'
- '{1}to dn.base=""
by * read'
- '{2}to *
by dn="cn=admin,{{ suffix }}" write
by * read'
- '{3}to dn.subtree="{{ suffix }}"
by self read
by * read'
- '{4}to *
by * none'
この2つのタスクでは、 absent
でslapdインストール時にデフォルトで設定されるアクセス設定を一度削除し、 present
で新しく設定しています。
このやり方は面倒ですね。代わりに exact
を使えばひとつのタスクで変更できます。
- name: override olcAccess exactly
ldap_attr:
dn: "olcDatabase={1}{{ backend | lower }},cn=config"
name: olcAccess
state: exact
values:
- '{0}to attrs=userPassword,shadowLastChange
by self write
by anonymous auth
by dn="cn=admin,{{ suffix }}" write
by * none'
- '{1}to dn.base=""
by * read'
- '{2}to *
by dn="cn=admin,{{ suffix }}" write
by * read'
- '{3}to dn.subtree="{{ suffix }}"
by self read
by * read'
- '{4}to *
by * none'
name
で 変更する属性を指定し、 values
で一つもしくは一つ以上の値を指定します。複数設定できるか否かは、設定するattributeのスキーマ次第です。他のパラメータは基本的には ldap_entry
と同じです。
ansible-ldapのインストール方法¶
現状、ansible-ldapは Ansible Galaxyには登録されてません。また、Ansible Galaxyで公開できる形式になっていないため、requirements.yml に
- src: https://bitbucket.org/psagers/ansible-ldap
name: ldap
scm: hg
のように記述し、 ansible-galaxy install -p library -r requirements.yml
と実行してもインストールできません。手動で hg clone
を実行し、playbookのlibraryディレクトリを以下にモジュールをコピーする必要があります。とても面倒です。ということで、パッチ書いてPRを送っておきました。
マージされるまでの間 2 は、下記のように記述することで ansible-galaxy install
コマンドでインストールすることができます。ただし、 --no-deps
オプションが必要ですので気をつけましょう。
- src: https://bitbucket.org/mkouhei/ansible-ldap
name: ldap
scm: hg
version: for-ansible-galaxy
さらにもしAnsible Galaxyに登録されたら、おそらくこんな記述になることでしょう。
- src: psagers.ldap
C bindingとPure Python¶
今回紹介した django-auth-ldap、mock-ldapは OpenLDAPライブラリの C bindingと実装された Python-LDAP やそのPython3対応としてのforkの pyldap に依存してします。 ansible-ldapもPython-LDAPに依存しています。 3 Pure PythonでのLDAPクライアントの実装としての ldap3 は使用されていません。今回紹介する ansible-ldap も やはり Python-LDAPに依存しています。
今までに何度かLDAP用のAnsibleモジュールを書こうかな、と思ったことも何度かあったのですが 4 、slapdの設定変更に必要なのは ldapi://
(LDAP over IPC) でアクセス、操作できることなので、少なくともこの10月末まではC bindingのPython-LDAP / pyldapしかその機能があるPythonモジュールはありませんでした。 Pure Pythonのldap3では本当にこの最近(2015-11-15)、 v0.9.9.3 として LDAPIの機能が実装された ようです。
一方、このansible-ldapは 昨年の11月に基本機能を実装して公開されていた いたので、ldap3を使っていないのは当然といえます。
DebianシステムではPython-LDAPは python-ldap
パッケージとして提供されていますが、pyldapはDebianパッケージとして提供されていません。ldap3 は python-ldap3
(Python2版) および python3-ldap3
(Python3版) として提供されています。Ansible は Python3はまだ正式対応されていないので現状では playbook の中で、
- apt: pkg=python-ldap state=present
と python-ldap
パッケージをインストールすればよいですが 5 、Ubuntu の次のLTSではPython3だけになるので、pyenvなどでPython2.7を構築するタスクを書いた上 6 で
- apt:
pkg={{ item }}
state=present
with_items:
- build-essential
- libldap2-dev
- pip: name=Python-LDAP
として、slapdを動かすホスト上でPython-LDAPのコンパイルも必要な上、ansible-ldapでは現状任意の PYTHONPATH
を指定することができないので、 /usr/local/lib/python2.7/dist-packages
の下にPython-LDAPをインストール必要があります。
(おまけ) CSVからvarsを読み込むモジュールを作りました¶
サンプルのPlaybookではユーザーの作成や、SSH公開鍵を登録するための タスク もあるのですが、ユーザー作成用のパラメータや公開鍵をいちいちYAMLで記述するのはとても億劫です。なので、CSVで記述したものをvarsとして読み込むことのできる include_csv というモジュールもついでに作りました。使い方としては、コアモジュールの include_vars のような使い方になります。詳しくはAnsible GalaxyのREADMEのページを参照してください。
まとめ¶
今までは、Ansibleらしくない書き方でslapdの構築を行い、それが故に冪等にすることが難しいため変更はAnsibleで行わない、という運用になってしまっていましたが、このansible-ldapモジュールのおかげで冪等性を保つことができるようになりました。個人的には ldapvi コマンド、Python-LDAPに続く、LDAPの運用・利用が非常に楽になるツールが登場したと思ってます。勝手に三種の神器と呼びたい。
また、include_csv も便利そうという意見ももらったので結構うれしいですね。 7
footnotes
- 1
11月からRuby on Railsの仕事をすることになり、業務では現時点ではPythonもLDAPも使っていません。
- 2
マージされるかはわかりませんが。
- 3
ansible 2.0.0-0.8.rc3 も試してみましたが、現状ではまだ
ansible-galaxy
コマンドが Python3 に対応していませんでした。- 4
頻度の問題で、結局作らずに済ませてしまってきたのですが…。
- 5
Debianシステムの場合。
- 6
今回の話と少しずれるので省略します。
- 7
ちなみに今回、初Ansible Galaxy、つまり初のAnsible モジュール作成、初のアカウント作成、初のRole登録、初の
ansible-galaxy
コマンド利用、と初ものづくしでした。