Skip to content

Feature/grafana - #76

Merged
timn21 merged 3 commits into
mainfrom
feature/grafana
Jul 3, 2026
Merged

timn21 merged 3 commits into
mainfrom
feature/grafana

Conversation

@maliklesch

@maliklesch maliklesch commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Added grafana dashboard

Summary by CodeRabbit

  • New Features

    • Added Grafana monitoring alongside Prometheus, including a prebuilt dashboard and dashboard/data source setup.
    • Exposed Grafana in local Docker, Kubernetes, and cloud deployments with documented access details.
    • Expanded deployment summaries and outputs to show links for the app, Prometheus, and Grafana.
  • Documentation

    • Updated setup and deployment docs to cover monitoring access, Grafana credentials, and environment-specific URLs.
  • Bug Fixes

    • Improved monitoring visibility so service metrics and dashboards are available out of the box.

@coderabbitai

coderabbitai Bot commented Jul 3, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds Grafana as a monitoring component alongside existing Prometheus infrastructure across Docker Compose, the Helm chart (ConfigMaps, Deployment, Service, Secret, ingress, values), Terraform networking rules, Ansible deployment tasks/variables, CI summary output, and documentation.

Changes

Prometheus + Grafana Monitoring Rollout

Layer / File(s) Summary
Local Docker Compose Grafana service
compose.yaml, monitoring/grafana/dashboards/bytebite-overview.json, monitoring/grafana/provisioning/dashboards/bytebite.yml, monitoring/grafana/provisioning/datasources/prometheus.yml, monitoring/prometheus.yml, README.md
Adds a Grafana service to compose.yaml, its dashboard/provisioning files, a new Prometheus scrape job for Grafana, and updated root README Docker monitoring docs.
Grafana dashboard definition (Helm chart copy)
helm/bytebite/dashboards/bytebite-overview.json
Packages the same ByteBite Overview dashboard JSON (six panels, annotations, metadata) into the Helm chart directory.
Helm templates for Prometheus/Grafana workloads
helm/bytebite/templates/prometheus-configmap.yaml, .../prometheus-deployment.yaml, .../prometheus-service.yaml, .../grafana-configmap.yaml, .../grafana-dashboard-configmap.yaml, .../grafana-deployment.yaml, .../grafana-service.yaml, .../monitoring-secret.yaml, .../ingress.yaml
Adds conditional Kubernetes manifests (ConfigMaps, Deployments, Services, Secret) for Prometheus and Grafana gated by monitoring.enabled, plus a Grafana ingress path.
Helm values and chart docs for monitoring
helm/bytebite/values.yaml, helm/bytebite/values-local.yaml, helm/bytebite/README.md, README.md
Adds monitoring config blocks (images, ports, replicas, credentials, ingress path) to values files and documents new parameters and endpoints.
Terraform NSG rules for Prometheus/Grafana ports
infra/terraform/main.tf, infra/terraform/outputs.tf, infra/terraform/README.md
Adds a Grafana inbound NSG rule (port 3000) and updates public IP output description and README docs to cover ports 9090/3000.
Ansible deployment of monitoring config and credentials
infra/ansible/roles/deploy/tasks/main.yml, infra/ansible/roles/deploy/templates/env.j2, infra/ansible/group_vars/bytebite.yml, infra/ansible/deploy-vars.example.yml, infra/ansible/README.md, .github/workflows/deploy-azure.yml
Copies the full monitoring directory during deploy, adds Grafana admin credential variables/templating, and updates deployment README and CI step summary with Prometheus/Grafana URLs.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant Ingress as Ingress (grafana path)
  participant GrafanaSvc as grafana-service
  participant GrafanaPod as bytebite-grafana Deployment
  participant PromSvc as prometheus-service

  Client->>Ingress: GET /grafana
  Ingress->>GrafanaSvc: route request
  GrafanaSvc->>GrafanaPod: forward to pod
  GrafanaPod->>PromSvc: query metrics (datasource provisioning)
  PromSvc-->>GrafanaPod: metrics data
  GrafanaPod-->>Client: rendered dashboard
Loading

Related PRs: None found.

Suggested labels: infrastructure, monitoring, helm, terraform, ansible

Suggested reviewers: None found.

