Security
Service Accounts¶
Get¶
kubectl get serviceaccounts
Create¶
kubectl get serviceaccounts kubectl get serviceaccounts jenkins -o yaml
Pod Example¶
apiVersion: v1 kind: Pod metadata: name: busybox namespace: default spec: serviceAccountName: jenkins containers: - image: busybox:1.28.4 command: - sleep - "3600" imagePullPolicy: IfNotPresent name: busybox restartPolicy: Always
View the token file from within a pod¶
kubectl get pods -n my-ns
kubectl exec -it <name-of-pod> -n my-ns sh
cat /var/run/secrets/kubernetes.io/serviceaccount/token
Users¶
Create¶
kubectl config view kubectl config set-credentials chad --username=chad --password=password # Create a role binding for anonymous users (not recommended in production): kubectl create clusterrolebinding cluster-system-anonymous --clusterrole=cluster-admin --user=system:anonymous # Need Copy /etc/kubernetes/pki/ca.crt to remote machine # Remote Machine (Install Kubectl) kubectl config set-cluster kubernetes --server=https://172.31.41.61:6443 --certificate-authority=ca.crt --embed-certs=true kubectl config set-credentials chad --username=chad --password=password kubectl config set-context kubernetes --cluster=kubernetes --user=chad --namespace=default kubectl config use-context kubernetes
Generate client Cert¶
- Generate Private Key
openssl genrsa -out mia.key 2048
- Generate CSR
openssl req -new -key mia.key -out mia.csr -subj "/CN=mia/O=acg"
- Generate Client Certificate
openssl x509 -req \ -in mia.csr \ -CA cluster.crt \ -CAkey cluster.key \ -CAcreateserial \ -out mia.crt \ -days 365
- Kubectl
kubectl config set-credentials mia --client-certificate=mia.crt --client-key=mia.key kubectl config set-context mia --cluster=tiagomsantos.com --namespace=development --user=mia kubectl config use-context mia
Roles¶
Role¶
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: namespace: web name: service-reader rules: - apiGroups: [""] verbs: ["get", "list"] resources: ["services"]
RoleBinding¶
kubectl create rolebinding test --role=service-reader --serviceaccount=web:default -n web
Cluster Roles¶
Cluster Role¶
kubectl create clusterrole pv-reader --verb=get,list --resource=persistentvolumes
Cluster Role Binding¶
kubectl create clusterrolebinding pv-test --clusterrole=pv-reader --serviceaccount=web:default
Test¶
apiVersion: v1 kind: Pod metadata: name: curlpod namespace: web spec: containers: - image: tutum/curl command: ["sleep", "9999999"] name: main - image: linuxacademycontent/kubectl-proxy name: proxy restartPolicy: Always
kubectl apply -f curl-pod.yaml
kubectl get pods -n web
kubectl exec -it curlpod -n web -- sh
curl localhost:8001/api/v1/persistentvolumes
TLS Certficates¶
Install cfssl¶
# Download the binaries for the cfssl tool: wget -q --timestamping \ https://pkg.cfssl.org/R1.2/cfssl_linux-amd64 \ https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64 # Make the binary files executable: chmod +x cfssl_linux-amd64 cfssljson_linux-amd64 sudo mv cfssl_linux-amd64 /usr/local/bin/cfssl sudo mv cfssljson_linux-amd64 /usr/local/bin/cfssljson
Create Certificate Authority to Kubernetes¶
cd ~/ mkdir kthw cd kthw/ { cat > ca-config.json << EOF { "signing": { "default": { "expiry": "8760h" }, "profiles": { "kubernetes": { "usages": ["signing", "key encipherment", "server auth", "client auth"], "expiry": "8760h" } } } } EOF cat > ca-csr.json << EOF { "CN": "Kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "CA", "ST": "Oregon" } ] } EOF cfssl gencert -initca ca-csr.json | cfssljson -bare ca }
Generating Client Certificates¶
will generate the following client certificates: admin, kubelet (one for each worker node), kube-controller-manager, kube-proxy, and kube-scheduler
- Admin Client Certificate
{ cat > admin-csr.json << EOF { "CN": "admin", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:masters", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ admin-csr.json | cfssljson -bare admin }
- Kubelet Client certificates
export WORKER0_HOST=<Public hostname of your first worker node cloud server> export WORKER0_IP=<Private IP of your first worker node cloud server> export WORKER1_HOST=<Public hostname of your second worker node cloud server> export WORKER1_IP=<Private IP of your second worker node cloud server> { cat > ${WORKER0_HOST}-csr.json << EOF { "CN": "system:node:${WORKER0_HOST}", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:nodes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=${WORKER0_IP},${WORKER0_HOST} \ -profile=kubernetes \ ${WORKER0_HOST}-csr.json | cfssljson -bare ${WORKER0_HOST} cat > ${WORKER1_HOST}-csr.json << EOF { "CN": "system:node:${WORKER1_HOST}", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:nodes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=${WORKER1_IP},${WORKER1_HOST} \ -profile=kubernetes \ ${WORKER1_HOST}-csr.json | cfssljson -bare ${WORKER1_HOST} }
- Controller Manager Client certificate
{ cat > kube-controller-manager-csr.json << EOF { "CN": "system:kube-controller-manager", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:kube-controller-manager", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-controller-manager-csr.json | cfssljson -bare kube-controller-manager }
- Kube-proxy Client certificate
{ cat > kube-proxy-csr.json << EOF { "CN": "system:kube-proxy", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:node-proxier", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-proxy-csr.json | cfssljson -bare kube-proxy }
- Kube Scheduler Client Certificate
{ cat > kube-scheduler-csr.json << EOF { "CN": "system:kube-scheduler", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "system:kube-scheduler", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ kube-scheduler-csr.json | cfssljson -bare kube-scheduler }
Generating the Kubernetes API Server Certificate¶
- Note: 10.32.0.1 - Common use this IP. Can be used by the pods in some scenarios
cd ~/kthw export CERT_HOSTNAME=10.32.0.1,<controller node 1 Private IP>,<controller node 1 hostname>,<controller node 2 Private IP>,<controller node 2 hostname>,<API load balancer Private IP>,<API load balancer hostname>,127.0.0.1,localhost,kubernetes.default { cat > kubernetes-csr.json << EOF { "CN": "kubernetes", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -hostname=${CERT_HOSTNAME} \ -profile=kubernetes \ kubernetes-csr.json | cfssljson -bare kubernetes }
Generating the Service Account Key Pair¶
cd ~/kthw { cat > service-account-csr.json << EOF { "CN": "service-accounts", "key": { "algo": "rsa", "size": 2048 }, "names": [ { "C": "US", "L": "Portland", "O": "Kubernetes", "OU": "Kubernetes The Hard Way", "ST": "Oregon" } ] } EOF cfssl gencert \ -ca=ca.pem \ -ca-key=ca-key.pem \ -config=ca-config.json \ -profile=kubernetes \ service-account-csr.json | cfssljson -bare service-account }
Distributing the Certificate Files¶
Move certificate files to the worker nodes:¶
scp ca.pem <worker 1 hostname>-key.pem <worker 1 hostname>.pem user@<worker 1 public IP>:~/ scp ca.pem <worker 2 hostname>-key.pem <worker 2 hostname>.pem user@<worker 2 public IP>:~/
Move certificate files to the Master nodes:¶
scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem service-account-key.pem service-account.pem user@<master 1 public IP>:~/ scp ca.pem ca-key.pem kubernetes-key.pem kubernetes.pem service-account-key.pem service-account.pem user@<master 2 public IP>:~/
Create TLS For Applications (Not Cluster, only same pods)¶
# Find the CA certificate on a pod in your cluster: kubectl exec busybox -- ls /var/run/secrets/kubernetes.io/serviceaccount cfssl version # Create a CSR file - Need instal cfssl tool cat <<EOF | cfssl genkey - | cfssljson -bare server { "hosts": [ "my-svc.my-namespace.svc.cluster.local", "my-pod.my-namespace.pod.cluster.local", "172.168.0.24", "10.0.34.2" ], "CN": "my-pod.my-namespace.pod.cluster.local", "key": { "algo": "ecdsa", "size": 256 } } EOF # Create a CertificateSigningRequest API object: cat <<EOF | kubectl create -f - apiVersion: certificates.k8s.io/v1beta1 kind: CertificateSigningRequest metadata: name: pod-csr.web spec: groups: - system:authenticated request: $(cat server.csr | base64 | tr -d '\n') usages: - digital signature - key encipherment - server auth EOF # View the CSRs in the cluster: kubectl get csr # View additional details about the CSR: kubectl describe csr pod-csr.web # Approve the CSR: kubectl certificate approve pod-csr.web # View the certificate within your CSR: kubectl get csr pod-csr.web -o yaml # Extract and decode your certificate to use in a file: kubectl get csr pod-csr.web -o jsonpath='{.status.certificate}' \ | base64 --decode > server.crt
Container Registry¶
- Create
# Create a new docker-registry secret: kubectl create secret docker-registry acr --docker-server=https://podofminerva.azurecr.io --docker-username=podofminerva --docker-password='otj701c9OucKZOCx5qrRblofcNRf3W+e' --docker-email=user@example.com # Modify the default service account to use your new docker-registry secret: kubectl patch sa default -p '{"imagePullSecrets": [{"name": "acr"}]}'
apiVersion: v1 kind: Pod metadata: name: acr-pod labels: app: busybox spec: containers: - name: busybox image: podofminerva.azurecr.io/busybox:latest command: ['sh', '-c', 'echo Hello Kubernetes! && sleep 3600'] imagePullPolicy: Always
Security Contexts¶
- The YAML for a container that runs as a user
apiVersion: v1 kind: Pod metadata: name: alpine-user-context spec: containers: - name: main image: alpine command: ["/bin/sleep", "999999"] securityContext: runAsUser: 405
- The YAML for a pod that runs the container as non-root
apiVersion: v1 kind: Pod metadata: name: alpine-nonroot spec: containers: - name: main image: alpine command: ["/bin/sleep", "999999"] securityContext: runAsNonRoot: true
- The YAML for a privileged container pod
apiVersion: v1 kind: Pod metadata: name: privileged-pod spec: containers: - name: main image: alpine command: ["/bin/sleep", "999999"] securityContext: privileged: true
- The YAML for a container that will allow you to change the time
apiVersion: v1 kind: Pod metadata: name: kernelchange-pod spec: containers: - name: main image: alpine command: ["/bin/sleep", "999999"] securityContext: capabilities: add: - SYS_TIME
- The YAML for a container that removes capabilities
apiVersion: v1 kind: Pod metadata: name: remove-capabilities spec: containers: - name: main image: alpine command: ["/bin/sleep", "999999"] securityContext: capabilities: drop: - CHOWN
- The YAML for a pod container that can’t write to the local filesystem
apiVersion: v1 kind: Pod metadata: name: readonly-pod spec: containers: - name: main image: alpine command: ["/bin/sleep", "999999"] securityContext: readOnlyRootFilesystem: true volumeMounts: - name: my-volume mountPath: /volume readOnly: false volumes: - name: my-volume emptyDir:
- The YAML for a pod that has different group permissions for different pods
apiVersion: v1 kind: Pod metadata: name: group-context spec: securityContext: fsGroup: 555 supplementalGroups: [666, 777] containers: - name: first image: alpine command: ["/bin/sleep", "999999"] securityContext: runAsUser: 1111 volumeMounts: - name: shared-volume mountPath: /volume readOnly: false - name: second image: alpine command: ["/bin/sleep", "999999"] securityContext: runAsUser: 2222 volumeMounts: - name: shared-volume mountPath: /volume readOnly: false volumes: - name: shared-volume emptyDir:
Persistent Key Value Store¶
# Generate a key for your https server: openssl genrsa -out https.key 2048 # Generate a certificate for the https server: openssl req -new -x509 -key https.key -out https.cert -days 3650 -subj /CN=www.example.com # Create an empty file to create the secret: touch file # Create a secret from your key, cert, and file: kubectl create secret generic example-https --from-file=https.key --from-file=https.cert --from-file=file
- Create the configMap that will mount to your pod
apiVersion: v1 kind: ConfigMap metadata: name: config data: my-nginx-config.conf: | server { listen 80; listen 443 ssl; server_name www.example.com; ssl_certificate certs/https.cert; ssl_certificate_key certs/https.key; ssl_protocols TLSv1 TLSv1.1 TLSv1.2; ssl_ciphers HIGH:!aNULL:!MD5; location / { root /usr/share/nginx/html; index index.html index.htm; } } sleep-interval: | 25
- The YAML for a pod using the new secret
apiVersion: v1 kind: Pod metadata: name: example-https spec: containers: - image: linuxacademycontent/fortune name: html-web env: - name: INTERVAL valueFrom: configMapKeyRef: name: config key: sleep-interval volumeMounts: - name: html mountPath: /var/htdocs - image: nginx:alpine name: web-server volumeMounts: - name: html mountPath: /usr/share/nginx/html readOnly: true - name: config mountPath: /etc/nginx/conf.d readOnly: true - name: certs mountPath: /etc/nginx/certs/ readOnly: true ports: - containerPort: 80 - containerPort: 443 volumes: - name: html emptyDir: {} - name: config configMap: name: config items: - key: my-nginx-config.conf path: https.conf - name: certs secret: secretName: example-https
# Use port forwarding on the pod to server traffic from 443: kubectl port-forward example-https 8443:443 & # Curl the web server to get a response: curl https://localhost:8443 -k