Kubi

Kubi is the missing tool for Active Directory or LDAP driven company. It handles OpenLDAP or Active Directory LDS authentication for Kubernetes clusters. It acts as a Kubernetes Token Server, authenticating user through LDAP, AD LDS and assigns permissions dynamically using a predefined naming convention (LDAP Group).
Kubi is a webhook for the server part and has a cli for linux and windows users.
Index
General
Namespaces and Rolebindings are automatically created and managed by Kubi. Kubi parse the LDAP group and find the namespace and the role.
The first part (from the right) is the role, and the second is the namespace.
The _ is used to split Role and Namespace, the pattern is <whatever>_<namespace>_<role>. Namespace must be DNS1123 compatible and can´t exceed 63 characters ( kubernetes constraint ).
For example:
- a ldap group named:
WHATYOUWANT_DEMO_ADMIN give cluster role binding admin permissions to the namespace DEMO.
- a ldap group named:
WHATYOUWANT_PROJECT-IN-PRODUCTION_ADMIN give cluster role binding admin permissions to the namespace PROJECT-IN-PRODUCTION.
If the namespace is missing, it will be automatically created at startup. You can refresh it by calling /refresh. Some namespace are protected: kube-system, kube-public, default. Kubi can generate NetworkPolicy if PROVISIONING_NETWORK_POLICIES flag is enabled. In this case, it create a Networpolicy that create something like a bubble.
The network policy works like this principle:
- Every pods can communicate inside the namespace
- Pods cannot communicate with external resources ( outside cluster )
- Dns is not filtered
You can customize PROVISIONING_EGRESS_ALLOWED_PORTS, PROVISIONING_EGRESS_ALLOWED_CIDR, PROVISIONING_INGRESS_ALLOWED_NAMESPACES to add default rules.
For specific exceptions, add another network policy.
Parameters
| Name |
Description |
Example |
Mandatory |
Default |
| PUBLIC_APISERVER_URL |
Api server url (public) |
https://k8s.macompany.com |
yes |
- |
| LDAP_USERBASE |
BaseDn for user base search |
ou=People,dc=example,dc=org |
yes |
- |
| LDAP_GROUPBASE |
BaseDn for group base search |
ou=CONTAINER,dc=example,dc=org |
yes |
- |
| LDAP_APP_GROUPBASE |
BaseDn for group base search |
ou=CONTAINER,dc=example,dc=org |
no |
- |
| LDAP_OPS_GROUPBASE |
BaseDn for group base search |
ou=CONTAINER,dc=example,dc=org |
no |
- |
| LDAP_CUSTOMER_OPS_GROUPBASE |
*BaseDn for customer group base * |
ou=CONTAINER,dc=example,dc=org |
no |
- |
| LDAP_ADMIN_USERBASE |
BaseDn for admin base search |
ou=Admin,dc=example,dc=org |
yes |
- |
| LDAP_ADMIN_GROUPBASE |
BaseDn for admin group base search |
ou=AdminGroup,dc=example,dc=org |
yes |
- |
| LDAP_SERVER |
LDAP server ip address |
"192.168.2.1" |
yes |
- |
| LDAP_PORT |
LDAP server port 389, 636... |
389 |
no |
389 |
| LDAP_USE_SSL |
Use SSL or no |
true |
no |
false |
| LDAP_START_TLS |
Use StartTLS ( use with 389 port) |
true |
false |
false |
| LDAP_SKIP_TLS_VERIFICATION |
Skip TLS verification |
true |
false |
true |
| LDAP_BINDDN |
LDAP bind account DN |
"CN=admin,DC=example,DC=ORG" |
yes |
- |
| LDAP_PASSWD |
LDAP bind account password |
"password" |
yes |
- |
| LDAP_USERFILTER |
LDAP filter for user search |
"(userPrincipalName=%s)" |
no |
(cn=%s) |
| TOKEN_LIFETIME |
Duration for the JWT token |
"4h" |
no |
4h |
| LOCATOR |
Locator: must be internet or extranet |
"intranet" |
no |
intranet |
| PROVISIONING_NETWORK_POLICIES |
Enable or disable NetPol Mgmt |
true |
no |
yes |
| CUSTOM_LABELS |
Add custom labels to namespaces |
quota=managed,monitoring=true |
no |
- |
| DEFAULT_PERMISSION |
ClusterRole associated with default service account |
view |
no |
- |
Client
For Windows users
- Download the cli: download here
- Open Cmd
# Get help
.\kubi.exe --help
# Connect and generate config file
.\kubi.exe --kubi-url <kubi-server-fqdn-or-ip>:30003 --generate-config --username <user_cn>
# Connect with your password and generate config file
.\kubi.exe --kubi-url <kubi-server-fqdn-or-ip>:30003 --generate-config --username <user_cn> --password your_pwd
For Linux
With kubi cli
For Linux
# Install the kubi cli
sudo wget https://github.com/ca-gip/kubi/releases/download/v1.7.0/kubi -P /usr/local/bin
sudo chmod a+x /usr/local/bin/kubi
# Connect to the cluster
kubi --kubi-url <kubi-server-fqdn-or-ip>:30003 --generate-config --username <user_cn>
# Connect with your password and generate config file
kubi --kubi-url <kubi-server-fqdn-or-ip>:30003 --generate-config --username <user_cn> --password your_pwd
For Mac
# Install wget with brew
brew install wget
# Install the kubi cli
sudo wget https://github.com/ca-gip/kubi/releases/download/v1.7.0/kubi-darwin -O /usr/local/bin/kubi
sudo chmod a+x /usr/local/bin/kubi
Connection
# Connect to the cluster
kubi --kubi-url <kubi-server-fqdn-or-ip>:30003 --generate-config --username <user_cn>
# Connect with your password and generate config file
kubi --kubi-url <kubi-server-fqdn-or-ip>:30003 --generate-config --username <user_cn> --password your_pwd
With curl
curl -v -k --user <user_cn> https://<kubi-server-fqdn-or-ip>:30003/config
It is not recommended to use curl, because it is used with -k parameter ( insecure mode).
Installation
Prerequisites
Create a crt signed by Kubernetes CA
Change kubi.devops.managed.kvm to an existing kubernetes node ip, vip, or fqdn
that point to an existing Kubernetes Cluster node.
Eg: 10.56.221.4, kubernetes.<my_domain>...
cat <<EOF | cfssl genkey - | cfssljson -bare server
{
"hosts": [
"kubi.devops.managed.kvm",
"kubi-svc",
"kubi-svc.kube-system",
"kubi-svc.kube-system.svc",
"kubi-svc.kube-system.svc.cluster.local",
"kubi.devops.managed.kvm"
],
"CN": "kubi-svc.kube-system.svc.cluster.local",
"key": {
"algo": "ecdsa",
"size": 256
}
}
EOF
Create the signing request
cat <<EOF | kubectl create -f -
apiVersion: certificates.k8s.io/v1beta1
kind: CertificateSigningRequest
metadata:
name: kubi-svc.kube-system
spec:
groups:
- system:authenticated
request: $(cat server.csr | base64 | tr -d '\n')
usages:
- digital signature
- key encipherment
- server auth
EOF
Approve the csr
kubectl certificate approve kubi-svc.kube-system
Retrieve the crt
kubectl get csr kubi-svc.kube-system -o jsonpath='{.status.certificate}' | base64 --decode > server.crt
Create a secret for the deployment
kubectl -n kube-system create secret tls kubi --key server-key.pem --cert server.crt
Create a secret for LDAP Bind password
kubectl -n kube-system create secret generic kubi-secret \
--from-literal ldap_passwd='changethispasswordnow!'
Deploy the config map
** YOU MUST CHANGE VALUE WITH YOUR OWN **
cat <<EOF | kubectl -n kube-system create -f -
apiVersion: v1
kind: ConfigMap
data:
LDAP_USERBASE: "ou=People,dc=kubi,dc=fr"
LDAP_GROUPBASE: "ou=local_platform,ou=Groups,dc=kubi,dc=fr"
LDAP_SERVER: "192.168.2.1"
LDAP_PORT: "389"
LDAP_BINDDN: "cn=admin,dc=kubi,dc=fr"
LDAP_ADMIN_USERBASE: "ou=People,dc=kubi,dc=fr"
LDAP_ADMIN_GROUPBASE: "ou=Administrators,ou=Groups,dc=kubi,dc=fr"
PUBLIC_APISERVER_URL: https://api.devops.managed.kvm
metadata:
name: kubi-config
EOF
Deploy the Custom Resource Definitions
kubectl apply -f https://raw.githubusercontent.com/ca-gip/kubi/master/deployments/kube-crds.yml
Deploy the prerequisites
kubectl apply -f https://raw.githubusercontent.com/ca-gip/kubi/master/deployments/kube-prerequisites.yml
Deploy Kubi
kubectl apply -f https://raw.githubusercontent.com/ca-gip/kubi/master/deployments/kube-deployment.yml
Customize the default network policy
You can customize the default network policy named kubi-default, for example:
apiVersion: "ca-gip.github.com/v1"
kind: NetworkPolicyConfig
metadata:
name: kubi-default
spec:
egress:
# ports allowed for egress
ports:
- 636
- 389
- 123
- 53
# cidrs allowed for egress
# for ipvs, add the network used by calico, for kubernetes svc in default ns
cidrs:
- 192.168.2.0/24
- 172.10.0.0/24
ingress:
# namespaces allowed for ingress rules ( here only nginx )
namespaces:
- ingress-nginx
** Deploy the example : **
kubectl apply -f https://raw.githubusercontent.com/ca-gip/kubi/master/deployments/kube-example-netpolconf.yml
Basic Webhook configuration
Kubi is installed as a Kubernetes webhook.
For more information about webhook: https://kubernetes.io/docs/reference/access-authn-authz/authentication/#webhook-token-authentication
- On each master node in /etc/kubernetes/pki/webhook
# Kubernetes API version
apiVersion: v1
# kind of the API object
kind: Config
# clusters refers to the remote service.
clusters:
- name: kubi
cluster:
certificate-authority: /etc/kubernetes/pki/ca.crt
server: https://kube-svc:8001/authenticate
users:
- name: apiserver
user:
client-certificate: /etc/kubernetes/pki/apiserver.crt
client-key: /etc/kubernetes/pki/apiserver.key
current-context: kubi
contexts:
- context:
cluster: kubi
user: apiserver
name: webhook
# vim /etc/kubernetes/manifests/kube-apiserver.yaml
- --authentication-token-webhook-config-file=/etc/kubernetes/pki/webhook.yml
Api servers reboot automatically, check logs kubectl logs -f kube-apiserver-master-01 -n kube-system.
Advanced Webhook configuration
You could change apiserver mount and create an aditionnal folder.
Here we use /etc/kubernetes/pki which is automatically mounted.
- Add these params to kubeadm config in
ClusterConfiguration:
# Before, create the additionnals folder in all master nodes
mkdir /etc/kubernetes/additionnals
- And edit your
kubeadm-config.yml file with the following values:
extraArgs:
authentication-token-webhook-config-file: /etc/kubernetes/additionnals/webhook.yml
extraVolumes:
- name: additionnals
hostPath: /etc/kubernetes/additionnals
mountPath: /etc/kubernetes/additionnals
- Copy the webhook file to
/etc/kubernetes/additionnals folder.
Roadmap
The following features should be available soon.
- Allow usage of static mapping ( a json file mapping with LDAP group and Kubernetes namespaces)
- Expose /metrics
Development environment
You can easily contribute to this project by using a development environment, follow the installation step from Installation until the Deploy the prerequisites.
Deploy the local config
kubectl apply -f https://raw.githubusercontent.com/ca-gip/kubi/master/deployments/kube-local-config.yml
Copy the secret from you Kubernetes cluster
Create secret dirs on your local machine
mkdir -p /var/run/secrets/{certs,ecdsa,kubernetes.io}
The mapping between the secret:key and file path
| Secret Name |
Secret Key |
Local Path |
| kubi-user- |
ca.crt |
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt |
| kubi-user- |
token |
/var/run/secrets/kubernetes.io/serviceaccount/token |
| kubi |
tls.crt |
/var/run/secrets/certs/tls.crt |
| kubi |
tls.key |
/var/run/secrets/certs/tls.key |
| kubi-encryption-secret |
ecdsa-key.pem |
/var/run/secrets/ecdsa/ecdsa-key.pem |
| kubi-encryption-secret |
ecdsa-public.pem |
/var/run/secrets/ecdsa/ecdsa-public.pem |
You can execute the following commands to gather all the required secrets then decode and save them
kubectl -n kube-system get secrets $(kubectl -n kube-system get sa kubi-user -o "jsonpath={.secrets[0].name}") -o "jsonpath={.data['ca\.crt']}" | base64 -d > /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
kubectl -n kube-system get secrets $(kubectl -n kube-system get sa kubi-user -o "jsonpath={.secrets[0].name}") -o "jsonpath={.data['token']}" | base64 -d > /var/run/secrets/kubernetes.io/serviceaccount/token
kubectl -n kube-system get secrets kubi -o "jsonpath={.data['tls\.crt']}" | base64 -d > /var/run/secrets/certs/tls.crt
kubectl -n kube-system get secrets kubi -o "jsonpath={.data['tls\.key']}" | base64 -d > /var/run/secrets/certs/tls.key
kubectl -n kube-system get secrets kubi-encryption-secret -o "jsonpath={.data['ecdsa-key\.pem']}" | base64 -d > /var/run/secrets/ecdsa/ecdsa-key.pem
kubectl -n kube-system get secrets kubi-encryption-secret -o "jsonpath={.data['ecdsa-public\.pem']}" | base64 -d > /var/run/secrets/ecdsa/ecdsa-public.pem
Running
At the base of this project execute the go run with the required variable
LDAP_ADMIN_GROUPBASE="cn=DL_ADMIN_TEAM,OU=GLOBAL,ou=Groups,dc=kubi,dc=ca-gip,dc=github,dc=com" \
LDAP_ADMIN_USERBASE="dc=kubi,dc=ca-gip,dc=github,dc=com" \
LDAP_BINDDN="cn=admin,dc=kubi,dc=ca-gip,dc=github,dc=com" \
LDAP_GROUPBASE="ou=LOCAL,ou=Groups,dc=kubi,dc=ca-gip,dc=github,dc=com" \
LDAP_PORT="389" \
LDAP_SERVER="kube-ldap.kube-system.svc.cluster.local" \
LDAP_USE_SSL="false" \
LDAP_USERBASE="ou=People,dc=kubi,dc=ca-gip,dc=github,dc=com" \
LDAP_USERFILTER="(cn=%s)" \
LOCATOR="local" \
PUBLIC_APISERVER_URL="https://kubernetes.default.svc.cluster.local" \
TENANT="cagip" \
KUBERNETES_SERVICE_HOST="kubernetes.default.svc.cluster.local" \
KUBERNETES_SERVICE_PORT="443" \
LDAP_PASSWD="password" \
go run cmd/main.go