🐰 A rabbit hops through charts and yaml streams,
Grafana joins Prometheus in monitoring dreams,
Ports open wide on Azure's plain,
Ansible copies the monitoring domain,
Dashboards glow with panels six —
a tidy stack of ops-y tricks. 🌱

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the change set, but it is too vague and reads like a branch name rather than a descriptive summary. Rename it to a concise, descriptive title such as "Add Grafana monitoring stack and dashboard support".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/grafana

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (1)
helm/bytebite/values.yaml (1)

120-122: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

monitoring.grafana.ingress.path lacks a trailing slash used for sub-path serving.

This path value is reused in ingress.yaml (Ingress rule) and in grafana-deployment.yaml to build GF_SERVER_ROOT_URL under GF_SERVER_SERVE_FROM_SUB_PATH=true. Grafana's documented sub-path examples always end the root URL in / (e.g. .../grafana/); omitting it is a known cause of Grafana failing to load static assets behind a reverse-proxy sub-path. See related comment on grafana-deployment.yaml for a defensive template-side fix; alternatively set the default here to /grafana/.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/values.yaml` around lines 120 - 122, The Grafana ingress path
default is missing the trailing slash needed for sub-path serving. Update the
monitoring.grafana.ingress.path value in values.yaml to use the sub-path form
with a trailing slash so it matches its reuse in ingress.yaml and
grafana-deployment.yaml when building GF_SERVER_ROOT_URL. Keep the path
consistent with the Grafana sub-path configuration used by the chart.
🧹 Nitpick comments (5)
helm/bytebite/dashboards/bytebite-overview.json (1)

1-490: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicate dashboard JSON risks drift between local and Helm deployments.

This file is byte-for-byte identical to monitoring/grafana/dashboards/bytebite-overview.json. Maintaining two independent copies of a 490-line dashboard definition means future panel/query edits must be manually kept in sync in both places, or the Helm-deployed dashboard silently diverges from the local one.

Consider generating this file from a single source of truth (e.g., a build/sync script, symlink, or Helm .Files.Get reference to a shared dashboards directory) so the two never drift.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/dashboards/bytebite-overview.json` around lines 1 - 490, The
ByteBite Overview dashboard JSON is duplicated in two places, which can cause
the Helm-deployed version to drift from the local version. Consolidate the
dashboard definition into a single source of truth and have the Helm dashboard
consume it directly, for example via a shared dashboards asset or Helm file
reference, so changes to the dashboard title/uid and panel targets only need to
be made once.
helm/bytebite/templates/prometheus-deployment.yaml (2)

33-38: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Prometheus storage uses emptyDir — metrics lost on pod restart/reschedule.

Any pod restart, node drain, or rescheduling wipes all historical metrics since prometheus-data is backed by emptyDir. Consider a PersistentVolumeClaim if metric retention across restarts matters for this deployment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/templates/prometheus-deployment.yaml` around lines 33 - 38, The
Prometheus data volume is currently backed by emptyDir in the
prometheus-deployment.yaml template, so metrics are lost on pod restarts or
rescheduling. Update the prometheus-data volume configuration to use persistent
storage instead, such as a PersistentVolumeClaim, and wire it into the
Prometheus deployment spec so the data survives restarts. Use the
prometheus-data volume name and the Prometheus deployment template as the main
reference points when making the change.

16-38: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add container security context.

Trivy flags this Deployment for using the default (root-capable, writable-root-fs) security context. For a service exposed externally (per the Terraform NSG changes opening port 9090), hardening the container is worthwhile.

🔒 Proposed fix
       containers:
         - name: prometheus
           image: "{{ .Values.monitoring.prometheus.image.repository }}:{{ .Values.monitoring.prometheus.image.tag }}"
           imagePullPolicy: {{ .Values.monitoring.prometheus.image.pullPolicy }}
