Kubernetes Storage and Security

Kubernetes Storage and Security

Kubernetes has become the go-to platform for deploying and managing containerized applications, and mastering its storage and security aspects is crucial for any DevOps professional. This guide provides an in-depth look at Persistent Volumes (PVs), Persistent Volume Claims (PVCs), Storage Classes, StatefulSets, Role-Based Access Control (RBAC), Secrets, Network Policies, and Transport Layer Security (TLS).


Persistent Volumes (PVs) and Persistent Volume Claims (PVCs)

Persistent Volumes (PVs)

Persistent Volumes (PVs) are storage units in a Kubernetes cluster that exist independently of any Pod. PVs can be provisioned manually or dynamically using Storage Classes and can utilize different storage backends like NFS, iSCSI, or cloud provider solutions.

Creating a Persistent Volume:

apiVersion: v1
kind: PersistentVolume
metadata:
  name: my-pv
spec:
  capacity:
    storage: 5Gi
  accessModes:
    - ReadWriteOnce
  nfs:
    path: /exported/path
    server: nfs-server.example.com

Apply the configuration with:

kubectl apply -f pv.yml

This creates a PV backed by an NFS server, with 5Gi of storage accessible by a single node.

Persistent Volume Claims (PVCs)

PVCs are how users request storage in Kubernetes. A PVC will bind to a matching PV based on the size and access mode requested.

Creating a Persistent Volume Claim:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: my-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi

Apply with:

kubectl apply -f pvc.yml

Using PVCs in Pods:

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
    - name: my-container
      image: nginx
      volumeMounts:
        - mountPath: "/usr/share/nginx/html"
          name: my-storage
  volumes:
    - name: my-storage
      persistentVolumeClaim:
        claimName: my-pvc

Deploy the Pod with:

kubectl apply -f pod.yml

This configuration mounts the PVC at /usr/share/nginx/html in the NGINX container.

Short Name

$ kubectl api-resources
NAME                   SHORTNAMES APIVERSION        NAMESPACED KIND
persistentvolumeclaims pvc        v1                true       PersistentVolumeClaim
persistentvolumes      pv         v1                false      PersistentVolume
storageclasses         sc         storage.k8s.io/v1 false      StorageClass

Storage Classes

Overview

Storage Classes allow administrators to define different storage profiles, enabling dynamic PV provisioning with specific characteristics like performance and replication.

Creating a Storage Class:

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: fast-storage
provisioner: kubernetes.io/aws-ebs
parameters:
  type: io1
  iopsPerGb: "10"
  fsType: ext4

Apply with:

kubectl apply -f storageclass.yml

Using a Storage Class with a PVC:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: fast-pvc
spec:
  storageClassName: fast-storage
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 10Gi

Deploy the PVC with:

kubectl apply -f pvc-with-sc.yml

This PVC requests 10Gi of storage from the fast-storage class.


StatefulSets

Overview

StatefulSets are designed for stateful applications, ensuring stable identities for Pods, ordered deployment, and storage persistence. Each Pod in a StatefulSet has its own PVC, guaranteeing data consistency.

Deploying a StatefulSet:

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  serviceName: "nginx"
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: k8s.gcr.io/nginx-slim:0.8
        ports:
        - containerPort: 80
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:
  - metadata:
      name: www
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi

Apply the StatefulSet with:

kubectl apply -f statefulset.yml

Each Pod will have a dedicated PVC for its data.


Role-Based Access Control (RBAC)

Node Authorization

In a Kubernetes cluster, each node runs a kubelet process that communicates with the Kubernetes API server to manage pods and containers running on that node. The kubelet process is responsible for reporting the status of the node and its containers, and for executing commands and pulling images from the container registry.

Node Authorization is a specific type of authorization mode in Kubernetes that is used to authorize API requests made by Kubelets. It is not intended for user authorization.

To be authorized by the Node authorizer, a kubelet must use a credential that identifies it as a member of the system:nodes group and has a specific username format of system:node:<nodeName>. Therefore, when a kubelet makes an API request to the Kubernetes API server, it includes this TLS certificate as part of its credentials, along with the system:node:<nodeName> username and system:nodes group. The Node authorizer verifies these credentials to ensure that the kubelet is authorized to make the requested API call.

node authorization

ABAC (Attribute-Based Access Control)

Attribute-Based Access Control (ABAC) uses attributes to determine if a user or process has access to a resource. These policies consist of rules that match attributes in a user’s request with attributes in the policy.

ABAC (Attribute-Based Access Control)

However, managing and updating ABAC policies can become complex, especially as the number of policies and attributes increase. This can lead to potential difficulties in maintaining the system over time.

ABAC drawback

RBAC (Role-Based Access Control)

Role-Based Access Control (RBAC) is a widely adopted authorization mode in Kubernetes that allows cluster administrators to create roles and bind them to users, groups, or service accounts. Roles define a set of specific permissions, while role bindings attach these roles to the appropriate entities, granting administrators precise control over resource access. When changes are required to user access, administrators can easily modify the assigned role, which is immediately reflected across all users assigned to that role. Unlike ABAC, RBAC provides a simpler and more scalable approach to managing access by allowing administrators to define specific roles and permissions for different entities.

