Skip to content

Commit 6a2a767

Browse files
authored
Merge pull request #207 from Linuxfabrik/feat/grafana-jwt
feat(roles/grafana): Add JWT support
2 parents 80107bd + 802d1bb commit 6a2a767

9 files changed

Lines changed: 70 additions & 12 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929

3030
### Added
3131

32+
* **role:icingaweb2_module_grafana**: Add JWT support
33+
* **role:grafana**: Add JWT support
3234
* Add `playbooks/README.md` documenting all playbooks with their roles in execution order and available skip variables
3335
* **role:apache_httpd**: Add platform-specific behavior section, wsgi example, and document localhost endpoints in README
3436
* **role:apache_httpd**: Add skip variables section to README linking to relevant playbooks

roles/grafana/README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ grafana__root_url: 'https://monitoring.example.com/grafana'
4747
| `grafana__auth_anonymous_enabled` | Whether to allow anonymous (passwordless) access or not. Possible options: `true` or `false` | `false` |
4848
| `grafana__auth_anonymous_org_name` | The organization name that should be used for unauthenticated users. | `'Main Org.'` |
4949
| `grafana__auth_anonymous_org_role` | The role for unauthenticated users. | `'Viewer'` |
50+
| `grafana__auth_jwt` | Enable JWT-based authentication for Grafana requests. | `false` |
51+
| `grafana__auth_jwt__priv_key_file` | Path to the private key file used to verify JWT signatures for Grafana authentication. | `'/etc/grafana/jwt.key.priv'` |
52+
| `grafana__auth_jwt__pub_key_file` | Path to the public key file used to verify JWT signatures for Grafana authentication. | `'/etc/grafana/jwt.key.pub'` |
5053
| `grafana__bitwarden_collection_id` | Will be used to store the token of the created service accounts to this Bitwarden Collection. Can be obtained from the URL in Bitwarden WebGUI. | `'{{ lfops__bitwarden_collection_id | default() }}'` |
5154
| `grafana__bitwarden_organization_id` | Will be used to store the token of the created service accounts to this Bitwarden Organization. Can be obtained from the URL in Bitwarden WebGUI. | `'{{ lfops__bitwarden_organization_id | default() }}'` |
5255
| `grafana__cookie_samesite` | The [SameSite cookie attribute](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite). Possible options:<br> * disabled<br> * lax<br> * none<br> * strict | `'lax'` |
@@ -71,6 +74,9 @@ grafana__api_url: 'https://grafana01.example.com/grafana'
7174
grafana__auth_anonymous_enabled: false
7275
grafana__auth_anonymous_org_name: 'Main Org.'
7376
grafana__auth_anonymous_org_role: 'Viewer'
77+
grafana__auth_jwt: false
78+
grafana__auth_jwt__priv_key_file: '/etc/grafana/jwt.key.priv'
79+
grafana__auth_jwt__pub_key_file: '/etc/grafana/jwt.key.pub'
7480
grafana__cookie_samesite: 'lax'
7581
grafana__https_config:
7682
cert_file: '/etc/ssl/ssl-certificate.crt'

roles/grafana/defaults/main.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ grafana__api_url: '{{ grafana__root_url }}'
33
grafana__auth_anonymous_enabled: false
44
grafana__auth_anonymous_org_name: 'Main Org.'
55
grafana__auth_anonymous_org_role: 'Viewer'
6+
grafana__auth_jwt: false
7+
grafana__auth_jwt__priv_key_file: '/etc/grafana/jwt.key.priv'
8+
grafana__auth_jwt__pub_key_file: '/etc/grafana/jwt.key.pub'
69
grafana__bitwarden_collection_id: '{{ lfops__bitwarden_collection_id | default() }}'
710
grafana__bitwarden_organization_id: '{{ lfops__bitwarden_organization_id | default() }}'
811
grafana__cookie_samesite: 'lax'
@@ -58,3 +61,4 @@ grafana__serve_from_sub_path: false
5861
grafana__service_enabled: true
5962
grafana__skip_token_to_bitwarden: false
6063
grafana__validate_certs: true
64+