+          securityContext:
+            runAsNonRoot: true
+            readOnlyRootFilesystem: true
+            allowPrivilegeEscalation: false
           args:
             - --config.file=/etc/prometheus/prometheus.yml
             - --storage.tsdb.path=/prometheus
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/templates/prometheus-deployment.yaml` around lines 16 - 38, The
prometheus Deployment is missing an explicit container security context, so the
container runs with the default root-capable settings. Update the prometheus
container in the prometheus-deployment template to add a hardened
securityContext for the container, using the prometheus container spec as the
anchor. Disable privilege escalation, set a non-root user, and drop unnecessary
capabilities; if the image and mounted paths allow it, also mark the root
filesystem read-only while keeping the existing /prometheus volume writable.

Source: Linters/SAST tools

helm/bytebite/templates/grafana-deployment.yaml (2)

16-58: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Add a securityContext (non-root, read-only rootfs).

Static analysis flags the Deployment/container as using the default security context (root-capable, writable rootfs). Since grafana-data is already an emptyDir, adding securityContext with runAsNonRoot, a fixed runAsUser/fsGroup (Grafana's official image runs as uid 472), and readOnlyRootFilesystem: true would harden this without functional loss.

🔒 Suggested hardening
     spec:
+      securityContext:
+        runAsNonRoot: true
+        runAsUser: 472
+        fsGroup: 472
       containers:
         - name: grafana
           image: "{{ .Values.monitoring.grafana.image.repository }}:{{ .Values.monitoring.grafana.image.tag }}"
           imagePullPolicy: {{ .Values.monitoring.grafana.image.pullPolicy }}
+          securityContext:
+            allowPrivilegeEscalation: false
+            readOnlyRootFilesystem: true
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/templates/grafana-deployment.yaml` around lines 16 - 58, The
Grafana container in the deployment template is missing a hardened
securityContext and is still using the default root-capable, writable root
filesystem. Update the grafana container spec to add a securityContext with
runAsNonRoot, a fixed runAsUser/fsGroup matching Grafana’s official uid 472, and
readOnlyRootFilesystem set to true, while keeping the existing grafana-data
emptyDir mount usable. Use the grafana container block in
grafana-deployment.yaml to apply this without changing the provisioning or
volumeMount setup.

Source: Linters/SAST tools


56-57: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚖️ Poor tradeoff

Grafana state is not persisted across restarts.

grafana-data (/var/lib/grafana, DB/session/org state) is an emptyDir, so any manual dashboard edits, users, or annotations created outside provisioning are lost on pod restart/reschedule. Since dashboards/datasources are provisioned via ConfigMap this may be acceptable, but worth calling out for production use.

