From 0ec76d7d1d540ee9f954a420d5841c0052f70812 Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Mon, 6 Apr 2026 10:59:46 +0100 Subject: [PATCH 1/2] feat: [#415] add GF_SERVER_ROOT_URL to Grafana service in generated docker-compose --- .../services/rendering/docker_compose.rs | 9 +++++++++ .../wrappers/docker_compose/context/grafana.rs | 17 +++++++++++++++++ .../template/wrappers/env/context.rs | 16 +++++++++++++++- templates/docker-compose/.env.tera | 4 ++++ .../docker-compose/docker-compose.yml.tera | 3 +++ 5 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/application/services/rendering/docker_compose.rs b/src/application/services/rendering/docker_compose.rs index 1be6d94f3..94652d4a7 100644 --- a/src/application/services/rendering/docker_compose.rs +++ b/src/application/services/rendering/docker_compose.rs @@ -318,9 +318,18 @@ impl DockerComposeTemplateRenderingService { /// Apply Grafana credentials to environment context if Grafana is configured fn apply_grafana_env_context(env_context: EnvContext, user_inputs: &UserInputs) -> EnvContext { if let Some(grafana_config) = user_inputs.grafana() { + let server_root_url = grafana_config.domain().map(|domain| { + let scheme = if grafana_config.use_tls_proxy() { + "https" + } else { + "http" + }; + format!("{scheme}://{}", domain.as_str()) + }); env_context.with_grafana( grafana_config.admin_user().to_string(), grafana_config.admin_password().expose_secret().to_string(), + server_root_url, ) } else { env_context diff --git a/src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/context/grafana.rs b/src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/context/grafana.rs index 101467f67..62eca609b 100644 --- a/src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/context/grafana.rs +++ b/src/infrastructure/templating/docker_compose/template/wrappers/docker_compose/context/grafana.rs @@ -23,6 +23,14 @@ pub struct GrafanaServiceContext { /// Flattened for template compatibility - serializes ports/networks at top level. #[serde(flatten)] pub topology: ServiceTopology, + + /// Server root URL for public dashboard share links (optional) + /// + /// When set, this is injected as `GF_SERVER_ROOT_URL` env var so that + /// Grafana generates correct absolute URLs for shared dashboards. + /// Derived from the configured domain and TLS setting. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_root_url: Option, } impl GrafanaServiceContext { @@ -43,8 +51,17 @@ impl GrafanaServiceContext { .iter() .map(PortDefinition::from) .collect(); + let server_root_url = config.domain().map(|domain| { + let scheme = if config.use_tls_proxy() { + "https" + } else { + "http" + }; + format!("{scheme}://{}", domain.as_str()) + }); Self { topology: ServiceTopology::new(ports, networks), + server_root_url, } } diff --git a/src/infrastructure/templating/docker_compose/template/wrappers/env/context.rs b/src/infrastructure/templating/docker_compose/template/wrappers/env/context.rs index 09e50aa90..1c1113e1f 100644 --- a/src/infrastructure/templating/docker_compose/template/wrappers/env/context.rs +++ b/src/infrastructure/templating/docker_compose/template/wrappers/env/context.rs @@ -49,6 +49,12 @@ pub struct GrafanaServiceConfig { pub admin_user: String, /// Grafana admin password (exposed from secrecy wrapper) pub admin_password: String, + /// Grafana server root URL for correct public dashboard share links (optional) + /// + /// Derived from the configured domain and TLS setting. + /// Maps to the `GF_SERVER_ROOT_URL` environment variable. + #[serde(skip_serializing_if = "Option::is_none")] + pub server_root_url: Option, } /// Context for rendering the .env template @@ -186,11 +192,19 @@ impl EnvContext { /// /// * `admin_user` - Grafana admin username /// * `admin_password` - Grafana admin password (plain String, already exposed) + /// * `server_root_url` - Optional full URL (e.g. `https://grafana.example.com`) for + /// public dashboard share links; derived from domain + TLS setting #[must_use] - pub fn with_grafana(mut self, admin_user: String, admin_password: String) -> Self { + pub fn with_grafana( + mut self, + admin_user: String, + admin_password: String, + server_root_url: Option, + ) -> Self { self.grafana = Some(GrafanaServiceConfig { admin_user, admin_password, + server_root_url, }); self } diff --git a/templates/docker-compose/.env.tera b/templates/docker-compose/.env.tera index 3b8529190..48de51d5d 100644 --- a/templates/docker-compose/.env.tera +++ b/templates/docker-compose/.env.tera @@ -55,4 +55,8 @@ MYSQL_PASSWORD='{{ mysql.password }}' # WARNING: Change default credentials in production deployments for security GF_SECURITY_ADMIN_USER='{{ grafana.admin_user }}' GF_SECURITY_ADMIN_PASSWORD='{{ grafana.admin_password }}' +{%- if grafana.server_root_url %} +# Grafana server root URL — used to generate correct public dashboard share links +GF_SERVER_ROOT_URL='{{ grafana.server_root_url }}' +{%- endif %} {%- endif %} diff --git a/templates/docker-compose/docker-compose.yml.tera b/templates/docker-compose/docker-compose.yml.tera index 450ec10ad..7df5c2ade 100644 --- a/templates/docker-compose/docker-compose.yml.tera +++ b/templates/docker-compose/docker-compose.yml.tera @@ -173,6 +173,9 @@ services: environment: - GF_SECURITY_ADMIN_USER=${GF_SECURITY_ADMIN_USER} - GF_SECURITY_ADMIN_PASSWORD=${GF_SECURITY_ADMIN_PASSWORD} +{%- if grafana.server_root_url %} + - GF_SERVER_ROOT_URL=${GF_SERVER_ROOT_URL} +{%- endif %} volumes: - ./storage/grafana/data:/var/lib/grafana - ./storage/grafana/provisioning:/etc/grafana/provisioning:ro From 8a269d10b592db7b8158158449f756ec01637b3a Mon Sep 17 00:00:00 2001 From: Jose Celano Date: Mon, 6 Apr 2026 11:16:22 +0100 Subject: [PATCH 2/2] chore: [#415] update trivy-action from 0.34.0 to 0.35.0 --- .github/workflows/docker-security-scan.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker-security-scan.yml b/.github/workflows/docker-security-scan.yml index e1e295882..455f67f8a 100644 --- a/.github/workflows/docker-security-scan.yml +++ b/.github/workflows/docker-security-scan.yml @@ -62,7 +62,7 @@ jobs: # Human-readable output in logs # This NEVER fails the job; it’s only for visibility - name: Display vulnerabilities (table format) - uses: aquasecurity/trivy-action@0.34.0 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: torrust-tracker-deployer/${{ matrix.image.name }}:latest format: "table" @@ -76,7 +76,7 @@ jobs: # - Trivy sometimes exits with 1 even when no vulns exist # - GitHub Security UI is responsible for enforcement - name: Generate SARIF (Code Scanning) - uses: aquasecurity/trivy-action@0.34.0 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: torrust-tracker-deployer/${{ matrix.image.name }}:latest format: "sarif" @@ -114,7 +114,7 @@ jobs: steps: - name: Display vulnerabilities (table format) - uses: aquasecurity/trivy-action@0.34.0 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: ${{ matrix.image }} format: "table" @@ -124,7 +124,7 @@ jobs: # Third-party images should NEVER block CI. # We only report findings to GitHub Security. - name: Generate SARIF (Code Scanning) - uses: aquasecurity/trivy-action@0.34.0 + uses: aquasecurity/trivy-action@0.35.0 with: image-ref: ${{ matrix.image }} format: "sarif"