roles/grafana/tasks/main.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,25 @@
2626
when:
2727
- 'grafana__https_config is defined and grafana__https_config | length > 0'
2828

29+
- name: 'generate JWT RSA private key'
30+
community.crypto.openssl_privatekey:
31+
path: '{{ grafana__auth_jwt__priv_key_file }}'
32+
size: 2048
33+
type: 'RSA'
34+
owner: 'root'
35+
group: 'grafana'
36+
mode: 0o644
37+
when: 'grafana__auth_jwt | bool'
38+
39+
- name: 'generate JWT RSA public key'
40+
community.crypto.openssl_publickey:
41+
path: '{{ grafana__auth_jwt__pub_key_file }}'
42+
privatekey_path: '{{ grafana__auth_jwt__priv_key_file }}'
43+
owner: 'root'
44+
group: 'grafana'
45+
mode: 0o644
46+
when: 'grafana__auth_jwt | bool'
47+
2948
- name: 'deploy /etc/grafana/grafana.ini'
3049
ansible.builtin.template:
3150
backup: true

roles/grafana/templates/etc/grafana/grafana.ini.j2

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# {{ ansible_managed }}
2-
# 2024031901
2+
# 2026032701
33

44
##################### Grafana Configuration Example #####################
55
#
@@ -606,16 +606,18 @@ hide_version = true
606606

607607
#################################### Auth JWT ##########################
608608
[auth.jwt]
609-
;enabled = true
610-
;header_name = X-JWT-Assertion
611-
;email_claim = sub
612-
;username_claim = sub
609+
enabled = {{ grafana__auth_jwt | lower }}
610+
header_name = X-JWT-Assertion
611+
email_claim = sub
612+
username_claim = sub
613613
;jwk_set_url = https://foo.bar/.well-known/jwks.json
614614
;jwk_set_file = /path/to/jwks.json
615615
;cache_ttl = 60m
616616
;expected_claims = {"aud": ["foo", "bar"]}
617-
;key_file = /path/to/key/file
618-
;auto_sign_up = false
617+
key_file = {{ grafana__auth_jwt__pub_key_file }}
618+
auto_sign_up = false
619+
url_login = true
620+
skip_org_role_sync = true
619621

620622
#################################### Auth LDAP ##########################
621623
[auth.ldap]

