IBM Cloud Privateでビルトイン管理者ユーザー以外を使用するためには外部にLDAPが必要だが、外部にLDAPサーバーを立てるのではなく、ICPのKubernetesクラスタ内でOpenLDAPをPodとして稼働させてICPのユーザー認証に使用する方法をICP v3.1.2で確認。
Podではなく普通に外部にOpenLDAPを立てる場合は以下が参考になりそう。
使用するHelmチャートについて
この目的で使えるチャートと手順が以下のリポジトリで提供されている。今回はこのチャートを利用する。
このチャートはibm-chartsではなく、あまりメンテナンスされていないようにみえる。phpLDAPadminのPodを一緒にデプロイしてくれたり、初期データをロードする機能もあり、テスト用途としては便利。PersistentVolumeを使っておらず、データは永続化されない。リポジトリのREADMEにもデモ用と記載がある。
ICP用ではなく一般的なOpenLDAPのHelmチャートもあるので、本格的に利用する場合はこちらを利用するべきと思われる。こちらはPersistentVolumeを利用してデータの永続化が可能。どちらもosixia/openldapのDockerイメージを利用している。
OpenLDAPのデプロイ
リポジトリのクローン
リポジトリをクローンする。
git clone https://github.com/ibm-cloud-architecture/icp-openldap.git
$ git clone https://github.com/ibm-cloud-architecture/icp-openldap.git
Cloning into 'icp-openldap'...
remote: Enumerating objects: 2, done.
remote: Counting objects: 100% (2/2), done.
remote: Compressing objects: 100% (2/2), done.
remote: Total 22 (delta 0), reused 1 (delta 0), pack-reused 20
Unpacking objects: 100% (22/22), done.
$
チャートのアップロード(参考)
チャートをICPに登録する場合は以下の手順で行うが、今回はローカルでチャートを編集する必要があるのでやらない。
チャートをパッケージする。
helm package icp-openldap
$ helm package icp-openldap
Successfully packaged chart and saved it to: /home/sotoiwa/workspace/icp-openldap-0.1.5.tgz
$
ICPにログインする。
cloudctl login \
-a https://mycluster.icp:8443 \
--skip-ssl-validation \
Helmチャートをロードする。
cloudctl catalog load-helm-chart --archive icp-openldap-0.1.5.tgz
$ cloudctl catalog load-helm-chart --archive icp-openldap-0.1.5.tgz
Loading helm chart
Loaded helm chart
Synch charts
Synch started
OK
$
ICPコンソールでカタログに追加されたことを確認する。
イメージのプライベートレジストリへの登録
今回、ICPがオフライン環境にあるため、使用するイメージをICPのプライベートレジストリに登録する。インターネットにつながる環境の場合はこの手順は不要。
ICPはイメージはNamespaceに紐付ける必要があるため、イメージをpushする先のNamespaceを作成する。
GUIでNamespaceを作成する場合はibm-anyuid-hostpath-psp
を割り当てること。NamespaceをCLIで作成した場合は、あとでPodSecurityPolicyをバインドするClusterRoleBindingの作成が必要。
kubectl create ns openldap
チャート内のマニフェストを確認し、使用しているイメージをpullしてICPにpushする。busyboxはタグが指定されていなかったが指定するようにした。
docker pull osixia/openldap:1.1.10
docker pull osixia/phpldapadmin:0.7.0
docker pull busybox:1.30.0
docker tag osixia/openldap:1.1.10 mycluster.icp:8500/openldap/openldap:1.1.10
docker tag osixia/phpldapadmin:0.7.0 mycluster.icp:8500/openldap/phpldapadmin:0.7.0
docker tag busybox:1.30.0 mycluster.icp:8500/openldap/busybox:1.30.0
docker push mycluster.icp:8500/openldap/openldap:1.1.10
docker push mycluster.icp:8500/openldap/phpldapadmin:0.7.0
docker push mycluster.icp:8500/openldap/busybox:1.30.0
valuesファイルの作成とチャートの修正
valuesファイルをコピーする。
cp icp-openldap/values.yaml icp-openldap-values.yaml
valuesファイルでイメージのリポジトリとタグをを書き換えるが、マニフェスト・テンプレートにイメージのリポジトリとタグがハードコードされてしまっているので、テンプレート自体も修正が必要。OpenLDAPのイメージ、初期化コンテナとして利用しているbusyboxのイメージ、phpLDAPadminのイメージについて、プライベートレジストリにpushしたイメージと、valuesとテンプレートで指定されているイメージの整合性がとれるようにする。
(追記)プルリクしてマージしてもらったので、マニフェスト・テンプレートの修正は不要でvaluesファイルの修正だけでよいはず。
なお、デフォルトでuser1
〜user4
を作るようになっているが必要に応じてユーザーをカスタマイズ可能。管理者のパスワード、ユーザーのパスワードも必要に応じてカスタマイズする。
OpenLdap:
Image: "mycluster.icp:8500/openldap/openldap"
ImageTag: "1.1.10"
ImagePullPolicy: "Always"
Component: "openldap"
Replicas: 1
Cpu: "512m"
Memory: "200Mi"
Domain: "local.io"
AdminPassword: "admin"
Https: "false"
SeedUsers:
usergroup: "icpusers"
userlist: "user1,user2,user3,user4"
initialPassword: "ChangeMe"
PhpLdapAdmin:
Image: "mycluster.icp:8500/openldap/phpldapadmin"
ImageTag: "0.7.0"
ImagePullPolicy: "Always"
Component: "phpadmin"
Replicas: 1
NodePort: 31080
Cpu: "512m"
Memory: "200Mi"
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-seedusers
data:
seedusers.ldif: |
{{- include (print .Template.BasePath "/_seedusers2.ldif.tpl") . | indent 4 }}
---
apiVersion: v1
kind: Service
metadata:
labels:
app: {{.Release.Name}}
name: {{.Release.Name}}
spec:
ports:
- port: 389
selector:
app: {{.Release.Name}}
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: {{.Release.Name}}
labels:
app: {{.Release.Name}}
spec:
replicas: 1
template:
metadata:
labels:
app: {{.Release.Name}}
spec:
initContainers:
- name: init-copy
image: mycluster.icp:8500/openldap/busybox:1.30.0
command: ['sh','-c','cp /config-map/* /config-storage; ls -lrt /config-storage']
volumeMounts:
- name: {{.Release.Name}}-seedusers
mountPath: /config-map
- name: config-storage
mountPath: /config-storage
containers:
- name: {{.Release.Name}}
image: {{.Values.OpenLdap.Image}}:{{.Values.OpenLdap.ImageTag}}
args: ["--loglevel", "debug"]
volumeMounts:
- name: config-storage
mountPath: /container/service/slapd/assets/config/bootstrap/ldif/custom
- name: ldap-certs
mountPath: /container/service/slapd/assets/certs
ports:
- containerPort: 389
- containerPort: 636
env:
- name: LDAP_LOG_LEVEL
value: "256"
- name: LDAP_ORGANISATION
value: "Example Inc."
- name: LDAP_DOMAIN
value: "{{.Values.OpenLdap.Domain}}"
- name: LDAP_ADMIN_PASSWORD
value: "{{.Values.OpenLdap.AdminPassword}}"
- name: LDAP_REMOVE_CONFIG_AFTER_SETUP
value: "false"
volumes:
- name: config-storage
emptyDir: {}
- name: ldap-certs
hostPath:
path: "/data/ldap/certs"
- name: {{.Release.Name}}-seedusers
configMap:
name: {{.Release.Name}}-seedusers
RoleBindingの作成
ICP v3.1.1以降ではデフォルトではコンテナはroot
で起動できないが、OpenLDAPのコンテナはroot
で起動する。また、hostPathをマウントする。そのため、root
での起動が可能でhostPathをマウントできるPodSecurityPolicyを利用可能なClusterRoleを、Podを起動するServiceAccoutにバインドする必要がある
(GUIでNamespaceを作成する場合は、作成時にPodSecurityPolicyを選択すると、自動的にRoleBindingが作成される)。
ICPでは以下の事前定義されたPodSecurityPolicyが存在し、特に指定しなければibm-restricted-psp
が利用される。root
で起動し、hostPathをマウントするためibm-anyuid-hostpath-psp
を割り当てる。
それぞれのPodSecurityPolicyについては以下のリンク先に説明がある。
$ kubectl get psp
NAME PRIV CAPS SELINUX RUNASUSER FSGROUP SUPGROUP READONLYROOTFS VOLUMES
ibm-anyuid-hostaccess-psp false SETPCAP,AUDIT_WRITE,CHOWN,NET_RAW,DAC_OVERRIDE,FOWNER,FSETID,KILL,SETUID,SETGID,NET_BIND_SERVICE,SYS_CHROOT,SETFCAP RunAsAny RunAsAny RunAsAny RunAsAny false *
ibm-anyuid-hostpath-psp false SETPCAP,AUDIT_WRITE,CHOWN,NET_RAW,DAC_OVERRIDE,FOWNER,FSETID,KILL,SETUID,SETGID,NET_BIND_SERVICE,SYS_CHROOT,SETFCAP RunAsAny RunAsAny RunAsAny RunAsAny false *
ibm-anyuid-psp false SETPCAP,AUDIT_WRITE,CHOWN,NET_RAW,DAC_OVERRIDE,FOWNER,FSETID,KILL,SETUID,SETGID,NET_BIND_SERVICE,SYS_CHROOT,SETFCAP RunAsAny RunAsAny RunAsAny RunAsAny false configMap,emptyDir,projected,secret,downwardAPI,persistentVolumeClaim
ibm-privileged-psp true * RunAsAny RunAsAny RunAsAny RunAsAny false *
ibm-restricted-psp false RunAsAny MustRunAsNonRoot MustRunAs MustRunAs false configMap,emptyDir,projected,secret,downwardAPI,persistentVolumeClaim
$
ibm-anyuid-psp
のPodSecurityPolicyが利用可能なibm-anyuid-clusterrole
というClusterRoleが定義されているのでこのClusterRoleを利用する。ちなみに他のibm-privileged-psp
などのPodSecurityPolicyもそれを利用可能なClusterRoleが定義されている。
$ kubectl get clusterrole | grep ibm
ibm-anyuid-clusterrole 5d
ibm-anyuid-hostaccess-clusterrole 5d
ibm-anyuid-hostpath-clusterrole 5d
ibm-cert-manager-cert-manager 5d
ibm-privileged-clusterrole 5d
ibm-restricted-clusterrole 5d
$
今回openldap
というNamespaceにデプロイするので、このNamespaceの全てのServiceAccoutにibm-anyuid-hostpath-clusterrole
のClusterRoleをバインドするためのRoleBindingを作成する。
kubectl create rolebinding ibm-anyuid-hostpath-clusterrole-rolebinding \
--clusterrole=ibm-anyuid-hostpath-clusterrole \
--group=system:serviceaccounts:openldap \
-n openldap
チャートのインストール
準備ができたので、チャートをインストールする。
helm install --tls --name icp-openldap \
--namespace openldap \
-f icp-openldap-values.yaml \
icp-openldap
$ helm install --tls --name icp-openldap \
> --namespace openldap \
> -f icp-openldap-values.yaml \
> icp-openldap
NAME: icp-openldap
LAST DEPLOYED: Sun Feb 17 15:12:54 2019
NAMESPACE: openldap
STATUS: DEPLOYED
RESOURCES:
==> v1/Service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
icp-openldap ClusterIP 10.0.139.191 <none> 389/TCP 0s
icp-openldap-admin NodePort 10.0.253.179 <none> 80:31080/TCP 0s
==> v1beta1/Deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
icp-openldap 1 1 1 0 0s
icp-openldap-admin 1 1 1 0 0s
==> v1/Pod(related)
NAME READY STATUS RESTARTS AGE
icp-openldap-69dcc4d564-zxfcv 0/1 Init:0/1 0 0s
icp-openldap-admin-59547dd5f8-f74d9 0/1 ContainerCreating 0 0s
==> v1/ConfigMap
NAME DATA AGE
icp-openldap-seedusers 1 0s
$
Podが稼働していることを確認する。
$ kubectl get po -n openldap
NAME READY STATUS RESTARTS AGE
icp-openldap-69dcc4d564-zxfcv 1/1 Running 0 51s
icp-openldap-admin-59547dd5f8-f74d9 1/1 Running 0 51s
$
後で使用するのでServiceのIPアドレスを確認する。
$ kubectl get svc -n openldap
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
icp-openldap ClusterIP 10.0.139.191 <none> 389/TCP 114s
icp-openldap-admin NodePort 10.0.253.179 <none> 80:31080/TCP 114s
$
ICPコンソールで「管理 > IDおよびアクセス > 認証」からLDAP接続を作成する。
項目 | 値 | 備考 |
---|---|---|
接続名 | openldap | |
サーバー・タイプ | カスタム | |
基本DN | dc=local,dc=io |
|
バインドDN | cn=admin,dc=local,dc=io |
|
バインドDNパスワード | admin |
|
LDAPサーバー | ldap://icp-openldap.openldap:389 |
|
グループ・フィルター | (&(cn=%v)(objectclass=groupOfUniqueNames)) |
|
ユーザー・フィルター | (&(uid=%v)(objectclass=person)) |
事前入力値から変更が必要 |
グループIDマップ | *:cn |
|
ユーザーIDマップ | *:uid |
|
グループ・メンバーIDマップ | groupOfUniqueNames:uniquemember |
以上でLDAPの接続は完了。
「管理 > IDおよびアクセス > チーム」からチームを作成し、チームにユーザーまたはグループを追加することでLDAP内のユーザーでもログインできるようなる。
LDAP側でのユーザーの管理
LDAP側でユーザーを管理するためには、phpLDAPadminが利用できる。NodePort Serviceとして公開されているので、Serviceのポートを確認してアクセスする。
$ kubectl get svc -n openldap
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
icp-openldap ClusterIP 10.0.139.191 <none> 389/TCP 27m
icp-openldap-admin NodePort 10.0.253.179 <none> 80:31080/TCP 27m
$
ログインDNはcn=admin,dc=local,dc=io
、パスワードはadmin
でログインする。
補足
LDAPに接続できなくなるとデフォルトの管理者ユーザーでもログインできなくなる。そのような場合の復旧は以下を参照。