Home Audit a K3s Cluster in Wazuh
Post
Cancel

Audit a K3s Cluster in Wazuh

Configure the Wazuh Server

Special thanks to the awesome documentation from the Wazuh team. Where I got the first part of this blog post from.

Create certificates for communication between the Wazuh server and Kubernetes

  1. Login your Wazuh server.

    1
    
     ssh user@wazuh-server
    
  2. Create a directory for the webhook endpoint.

    1
    
     mkdir -p /var/ossec/integrations/kubernetes-webhook/
    
  3. Add the following to /var/ossec/integrations/kubernetes-webhook/csr.conf. Replace <wazuh_server_ip> and <wazuh_server_ip> with your server’s IP address. The file content from the Wazuh documentation didn’t work for me.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
     cat > /var/ossec/integrations/kubernetes-webhook/csr.conf << EOF
     [ req ]
     prompt = no
     default_bits = 2048
     default_md = sha256
     distinguished_name = req_distinguished_name
     x509_extensions = v3_req
     [req_distinguished_name]
     C = US
     ST = California
     L = San Jose
     O = Wazuh
     OU = Research and development
     emailAddress = info@wazuh.com
     CN = <wazuh_server_ip>
     [ v3_req ]
     authorityKeyIdentifier=keyid,issuer
     basicConstraints = CA:FALSE
     keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
     subjectAltName = @alt_names
     [alt_names]
     IP.1 = <wazuh_server_ip>
     EOF
    
  4. Create the root CA public and private keys.

    1
    2
    3
    4
    
     openssl req -x509 -new -nodes -newkey rsa:2048 \
       -keyout /var/ossec/integrations/kubernetes-webhook/rootCA.key \
       -out /var/ossec/integrations/kubernetes-webhook/rootCA.pem \
       -batch -subj "/C=US/ST=California/L=San Jose/O=Wazuh"
    
  5. Create the certificate signing request and the server’s private key.

    1
    2
    3
    4
    
     openssl req -new -nodes -newkey rsa:2048 \
       -keyout /var/ossec/integrations/kubernetes-webhook/server.key \
       -out /var/ossec/integrations/kubernetes-webhook/server.csr \
       -config /var/ossec/integrations/kubernetes-webhook/csr.conf
    
  6. Generate the server’s certificate.

    1
    2
    3
    4
    5
    6
    7
    
     openssl x509 -req -in /var/ossec/integrations/kubernetes-webhook/server.csr \
       -CA /var/ossec/integrations/kubernetes-webhook/rootCA.pem \
       -CAkey /var/ossec/integrations/kubernetes-webhook/rootCA.key \
       -CAcreateserial \
       -out /var/ossec/integrations/kubernetes-webhook/server.crt \
       -extfile /var/ossec/integrations/kubernetes-webhook/csr.conf \
       -extensions v3_req
    

Create Listener using Python

The listener acts as a webhook and parses all incoming requests from the Kubernetes cluster.

  1. Install Flask using pip.

    1
    
     /var/ossec/framework/python/bin/pip3 install flask
    
  2. Replace <wazuh_server_ip> and copy the content into the file /var/ossec/integrations/custom-webhook.py.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    
     #!/var/ossec/framework/python/bin/python3
     import json
     from socket import socket, AF_UNIX, SOCK_DGRAM
     from flask import Flask, request
    
     PORT     = 8080
     CERT     = '/var/ossec/integrations/kubernetes-webhook/server.crt'
     CERT_KEY = '/var/ossec/integrations/kubernetes-webhook/server.key'
     socket_addr = '/var/ossec/queue/sockets/queue'
    
     def send_event(msg):
         string = '1:k8s:{0}'.format(json.dumps(msg))
         sock = socket(AF_UNIX, SOCK_DGRAM)
         sock.connect(socket_addr)
         sock.send(string.encode())
         sock.close()
         return True
    
     app = Flask(__name__)
     context = (CERT, CERT_KEY)
     @app.route('/', methods=['POST'])
     def webhook():
         if request.method == 'POST':
             if send_event(request.json):
                 print("Request sent to Wazuh")
             else:
                 print("Failed to send request to Wazuh")
         return "Webhook received!"
    
     if __name__ == '__main__':
         app.run(host='<wazuh_server_ip>', port=PORT, ssl_context=context)
    
  3. Create a systemd service at /lib/systemd/system/wazuh-webhook.service.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    
     [Unit]
     Description=Wazuh webhook
     Wants=network-online.target
     After=network.target network-online.target
    
     [Service]
     ExecStart=/var/ossec/framework/python/bin/python3 /var/ossec/integrations/custom-webhook.py
     Restart=on-failure
    
     [Install]
     WantedBy=multi-user.target
    
  4. Start the service

    1
    2
    3
    4
    
     systemctl daemon-reload
     systemctl enable wazuh-webhook.service
     systemctl start wazuh-webhook.service
     systemctl status wazuh-webhook.service
    
  5. Enable access to port 8080 if a firewall is running on the Wazuh server.

