~/blog/2024-03-28-nixos-and-wazuh

Wazuh on NixOS

cat ./table-of-contents

I’ve been wanting to get a proxmox server with Wazuh running on one of the VMs, but there wasn’t any services for Wazuh in nixpkgs yet. I ended creating this nix docker unit, which corresponds to the wazuh-docker single node configuration. Wazuh Agent is currently under development!

Initial Steps #

Make sure to generate the certificates within that directory:

docker-compose -f generate-indexer-certs.yml run --rm generator

You’ll also want the entire config directory to be relative to this unit.

Nix Config #

The pkgs.dockerTools.pullImage will pull the image beforehand and build it directly into the nix store. That way docker doesn’t have to pull down the image before running the wazuh services.

[!NOTE]

The image needs at least 20GB and 8GB of ram if you end up virtualizing it. See requirements
{ pkgs, lib, ... }:
with lib;
let
  version = "4.7.3";
  enable = true;
  ulimits = [ "--ulimit" "memlock=-1:-1" ];
  wazuh =
    {
      manager = {
        hostname = "wazuh.manager";
        image = pkgs.dockerTools.pullImage {
          imageName = "wazuh/wazuh-manager";
          imageDigest =
            "sha256:8b418c4faf64e5ba8d9f16f20f9697a16b371058c7b2d7be6ff7e88de54e8603";
          finalImageTag = version;
          sha256 = "sha256-EKxBbJv9c9/jdjITPtGERlyxSZmhZWeGzqq0TIZqQgc=";
        };
      };
      indexer = {
        hostname = "wazuh.indexer";
        image = pkgs.dockerTools.pullImage {
          imageName = "wazuh/wazuh-indexer";
          imageDigest =
            "sha256:b976a99ec2d0311ee468d3e9ab4abebd941aea1bb4b32b337216e8cb63c8be3b";
          finalImageTag = version;
          sha256 = "sha256-isgFZPuQWGlXM3zyOdAd4rbP0r+qM/myMlMAXtJaz2c=";
        };
      };
      dashboard = {
        hostname = "wazuh.dashboard";
        image = pkgs.dockerTools.pullImage
          {
            imageName = "wazuh/wazuh-dashboard";
            imageDigest =
              "sha256:f151b074dce5b1f95fa5490359a2b34d5c8b0331e264fe26beb3ef48f8375bde";
            finalImageTag = version;
            sha256 = "sha256-xCeWyDwVxBkprIgqCn3x6UKx474+c+ZLx9LW/mrHYus=";
          };
      };
    };