Also applies to: 74-75

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/templates/grafana-deployment.yaml` around lines 56 - 57,
Grafana state is currently backed by an ephemeral emptyDir, so manual
dashboards/users/annotations are lost on restart; update the grafana-data volume
in the grafana-deployment template to use persistent storage instead of
emptyDir, or make persistence configurable for production use. Keep the
mountPath at /var/lib/grafana and ensure the corresponding volume definition for
grafana-data is changed consistently wherever it is referenced.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/deploy-azure.yml:
- Around line 121-124: The summary lines in the deploy-azure workflow are using
inline ${{ }} expressions inside the run script, which triggers
template-injection warnings. Move steps.tf.outputs.public_ip into env for that
step, then reference it via a shell variable in the summary writes instead of
interpolating it directly in the run block. Update the three echo statements
that build the App, Prometheus, and Grafana URLs to use the env-bound value
while keeping the logic in the same workflow step.

In `@helm/bytebite/README.md`:
- Line 35: The Grafana admin password default is documented as cleartext, so
update the chart docs and default configuration around
monitoring.grafana.adminPassword to avoid shipping a known credential. Remove
the committed password default from the README and values-related templates, and
either require an explicit override or generate a random secret via the chart
helpers (for example in _helpers.tpl) so users do not deploy with a predictable
admin password.

In `@helm/bytebite/values.yaml`:
- Around line 118-119: The Grafana admin credentials in values.yaml are
hardcoded defaults, which should not be committed for the real deployment.
Update the chart values around adminUser/adminPassword so only the username
stays as a default and the password must be supplied externally at install time,
then ensure the bytebite-monitoring-secret path still reads from an injected
secret or --set value in monitoring-secret.yaml and any related Grafana config.

In `@infra/ansible/group_vars/bytebite.yml`:
- Around line 15-17: The Grafana defaults in the bytebite group vars are
insecure because they ship a real, guessable admin credential. Update the
Grafana variables in the bytebite vars file to use empty placeholders instead of
a working username/password, and add a fail-fast check in the Ansible flow (for
example near the Grafana setup task) to assert both values are explicitly
provided. Also document the required override in the example deploy vars/README,
ideally pointing users to vault-encrypted secrets, so the playbook never deploys
with a public default login.

In `@infra/ansible/roles/deploy/templates/env.j2`:
- Around line 7-8: The Grafana credential values in the env template still fall
back to weak defaults, duplicating the same fallback in other deploy paths.
Update the env.j2 template to remove the default filters from GRAFANA_ADMIN_USER
and GRAFANA_ADMIN_PASSWORD so the deploy must supply explicit or vaulted values,
and keep the credential handling consistent with the related Grafana config in
the Ansible variables and compose setup.

In `@infra/terraform/main.tf`:
- Around line 6-15: The Grafana and Prometheus ingress rules in the Terraform
security group are too open because they inherit the wildcard source prefix,
which exposes Grafana on port 3000 with default admin credentials. Update the
inbound rule handling in the main Terraform security group so the `grafana` (and
preferably `prometheus`) entry uses a restricted `source_address_prefix` or a
safer allowlist/VPN/SSH-tunnel approach instead of public access. If you keep
Grafana exposed for dev-stage, also add a guard in the Ansible deploy path for
the Grafana defaults (`grafana_admin_user` / `grafana_admin_password`) so
deployment fails when the password is still the default.

---

Duplicate comments:
In `@helm/bytebite/values.yaml`:
- Around line 120-122: The Grafana ingress path default is missing the trailing
slash needed for sub-path serving. Update the monitoring.grafana.ingress.path
value in values.yaml to use the sub-path form with a trailing slash so it
matches its reuse in ingress.yaml and grafana-deployment.yaml when building
GF_SERVER_ROOT_URL. Keep the path consistent with the Grafana sub-path
configuration used by the chart.

---

Nitpick comments:
In `@helm/bytebite/dashboards/bytebite-overview.json`:
- Around line 1-490: The ByteBite Overview dashboard JSON is duplicated in two
places, which can cause the Helm-deployed version to drift from the local
version. Consolidate the dashboard definition into a single source of truth and
have the Helm dashboard consume it directly, for example via a shared dashboards
asset or Helm file reference, so changes to the dashboard title/uid and panel
targets only need to be made once.

In `@helm/bytebite/templates/grafana-deployment.yaml`:
- Around line 16-58: The Grafana container in the deployment template is missing
a hardened securityContext and is still using the default root-capable, writable
root filesystem. Update the grafana container spec to add a securityContext with
runAsNonRoot, a fixed runAsUser/fsGroup matching Grafana’s official uid 472, and
readOnlyRootFilesystem set to true, while keeping the existing grafana-data
emptyDir mount usable. Use the grafana container block in
grafana-deployment.yaml to apply this without changing the provisioning or
volumeMount setup.
- Around line 56-57: Grafana state is currently backed by an ephemeral emptyDir,
so manual dashboards/users/annotations are lost on restart; update the
grafana-data volume in the grafana-deployment template to use persistent storage
instead of emptyDir, or make persistence configurable for production use. Keep
the mountPath at /var/lib/grafana and ensure the corresponding volume definition
for grafana-data is changed consistently wherever it is referenced.

In `@helm/bytebite/templates/prometheus-deployment.yaml`:
- Around line 33-38: The Prometheus data volume is currently backed by emptyDir
in the prometheus-deployment.yaml template, so metrics are lost on pod restarts
or rescheduling. Update the prometheus-data volume configuration to use
persistent storage instead, such as a PersistentVolumeClaim, and wire it into
the Prometheus deployment spec so the data survives restarts. Use the
prometheus-data volume name and the Prometheus deployment template as the main
reference points when making the change.
- Around line 16-38: The prometheus Deployment is missing an explicit container
security context, so the container runs with the default root-capable settings.
Update the prometheus container in the prometheus-deployment template to add a
hardened securityContext for the container, using the prometheus container spec
as the anchor. Disable privilege escalation, set a non-root user, and drop
unnecessary capabilities; if the image and mounted paths allow it, also mark the
root filesystem read-only while keeping the existing /prometheus volume
writable.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: aa68787d-fe47-4df1-9cac-5683035e37bf

📥 Commits

Reviewing files that changed from the base of the PR and between b234a21 and ff99a45.

📒 Files selected for processing (28)
  • .github/workflows/deploy-azure.yml
  • README.md
  • compose.yaml
  • helm/bytebite/README.md
  • helm/bytebite/dashboards/bytebite-overview.json
  • helm/bytebite/templates/grafana-configmap.yaml
  • helm/bytebite/templates/grafana-dashboard-configmap.yaml
  • helm/bytebite/templates/grafana-deployment.yaml
  • helm/bytebite/templates/grafana-service.yaml
  • helm/bytebite/templates/ingress.yaml
  • helm/bytebite/templates/monitoring-secret.yaml
  • helm/bytebite/templates/prometheus-configmap.yaml
  • helm/bytebite/templates/prometheus-deployment.yaml
  • helm/bytebite/templates/prometheus-service.yaml
  • helm/bytebite/values-local.yaml
  • helm/bytebite/values.yaml
  • infra/ansible/README.md
  • infra/ansible/deploy-vars.example.yml
  • infra/ansible/group_vars/bytebite.yml
  • infra/ansible/roles/deploy/tasks/main.yml
  • infra/ansible/roles/deploy/templates/env.j2
  • infra/terraform/README.md
  • infra/terraform/main.tf
  • infra/terraform/outputs.tf
  • monitoring/grafana/dashboards/bytebite-overview.json
  • monitoring/grafana/provisioning/dashboards/bytebite.yml
  • monitoring/grafana/provisioning/datasources/prometheus.yml
  • monitoring/prometheus.yml

Comment on lines +121 to +124
run: |
echo "App: http://${{ steps.tf.outputs.public_ip }}:8081" >> "$GITHUB_STEP_SUMMARY"
echo "Prometheus: http://${{ steps.tf.outputs.public_ip }}:9090" >> "$GITHUB_STEP_SUMMARY"
echo "Grafana: http://${{ steps.tf.outputs.public_ip }}:3000" >> "$GITHUB_STEP_SUMMARY"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟡 Minor | ⚡ Quick win

Avoid inline ${{ }} expansion in run: — use env: instead.

zizmor flags all three lines as template-injection. Even though public_ip here comes from Terraform (Azure-assigned), the safe, idiomatic pattern is to bind it to an env var and reference it as a shell variable rather than interpolating the expression directly into the script.

🔒 Proposed fix
       - name: Summary
         if: success()
+        env:
+          PUBLIC_IP: ${{ steps.tf.outputs.public_ip }}
         run: |
-          echo "App: http://${{ steps.tf.outputs.public_ip }}:8081" >> "$GITHUB_STEP_SUMMARY"
-          echo "Prometheus: http://${{ steps.tf.outputs.public_ip }}:9090" >> "$GITHUB_STEP_SUMMARY"
-          echo "Grafana: http://${{ steps.tf.outputs.public_ip }}:3000" >> "$GITHUB_STEP_SUMMARY"
+          echo "App: http://${PUBLIC_IP}:8081" >> "$GITHUB_STEP_SUMMARY"
+          echo "Prometheus: http://${PUBLIC_IP}:9090" >> "$GITHUB_STEP_SUMMARY"
+          echo "Grafana: http://${PUBLIC_IP}:3000" >> "$GITHUB_STEP_SUMMARY"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
run: |
echo "App: http://${{ steps.tf.outputs.public_ip }}:8081" >> "$GITHUB_STEP_SUMMARY"
echo "Prometheus: http://${{ steps.tf.outputs.public_ip }}:9090" >> "$GITHUB_STEP_SUMMARY"
echo "Grafana: http://${{ steps.tf.outputs.public_ip }}:3000" >> "$GITHUB_STEP_SUMMARY"
env:
PUBLIC_IP: ${{ steps.tf.outputs.public_ip }}
run: |
echo "App: http://${PUBLIC_IP}:8081" >> "$GITHUB_STEP_SUMMARY"
echo "Prometheus: http://${PUBLIC_IP}:9090" >> "$GITHUB_STEP_SUMMARY"
echo "Grafana: http://${PUBLIC_IP}:3000" >> "$GITHUB_STEP_SUMMARY"
🧰 Tools
🪛 zizmor (1.26.1)

[info] 122-122: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 123-123: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)


[info] 124-124: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy-azure.yml around lines 121 - 124, The summary lines
in the deploy-azure workflow are using inline ${{ }} expressions inside the run
script, which triggers template-injection warnings. Move
steps.tf.outputs.public_ip into env for that step, then reference it via a shell
variable in the summary writes instead of interpolating it directly in the run
block. Update the three echo statements that build the App, Prometheus, and
Grafana URLs to use the env-bound value while keeping the logic in the same
workflow step.

Source: Linters/SAST tools

Comment thread helm/bytebite/README.md
| `genai.openaiApiKey` | OpenAI API key for the selectable OpenAI provider | `""` |
| `monitoring.enabled` | Deploy Prometheus and Grafana | `true` |
| `monitoring.grafana.adminUser` | Grafana admin username | `admin` |
| `monitoring.grafana.adminPassword` | Grafana admin password | `bytebite` |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Default Grafana admin password documented in cleartext.

Publishing bytebite as the default monitoring.grafana.adminPassword in docs (and presumably values.yaml) means anyone reading the repo knows the default credential. Combined with monitoring.grafana.ingress.enabled: true and the values-local.yaml LoadBalancer service type in this same cohort, this could expose Grafana with a well-known default password if operators don't override it before deploying. Consider generating a random default (e.g., via Helm's randAlphaNum in _helpers.tpl) or requiring an explicit override with no committed default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/README.md` at line 35, The Grafana admin password default is
documented as cleartext, so update the chart docs and default configuration
around monitoring.grafana.adminPassword to avoid shipping a known credential.
Remove the committed password default from the README and values-related
templates, and either require an explicit override or generate a random secret
via the chart helpers (for example in _helpers.tpl) so users do not deploy with
a predictable admin password.

