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>
104 lines
3.4 KiB
Nix
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 ];
|
|
|
|
};
|
|
}
|