in
mkIf enable {


  # Since we use "--network=host" we need to add the hostnames to /etc/hosts
  networking.extraHosts = ''
    127.0.0.1 ${wazuh.manager.hostname} ${wazuh.indexer.hostname} ${wazuh.dashboard.hostname}
  '';

  networking.firewall = {
    allowedTCPPorts = [ 1514 1515 55000 9200 5601 ];
    allowedUDPPorts = [ 514 ];
  };

  virtualisation.oci-containers = {
    backend = "docker";
    containers = {

      wazuh-manager = {
        hostname = wazuh.manager.hostname;
        image = "wazuh/wazuh-manager:${version}";
        imageFile = wazuh.manager.image;
        autoStart = true;
        extraOptions = [ "--network=host" "--ulimit" "nofile=655360:655360" ] ++ ulimits;
        environment = {
          INDEXER_URL = "https://${wazuh.indexer.hostname}:9200";
          INDEXER_USERNAME = "admin";
          INDEXER_PASSWORD = "SecretPassword";
          FILEBEAT_SSL_VERIFICATION_MODE = "full";
          SSL_CERTIFICATE_AUTHORITIES = "/etc/ssl/root-ca.pem";
          SSL_CERTIFICATE = "/etc/ssl/filebeat.pem";
          SSL_KEY = "/etc/ssl/filebeat.key";
          API_USERNAME = "wazuh-wui";
          API_PASSWORD = "MyS3cr37P450r.*-";
        };
        ports = [
          "1514:1514"
          "1515:1515"
          "514:514/udp"
          "55000:55000"
        ];
        volumes = [
          "wazuh_api_configuration:/var/ossec/api/configuration"
          "wazuh_etc:/var/ossec/etc"
          "wazuh_logs:/var/ossec/logs"
          "wazuh_queue:/var/ossec/queue"
          "wazuh_var_multigroups:/var/ossec/var/multigroups"
          "wazuh_integrations:/var/ossec/integrations"
          "wazuh_active_response:/var/ossec/active-response/bin"
          "wazuh_agentless:/var/ossec/agentless"
          "wazuh_wodles:/var/ossec/wodles"
          "filebeat_etc:/etc/filebeat"
          "filebeat_var:/var/lib/filebeat"
          "${./config/wazuh_indexer_ssl_certs/root-ca-manager.pem}:/etc/ssl/root-ca.pem"
          "${./config/wazuh_indexer_ssl_certs/wazuh.manager.pem}:/etc/ssl/filebeat.pem"
          "${./config/wazuh_indexer_ssl_certs/wazuh.manager-key.pem}:/etc/ssl/filebeat.key"
          "${./config/wazuh_cluster/wazuh_manager.conf}:/wazuh-config-mount/etc/ossec.conf"
        ];
      };

      wazuh-indexer = {
        hostname = wazuh.indexer.hostname;
        image = "wazuh/wazuh-indexer:${version}";
        imageFile = wazuh.indexer.image;
        autoStart = true;
        extraOptions = [ "--network=host" "--ulimit" "nofile=65536:65536" ] ++ ulimits;
        dependsOn = [ ];
        environment = {
          OPENSEARCH_JAVA_OPTS = "-Xms1g -Xmx1g";
        };
        ports = [
          "9200:9200"
        ];
        volumes = [
          "wazuh-indexer-data:/var/lib/wazuh-indexer"
          "${./config/wazuh_indexer_ssl_certs/root-ca.pem}:/usr/share/wazuh-indexer/certs/root-ca.pem"
          "${./config/wazuh_indexer_ssl_certs/wazuh.indexer-key.pem}:/usr/share/wazuh-indexer/certs/wazuh.indexer.key"
          "${./config/wazuh_indexer_ssl_certs/wazuh.indexer.pem}:/usr/share/wazuh-indexer/certs/wazuh.indexer.pem"
          "${./config/wazuh_indexer_ssl_certs/admin.pem}:/usr/share/wazuh-indexer/certs/admin.pem"
          "${./config/wazuh_indexer_ssl_certs/admin-key.pem}:/usr/share/wazuh-indexer/certs/admin-key.pem"
          "${./config/wazuh_indexer/wazuh.indexer.yml}:/usr/share/wazuh-indexer/opensearch.yml"
          "${./config/wazuh_indexer/internal_users.yml}:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml"
        ];
      };

      wazuh-dashboard = {
        hostname = wazuh.dashboard.hostname;
        image = "wazuh/wazuh-dashboard:${version}";
        imageFile = wazuh.dashboard.image;
        autoStart = true;
        ports = [
          "443:5601"
        ];
        environment = {
          INDEXER_USERNAME = "admin ";
          INDEXER_PASSWORD = "SecretPassword";
          WAZUH_API_URL = "https://${wazuh.manager.hostname}";
          DASHBOARD_USERNAME = "kibanaserver";
          DASHBOARD_PASSWORD = "kibanaserver";
          API_USERNAME = "wazuh-wui";
          API_PASSWORD = "MyS3cr37P450r.*-";
        };
        extraOptions = [ "--network=host" ];
        dependsOn = [ "wazuh-indexer" ];
        volumes = [
          "${./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem}:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem"
          "${./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem}:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem"
          "${./config/wazuh_indexer_ssl_certs/root-ca.pem}:/usr/share/wazuh-dashboard/certs/root-ca.pem"
          "${./config/wazuh_dashboard/opensearch_dashboards.yml}:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml"
          "${./config/wazuh_dashboard/wazuh.yml}:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml"
          "wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config"
          "wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom"
        ];
      };
    };
  };
}