nixos/services/elk.nix
ediblerope ddb208b95d Fix ELK: explicitly disable ES 8.x security on both containers
ES 8.x enables security and enrollment by default. Adding
xpack.security.enrollment.enabled=false to Elasticsearch and
xpack.security.enabled=false to Kibana suppresses the enrollment
token screen and lets Kibana connect directly over HTTP.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 21:31:02 +01:00

104 lines
3.4 KiB
Nix

{ config, lib, pkgs, ... }:
let
# Pin all three to the same version — mismatches between ES/Kibana/Filebeat cause errors
# Check https://www.docker.elastic.co for latest 8.x tag
elasticVersion = "8.17.0";
filebeatConfig = pkgs.writeText "filebeat.yml" ''
filebeat.modules:
- module: suricata
eve:
enabled: true
var.paths: ["/var/log/suricata/eve.json"]
# Auto-install Kibana dashboards on first run
setup.dashboards.enabled: true
setup.kibana:
host: "kibana:5601"
output.elasticsearch:
hosts: ["elasticsearch:9200"]
# Reduce logging noise
logging.level: warning
'';
in
{
config = lib.mkIf (config.networking.hostName == "FredOS-Mediaserver") {
virtualisation.oci-containers.containers = {
elasticsearch = {
image = "docker.elastic.co/elasticsearch/elasticsearch:${elasticVersion}";
environment = {
# Single-node cluster — no replica shards needed
"discovery.type" = "single-node";
# Security disabled — ES is not exposed externally
"xpack.security.enabled" = "false";
# Disable enrollment flow (ES 8.x auto-enables this when security is on)
"xpack.security.enrollment.enabled" = "false";
# Keep heap at 1g; ES default is 50% of RAM which is excessive here
"ES_JAVA_OPTS" = "-Xms1g -Xmx1g";
};
volumes = [ "elasticsearch-data:/usr/share/elasticsearch/data" ];
extraOptions = [ "--network=elk" ];
};
kibana = {
image = "docker.elastic.co/kibana/kibana:${elasticVersion}";
environment = {
"ELASTICSEARCH_HOSTS" = "http://elasticsearch:9200";
# Tell Kibana security is off — suppresses the enrollment token screen
"xpack.security.enabled" = "false";
# Cap Node.js heap — default is uncapped
"NODE_OPTIONS" = "--max-old-space-size=512";
};
ports = [ "5601:5601" ];
extraOptions = [ "--network=elk" ];
dependsOn = [ "elasticsearch" ];
};
filebeat = {
image = "docker.elastic.co/beats/filebeat:${elasticVersion}";
extraOptions = [
"--network=elk"
# root so it can read /var/log/suricata owned by the suricata user
"--user=root"
# Filebeat refuses to start if config file is group/world writable
"--security-opt=no-new-privileges:true"
];
volumes = [
"/var/log/suricata:/var/log/suricata:ro"
"${filebeatConfig}:/usr/share/filebeat/filebeat.yml:ro"
];
dependsOn = [ "elasticsearch" "kibana" ];
};
};
# Create the elk bridge network before any container starts
systemd.services.init-elk-docker-network = {
description = "Create elk Docker network";
after = [ "docker.service" ];
requires = [ "docker.service" ];
before = [
"docker-elasticsearch.service"
"docker-kibana.service"
"docker-filebeat.service"
];
wantedBy = [ "multi-user.target" ];
serviceConfig = {
Type = "oneshot";
RemainAfterExit = true;
};
script = ''
${pkgs.docker}/bin/docker network inspect elk >/dev/null 2>&1 \
|| ${pkgs.docker}/bin/docker network create elk
'';
};
# Kibana accessible on the LAN
networking.firewall.allowedTCPPorts = [ 5601 ];
};
}