midPoint by OpenStandia Advent Calendar 2024 の15日目は、新たな連携先としてActive Directory(以下、AD)を追加していくにあたって、まずはAD互換環境を準備します。Windows ServerをたててADをインストールするでもよいのですが、今回のAdvent CalendarではDocker Composeを使って開発/検証環境を構築していますので、AD環境もその上で準備できると色々捗ります。幸いなことに、Linux上で動作するAD互換ドメインコントローラーのOSS実装であるSamba4がありますので、これをDocker Compose環境に組み込みます。
14日目までの環境を前提としています。
Dockerfileの作成
ad
ディレクトリを作成し、配下にDockerfile
とSamba4構築のための各種ファイルを作成していきます。
https://github.com/myrjola/docker-samba-ad-dc など、Samba4を使ったドメインコントローラーのDockernizeに取り組まれている先人達がいらっしゃいますので、本記事でもそれらを参考にしています。なお、本記事ではあくまで開発/検証用途でSamba4を使用しています。実際に運用するSamba4を使ったドメインコントローラーの構築方法を紹介しているわけではありませんので、ご注意ください。特に、LDAPSではなくLDAP接続を許可したりとセキュリティレベルを下げている点に注意してください。
Dockerfile
の中身は以下のとおりです。
FROM ubuntu:24.04
# Install supervisor
RUN apt-get update
RUN apt-get install -y supervisor
RUN mkdir -p /var/log/supervisor
# Install samba and dependencies to make it an Active Directory Domain Controller
RUN apt-get install -y bind9 samba smbclient attr winbind krb5-config krb5-user ldb-tools dnsutils
# Install utilities needed for setup
RUN apt-get install -y expect pwgen
# Install rsyslog to get better logging of ie. bind9
RUN apt-get install -y rsyslog
# Install ldap-utils
RUN apt-get install -y ldap-utils
# Add bind9 config
ADD named.conf* /etc/bind/
# Add supervisord
ADD supervisord.conf /etc/supervisor/conf.d/supervisord.conf
# Add scripts
COPY --chmod=755 entrypoint.sh /entrypoint.sh
EXPOSE 22 53 389 88 135 139 138 445 464 3268 3269
ENTRYPOINT ["/entrypoint.sh"]
CMD ["app:start"]
Dockerfile
内で追加しているその他のファイルは、以下のとおりです。
./ad/entrypoint.sh
#!/bin/bash
set -e
SAMBA_DOMAIN=${SAMBA_DOMAIN:-SAMDOM}
SAMBA_REALM=${SAMBA_REALM:-SAMDOM.EXAMPLE.COM}
LDAP_ALLOW_INSECURE=${LDAP_ALLOW_INSECURE:-false}
SETUP_LOCK_FILE="/var/lib/samba/private/.setup.lock.do.not.remove"
if [[ $SAMBA_HOST_IP ]]; then
SAMBA_HOST_IP="--host-ip=${SAMBA_HOST_IP}"
fi
appSetup () {
echo "Initializing samba database..."
SAMBA_ADMIN_PASSWORD=${SAMBA_ADMIN_PASSWORD:-$(pwgen -cny 10 1)}
export KERBEROS_PASSWORD=${KERBEROS_PASSWORD:-$(pwgen -cny 10 1)}
echo Samba administrator password: $SAMBA_ADMIN_PASSWORD
echo Kerberos KDC database master key: $KERBEROS_PASSWORD
# Workaround for some environment
# https://github.com/lxc/lxc/issues/2708
sed -i "s|lowerBound: 3000000|lowerBound: 655|" /usr/share/samba/setup/idmap_init.ldif
sed -i "s|upperBound: 4000000|upperBound: 65533|" /usr/share/samba/setup/idmap_init.ldif
# Provision Samba
rm -f /etc/samba/smb.conf
rm -rf /var/lib/samba/*
mkdir -p /var/lib/samba/private
samba-tool domain provision \
--use-rfc2307 \
--domain=$SAMBA_DOMAIN \
--realm=$SAMBA_REALM \
--server-role=dc \
--option="vfs objects = acl_xattr xattr_tdb" \
--option="idmap config * : range = 655-65533" \
--dns-backend=BIND9_DLZ \
--adminpass=$SAMBA_ADMIN_PASSWORD \
$SAMBA_HOST_IP
cp /var/lib/samba/private/krb5.conf /etc/krb5.conf
# Configure smb.conf
# Allow schema extension
sed -i "s|\[global\]|[global]\n\t# allow schema extension\n\tdsdb:schema update allowed = true|" /etc/samba/smb.conf
# Allow unencrypted connection
sed -i "s|\[global\]|[global]\n\t# allow unencrypted connection\n\tldap server require strong auth = no|" /etc/samba/smb.conf
touch "${SETUP_LOCK_FILE}"
}
appStart () {
if [ ! -f "${SETUP_LOCK_FILE}" ]; then
appSetup
fi
exec /usr/bin/supervisord -c /etc/supervisor/conf.d/supervisord.conf
}
appHelp () {
echo "Available options:"
echo " app:start - Starts all services needed for Samba AD DC"
echo " app:setup - First time setup."
echo " app:setup_start - First time setup and start."
echo " app:help - Displays the help"
echo " [command] - Execute the specified linux command eg. /bin/bash."
}
case "$1" in
app:start)
appStart
;;
app:setup)
appSetup
;;
app:setup_start)
appSetup
appStart
;;
app:help)
appHelp
;;
*)
if [ -x $1 ]; then
$1
else
prog=$(which $1)
if [ -n "${prog}" ] ; then
shift 1
$prog $@
else
appHelp
fi
fi
;;
esac
exit 0
./ad/named.conf.options
acl localnet {
10.254.249.0/24;
127.0.0.1;
};
options {
directory "/var/cache/bind";
listen-on port 53 {
localnet;
};
allow-query {
localnet;
};
allow-transfer {
localnet;
};
// If there is a firewall between you and nameservers you want
// to talk to, you may need to fix the firewall to allow multiple
// ports to talk. See http://www.kb.cert.org/vuls/id/800113
// If your ISP provided one or more IP addresses for stable
// nameservers, you probably want to use them as forwarders.
// Uncomment the following block, and insert the addresses replacing
// the all-0's placeholder.
forwarders {
127.0.0.11;
};
//========================================================================
// If BIND logs error messages about the root key being expired,
// you will need to update your keys. See https://www.isc.org/bind-keys
//========================================================================
dnssec-validation no;
listen-on-v6 { none; };
tkey-gssapi-keytab "/var/lib/samba/bind-dns/dns.keytab";
};
./ad/supervisord.conf
[supervisord]
nodaemon=true
[program:bind9]
command=/usr/sbin/named -c /etc/bind/named.conf -u bind -f
[program:samba]
command=/usr/sbin/samba -i
[program:syslog]
command=/usr/sbin/rsyslogd -n
AD初期データの登録
次回以降の記事で、ADのグループとユーザーをプロビジョニングする予定です。そのための準備として、プロビジョニング対象のエントリを配置するOUを初期データとして登録しておきます。
初期データ登録用のシェルスクリプトとして、ad-init.sh
を作成します。このシェルスクリプトでは以下のように、ldapsearch
が成功したら(ADがLDAPリクエストを受け付け可能な状態になったら)ldapadd
を実行し、いくつかOUを作成しておきます。
#!/bin/sh
set -e
cmd="$@"
until ldapsearch -x -D $ADMIN_BIND_DN -w $SAMBA_ADMIN_PASSWORD -b $ADMIN_BIND_DN cn=Administrator; do
echo "AD is unavailable - sleeping"
sleep 5
done
echo "AD is up - executing command"
ldapadd -D $ADMIN_BIND_DN -w $SAMBA_ADMIN_PASSWORD <<EOF
dn: OU=IDM,$BASE_DN
objectClass: organizationalUnit
dn: OU=Groups,OU=IDM,$BASE_DN
objectClass: organizationalUnit
dn: OU=Users,OU=IDM,$BASE_DN
objectClass: organizationalUnit
EOF
./ad/supervisord.conf
を修正し、ad-init.sh
をワンショットで実行するようにしておきます。
[program:syslog]
command=/usr/sbin/rsyslogd -n
+[program:ad_init]
+command=/ad-init.sh
+autorestart=false
Dockerfile
を修正し、add-init.sh
をコンテナ内にコピーするようにします。
# Add scripts
COPY --chmod=755 entrypoint.sh /entrypoint.sh
+COPY --chmod=755 ad-init.sh /ad-init.sh
EXPOSE 22 53 389 88 135 139 138 445 464 3268 3269
docker-compose.ymlの修正
準備したSamba4コンテナをDocker Composeに組み込みます。今回、構築するドメインコントローラのドメインはad.example.com
としています。
- ./hr:/var/lib/hr
- ./addressbook:/var/lib/addressbook
+ ad:
+ cap_add:
+ - SYS_ADMIN
+ build:
+ context: ./ad
+ environment:
+ - SAMBA_DOMAIN=AD
+ - SAMBA_REALM=AD.EXAMPLE.COM
+ - SAMBA_ADMIN_PASSWORD=p@ssw0rd
+ - KERBEROS_PASSWORD=p@ssw0rd
+ - SAMBA_HOST_IP=10.254.249.100
+ # for ad-init
+ - ADMIN_BIND_DN=cn=Administrator,cn=Users,dc=ad,dc=example,dc=com
+ - BASE_DN=dc=ad,dc=example,dc=com
+ ports:
+ - "1389:389"
+ networks:
+ net:
+ ad_network:
+ ipv4_address: 10.254.249.100
+
networks:
net:
driver: bridge
+ ad_network:
+ ipam:
+ driver: default
+ config:
+ - subnet: 10.254.249.0/24
volumes:
midpoint_data:
起動
docker compose up -d --build
でビルドしつつ環境を起動します。
稼働確認
docker compose exec ad bash
で起動したSamba4コンテナ(ad
コンテナ)の中に入り、ldapsearch
コマンドを実行して、Samba4に対してLDAP検索できることを確認しておきます。正常に起動して初期データが投入されていれば、以下のような結果になるはずです。
root@fd451d1f2926:/# ldapsearch -D cn=Administrator,cn=Users,dc=ad,dc=example,dc=com -w p@ssw0rd -b ou=IDM,dc=ad,dc=example,dc=com dn
# extended LDIF
#
# LDAPv3
# base <ou=IDM,dc=ad,dc=example,dc=com> with scope subtree
# filter: (objectclass=*)
# requesting: dn
#
# Groups, IDM, ad.example.com
dn: OU=Groups,OU=IDM,DC=ad,DC=example,DC=com
# IDM, ad.example.com
dn: OU=IDM,DC=ad,DC=example,DC=com
# Users, IDM, ad.example.com
dn: OU=Users,OU=IDM,DC=ad,DC=example,DC=com
# search result
search: 2
result: 0 Success
# numResponses: 4
# numEntries: 3
フォレスト構成について補足
大規模な組織では、複数のADでフォレスト間信頼を構成しているケースが考えられます。また、FSP(Foreign Security Principal)1を使用して、別ドメインのユーザーを自ドメイン内のセキュリティグループに追加し、リソースに対するパーミッションを付与する運用をしているケースも想定されます。そのようなケースであっても、Samba4で開発/検証環境を構築することが可能です。
今回の記事では単体のSamba4のみ構築していますが、これをベースに別のSamba4を追加してフォレスト構成設定を行うことで、Docker Compose環境でもフォレスト構成を構築することができます。Samba4でフォレスト間信頼を設定するには、こちらのStefan Kania氏のサイトで公開されているSeting up trusts between two Samba-domainsを参考にするとよいでしょう。
また、FSPを使ってセキュリティグループのメンバー管理をmidPointで行うには、少々複雑な設定が必要になります。過去に、以下の記事を書いたことがありますので、参考にしていただければと思います。
Microsoft Exchange Server用属性について補足
ADを運用されている組織において、Microsoft Exchange Serverも合わせて導入されているケースも多いでしょう。その場合、Microsoft Exchange Server用の拡張属性をADにインストールしているケースがあります。そのようなケースであっても、Samba4でも対応は可能です。Samba4ではスキーマの拡張機能が提供されており、以下のドキュメントに記載があります。
例えば、extensionAttribute1
属性を追加したい場合、AD起動後に以下のLDIFでldapmodify
により更新を行うことで、拡張属性の追加が可能です(ドメインがad.example.com
の場合)。
dn: CN=extensionAttribute1,CN=Schema,CN=Configuration,DC=ad,DC=example,DC=com
changetype: add
objectClass: attributeSchema
attributeID: 1.2.840.113556.1.2.423
lDAPDisplayName: extensionAttribute1
description: extensionAttribute1
attributeSyntax: 2.5.5.12
isSingleValued: TRUE
dn: CN=msExchCustomAttributes,CN=Schema,CN=Configuration,DC=ad,DC=example,DC=com
changetype: add
objectClass: classSchema
governsID: 1.2.840.113556.1.5.7000.62.6
lDAPDisplayName: msExchCustomAttributes
subClassOf: top
objectClassCategory: 3
description: msExchCustomAttributes
mayContain: extensionAttribute1
Microsoft Exchange Server用の拡張属性のスキーマ情報は、以下のサイトに記載されています。利用されているAD環境に合わせて、必要な拡張属性をSamba4に追加しておくとよいでしょう。
まとめ
15日目では、midPointからADへのプロビジョニングを行うための準備作業として、Docker Compose環境にAD互換環境となるSamba4を追加しました。開発者個人ごとに専用の環境としてADを準備するのは中々大変ですが、Samba4を使うと個人のDocker Compose環境に組み込むこともできて、midPointとともに即座にセットアップ可能という大きなメリットがあります。ただし、あくまでAD互換環境であることはお忘れのないようにご注意ください。最終的な連携テストは実際のADと繋げて実施するようにしましょう。
明日は、本日の環境を使ってリソース設定を追加し、AD(Samba4)にプロビジョニングを実際に行う予定です。お楽しみに!
-
FSPについては、Microsoftのページ( https://learn.microsoft.com/en-us/archive/technet-wiki/51367.active-directory-foreign-security-principals-and-special-identities )を参照するとよいでしょう。 ↩