Configure the Kubernetes Cluster

SSH into the server your k3s master node is running on.

  1. Create log directory

    1
    
     mkdir -p -m 700 /var/lib/rancher/k3s/server/logs
    
  2. Create an audit policy at /var/lib/rancher/k3s/server/audit.yaml.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    
     apiVersion: audit.k8s.io/v1
     kind: Policy
     rules:
         # Don’t log requests to the following API endpoints
         - level: None
           nonResourceURLs:
               - '/healthz*'
               - '/logs'
               - '/metrics'
               - '/swagger*'
               - '/version'
    
         # Limit requests containing tokens to Metadata level so the token is not included in the log
         - level: Metadata
           omitStages:
               - RequestReceived
           resources:
               - group: authentication.k8s.io
                 resources:
                     - tokenreviews
    
         # Extended audit of auth delegation
         - level: RequestResponse
           omitStages:
               - RequestReceived
           resources:
               - group: authorization.k8s.io
                 resources:
                     - subjectaccessreviews
    
         # Log changes to pods at RequestResponse level
         - level: RequestResponse
           omitStages:
               - RequestReceived
           resources:
               # core API group; add third-party API services and your API services if needed
               - group: ''
                 resources: ['pods']
                 verbs: ['create', 'patch', 'update', 'delete']
    
         # Log everything else at Metadata level
         - level: Metadata
           omitStages:
               - RequestReceived
    
  3. Replace <wazuh_server_ip> and create the webhook configuration file /var/lib/rancher/k3s/server/audit-webhook.yaml.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
     apiVersion: v1
     kind: Config
     preferences: {}
     clusters:
       - name: wazuh-webhook
         cluster:
           insecure-skip-tls-verify: true
           server: https://<wazuh_server_ip>:8080 
    
     # kubeconfig files require a context. Provide one for the API server.
     current-context: webhook
     contexts:
     - context:
         cluster: wazuh-webhook
         user: kube-apiserver # Replace with name of API server if it’s different
       name: webhook
    
  4. Add the Kubeapi parameters to load the auditing and webhook configurations.

    1
    2
    3
    4
    5
    6
    
     ExecStart=/usr/local/bin/k3s \
     server \
     '--kube-apiserver-arg=audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log' \
     '--kube-apiserver-arg=audit-policy-file=/var/lib/rancher/k3s/server/audit.yaml' \
     '--kube-apiserver-arg=audit-webhook-config-file=/var/lib/rancher/k3s/server/audit-webhook.yaml' \
     '--kube-apiserver-arg=audit-webhook-batch-max-size=1' \
    

Create detection rule on Wazuh Server

  1. Add the following rules to the Wazuh server at /var/ossec/etc/rules/local_rules.xml.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
     <group name="k8s_audit,">
       <rule id="110002" level="0">
         <location>k8s</location>
         <field name="apiVersion">audit</field>
         <description>Kubernetes audit log.</description>
       </rule>
    
       <rule id="110003" level="5">
         <if_sid>110002</if_sid>
         <regex type="pcre2">requestURI\":.+", \"verb\": \"create</regex>
         <description>Kubernetes request to create resource</description>
       </rule>
    
       <rule id="110004" level="5">
         <if_sid>110002</if_sid>
         <regex type="pcre2">requestURI\":.+", \"verb\": \"delete</regex>
         <description>Kubernetes request to delete resource</description>
       </rule>
     </group>
    
  2. Restart the server.

    1
    
     systemctl restart wazuh-manager
    

Test Configuration

  1. Run the following command on the Kubernetes master node to create a new deployment.

    1
    
     kubectl create deployment hello-wazuh --image=k8s.gcr.io/echoserver:1.4
    
  2. Run the following command to delete the deployment.

    1
    
     kubectl delete deployment hello-wazuh
    
This post is licensed under CC BY 4.0 by the author.