Comment thread helm/bytebite/values.yaml
Comment on lines +118 to +119
adminUser: admin
adminPassword: bytebite

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Default Grafana admin credentials committed to repo.

adminUser: admin / adminPassword: bytebite are plaintext, predictable defaults checked into values.yaml, which is the file used for the real cluster deployment (host team-bytebite.stud.k8s.aet.cit.tum.de, not values-local.yaml). These flow straight into bytebite-monitoring-secret (see monitoring-secret.yaml) and, if not overridden at install time, leave Grafana admin access exposed with a widely-known default password, especially once monitoring.grafana.ingress.enabled exposes it publicly.

Consider removing the default password from tracked values (require --set / a sealed secret / external secret manager at install time) and only keeping the username default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@helm/bytebite/values.yaml` around lines 118 - 119, The Grafana admin
credentials in values.yaml are hardcoded defaults, which should not be committed
for the real deployment. Update the chart values around adminUser/adminPassword
so only the username stays as a default and the password must be supplied
externally at install time, then ensure the bytebite-monitoring-secret path
still reads from an injected secret or --set value in monitoring-secret.yaml and
any related Grafana config.

Comment on lines +15 to +17

grafana_admin_user: admin
grafana_admin_password: bytebite

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🔴 Critical | ⚡ Quick win

Weak default Grafana credentials committed in plaintext, exposed to the internet.

grafana_admin_user: admin / grafana_admin_password: bytebite are checked into version control as functional defaults (not placeholders like deploy-vars.example.yml's "change-me"). Since the paired Terraform layer opens an NSG rule for Grafana's port 3000 to the internet, any deployment that doesn't explicitly override these in the gitignored deploy-vars.yml will run a publicly reachable Grafana instance with a known, weak admin password.

Compare this to ghcr_username/ghcr_token, which default to empty strings, forcing explicit configuration rather than silently using a working-but-insecure default. Consider the same pattern here (empty defaults + a "fail fast" check, or a vaulted secret) instead of a real, guessable password baked into source control.

🔒 Suggested fix: force explicit override instead of shipping a working default
-grafana_admin_user: admin
-grafana_admin_password: bytebite
+grafana_admin_user: ""
+grafana_admin_password: ""

Then add a pre-task assertion (or ansible.builtin.assert) that fails the play if these are empty, and document in deploy-vars.example.yml/README that they must be supplied (ideally via ansible-vault encrypt_string).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
grafana_admin_user: admin
grafana_admin_password: bytebite
grafana_admin_user: ""
grafana_admin_password: ""
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/ansible/group_vars/bytebite.yml` around lines 15 - 17, The Grafana
defaults in the bytebite group vars are insecure because they ship a real,
guessable admin credential. Update the Grafana variables in the bytebite vars
file to use empty placeholders instead of a working username/password, and add a
fail-fast check in the Ansible flow (for example near the Grafana setup task) to
assert both values are explicitly provided. Also document the required override
in the example deploy vars/README, ideally pointing users to vault-encrypted
secrets, so the playbook never deploys with a public default login.

