Files
linux-scripts/alloy-config-generator.sh
T
chiefgeek a1a17e81a1 Sync all scripts from website downloads — 352 scripts total
Includes updated JS challenge scripts with Claude-User whitelist,
same-site referer bypass, Blackbox-Exporter allowed bot, and all
new exporters, cheat sheets, and automation scripts.
2026-05-25 03:31:08 +02:00

577 lines
14 KiB
Bash
Executable File

#!/usr/bin/env bash
#
# Alloy Config Generator
#
# Interactive script that generates a Grafana Alloy configuration
# file based on your environment. Asks what backends you use, what
# signals to collect, and what services to monitor, then outputs
# a working config.alloy ready to deploy.
#
# Usage:
# ./alloy-config-generator.sh
# ./alloy-config-generator.sh -o /etc/alloy/config.alloy
# ./alloy-config-generator.sh --non-interactive --metrics --logs --prometheus-url http://mimir:9009
#
# Parameters:
# -o, --output FILE Write config to file (default: stdout)
# --non-interactive Skip prompts, use flags and defaults
# --metrics Enable host metrics collection
# --logs Enable log collection
# --traces Enable OTLP trace collection
# --journald Enable journald log collection
# --docker Enable Docker container log collection
# --nginx Enable nginx log collection
# --prometheus-url URL Prometheus/Mimir remote_write URL
# --loki-url URL Loki push URL
# --tempo-url URL Tempo OTLP endpoint (host:port)
# --hostname NAME Hostname label for metrics and logs
# --scrape-targets LIST Comma-separated host:port targets to scrape
# --help Show usage
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
# Version: 1.0
set -euo pipefail
# --- Configuration ---
readonly VERSION="1.0"
readonly SCRIPT_NAME="$(basename "$0")"
# Defaults
OUTPUT=""
NON_INTERACTIVE=false
ENABLE_METRICS=false
ENABLE_LOGS=false
ENABLE_TRACES=false
ENABLE_JOURNALD=false
ENABLE_DOCKER=false
ENABLE_NGINX=false
PROMETHEUS_URL=""
LOKI_URL=""
TEMPO_URL=""
HOSTNAME=""
SCRAPE_TARGETS=""
# --- Functions ---
usage() {
cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS]
Alloy Config Generator v${VERSION}
Generates a Grafana Alloy configuration file interactively or from flags.
Options:
-o, --output FILE Write config to file (default: stdout)
--non-interactive Skip prompts, use flags and defaults
--metrics Enable host metrics collection
--logs Enable log collection
--traces Enable OTLP trace collection
--journald Enable journald log collection
--docker Enable Docker container log collection
--nginx Enable nginx log collection
--prometheus-url URL Prometheus/Mimir remote_write URL
--loki-url URL Loki push URL
--tempo-url URL Tempo OTLP endpoint (host:port)
--hostname NAME Hostname label
--scrape-targets LIST Comma-separated host:port targets to scrape
--help Show this help
Examples:
$SCRIPT_NAME
$SCRIPT_NAME -o /etc/alloy/config.alloy
$SCRIPT_NAME --non-interactive --metrics --logs --prometheus-url http://mimir:9009/api/v1/push --loki-url http://loki:3100/loki/api/v1/push
EOF
exit 0
}
ask() {
local prompt="$1"
local default="$2"
local var_name="$3"
local response
if [[ "$NON_INTERACTIVE" == true ]]; then
eval "$var_name=\"$default\""
return
fi
if [[ -n "$default" ]]; then
read -r -p "$prompt [$default]: " response
eval "$var_name=\"${response:-$default}\""
else
read -r -p "$prompt: " response
eval "$var_name=\"$response\""
fi
}
ask_yn() {
local prompt="$1"
local default="$2"
local var_name="$3"
if [[ "$NON_INTERACTIVE" == true ]]; then
return
fi
local yn_hint="y/n"
[[ "$default" == "y" ]] && yn_hint="Y/n"
[[ "$default" == "n" ]] && yn_hint="y/N"
local response
read -r -p "$prompt ($yn_hint): " response
response="${response:-$default}"
case "$response" in
[Yy]*) eval "$var_name=true" ;;
*) eval "$var_name=false" ;;
esac
}
interactive_setup() {
echo ""
echo "Alloy Config Generator v${VERSION}"
echo "================================="
echo ""
echo "This generates a Grafana Alloy config.alloy file."
echo ""
# Hostname
local detected_hostname
detected_hostname=$(hostname -s 2>/dev/null || echo "server")
ask "Hostname label" "$detected_hostname" HOSTNAME
echo ""
# --- Backends ---
echo "== Backends =="
echo ""
ask "Prometheus/Mimir remote_write URL (leave empty to skip metrics)" "http://prometheus:9090/api/v1/write" PROMETHEUS_URL
if [[ -n "$PROMETHEUS_URL" ]]; then
ENABLE_METRICS=true
fi
ask "Loki push URL (leave empty to skip logs)" "http://loki:3100/loki/api/v1/push" LOKI_URL
if [[ -n "$LOKI_URL" ]]; then
ENABLE_LOGS=true
fi
ask "Tempo OTLP endpoint host:port (leave empty to skip traces)" "" TEMPO_URL
if [[ -n "$TEMPO_URL" ]]; then
ENABLE_TRACES=true
fi
echo ""
# --- Metrics options ---
if [[ "$ENABLE_METRICS" == true ]]; then
echo "== Metrics =="
echo ""
local extra_targets=""
ask "Additional Prometheus scrape targets (comma-separated host:port, or empty)" "" extra_targets
if [[ -n "$extra_targets" ]]; then
SCRAPE_TARGETS="$extra_targets"
fi
echo ""
fi
# --- Log options ---
if [[ "$ENABLE_LOGS" == true ]]; then
echo "== Logs =="
echo ""
ask_yn "Collect journald logs?" "y" ENABLE_JOURNALD
ask_yn "Collect Docker container logs?" "n" ENABLE_DOCKER
ask_yn "Collect nginx logs?" "n" ENABLE_NGINX
echo ""
fi
}
# --- Config generation functions ---
generate_header() {
cat <<EOF
// Grafana Alloy Configuration
// Generated by alloy-config-generator.sh v${VERSION}
// $(date +"%Y-%m-%d %H:%M:%S")
//
// Hostname: ${HOSTNAME}
// Docs: https://mylinux.work/guides/grafana-alloy-setup/
EOF
}
generate_metrics() {
if [[ "$ENABLE_METRICS" != true ]]; then
return
fi
cat <<EOF
// =========================================
// Metrics: host metrics → ${PROMETHEUS_URL}
// =========================================
prometheus.exporter.unix "default" {
set_collectors = ["cpu", "diskstats", "filesystem", "loadavg", "meminfo", "netdev", "uname"]
filesystem {
mount_points_exclude = "^/(sys|proc|dev|run)(\$|/)"
}
}
prometheus.scrape "node" {
targets = prometheus.exporter.unix.default.targets
scrape_interval = "15s"
forward_to = [prometheus.remote_write.default.receiver]
}
// Scrape Alloy internal metrics
prometheus.scrape "alloy_self" {
targets = [
{"__address__" = "localhost:12345"},
]
scrape_interval = "30s"
forward_to = [prometheus.remote_write.default.receiver]
}
EOF
# Additional scrape targets
if [[ -n "$SCRAPE_TARGETS" ]]; then
cat <<EOF
// Additional scrape targets
prometheus.scrape "extra" {
targets = [
EOF
IFS=',' read -ra targets <<< "$SCRAPE_TARGETS"
for target in "${targets[@]}"; do
target=$(echo "$target" | xargs) # trim whitespace
echo " {\"__address__\" = \"${target}\"},"
done
cat <<EOF
]
scrape_interval = "15s"
forward_to = [prometheus.remote_write.default.receiver]
}
EOF
fi
cat <<EOF
prometheus.remote_write "default" {
endpoint {
url = "${PROMETHEUS_URL}"
}
external_labels = {
host = "${HOSTNAME}",
}
}
EOF
}
generate_logs_header() {
if [[ "$ENABLE_LOGS" != true ]]; then
return
fi
cat <<EOF
// =========================================
// Logs → ${LOKI_URL}
// =========================================
EOF
}
generate_logs_system() {
if [[ "$ENABLE_LOGS" != true ]]; then
return
fi
# Detect OS family for log paths
local syslog_path="/var/log/syslog"
local auth_path="/var/log/auth.log"
if [[ -f /etc/redhat-release ]] || [[ -f /etc/rocky-release ]] || [[ -f /etc/almalinux-release ]]; then
syslog_path="/var/log/messages"
auth_path="/var/log/secure"
fi
cat <<EOF
// System log files
local.file_match "system_logs" {
path_targets = [
{__path__ = "${syslog_path}", job = "syslog"},
{__path__ = "${auth_path}", job = "auth"},
]
}
loki.source.file "system" {
targets = local.file_match.system_logs.targets
forward_to = [loki.process.add_host.receiver]
}
loki.process "add_host" {
forward_to = [loki.write.default.receiver]
stage.static_labels {
values = {
host = "${HOSTNAME}",
}
}
}
EOF
}
generate_logs_journald() {
if [[ "$ENABLE_JOURNALD" != true ]]; then
return
fi
cat <<EOF
// Journald
loki.source.journal "default" {
max_age = "12h"
relabel_rules = loki.relabel.journal.rules
forward_to = [loki.write.default.receiver]
}
loki.relabel "journal" {
forward_to = []
rule {
source_labels = ["__journal__systemd_unit"]
target_label = "unit"
}
rule {
source_labels = ["__journal__hostname"]
target_label = "host"
}
rule {
source_labels = ["__journal__priority_keyword"]
target_label = "level"
}
}
EOF
}
generate_logs_docker() {
if [[ "$ENABLE_DOCKER" != true ]]; then
return
fi
cat <<EOF
// Docker container logs
discovery.docker "containers" {
host = "unix:///var/run/docker.sock"
}
discovery.relabel "docker" {
targets = discovery.docker.containers.targets
rule {
source_labels = ["__meta_docker_container_name"]
target_label = "container"
regex = "/(.*)"
}
rule {
source_labels = ["__meta_docker_container_log_stream"]
target_label = "stream"
}
}
loki.source.docker "default" {
host = "unix:///var/run/docker.sock"
targets = discovery.docker.containers.targets
labels = {"host" = "${HOSTNAME}", "job" = "docker"}
forward_to = [loki.write.default.receiver]
relabel_rules = discovery.relabel.docker.rules
}
EOF
}
generate_logs_nginx() {
if [[ "$ENABLE_NGINX" != true ]]; then
return
fi
cat <<EOF
// Nginx logs
local.file_match "nginx" {
path_targets = [
{__path__ = "/var/log/nginx/access.log", job = "nginx_access"},
{__path__ = "/var/log/nginx/error.log", job = "nginx_error"},
]
}
loki.source.file "nginx" {
targets = local.file_match.nginx.targets
forward_to = [loki.process.nginx.receiver]
}
loki.process "nginx" {
forward_to = [loki.write.default.receiver]
stage.static_labels {
values = {
host = "${HOSTNAME}",
}
}
stage.regex {
expression = "^(?P<remote_addr>\\\\S+) - (?P<remote_user>\\\\S+) \\\\[(?P<timestamp>.+?)\\\\] \"(?P<method>\\\\S+) (?P<path>\\\\S+) (?P<protocol>\\\\S+)\" (?P<status>\\\\d+) (?P<bytes>\\\\d+)"
}
stage.labels {
values = {
method = "",
status = "",
}
}
}
EOF
}
generate_logs_write() {
if [[ "$ENABLE_LOGS" != true ]]; then
return
fi
cat <<EOF
loki.write "default" {
endpoint {
url = "${LOKI_URL}"
}
}
EOF
}
generate_traces() {
if [[ "$ENABLE_TRACES" != true ]]; then
return
fi
cat <<EOF
// =========================================
// Traces: OTLP → ${TEMPO_URL}
// =========================================
otelcol.receiver.otlp "default" {
grpc {
endpoint = "0.0.0.0:4317"
}
http {
endpoint = "0.0.0.0:4318"
}
output {
traces = [otelcol.processor.batch.default.input]
}
}
otelcol.processor.batch "default" {
timeout = "5s"
output {
traces = [otelcol.exporter.otlp.tempo.input]
}
}
otelcol.exporter.otlp "tempo" {
client {
endpoint = "${TEMPO_URL}"
tls {
insecure = true
}
}
}
EOF
}
generate_config() {
generate_header
generate_metrics
generate_logs_header
generate_logs_system
generate_logs_journald
generate_logs_docker
generate_logs_nginx
generate_logs_write
generate_traces
}
# --- Main ---
main() {
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-o|--output) OUTPUT="$2"; shift 2 ;;
--non-interactive) NON_INTERACTIVE=true; shift ;;
--metrics) ENABLE_METRICS=true; shift ;;
--logs) ENABLE_LOGS=true; shift ;;
--traces) ENABLE_TRACES=true; shift ;;
--journald) ENABLE_JOURNALD=true; shift ;;
--docker) ENABLE_DOCKER=true; shift ;;
--nginx) ENABLE_NGINX=true; shift ;;
--prometheus-url) PROMETHEUS_URL="$2"; ENABLE_METRICS=true; shift 2 ;;
--loki-url) LOKI_URL="$2"; ENABLE_LOGS=true; shift 2 ;;
--tempo-url) TEMPO_URL="$2"; ENABLE_TRACES=true; shift 2 ;;
--hostname) HOSTNAME="$2"; shift 2 ;;
--scrape-targets) SCRAPE_TARGETS="$2"; shift 2 ;;
--help|-h) usage ;;
*) echo "Unknown option: $1" >&2; usage ;;
esac
done
# Set hostname default
if [[ -z "$HOSTNAME" ]]; then
HOSTNAME=$(hostname -s 2>/dev/null || echo "server")
fi
# Set backend URL defaults for non-interactive mode
if [[ "$NON_INTERACTIVE" == true ]]; then
[[ "$ENABLE_METRICS" == true && -z "$PROMETHEUS_URL" ]] && PROMETHEUS_URL="http://prometheus:9090/api/v1/write"
[[ "$ENABLE_LOGS" == true && -z "$LOKI_URL" ]] && LOKI_URL="http://loki:3100/loki/api/v1/push"
[[ "$ENABLE_TRACES" == true && -z "$TEMPO_URL" ]] && TEMPO_URL="tempo:4317"
fi
# Interactive mode
if [[ "$NON_INTERACTIVE" != true ]]; then
interactive_setup
fi
# Check at least one signal is enabled
if [[ "$ENABLE_METRICS" != true && "$ENABLE_LOGS" != true && "$ENABLE_TRACES" != true ]]; then
echo "ERROR: No signals enabled. Enable at least one of: metrics, logs, traces" >&2
exit 1
fi
# Generate config
if [[ -n "$OUTPUT" ]]; then
generate_config > "$OUTPUT"
echo ""
echo "Config written to: $OUTPUT"
echo ""
echo "Signals enabled:"
[[ "$ENABLE_METRICS" == true ]] && echo " ✓ Metrics → $PROMETHEUS_URL"
[[ "$ENABLE_LOGS" == true ]] && echo " ✓ Logs → $LOKI_URL"
[[ "$ENABLE_TRACES" == true ]] && echo " ✓ Traces → $TEMPO_URL"
echo ""
echo "Next steps:"
echo " 1. Review the config: cat $OUTPUT"
echo " 2. Validate syntax: alloy fmt $OUTPUT"
echo " 3. Test it: alloy run $OUTPUT"
echo " 4. Deploy: sudo cp $OUTPUT /etc/alloy/config.alloy && sudo systemctl restart alloy"
else
generate_config
fi
}
main "$@"