From 4349141b410804d1225080e738797bbac70c0872 Mon Sep 17 00:00:00 2001 From: Giacomo Sanchietti Date: Tue, 3 Mar 2026 14:04:45 +0100 Subject: [PATCH] fix: enable webssh and prometheus auth on upgrade - Ensures prometheus basicAuth credentials are properly configured in grafana.yml - Ensures webssh forward_auth is properly configured in Traefik routes - Grafana configuration is now generated at every Grafana restart --- .../actions/configure-module/20configure | 42 ----------- imageroot/bin/expand-grafana-config | 70 +++++++++++++++++++ imageroot/systemd/user/grafana.service | 1 + .../update-module.d/12update_traefik_routes | 49 +++++++++++++ 4 files changed, 120 insertions(+), 42 deletions(-) create mode 100755 imageroot/bin/expand-grafana-config create mode 100755 imageroot/update-module.d/12update_traefik_routes diff --git a/imageroot/actions/configure-module/20configure b/imageroot/actions/configure-module/20configure index 78fe12f..09c2b71 100755 --- a/imageroot/actions/configure-module/20configure +++ b/imageroot/actions/configure-module/20configure @@ -8,7 +8,6 @@ import json import sys import agent -import agent.tasks import os import uuid @@ -169,47 +168,6 @@ with open('prometheus.env', 'w') as pfp: pfp.write(f"PROMETHEUS_PATH={config['prometheus_path']}\n") pfp.write(f"PROMETHEUS_RETENTION={metrics_retention_days}d\n") -# Grafana configuration -db = agent.read_envfile('db.env') -secret = agent.read_envfile('secret.env') -prometheus_user = secret.get('PROMETHEUS_AUTH_USERNAME', 'prometheus') -prometheus_pass = secret.get('PROMETHEUS_AUTH_PASSWORD', 'prometheus') -with open('grafana.yml', 'w') as fp: - fp.write("apiVersion: 1\n") - fp.write("datasources:\n") - fp.write(' - name: Local Promethus\n') - fp.write(' type: prometheus\n') - fp.write(' uid: prometheus\n') - fp.write(' access: proxy\n') - fp.write(f' url: http://127.0.0.1:{ports[7]}{config["prometheus_path"]}\n') - fp.write(' basicAuth: true\n') - fp.write(f' basicAuthUser: {prometheus_user}\n') - fp.write(' secureJsonData:\n') - fp.write(f' basicAuthPassword: {prometheus_pass}\n') - - fp.write(' - name: Local Loki\n') - fp.write(' type: loki\n') - fp.write(' uid: loki\n') - fp.write(' access: proxy\n') - fp.write(f' url: http://127.0.0.1:{ports[5]}\n') - - fp.write(' - name: Local Timescale\n') - fp.write(' type: postgres\n') - fp.write(' uid: timescale\n') - fp.write(f' url: 127.0.0.1:{db.get("POSTGRES_PORT")}\n') - fp.write(f' user: grafana\n') - fp.write(' secureJsonData:\n') - fp.write(f' password: {db.get("GRAFANA_POSTGRES_PASSWORD")}\n') - fp.write(' jsonData:\n') - fp.write(' database: report\n') - fp.write(' sslmode: disable\n') - fp.write(' maxOpenConns: 100\n') - fp.write(' maxIdleConns: 100\n') - fp.write(' maxIdleConnsAuto: true\n') - fp.write(' connMaxLifetime: 14400\n') - fp.write(' postgresVersion: 1500\n') - fp.write(' timescaledb: true\n') - network = agent.read_envfile('network.env') # Listen also on VPN server address diff --git a/imageroot/bin/expand-grafana-config b/imageroot/bin/expand-grafana-config new file mode 100755 index 0000000..8c9a9d2 --- /dev/null +++ b/imageroot/bin/expand-grafana-config @@ -0,0 +1,70 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2026 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import json +import agent + +# Load configuration +try: + with open('config.json', 'r') as f: + config = json.load(f) +except FileNotFoundError: + with open('grafana.yml', 'w') as fp: + fp.write("apiVersion: 1\n") + fp.write("datasources: []\n") + exit(0) + +# Load secrets and database configuration +secret = agent.read_envfile('secret.env') +db = agent.read_envfile('db.env') + +# Get Prometheus credentials +prometheus_user = secret.get('PROMETHEUS_AUTH_USERNAME', 'prometheus') +prometheus_pass = secret.get('PROMETHEUS_AUTH_PASSWORD', 'prometheus') + +# Get nework configuration +loki = agent.read_envfile('loki.env') +loki_port = loki.get('LOKI_HTTP_PORT') +prometheus_config = agent.read_envfile('prometheus.env') +prometheus_port = prometheus_config.get('PROMETHEUS_PORT') + +# Generate grafana.yml +with open('grafana.yml', 'w') as fp: + fp.write("apiVersion: 1\n") + fp.write("datasources:\n") + fp.write(' - name: Local Promethus\n') + fp.write(' type: prometheus\n') + fp.write(' uid: prometheus\n') + fp.write(' access: proxy\n') + fp.write(f' url: http://127.0.0.1:{prometheus_port}{config["prometheus_path"]}\n') + fp.write(' basicAuth: true\n') + fp.write(f' basicAuthUser: {prometheus_user}\n') + fp.write(' secureJsonData:\n') + fp.write(f' basicAuthPassword: {prometheus_pass}\n') + + fp.write(' - name: Local Loki\n') + fp.write(' type: loki\n') + fp.write(' uid: loki\n') + fp.write(' access: proxy\n') + fp.write(f' url: http://127.0.0.1:{loki_port}\n') + + fp.write(' - name: Local Timescale\n') + fp.write(' type: postgres\n') + fp.write(' uid: timescale\n') + fp.write(f' url: 127.0.0.1:{db.get("POSTGRES_PORT")}\n') + fp.write(f' user: grafana\n') + fp.write(' secureJsonData:\n') + fp.write(f' password: {db.get("GRAFANA_POSTGRES_PASSWORD")}\n') + fp.write(' jsonData:\n') + fp.write(' database: report\n') + fp.write(' sslmode: disable\n') + fp.write(' maxOpenConns: 100\n') + fp.write(' maxIdleConns: 100\n') + fp.write(' maxIdleConnsAuto: true\n') + fp.write(' connMaxLifetime: 14400\n') + fp.write(' postgresVersion: 1500\n') + fp.write(' timescaledb: true\n') diff --git a/imageroot/systemd/user/grafana.service b/imageroot/systemd/user/grafana.service index 0103966..9c2e94d 100644 --- a/imageroot/systemd/user/grafana.service +++ b/imageroot/systemd/user/grafana.service @@ -10,6 +10,7 @@ EnvironmentFile=%S/state/environment Restart=always TimeoutStopSec=70 ExecStartPre=/bin/rm -f %t/grafana.pid %t/grafana.ctr-id +ExecStartPre=runagent expand-grafana-config ExecStart=/usr/bin/podman run \ --conmon-pidfile %t/grafana.pid \ --cidfile %t/grafana.ctr-id \ diff --git a/imageroot/update-module.d/12update_traefik_routes b/imageroot/update-module.d/12update_traefik_routes new file mode 100755 index 0000000..0b1a59f --- /dev/null +++ b/imageroot/update-module.d/12update_traefik_routes @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 + +# +# Copyright (C) 2026 Nethesis S.r.l. +# SPDX-License-Identifier: GPL-3.0-or-later +# + +import json +import sys +import agent +import os + +(start,end) = os.environ["TCP_PORTS_RANGE"].split('-') +ports = [*range(int(start), int(end)+1)] + +try: + with open('config.json', 'r') as tmp: + config = json.load(tmp) +except: + sys.exit(0) + +# Exit if not configured +if not config.get('webssh_path'): + sys.exit(0) + +try: + network = agent.read_envfile('network.env') + webssh_port = network.get('WEBSSH_PORT') +except: + sys.exit(0) + +# Configure Traefik to route requests to the webssh service with forward_auth +set_route_data = { + 'instance': os.environ['MODULE_ID'] + '_webssh', + 'url': f'http://127.0.0.1:{webssh_port}', + 'http2https': True, + 'host': config.get('host', ''), + 'path': config['webssh_path'], + 'strip_prefix': True, + 'lets_encrypt_check': True, + 'lets_encrypt_cleanup': True, + 'forward_auth': { + 'address': f'http://127.0.0.1:{ports[3]}/api/auth', + }, +} +if config.get('lets_encrypt'): + set_route_data['lets_encrypt'] = config['lets_encrypt'] + +agent.set_route(set_route_data)