RBAC (Role-Based Access Control)

We will dive deeper into RBAC in the next story.

Webhook

Webhook authorization mode allows for custom authorization logic by delegating the authorization decision to an external HTTP service, known as a webhook.

When a user or process sends a request to the Kubernetes API server, the server sends a webhook authorization request to the external authorizer. The authorizer evaluates the request against the defined policies and sends a callback response indicating whether the request is authorized or not. This enables Kubernetes administrators to implement complex and customized authorization rules specific to their organization’s needs using any external system for making authorization decisions, such as an LDAP or Active Directory server, a database, or custom code.

Webhook

AlwaysAllow

AlwaysAllow mode allows all requests without any further authorization checks. This mode can be useful in certain development or testing environments, where ease of access is prioritized over security. However, it is not recommended for use in production environments.

AlwaysDeny

AlwaysDeny mode denies all requests without any further authorization checks. This mode can be useful in highly secure environments where strict access control is critical, or in situations where authorization is not required at all.

Configure Authorization Modes

  1. Edit the kube-apiserver.yaml configuration file located at /etc/kubernetes/manifests/kube-apiserver.yaml

  2. Add the desired authorization mode to the --authorization-mode flag, separating multiple modes with commas.

configure authorization modes

For example, to enable RBAC and Webhook modes, add the following line under the kube-apiserver command:

--authorization-mode=RBAC,Webhook

3. Save the configuration file and restart the kube-apiserver process to apply the changes.

Multiple Authorization Modes

Multiple Authorization Modes allows cluster administrators to use more than one authorization mode to secure access to Kubernetes resources. When a user makes a request to the Kubernetes API server, each authorization mode is evaluated in order until one of them successfully authorizes or denies the request. If all modes fail, the request is denied.

Creating a Role:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

Apply the Role with:

kubectl apply -f role.yml

Binding a Role to a User:

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: ojas
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Apply the RoleBinding with:

kubectl apply -f rolebinding.yml

The user ojas now has read-only access to Pods in the default namespace.


Secrets

Overview

Secrets securely store sensitive information like passwords, API tokens, and SSH keys. They can be used to inject environment variables or mount them as files in Pods.

Secret

In Kubernetes, Secrets are API objects used to store sensitive information, such as passwords, tokens, and keys. They are used in a similar way to ConfigMaps but are encoded in base64 format. Secrets can be used to separate sensitive data from application code and make it easier to manage and modify the data independently of the application. Secrets can be consumed by Pods as environment variables, command-line arguments, or as configuration files in a volume.

In a restaurant, a secure pantry is like a Secret in Kubernetes that stores valuable and confidential ingredients such as truffles, saffron, and caviar. This pantry is different from general pantries because it has strict access controls and security measures to prevent unauthorized access or theft.

Secret Data is Not Encrypted

Secret data in Kubernetes is stored in base64 format, which is not an encryption mechanism. This means that the encoded data can be easily decoded back to its original form, making it unsuitable for storing highly sensitive information.

One way to improve the security of Secrets is to use encryption. Kubernetes provides the ability to encrypt Secrets at rest using a feature called EncryptionConfig. EncryptionConfig allows you to encrypt sensitive data in Kubernetes using a data encryption key. When you create a Secret, Kubernetes will automatically encrypt the data using the encryption key, and store it in etcd in an encrypted format.

Another best practice is to use external secret management systems, such as Vault or Azure Key Vault, to store and manage sensitive data.

Encode and Decode in Base64

encode and decode in base64

encode a string

-n : no trailing newline

$ echo -n "<string>" | base64

example:

$ echo -n "hello world" | base64
output: aGVsbG8gd29ybGQ=

decode a base64-encoded string

$ echo -n "<base64_encoded_string>" | base64 --decode
$ echo -n "<base64_encoded_string>" | base64 -d

example:

$ echo -n "aGVsbG8gd29ybGQ=" | base64 --decode
hello world

Secret with YAML

Two steps to work with Secret in Kubernetes.

Secret with YAML

1. Create a Secret Object

secret.yaml

apiVersion: v1
kind: Secret
metedata:
  name: <secret_name>
data:
  <key1>: <base64_encoded_value1>
  <key2>: <base64_encoded_value2>
        :
  <keyM>: <base64_encoded_valueM>

Run kubectl createcommand to create a new Secret object.

2. Inject Secret to Pod

2.1 Environment variables

  • Configure with all key-value pairs in a Secret

pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: <pod_name>
  namespace: <namespace_name>
  labels:
    <key1>: <value1>
    <key2>: <value2>
           :
           :
    <keyN>: <valueN>
spec:
  containers:
    - name: <container_name>
      image: <image>
      envFrom:
      - secreteRef:
          name: <secret_name>
apiVersion: v1
kind: Pod
metadata:
  name: <pod_name>
  namespace: <namespace_name>
  labels:
    <key1>: <value1>
    <key2>: <value2>
           :
           :
    <keyN>: <valueN>