Comment on lines +7 to +8
GRAFANA_ADMIN_USER={{ grafana_admin_user | default('admin') }}
GRAFANA_ADMIN_PASSWORD={{ grafana_admin_password | default('bytebite') }}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== env.j2 ==\n'
cat -n infra/ansible/roles/deploy/templates/env.j2

printf '\n== bytebite group vars ==\n'
cat -n group_vars/bytebite.yml

printf '\n== search for grafana_admin_user/password references ==\n'
rg -n "grafana_admin_(user|password)|default\\('admin'\\)|default\\('bytebite'\\)" infra group_vars -S

Repository: AET-DevOps26/team-bytebite

Length of output: 738


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== locate bytebite group vars and grafana vars ==\n'
fd -a 'bytebite.yml' .
fd -a 'group_vars' .
rg -n "grafana_admin_(user|password)|bytebite" infra group_vars . -S --hidden

printf '\n== find env.j2 consumers and related defaults ==\n'
rg -n "GRAFANA_ADMIN_USER|GRAFANA_ADMIN_PASSWORD|default\\('admin'\\)|default\\('bytebite'\\)" infra -S --hidden

Repository: AET-DevOps26/team-bytebite

Length of output: 40273


Remove the Grafana credential fallback here. This template still silently falls back to admin/bytebite, duplicating the same weak defaults in infra/ansible/group_vars/bytebite.yml and compose.yaml. Make the Grafana credentials explicit or vaulted so every deploy path has to supply them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/ansible/roles/deploy/templates/env.j2` around lines 7 - 8, The Grafana
credential values in the env template still fall back to weak defaults,
duplicating the same fallback in other deploy paths. Update the env.j2 template
to remove the default filters from GRAFANA_ADMIN_USER and GRAFANA_ADMIN_PASSWORD
so the deploy must supply explicit or vaulted values, and keep the credential
handling consistent with the related Grafana config in the Ansible variables and
compose setup.

Comment thread infra/terraform/main.tf
Comment on lines +6 to +15
# gen-ai and the DBs are internal to the Compose network.
# NOTE: Prometheus (9090) has no authentication, and Grafana (3000) uses basic
# app credentials. These are exposed here for the dev-stage only; do not carry
# these rules into production without putting auth / an SSH tunnel in front of them.
inbound_ports = {
ssh = { priority = 100, port = "22" }
client = { priority = 110, port = "8081" }
api_gateway = { priority = 120, port = "8080" }
prometheus = { priority = 130, port = "9090" }
grafana = { priority = 140, port = "3000" }

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift

Grafana opened to the internet with default weak credentials.

The new grafana entry inherits source_address_prefix = "*" from the existing dynamic security_rule block, so port 3000 is reachable from anywhere. Per the Ansible defaults (infra/ansible/group_vars/bytebite.yml / env.j2), grafana_admin_user/grafana_admin_password default to admin/bytebite unless explicitly overridden — a well-known default combo.

The comment frames this as "exposed here for the dev-stage only," but deploy-azure.yml applies this exact config on every green build of main; there's no separate hardened/production Terraform config. That leaves a public brute-forceable Grafana admin login on whatever is actually deployed from main.

Consider restricting source_address_prefix for the prometheus/grafana rules to known IPs (or fronting with a VPN/SSH tunnel as the comment itself suggests), and/or failing the Ansible deploy if the Grafana password is left at its default.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@infra/terraform/main.tf` around lines 6 - 15, The Grafana and Prometheus
ingress rules in the Terraform security group are too open because they inherit
the wildcard source prefix, which exposes Grafana on port 3000 with default
admin credentials. Update the inbound rule handling in the main Terraform
security group so the `grafana` (and preferably `prometheus`) entry uses a
restricted `source_address_prefix` or a safer allowlist/VPN/SSH-tunnel approach
instead of public access. If you keep Grafana exposed for dev-stage, also add a
guard in the Ansible deploy path for the Grafana defaults (`grafana_admin_user`
/ `grafana_admin_password`) so deployment fails when the password is still the
default.

@timn21 timn21 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to me. We can research later how to handle the duplicate dashboard json, which is for now necessary due to the folder structure.

@timn21
timn21 merged commit 142b013 into main Jul 3, 2026
12 checks passed
@timn21
timn21 deleted the feature/grafana branch July 10, 2026 11:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants