Grafana Loki - Hardened Log Aggregation Server
بواسطة Lynxroute
Loki 3.7.8 - CIS Level 1 hardened log aggregation on Ubuntu 24.04 LTS, SBOM + CIS
What is Grafana Loki
Grafana Loki is a horizontally-scalable, multi-tenant log aggregation system, shipped as a single statically linked Go binary and run here in monolithic mode on one node. Instead of indexing the full text of every log line, Loki indexes only a small set of labels and stores compressed log chunks, which keeps storage and cost low. It exposes an HTTP API: push logs in with Promtail, Grafana Alloy, the Docker logging driver, Fluentd or Fluent Bit, and query them back with LogQL, Loki's label-and-filter query language. This image keeps chunks and the TSDB index on the local filesystem under /var/lib/loki, with retention and compaction enabled. Loki has no web UI of its own; you query it through Grafana as a data source, logcli, or curl, and an info landing page is served on the perimeter so the root URL responds.
Why self-host Grafana Loki
Running Loki on a VM you control keeps every log line - which often carries credentials, hostnames, user activity and infrastructure topology - inside your own tenant rather than a third-party logging service. Self-hosting suits teams with data residency requirements, organisations operating under GDPR or ISO 27001, and any architecture where a long-retention log store must sit next to the workloads it collects from with no per-gigabyte ingest fees. Loki is AGPL-3.0, fully auditable, with no vendor lock-in.
What this VM image adds
Security hardening:
- Loki API and gRPC bound to localhost only - the HTTP API (port 3100) and gRPC (9096) listen on 127.0.0.1 and are never exposed directly
- Nginx TLS reverse proxy on port 443 - the Loki API is reached only through Nginx, never the raw service port; an info landing page answers the root URL
- HTTP Basic Auth perimeter - a unique credential is generated per instance at first boot and stored in /root/loki-credentials.txt (Loki has no native authentication)
- Usage analytics disabled - no phone-home to the upstream telemetry endpoint
- Loki runs as a non-root service with a restricted systemd unit
- certbot and the Nginx plugin pre-installed - one-command CA-signed HTTPS to replace the self-signed certificate
- UFW firewall - SSH on 22, plus 80 and 443 only; Azure IMDS and WireServer egress pre-configured
- fail2ban - SSH brute-force protection
- AppArmor - mandatory access control
- CVE scan - every image is scanned with Trivy before release
OS hardening (CIS Level 1):
- CIS Ubuntu 24.04 LTS Level 1 Benchmark via ansible-lockdown
- auditd for system call auditing of critical paths
- SSH hardening - PasswordAuthentication disabled, key-only access, PermitRootLogin no, LoginGraceTime 60
- Kernel hardening - SYN cookies, ASLR, rp_filter, kexec disabled, IPv6 off
- /tmp as tmpfs with nosuid, nodev, noexec
Compliance artifacts (inside the VM):
- SBOM - CycloneDX 1.6 at /etc/lynxroute/sbom.json with Loki pinned by version, PURL, AGPL-3.0 license, supplier, and SHA-256 hash of the binary
- CIS Conformance Report at /etc/lynxroute/cis-report.html (OpenSCAP, Azure tailoring profile, 0 FAIL rules)
- Tailored CIS profile at /usr/share/doc/lynxroute/CIS_TAILORED_PROFILE.md
- Basic Auth credentials file at /root/loki-credentials.txt with the admin username and password
Quick Start
- Deploy VM from Azure Marketplace (Standard_D2s_v3 recommended)
- SSH: ssh -i key.pem azureuser@<PUBLIC_IP>, then sudo cat /root/loki-credentials.txt for the admin Basic Auth password
- Open NSG: TCP 443 and TCP 80 from your trusted sources, TCP 22 from your management IPs only
- Open https://<PUBLIC_IP>/ in your browser, accept the self-signed certificate warning, and authenticate as admin to see the Loki API info page
- Ship logs to https://<PUBLIC_IP>/loki/api/v1/push, add Loki to Grafana as a data source pointing at the same host, and query with LogQL
Loki has no web UI of its own; the Nginx HTTP Basic Auth credential (user admin) protects the entire API. The service listens on localhost only and is reached through Nginx on port 443.