Advanced Kubernetes 3 | LDAPο
1. LDAPο
(1) LDAP Introductionο
LDAP is a protocol initially used to manage telephone directories through telecommunication companies. Itβs operating on the application layer of OSI. The default port for LDAP is port 389
.
(2) LDAP Data Interchange Format (LDIF)ο
LDIF is the standard plain text data files used for LDAP directory content and update requests. There are several fields used in LDIF,
dn
: unique distinguished name for each entrydc
: domain component, for examplewww.mydomain.com
would be written asDC=www,DC=mydomain,DC=com
ou
: organizational unit, used to specifiy a user as part of one groupcn
: common name referring to individual objects
(3) Example: LDAP Pod in Minikubeο
Letβs continue with the minikube cluster we have built in the last article.
First, letβs create a new namespace called ldap
,
$ kubectl create ns ldap
Then, make a directory named ldif
and download the LDIF plain text files from github,
$ mkdir ldif
$ cd ldif
$ curl -LO https://raw.githubusercontent.com/Sadamingh/minikube-with-dex/main/ldap/ldif/0-ous.ldif
$ curl -LO https://raw.githubusercontent.com/Sadamingh/minikube-with-dex/main/ldap/ldif/1-users.ldif
$ curl -LO https://raw.githubusercontent.com/Sadamingh/minikube-with-dex/main/ldap/ldif/2-groups.ldif
Go back to the last directory after this,
$ cd ..
Create a secert
and a configmap
for the deployment under this nameapce,
$ kubectl create secret generic openldap \
--namespace ldap \
--from-literal=adminpassword=adminpassword
$ kubectl create configmap ldap \
--namespace ldap \
--from-file=ldif
Finally, curl the YAML file ldap.yaml
from github and then apply it to create the service
and deployment
.
$ curl -LO https://raw.githubusercontent.com/Sadamingh/minikube-with-dex/main/ldap/ldap.yaml
$ kubectl apply --namespace ldap -f ldap.yaml
Now we have an ldap
pod running for further exploration.
$ kubectl get pods -A | grep ldap
ldap openldap-6d86d655c5-5bkvc 1/1 Running 0 73m
(4) Interact with LDAPο
Now we can access the LDAP pod through,
$ kubectl exec --stdin --tty -n ldap $LDAP_POD -- /bin/sh
#
We can check the LDIF entries through ldapsearch
but for now because we have no user or group imported, we will receive No such object (32)
errors,
# ldapsearch -x -D "cn=admin,dc=example,dc=org" -w adminpassword -b "ou=groups,dc=example,dc=org"
No such object (32)
# ldapsearch -x -D "cn=admin,dc=example,dc=org" -w adminpassword -b "ou=people,dc=example,dc=org"
No such object (32)
From ldap.yaml
and config map ldap
, we have mounted local path ldap/ldif
to /ldifs
. Therefore, now we are able to access the LDIF files,
# ls -lrt /ldifs
lrwxrwxrwx 1 root root 20 Mar 15 03:31 2-groups.ldif -> ..data/2-groups.ldif
lrwxrwxrwx 1 root root 19 Mar 15 03:31 1-users.ldif -> ..data/1-users.ldif
lrwxrwxrwx 1 root root 17 Mar 15 03:31 0-ous.ldif -> ..data/0-ous.ldif
These LDIF files can be mapped to the following tree structure,
ββdc=example,dc=org # example.org
β ββou=people
β β ββcn=admin1
β β ββcn=admin2
β β ββcn=developer1
β β ββcn=developer2
β ββou=groups
β ββcn=admins
β β ββcn=admin1
β β ββcn=admin2
β ββcn=developers
β ββcn=developer1
β ββcn=developer2
Then, we can add them to the LDAP server at localhost:389
by using command ldapadd
,
# ldapadd -x -D "cn=admin,dc=example,dc=org" -w adminpassword -H ldap://localhost:389 -f /ldifs/0-ous.ldif
adding new entry "ou=people,dc=example,dc=org"
adding new entry "ou=groups,dc=example,dc=org"
# ldapadd -x -D "cn=admin,dc=example,dc=org" -w adminpassword -H ldap://localhost:389 -f /ldifs/1-users.ldif
adding new entry "cn=admin1,ou=people,dc=example,dc=org"
adding new entry "cn=admin2,ou=people,dc=example,dc=org"
adding new entry "cn=developer1,ou=people,dc=example,dc=org"
adding new entry "cn=developer2,ou=people,dc=example,dc=org"
# ldapadd -x -D "cn=admin,dc=example,dc=org" -w adminpassword -H ldap://localhost:389 -f /ldifs/2-groups.ldif
adding new entry "cn=admins,ou=groups,dc=example,dc=org"
adding new entry "cn=developers,ou=groups,dc=example,dc=org"
After the LDIF is added to the LDAP server, we can access the data through ldapsearch
. Note that cn=admin,dc=example,dc=org
means the default admin account for the LDAP server which has access to all the objects in the server.
# ldapsearch -x -D "cn=admin,dc=example,dc=org" -w adminpassword -b "ou=groups,dc=example,dc=org"
...
# numResponses: 4
# numEntries: 3
# ldapsearch -x -D "cn=admin,dc=example,dc=org" -w adminpassword -b "ou=people,dc=example,dc=org"
...
# numResponses: 6
# numEntries: 5
(5) Password Hashingο
Thereβs one remaining problem. In the LDIF file 1-users.ldif
, userPassword
is defined through a hash value {SSHA}RRN6AM9u0tpTEOn6oBcIt9X3BbFPKVk5
according to the code. You can check it out through this link.
This value is generated by slappasswd
command with,
# slappasswd -h {SSHA} -s secret
And here the original password for admin1
is actually secret
. People donβt store the original password secret
directly in LDIF files because someone can easily retrieve the password and itβs not secure.
Now, letβs try to use slappasswd
using SSHA
(using SHA-1 algorithm) to generate the hash value for secret secret
. We will have something like,
# slappasswd -h {SSHA} -s secret
{SSHA}wH8Y5PJH9h31HoWBj9ZafqB43pyslcjS
Here the hash code is very different from the one we have in the 1-users.ldif
. This is because slappasswd
utility uses a random salt
by default to make the hash more secure and the server can know both of the hash values means secret
.
(6) Salted Hashο
Letβs see how salted hash works.
The idea of salted hash is to add some random bits to the password each time before hash so that the hash value will become more randomlized. We will put this salt after the hash value as an appendix for the program to verify.
Suppose we use secret
as our password. Then after using command slappasswd
, we get a hash value as follows,
{SSHA}RRN6AM9u0tpTEOn6oBcIt9X3BbFPKVk5
This hash value is encoded with base64. So letβs first decode it, and then we od
command to change it to hexed bin values,
$ echo -n RRN6AM9u0tpTEOn6oBcIt9X3BbFPKVk5 | base64 -d | od -A n -t x1
45 13 7a 00 cf 6e d2 da 53 10 e9 fa a0 17 08 b7 d5 f7 05 b1 4f 29 59 39
The value above has two parts. The first 20 bytes are the actual hash value and the last 4 bytes is the salt we used for extra security.
When user enters password as secret
, we will append the salt after the secret as secret\x4f\x29\x59\x39
. Then the same encryption process will generate the hash value for us. It should be exactly the same as the one we stored in the LDAP server to pass the authentication.
$ slappasswd -h {SHA} -s $(printf 'secret\x4f\x29\x59\x39') | sed 's/{SHA}//' | base64 -d | od -A n -t x1
45 13 7a 00 cf 6e d2 da 53 10 e9 fa a0 17 08 b7 d5 f7 05 b1
Because the salt is randomly added, we will have different hash values even for the same password. This is useful when the passwords are simple, and the attackers can not guess the password through the hash value.