diff --git a/data.tf b/data.tf index c583380..51bb1bd 100644 --- a/data.tf +++ b/data.tf @@ -39,6 +39,9 @@ data "oci_secrets_secretbundle" "redis_passwords" { secret_id = each.value.id } +data "oci_secrets_secretbundle" "grafana_secret_key_secret" { + secret_id = oci_vault_secret.grafana_secret_key_secret.id +} data "oci_core_images" "main" { compartment_id = oci_identity_compartment.main.id diff --git a/dns.tf b/dns.tf index f977379..588f381 100644 --- a/dns.tf +++ b/dns.tf @@ -17,7 +17,7 @@ resource "cloudflare_dns_record" "www" { } resource "cloudflare_dns_record" "tunnels" { - for_each = toset(["@", "webdav", "vault", "rss", "status", "auth", "wireguard"]) + for_each = toset(["@", "webdav", "vault", "rss", "status", "auth", "wireguard", "logs"]) content = "${cloudflare_zero_trust_tunnel_cloudflared.main.id}.cfargotunnel.com" name = each.value proxied = true diff --git a/files/authelia.configuration.yml b/files/authelia.configuration.yml index 1afce8e..9d37ffe 100644 --- a/files/authelia.configuration.yml +++ b/files/authelia.configuration.yml @@ -92,6 +92,14 @@ notifier: identity_providers: oidc: hmac_secret: '{{ secret "/run/secrets/hmac-secret" }}' + claims_policies: + # https://www.authelia.com/integration/openid-connect/clients/grafana/#configuration-escape-hatch + grafana: + id_token: + - email + - name + - groups + - preferred_username jwks: - key_id: "main" algorithm: "RS256" @@ -169,3 +177,23 @@ identity_providers: pre_configured_consent_duration: 1 week require_pkce: true token_endpoint_auth_method: client_secret_post + - client_id: '{{ env "GRAFANA_CLIENT_ID" }}' + client_name: "Grafana" + client_secret: '{{ env "GRAFANA_CLIENT_SECRET" }}' + require_pkce: true + pkce_challenge_method: "S256" + redirect_uris: + - 'https://logs.{{ env "SERVER_DOMAIN" }}/login/generic_oauth' + scopes: + - "openid" + - "profile" + - "groups" + - "email" + claims_policy: "grafana" + response_types: + - "code" + grant_types: + - "authorization_code" + access_token_signed_response_alg: "none" + userinfo_signed_response_alg: "none" + token_endpoint_auth_method: "client_secret_basic" diff --git a/files/compose.yaml b/files/compose.yaml index ad419ac..1efb626 100644 --- a/files/compose.yaml +++ b/files/compose.yaml @@ -5,6 +5,11 @@ x-default-service: &default-service cap_drop: [ALL] security_opt: [no-new-privileges] pull_policy: weekly + logging: + driver: syslog + options: + tag: "{{.Name}}" + syslog-facility: "local0" services: vaultwarden: @@ -69,6 +74,7 @@ services: - freshrss - authelia - wg-easy + - grafana env_file: - gatus.env volumes: @@ -93,6 +99,7 @@ services: - freshrss - authelia - wg-easy + - grafana volumes: - "/mnt/oraclevdb/cloudflared:/user/nonroot/.cloudflared/" command: tunnel run @@ -218,6 +225,64 @@ services: uid: "999" gid: "999" + grafana: + <<: *default-service + container_name: grafana + image: grafana/grafana:latest + env_file: ./grafana.env + user: "472:0" # user grafana, group root + volumes: + - /mnt/oraclevdb/grafana/:/var/lib/grafana/ + configs: + - source: grafana-ini + target: /etc/grafana/grafana.ini + uid: "472" + gid: "0" + - source: grafana-datasources + target: /etc/grafana/provisioning/datasources/datasources.yaml + uid: "472" + gid: "0" + - source: grafana-dashboards + target: /etc/grafana/provisioning/dashboards/dashboards.yaml + uid: "472" + gid: "0" + secrets: + - grafana-database-url + + loki: + <<: *default-service + container_name: loki + image: grafana/loki:latest + user: 10001:10001 # loki user + volumes: + - /mnt/oraclevdb/loki/:/loki/ + configs: + - source: loki-config + target: /etc/loki/local-config.yaml + uid: "10001" + gid: "10001" + + alloy: + <<: *default-service + container_name: alloy + image: grafana/alloy:latest + user: 473:473 # alloy user + ports: + - 514:514/tcp + # https://github.com/grafana/alloy/blob/a473efe2b676ba826ec1ba68897b6b1f475bcbbc/Dockerfile#L82 + command: > + run + /etc/alloy/config.alloy + --server.http.listen-addr=0.0.0.0:12345 + --storage.path=/var/lib/alloy/data + volumes: + - /mnt/oraclevdb/alloy/:/var/lib/alloy/data + configs: + - source: alloy-config + target: /etc/alloy/config.alloy + uid: "473" + gid: "473" + configs: rclone-config: file: ./rclone.conf @@ -240,6 +305,16 @@ configs: file: ./authelia.configuration.yml redis-config: file: ./redis.conf + grafana-ini: + file: ./grafana.ini + grafana-datasources: + file: ./datasources.yaml + grafana-dashboards: + file: ./dashboards.yaml + loki-config: + file: ./loki-config.yaml + alloy-config: + file: ./config.alloy secrets: vaultwarden-database-url: @@ -262,3 +337,5 @@ secrets: file: "./authelia-redis-password" redis-users-acl: file: "./redis.users.acl" + grafana-database-url: + file: "./grafana-database-url" diff --git a/files/config.alloy b/files/config.alloy new file mode 100644 index 0000000..88b4f6c --- /dev/null +++ b/files/config.alloy @@ -0,0 +1,36 @@ +logging { + format = "logfmt" + level = "info" +} + + +loki.relabel "syslog" { + forward_to = [] + + rule { + action = "labelmap" + regex = "__syslog_(.+)" + } + + rule { + action = "labelmap" + regex = "message_(.+)" + } +} + +loki.source.syslog "syslog_receiver" { + listener { + address = "0.0.0.0:514" + protocol = "tcp" + labels = { job = "syslog" } + } + relabel_rules = loki.relabel.syslog.rules + + forward_to = [loki.write.default.receiver] +} + +loki.write "default" { + endpoint { + url = "http://loki:3100/loki/api/v1/push" + } +} diff --git a/files/gatus-config.yaml b/files/gatus-config.yaml index 1ccafcf..ae17c75 100644 --- a/files/gatus-config.yaml +++ b/files/gatus-config.yaml @@ -14,6 +14,9 @@ security: web: port: 8080 +ui: + default-sort-by: group + alerting: email: enabled: true @@ -76,8 +79,8 @@ endpoints: <<: *status-200 - name: FreshRSS group: core - url: "https://rss.${SERVER_DOMAIN}" - <<: *status-401 + url: "https://rss.${SERVER_DOMAIN}/api" + <<: *status-200 - name: Authelia group: core url: "https://auth.${SERVER_DOMAIN}/api/health" @@ -90,6 +93,14 @@ endpoints: group: core url: "https://wireguard.${SERVER_DOMAIN}" <<: *status-200 + - name: Grafana + group: core + url: "https://logs.${SERVER_DOMAIN}/api/health" + <<: *status-200 + conditions: &grafana-conditions + - "[STATUS] == 200" + - "[RESPONSE_TIME] < 1000" + - "[BODY].database == ok" # Internal - name: Vaultwarden (Internal) group: internal @@ -106,8 +117,8 @@ endpoints: <<: *status-200 - name: FreshRSS (Internal) group: internal - url: "http://freshrss:80" - <<: *status-401 + url: "http://freshrss:80/api" + <<: *status-200 - name: Authelia (Internal) group: internal url: "http://authelia:9091/api/health" @@ -117,6 +128,11 @@ endpoints: group: internal url: "http://wg-easy:51821" <<: *status-200 + - name: Grafana (Internal) + group: internal + url: "http://grafana:3000/api/health" + <<: *status-200 + conditions: *grafana-conditions - name: Folding@home group: internal url: "http://foldingathome:7396" diff --git a/files/grafana/dashboards.yaml b/files/grafana/dashboards.yaml new file mode 100644 index 0000000..cd2d4ec --- /dev/null +++ b/files/grafana/dashboards.yaml @@ -0,0 +1,7 @@ +apiVersion: 1 + +providers: + - name: "Redis" + type: file + options: + path: /var/lib/grafana/plugins/redis-datasource/dashboards diff --git a/files/grafana/datasources.yaml b/files/grafana/datasources.yaml new file mode 100644 index 0000000..b9da8cd --- /dev/null +++ b/files/grafana/datasources.yaml @@ -0,0 +1,38 @@ +apiVersion: 1 + +prune: true + +datasources: + - &mysql-datasource + name: MySQL (Grafana) + type: mysql + url: $_GRAFANA_MYSQL_URL + user: $_GRAFANA_MYSQL_USERNAME + jsonData: + database: grafana + secureJsonData: + password: $_GRAFANA_MYSQL_PASSWORD + - <<: *mysql-datasource + name: MySQL (Authelia) + jsonData: + database: authelia + - <<: *mysql-datasource + name: MySQL (FreshRSS) + jsonData: + database: freshrss + - name: Redis + type: redis-datasource + url: redis://redis:6379 + jsonData: + client: standalone + acl: true + user: $_GRAFANA_REDIS_USERNAME + secureJsonData: + password: $_GRAFANA_REDIS_PASSWORD + - name: Loki + type: loki + access: proxy + url: http://loki:3100 + jsonData: + timeout: 60 + maxLines: 1000 diff --git a/files/grafana/grafana.ini b/files/grafana/grafana.ini new file mode 100644 index 0000000..7ff9ad8 --- /dev/null +++ b/files/grafana/grafana.ini @@ -0,0 +1,72 @@ +########################## Grafana Configuration ######################### + +#################################### Server ############################## +[server] +protocol = http +http_addr = 0.0.0.0 +http_port = 3000 +domain = ${HOSTNAME} +enforce_domain = false +root_url = https://logs.%(domain)s/ +serve_from_sub_path = false +router_logging = false +static_root_path = public +enable_gzip = false + +#################################### Database ############################ +[database] +url = $__file{/run/secrets/grafana-database-url} +conn_max_lifetime = 14400 # 4 hours (14400 seconds) + +#################################### Security ############################ +[security] +secret_key = ${SECRET_KEY_SECRET} +disable_initial_admin_creation = true +brute_force_login_protection_max_attempts = 3 +cookie_secure = true + +#################################### Users ############################### +[users] +allow_sign_up=false +allow_org_create=false +default_theme=system + +[auth] +disable_login_form=true + +#################################### Generic OAuth ####################### +[auth.generic_oauth] +enabled = true +name = Authelia +icon = signin +client_id = ${CLIENT_ID} +client_secret = ${CLIENT_SECRET} +auth_url = https://auth.${HOSTNAME}/api/oidc/authorization +token_url = https://auth.${HOSTNAME}/api/oidc/token +api_url = https://auth.${HOSTNAME}/api/oidc/userinfo +auth_style = InHeader +scopes = openid profile email groups +empty_scopes = false +login_attribute_path = preferred_username +name_attribute_path = name +role_attribute_path = contains(groups[*], 'super_admin') && 'GrafanaAdmin' || contains(groups[*], 'admin') && 'Admin' || contains(groups[*], 'editor') && 'Editor' || 'Viewer' +allow_assign_grafana_admin = true +groups_attribute_path = groups +use_pkce = true + +#################################### SMTP / Emailing ##################### +[smtp] +enabled=true +host=${SMTP_HOST} +user=${SMTP_USERNAME} +password=${SMTP_PASSWORD} +from_address=${SMTP_EMAIL_FROM} +from_name=grafana + +#################################### Unified Alerting #################### +[unified_alerting] +enabled = true + +[date_formats] +use_browser_locale = true +default_timezone = browser diff --git a/files/loki-config.yaml b/files/loki-config.yaml new file mode 100644 index 0000000..945318d --- /dev/null +++ b/files/loki-config.yaml @@ -0,0 +1,32 @@ +auth_enabled: false + +server: + http_listen_port: 3100 + log_format: "logfmt" + log_level: "info" + log_source_ips_enabled: true + +storage_config: + filesystem: + directory: /loki/chunks + tsdb_shipper: + active_index_directory: /loki/tsdb-index + cache_location: /loki/tsdb-cache + +schema_config: + configs: + - from: 2020-05-15 + store: tsdb + object_store: filesystem + schema: v13 + index: + prefix: index_ + period: 24h + +common: + ring: + instance_addr: 127.0.0.1 + kvstore: + store: inmemory + replication_factor: 1 + path_prefix: /loki diff --git a/files/rsyslog.conf b/files/rsyslog.conf new file mode 100644 index 0000000..47fefaf --- /dev/null +++ b/files/rsyslog.conf @@ -0,0 +1,22 @@ +# /etc/rsyslog.d/10-docker-to-alloy.conf + +if $syslogfacility-text == 'local0' then { + action( + type="omfwd" + target="127.0.0.1" + port="514" + protocol="tcp" + template="RSYSLOG_SyslogProtocol23Format" + tcp_framing="octet-counted" + action.resumeRetryCount="-1" + queue.type="linkedList" + queue.filename="fwd_alloy_docker" + queue.maxDiskSpace="1g" + queue.saveOnShutdown="on" + ) + + action(type="omfile" file="/var/log/docker/docker.log") + + # Prevents these lines from also landing in /var/log/syslog + stop +} diff --git a/main.tf b/main.tf index 9068e5c..7e8343b 100644 --- a/main.tf +++ b/main.tf @@ -21,7 +21,7 @@ resource "oci_kms_key" "main" { } resource "oci_vault_secret" "database_passwords" { - for_each = toset(["admin", "vaultwarden", "authelia", "freshrss"]) + for_each = toset(["admin", "vaultwarden", "authelia", "freshrss", "grafana-storage", "grafana-reader"]) compartment_id = oci_identity_compartment.main.id key_id = oci_kms_key.main.id @@ -46,7 +46,7 @@ resource "oci_vault_secret" "cloudflare_tunnel_secret" { } resource "random_uuid4" "auth_client_ids" { - for_each = toset(["gatus", "vaultwarden", "freshrss", "wg_easy"]) + for_each = toset(["gatus", "vaultwarden", "freshrss", "wg_easy", "grafana"]) } resource "oci_vault_secret" "auth_client_secrets" { @@ -97,7 +97,7 @@ resource "oci_vault_secret" "authelia" { } resource "oci_vault_secret" "redis_passwords" { - for_each = toset(["jeremy"]) + for_each = toset(["jeremy", "grafana"]) compartment_id = oci_identity_compartment.main.id description = "Redis password for ${each.key}" enable_auto_generation = true @@ -112,6 +112,21 @@ resource "oci_vault_secret" "redis_passwords" { } } +resource "oci_vault_secret" "grafana_secret_key_secret" { + compartment_id = oci_identity_compartment.main.id + description = "Grafana secret key secret (AES-256-CFB)" + enable_auto_generation = true + key_id = oci_kms_key.main.id + secret_name = "grafana_secret_key_secret" + vault_id = oci_kms_vault.main.id + + secret_generation_context { + generation_template = "SECRETS_DEFAULT_PASSWORD" + generation_type = "PASSPHRASE" + passphrase_length = 21 + } +} + locals { database_passwords = { for database_user, database_password_secret in data.oci_secrets_secretbundle.database_passwords : database_user => one(database_password_secret.secret_bundle_content[*].content) } cloudflare_tunnel_secret = one(data.oci_secrets_secretbundle.cloudflare_tunnel_secret.secret_bundle_content[*].content) @@ -119,6 +134,7 @@ locals { authelia = { for authelia_secret_key, authelia_secret in data.oci_secrets_secretbundle.authelia : authelia_secret_key => one(authelia_secret.secret_bundle_content[*].content) } authelia_oidc_signing_key = one(data.oci_secrets_secretbundle.authelia_oidc_signing_key.secret_bundle_content[*].content) redis_passwords = { for redis_username, redis_password_secret in data.oci_secrets_secretbundle.redis_passwords : redis_username => one(redis_password_secret.secret_bundle_content[*].content) } + grafana_secret_key_secret = one(data.oci_secrets_secretbundle.grafana_secret_key_secret.secret_bundle_content[*].content) } resource "cloudflare_zero_trust_tunnel_cloudflared" "main" { @@ -206,7 +222,7 @@ resource "oci_identity_smtp_credential" "main" { } resource "oci_email_sender" "senders" { - for_each = toset(["vault", "status", "auth"]) + for_each = toset(["vault", "status", "auth", "logs"]) compartment_id = oci_identity_compartment.main.id email_address = "${each.value}@${var.server_domain}" @@ -280,6 +296,12 @@ locals { }) permissions = "0600" owner = "root:root" + }, + { + path = "/etc/rsyslog.d/10-docker-to-alloy.conf", + content = file("${path.module}/files/rsyslog.conf") + permissions = "0644" + owner = "root:root" } ], # Env @@ -372,6 +394,31 @@ locals { }) defer = true }, + { + path = "/home/jeremy/grafana.env", + content = templatefile("${path.module}/templates/env/grafana.env.tftpl", { + auth = { + client_id = random_uuid4.auth_client_ids["grafana"].id + client_secret = local.auth_client_secrets.grafana + } + server_domain = var.server_domain + email = { + grafana = oci_email_sender.senders["logs"].email_address + } + secret_key_secret = local.grafana_secret_key_secret + db = { + username = "grafana-reader" + password = base64decode(local.database_passwords.grafana-reader) + host = oci_mysql_mysql_db_system.main.ip_address + port = oci_mysql_mysql_db_system.main.port + } + redis_config = { + username = "grafana" + password = base64decode(local.redis_passwords.grafana) + } + }) + defer = true + }, ], # Secrets [ @@ -402,7 +449,7 @@ locals { }, { path = "/home/jeremy/authelia-redis-password", - content = local.redis_passwords.jeremy + content = base64decode(local.redis_passwords.jeremy) defer = true }, { @@ -428,6 +475,21 @@ locals { ]), defer = true }, + { + path = "/home/jeremy/grafana-database-url", + content = join("", [ + "mysql://", + "grafana-storage", + ":", + urlencode(base64decode(local.database_passwords.grafana-storage)), + "@", + oci_mysql_mysql_db_system.main.ip_address, + ":", + oci_mysql_mysql_db_system.main.port, + "/grafana" + ]), + defer = true + }, { path = "/home/jeremy/cloudflared-credentials-file.json", content = jsonencode({ @@ -469,7 +531,7 @@ locals { { path = "/home/jeremy/redis.users.acl", content = templatefile("${path.module}/templates/redis.users.acl.tftpl", { - redis_passwords = { for k, v in local.redis_passwords : k => sha256(v) } + redis_passwords = { for k, v in local.redis_passwords : k => sha256(base64decode(v)) } }) defer = true }, @@ -505,6 +567,29 @@ locals { }) defer = true }, + { + path = "/home/jeremy/grafana.ini", + content = file("${path.module}/files/grafana/grafana.ini") + defer = true + }, + { + path = "/home/jeremy/dashboards.yaml", + content = file("${path.module}/files/grafana/dashboards.yaml") + defer = true + }, + { + path = "/home/jeremy/datasources.yaml", + content = file("${path.module}/files/grafana/datasources.yaml") + defer = true + }, + { + path = "/home/jeremy/cloudflare.config.yml", + content = templatefile("${path.module}/templates/cloudflare.config.yml.tftpl", { + server_domain = var.server_domain + cloudflared_tunnel_id = cloudflare_zero_trust_tunnel_cloudflared.main.id + }) + defer = true + }, { path = "/home/jeremy/compose.yaml", content = file("${path.module}/files/compose.yaml") @@ -590,7 +675,7 @@ resource "oci_core_instance" "main" { # ignored. When the metadata actually changes, run `terraform plan` or # `terraform apply` with `-replace="oci_core_instance.main"` to force # replacement (or comment out this line). - metadata + # metadata ] } } @@ -643,7 +728,10 @@ locals { } resource "oci_core_volume_backup_policy_assignment" "main" { - for_each = toset([oci_core_instance.main.boot_volume_id, oci_core_volume.block.id]) + # The "for_each" set includes values derived from resource attributes that + # cannot be determined until apply, and so Terraform cannot determine the full + # set of keys that will identify the instances of this resource. + for_each = tomap({ boot_volume_id = oci_core_instance.main.boot_volume_id, block_volume_id = oci_core_volume.block.id }) asset_id = each.value policy_id = local.silver_volume_backup_policy.id diff --git a/templates/cloudflare.config.yml.tftpl b/templates/cloudflare.config.yml.tftpl index ed0428b..b1ebf24 100644 --- a/templates/cloudflare.config.yml.tftpl +++ b/templates/cloudflare.config.yml.tftpl @@ -9,7 +9,7 @@ edge-ip-version: auto region: us ingress: - # FreshRSS, Authelia already have a robots.txt + # FreshRSS, Authelia, Grafana already have a robots.txt %{~ for subdomain in ["vault.", "webdav.", "status.", "wireguard.", ""] ~} - hostname: ${subdomain}${server_domain}/robots.txt service: http://robots-txt:8008 @@ -26,4 +26,6 @@ ingress: service: http://authelia:9091 - hostname: wireguard.${server_domain} service: http://wg-easy:51821 + - hostname: logs.${server_domain} + service: http://grafana:3000 - service: http_status:404 diff --git a/templates/env/authelia.env.tftpl b/templates/env/authelia.env.tftpl index 92ec992..b84d7ac 100644 --- a/templates/env/authelia.env.tftpl +++ b/templates/env/authelia.env.tftpl @@ -8,6 +8,8 @@ FRESHRSS_CLIENT_ID=${auth.client_ids.freshrss} FRESHRSS_CLIENT_SECRET='${auth.client_secrets.freshrss}' WG_EASY_CLIENT_ID='${auth.client_ids.wg_easy}' WG_EASY_CLIENT_SECRET='${auth.client_secrets.wg_easy}' +GRAFANA_CLIENT_ID='${auth.client_ids.grafana}' +GRAFANA_CLIENT_SECRET='${auth.client_secrets.grafana}' SMTP_USERNAME=$${SMTP_USERNAME} SMTP_PASSWORD=$${SMTP_PASSWORD} SMTP_HOST=$${SMTP_HOST} diff --git a/templates/env/grafana.env.tftpl b/templates/env/grafana.env.tftpl new file mode 100644 index 0000000..e37eaa4 --- /dev/null +++ b/templates/env/grafana.env.tftpl @@ -0,0 +1,16 @@ +HOSTNAME=${server_domain} +GF_PLUGINS_PREINSTALL=redis-datasource +CLIENT_ID=${auth.client_id} +CLIENT_SECRET=${auth.client_secret} +SECRET_KEY_SECRET=${secret_key_secret} + +_GRAFANA_MYSQL_URL=${db.host}:${db.port} +_GRAFANA_MYSQL_USERNAME=${db.username} +_GRAFANA_MYSQL_PASSWORD=${db.password} +_GRAFANA_REDIS_USERNAME=${redis_config.username} +_GRAFANA_REDIS_PASSWORD=${redis_config.password} + +SMTP_EMAIL_FROM=${email.grafana} +SMTP_USERNAME="$${SMTP_USERNAME}" +SMTP_PASSWORD="$${SMTP_PASSWORD}" +SMTP_HOST="$${SMTP_HOST}" diff --git a/templates/init.sql.tftpl b/templates/init.sql.tftpl index d67055b..f61337c 100644 --- a/templates/init.sql.tftpl +++ b/templates/init.sql.tftpl @@ -15,3 +15,15 @@ CREATE DATABASE IF NOT EXISTS freshrss CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; CREATE USER IF NOT EXISTS 'freshrss'@'%' IDENTIFIED BY '${database_passwords.freshrss}'; GRANT ALL ON `freshrss`.* TO 'freshrss'@'%'; + +-- https://github.com/grafana/grafana/blob/29b4e114264c06bd4cf6c625444830f141695bb1/pkg/services/sqlstore/migrator/mysql_dialect.go#L110 +CREATE DATABASE IF NOT EXISTS grafana + CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; +CREATE USER IF NOT EXISTS 'grafana-storage'@'%' IDENTIFIED BY '${database_passwords.grafana-storage}'; +GRANT ALL ON `grafana`.* TO 'grafana-storage'@'%'; + +CREATE USER IF NOT EXISTS 'grafana-reader'@'%' IDENTIFIED BY '${database_passwords.grafana-reader}'; +GRANT SELECT ON `vaultwarden`.* TO 'grafana-reader'@'%'; +GRANT SELECT ON `authelia`.* TO 'grafana-reader'@'%'; +GRANT SELECT ON `freshrss`.* TO 'grafana-reader'@'%'; +GRANT SELECT ON `grafana`.* TO 'grafana-reader'@'%'; diff --git a/templates/redis.users.acl.tftpl b/templates/redis.users.acl.tftpl index 48daa9c..35846ca 100644 --- a/templates/redis.users.acl.tftpl +++ b/templates/redis.users.acl.tftpl @@ -1,2 +1,3 @@ user default off sanitize-payload resetchannels -@all user jeremy on sanitize-payload #${redis_passwords.jeremy} ~* &* +@all +user grafana on sanitize-payload #${redis_passwords.grafana} %R~* &* +@all diff --git a/templates/users_database.yml.tftpl b/templates/users_database.yml.tftpl index a17ce0b..4d6a456 100644 --- a/templates/users_database.yml.tftpl +++ b/templates/users_database.yml.tftpl @@ -3,7 +3,7 @@ users: displayname: Jeremy Nguyen password: ${user_config.password} email: ${user_config.email_address} - groups: [] + groups: ["super_admin", "admin"] given_name: "Jeremy" middle_name: "" family_name: "Nguyen"