spec:
  containers:
    - name: <container_name>
      image: <image>
      env:
        - name: <environment_variable_name>
          valueFrom:
            secretKeyRef:
              name: <secret_name>
              key: <key_in_secret>

2.2 Volume

pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: <pod_name>
  namespace: <namespace_name>
  labels:
    <key1>: <value1>
    <key2>: <value2>
           :
           :
    <keyN>: <valueN>
spec:
  containers:
    - name: <container_name>
      image: <image>
      volumeMounts:
      - name: <volume_name>
        mountPath: <mount_path>
  volumes:
    - name: <volume_name>
      secret:
        secretName: <secret_name>

Network Policies

Ingress and Egress Traffic

ingress and egress traffic

Ingress traffic refers to the incoming network traffic that is directed to a pod or a group of pods in the Kubernetes cluster. For example, if a user outside the cluster sends a request to a pod within the cluster, the traffic would be considered ingress traffic to that pod.

Egress traffic, on the other hand, refers to the outgoing network traffic from a pod or a group of pods in the Kubernetes cluster. For example, if a pod in the cluster sends a request to a service or an external endpoint outside the cluster, the traffic would be considered egress traffic from that pod.

Network Policies

By default, Kubernetes clusters allow unrestricted communication between pods and external access, which can pose security risks, especially in multi-tenant environments where multiple applications and teams coexist.

NetworkPolicy is a Kubernetes object that allows you to create policies that define how pods can communicate with each other and with external entities within a specific namespace. NetworkPolicy rules can be based on various factors such as IP addresses, ports, protocols, and labels, enabling you to restrict traffic to specific pods or groups of pods based on your security requirements.

Short Name: netpol

$ kubectl api-resources
NAME             SHORTNAMES   APIVERSION            NAMESPACED  KIND
networkpolicies  netpol       networking.k8s.io/v1  true        NetworkPolicy

NetworkPolicy with YAML

podSelector

The podSelector field selects pods based on their labels and determines which pods the policy applies to.

podSelector

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-network-policy
spec:
  podSelector:
    matchLabels:
      name: backend
  policyTypes:    
    - Ingress    
    - Egress
  ingress:
    - from:
        - podSelector:
            matchLabels:
            name: frontend
      ports:
        - port: 8080
          protocol: TCP
  egress:
    - to:
        - podSelector:
            matchLabels:
            name: database
      ports:
        - port: 5432
          protocol: TCP

In this case, this NetworkPolicy targets pods labeled with name: backend. The ingress section defines incoming traffic rules from name: frontend pods on port 8080. The egress section defines outgoing traffic rules to name: database pods on port 5432.

namespaceSelector

namespaceSelector is a field that allows you to select particular namespaces and apply network policy rules to all the pods within those namespaces.

namespaceSelector

...
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
          name: namespace1
    ports:
      - port: 8080
        protocol: TCP
...

In this case, it allows traffic from the pods in namespace1.

ipBlock

ipBlock is a field used to specify IP address blocks that are allowed to access or denied access to the pod. It can be used to define a CIDR block or a single IP address.

ipBlock

...
ingress:
  - from:
      - ipBlock:
          cidr: 192.168.0.0/16
    ports:
      - port: 8080
        protocol: TCP
...

In this example, the ipBlock field is used to specify the CIDR block 192.168.0.0/16. The ingress section allows incoming traffic to the pod on port 8080 using the TCP protocol only if the source IP address is within this CIDR block.


Transport Layer Security (TLS)

TLS Certificates

TLS certificates are utilized in Kubernetes to secure communication between various components within a Kubernetes cluster. Each component has its own digital certificate issued by a trusted third-party organization known as a Certificate Authority (CA). These certificates enable components to authenticate their identities when communicating with one another, ensuring that only authorized entities can access sensitive data or services.

To verify the identity of other components, each component needs to have a copy of the CA’s public certificate. This public certificate is used to verify that the digital certificate presented by the other component is valid and was issued by the same trusted CA. This ensures that the communication between components is secure and that sensitive data or services are protected.

Client Certificates

A client certificate is used to identify a client or user who is accessing a Kubernetes cluster or its components.

Client certificates

Server Certificates

A server certificate is used to identify a Kubernetes component, such as the API server, etcd, or kubelet.

Generating TLS Certificates: Use OpenSSL to create a certificate:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=my-app/O=my-org"

Creating a TLS Secret:

kubectl create secret tls my-tls-secret --cert=tls.crt --key=tls.key

Using TLS in Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: tls-ingress
spec:
  tls:
  - hosts:
    - my-app.example.com
    secretName: my-tls-secret
  rules:
  - host: my-app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-service
            port:
              number: 80

Deploy the Ingress with:

kubectl apply -f ingress.yml

This Ingress routes traffic to my-service and secures it using TLS.


💡
If you need help or have any questions, just leave them in the comments! 📝 I would be happy to answer them!
💡
If you found this post useful, please give it a thumbs up 👍 and consider following for more helpful content. 😊

Thank you for taking the time to read! 💚