roles/icingaweb2_module_grafana/README.md

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ Additionally, it deploys the the graph configuration for the [Linuxfabrik Monito
55

66
This role is tested with the following IcingaWeb2 Grafana Module versions:
77

8-
* 3.0.1
8+
* 3.1.3
99

1010

1111
## Mandatory Requirements
@@ -36,14 +36,16 @@ Example:
3636
```yaml
3737
# mandatory
3838
icingaweb2_module_grafana__monitoring_plugins_version: '1.2.0.11'
39-
icingaweb2_module_grafana__version: 'v3.1.1'
39+
icingaweb2_module_grafana__version: 'v3.1.3'
4040
```
4141
4242
4343
## Optional Role Variables
4444
4545
| Variable | Description | Default Value |
4646
| -------- | ----------- | ------------- |
47+
| `icingaweb2_module_grafana__auth_jwt` | Enable JWT-based authentication for Grafana requests | `'{{ grafana__auth_jwt }}'` |
48+
| `icingaweb2_module_grafana__auth_jwt__priv_key_file` | Path to the private key file used for JWT-based Grafana authentication | `'{{ grafana__auth_jwt__priv_key_file }}'` |
4749
| `icingaweb2_module_grafana__custom_graphs_config` | Multiline string. Custom configuration for the Grafana Graphs, will be deployed to `/etc/icingweb2/modules/grafana/graphs.ini` along with the configuration for the [Linuxfabrik Monitoring Plugins](https://github.com/Linuxfabrik/monitoring-plugins) | `''` |
4850
| `icingaweb2_module_grafana__default_dashboard` | Name of the default Grafana dashboard | `'Default'` |
4951
| `icingaweb2_module_grafana__skip_monitoring_plugins_graphs_config` | Skip the deployment of the graph configuration for [Linuxfabrik Monitoring Plugins](https://github.com/Linuxfabrik/monitoring-plugins). | `false` |
@@ -53,6 +55,8 @@ icingaweb2_module_grafana__version: 'v3.1.1'
5355
Example:
5456
```yaml
5557
# optional
58+
icingaweb2_module_grafana__auth_jwt: false
59+
icingaweb2_module_grafana__auth_jwt__priv_key_file: '/etc/grafana/jwt.key.priv'
5660
icingaweb2_module_grafana__custom_graphs_config: |-
5761
[icingacli-x509]
5862
dashboard = "Default"

roles/icingaweb2_module_grafana/defaults/main.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
icingaweb2_module_grafana__auth_jwt: '{{ grafana__auth_jwt }}'
2+
icingaweb2_module_grafana__auth_jwt__priv_key_file: '{{ grafana__auth_jwt__priv_key_file }}'
13
icingaweb2_module_grafana__custom_graphs_config: ''
24
icingaweb2_module_grafana__default_dashboard: 'Default'
35
icingaweb2_module_grafana__monitoring_plugins_version: '{{ lfops__monitoring_plugins_version | default() }}'

roles/icingaweb2_module_grafana/tasks/main.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,16 @@
8888
group: 'icingaweb2'
8989
mode: 0o660
9090

91+
- name: 'copy {{ icingaweb2_module_grafana__auth_jwt__priv_key_file }} to /etc/icingaweb2/modules/grafana/jwt.key.priv'
92+
ansible.builtin.copy:
93+
src: '{{ icingaweb2_module_grafana__auth_jwt__priv_key_file }}'
94+
dest: '/etc/icingaweb2/modules/grafana/jwt.key.priv'
95+
remote_src: true
96+
owner: 'apache'
97+
group: 'icingaweb2'
98+
mode: 0o644
99+
when: 'icingaweb2_module_grafana__auth_jwt'
100+
91101
tags:
92102
- 'icingaweb2_module_grafana'
93103
- 'icingaweb2_module_grafana:configure'
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
; {{ ansible_managed }}
2-
; 2023050802
2+
; 2026032601
33

44
[grafana]
55
accessmode = "iframe"
@@ -9,9 +9,18 @@ defaultdashboard = "{{ icingaweb2_module_grafana__default_dashboard }}"
99
defaultdashboardpanelid = "1"
1010
defaultdashboarduid = "default"
1111
defaultorgid = "1"
12-
host = "{{ (icingaweb2_module_grafana__url | split('://'))[1] }}"
13-
protocol = "{{ (icingaweb2_module_grafana__url | split('://'))[0] }}"
12+
host = "{{ icingaweb2_module_grafana__url | ansible.builtin.urlsplit('hostname') }}{{ icingaweb2_module_grafana__url | ansible.builtin.urlsplit('path') }}"
13+
protocol = "{{ icingaweb2_module_grafana__url | ansible.builtin.urlsplit('scheme') }}"
1414
shadows = "0"
1515
theme = "{{ icingaweb2_module_grafana__theme }}"
1616
timerange = "2d"
1717
timerangeAll = "1w/w"
18+
ssl_verifypeer = "0"
19+
ssl_verifyhost = "0"
20+
dashboardlink = "0"
21+
{% if icingaweb2_module_grafana__auth_jwt %}
22+
jwtEnable = "1"
23+
jwtUser = "grafana-admin"
24+
jwtIssuer = "{{ icingaweb2_module_grafana__url }}"
25+
jwtExpires = "30"
26+
{% endif %}

0 commit comments

Comments
 (0)