Skip to content

Commit ae4b043

Browse files
committed
feat: add TLS certificate and key support for Grafana for HTTPS
Feature: Add the ability to configure TLS/HTTPS for the Grafana graphing service using the following new role variables: * metrics_grafana_certificates - use the certificate role to generate certs * metrics_grafana_cert/metrics_grafana_private_key - paths to existing certs * metrics_grafana_cert_src/metrics_grafana_private_key_src - copy local files Reason: Users need the ability to secure Grafana connections with TLS/HTTPS to protect metrics data in transit, which is a common security requirement for production deployments. Result: The metrics role can now configure Grafana to use HTTPS by either: 1. Generating certificates via the certificate system role 2. Using existing certificate files on the target system 3. Copying certificate files from the control node All tests updated to verify HTTPS functionality, and a new test added to verify certificate role integration. In addition - the tests were written incorrectly to run the handlers. In order to refresh the services, the handlers must be run immediately after running the role, so that the checks and verify tasks are testing the actual result of running the role. This fixes several test flakes that are caused when the services were not refreshed when doing the verification. Signed-off-by: Rich Megginson <rmeggins@redhat.com>
1 parent 28eb5d3 commit ae4b043

27 files changed

+355
-92
lines changed

README.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,88 @@ enable additional metrics, export to alternate data sinks, and so on.
185185
metrics_optional_packages: [pcp-pmda-apache]
186186
```
187187

188+
### metrics_grafana_certificates: []
189+
190+
For generating a new certificate for grafana it is recommended to set the
191+
`metrics_grafana_certificates` variable. If you have your own certs/keys, or
192+
will create them from your own CA provider, see below `metrics_grafana_cert` et
193+
al. for information about how to pass in and/or use your own certs.
194+
195+
The value of `metrics_grafana_certificates` is passed on to the
196+
`certificate_requests` variable of the `certificate` role called internally in
197+
the `metrics` role and it generates the private key and certificate. For the
198+
supported parameters of `metrics_grafana_certificates`, see the
199+
[`certificate_requests` role documentation section](https://github.com/linux-system-roles/certificate/#certificate_requests).
200+
201+
When you set `metrics_grafana_certificates`, you must not set
202+
`metrics_grafana_private_key` and `metrics_grafana_cert` variables because they
203+
are ignored.
204+
205+
This example installs grafana with an IdM-issued web server certificate assuming
206+
your machines are joined to a FreeIPA domain.
207+
208+
```yaml
209+
- name: Install grafana with server certificate and key
210+
include_role:
211+
name: linux-system-roles.metrics
212+
vars:
213+
metrics_graph_service: true
214+
metrics_grafana_certificates:
215+
- name: grafana-server
216+
dns: ['localhost', 'www.example.com']
217+
ca: ipa
218+
```
219+
220+
NOTE: The `certificate` role, unless using IPA and joining the systems to an IPA
221+
domain, creates self-signed certificates, so you will need to explicitly
222+
configure trust, which is not currently supported by the system roles. To use
223+
`ca: self-sign` or `ca: local`, depending on your certmonger usage, see the
224+
[linux-system-roles.certificate documentation](https://github.com/linux-system-roles/certificate/#cas-and-providers) for details.
225+
226+
NOTE: Creating a self-signed certificate is not supported on RHEL/CentOS-7.
227+
228+
### metrics_grafana_cert: ''
229+
230+
TLS certificate file for the grafana server. This should be the full absolute path
231+
on the grafana server machine e.g. `/etc/pki/tls/certs/grafana-server.crt`. This
232+
file should already exist. If you want to copy a local file, see `metrics_grafana_cert_src`.
233+
234+
```yaml
235+
metrics_grafana_cert: /etc/pki/tls/certs/grafana-server.crt
236+
```
237+
238+
### metrics_grafana_cert_src: ''
239+
240+
TLS certificate file from the local machine to copy to `metrics_grafana_cert` on
241+
the grafana server machine. If `metrics_grafana_cert` is not specified, then
242+
`metrics_grafana_cert_src` will be copied to `/etc/pki/tls/certs/basename.crt` on
243+
the grafana server machine, where `basename` is the basename of `metrics_grafana_cert_src`.
244+
245+
```yaml
246+
metrics_grafana_cert_src: /my/local/grafana-server.crt
247+
```
248+
249+
### metrics_grafana_private_key: ''
250+
251+
TLS private key file for the grafana server. This should be the full absolute path
252+
on the grafana server machine e.g. `/etc/pki/tls/private/grafana-server.key`. This
253+
file should already exist. If you want to copy a local file, see `metrics_grafana_private_key_src`.
254+
255+
```yaml
256+
metrics_grafana_private_key: /etc/pki/tls/private/grafana-server.key
257+
```
258+
259+
### metrics_grafana_private_key_src: ''
260+
261+
TLS private key file from the local machine to copy to `metrics_grafana_private_key` on
262+
the grafana server machine. If `metrics_grafana_private_key` is not specified, then
263+
`metrics_grafana_private_key_src` will be copied to `/etc/pki/tls/private/basename.key` on
264+
the grafana server machine, where `basename` is the basename of `metrics_grafana_private_key_src`.
265+
266+
```yaml
267+
metrics_grafana_private_key_src: /my/local/grafana-server.key
268+
```
269+
188270
## Example Playbook
189271

190272
Basic metric recording setup for each managed host only, with one

defaults/main.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,10 @@ metrics_optional_domains: []
6868
# Additional metrics packages that should be installed, beyond the default set,
6969
# to enable additional metrics, export to alternate data sinks, and so on.
7070
metrics_optional_packages: []
71+
72+
# Certificate and key for grafana server
73+
metrics_grafana_certificates: []
74+
metrics_grafana_cert: ""
75+
metrics_grafana_private_key: ""
76+
metrics_grafana_cert_src: ""
77+
metrics_grafana_private_key_src: ""

tasks/main.yml

Lines changed: 65 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,7 @@
11
# SPDX-License-Identifier: MIT
22
---
3-
- name: Ensure ansible_facts used by role
4-
setup:
5-
gather_subset: "{{ __metrics_required_facts_subsets }}"
6-
when: __metrics_required_facts |
7-
difference(ansible_facts.keys() | list) | length > 0
8-
9-
- name: Determine if system is booted with systemd
10-
when: __metrics_is_booted is not defined
11-
block:
12-
- name: Run systemctl
13-
# noqa command-instead-of-module
14-
command: systemctl is-system-running
15-
register: __is_system_running
16-
changed_when: false
17-
failed_when: false
18-
19-
- name: Require installed systemd
20-
fail:
21-
msg: "Error: This role requires systemd to be installed."
22-
when: '"No such file or directory" in __is_system_running.msg | d("")'
23-
24-
- name: Set flag to indicate that systemd runtime operations are available
25-
set_fact:
26-
# see https://www.man7.org/linux/man-pages/man1/systemctl.1.html#:~:text=is-system-running%20output
27-
__metrics_is_booted: "{{ __is_system_running.stdout != 'offline' }}"
3+
- name: Ensure facts and vars used by role are set
4+
include_tasks: set_vars.yml
285

296
- name: Add Elasticsearch to metrics domain list
307
set_fact:
@@ -137,13 +114,71 @@
137114
name: "{{ role_path }}/roles/pcp"
138115
when: metrics_provider == 'pcp'
139116

140-
- name: Setup metric graphing service.
117+
- name: Manage metrics graphing service
141118
vars:
142-
grafana_metrics_provider: "{{ metrics_provider }}"
143-
include_role:
144-
# noqa role-name[path]
145-
name: "{{ role_path }}/roles/grafana"
119+
grafana_cert: "{{ __metrics_grafana_cert_dir + '/' + metrics_grafana_certificates.0.name + '.crt'
120+
if metrics_grafana_certificates | length > 0
121+
else metrics_grafana_cert if metrics_grafana_cert.startswith('/')
122+
else __metrics_grafana_cert_dir + '/' + metrics_grafana_cert if metrics_grafana_cert | length > 0
123+
else __metrics_grafana_cert_dir + '/' + metrics_grafana_cert_src | basename
124+
if metrics_grafana_cert_src | length > 0
125+
else '' }}"
126+
grafana_private_key: "{{ __metrics_grafana_private_key_dir + '/' + metrics_grafana_certificates.0.name + '.key'
127+
if metrics_grafana_certificates | length > 0
128+
else metrics_grafana_private_key if metrics_grafana_private_key.startswith('/')
129+
else __metrics_grafana_private_key_dir + '/' + metrics_grafana_private_key if metrics_grafana_private_key | length > 0
130+
else __metrics_grafana_private_key_dir + '/' + metrics_grafana_private_key_src | basename
131+
if metrics_grafana_private_key_src | length > 0
132+
else '' }}"
146133
when: metrics_graph_service | bool
134+
block:
135+
- name: Create certificates using the certificate role
136+
when:
137+
- metrics_grafana_certificates | length > 0
138+
- ansible_facts['os_family'] == 'RedHat'
139+
block:
140+
- name: Check the OS version for self-sign
141+
when:
142+
- (ansible_facts['distribution_version'] | int == 7 and
143+
metrics_grafana_certificates.0.ca == 'self-sign')
144+
fail:
145+
msg: >-
146+
Creating a self-signed certificate is not supported on
147+
{{ ansible_facts['distribution'] }}-{{
148+
ansible_facts['distribution_version'] }}
149+
150+
- name: Create certificates using the certificate role
151+
include_role:
152+
name: fedora.linux_system_roles.certificate
153+
vars:
154+
certificate_requests: "{{ metrics_grafana_certificates }}"
155+
156+
- name: Copy grafana cert
157+
copy:
158+
src: "{{ metrics_grafana_cert_src }}"
159+
dest: "{{ grafana_cert }}"
160+
mode: "0644"
161+
owner: root
162+
group: root
163+
when: metrics_grafana_cert_src | length > 0
164+
165+
- name: Copy grafana private key
166+
copy:
167+
src: "{{ metrics_grafana_private_key_src }}"
168+
dest: "{{ grafana_private_key }}"
169+
mode: "0600"
170+
owner: root
171+
group: root
172+
when: metrics_grafana_private_key_src | length > 0
173+
no_log: true
174+
175+
- name: Setup metric graphing service.
176+
vars:
177+
grafana_metrics_provider: "{{ metrics_provider }}"
178+
include_role:
179+
# noqa role-name[path]
180+
name: "{{ role_path }}/roles/grafana"
181+
when: metrics_graph_service | bool
147182

148183
- name: Configure firewall
149184
include_tasks: firewall.yml

tasks/set_vars.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# SPDX-License-Identifier: MIT
2+
---
3+
- name: Ensure ansible_facts used by role
4+
setup:
5+
gather_subset: "{{ __metrics_required_facts_subsets }}"
6+
when: __metrics_required_facts |
7+
difference(ansible_facts.keys() | list) | length > 0
8+
9+
- name: Determine if system is booted with systemd
10+
when: __metrics_is_booted is not defined
11+
block:
12+
- name: Run systemctl
13+
# noqa command-instead-of-module
14+
command: systemctl is-system-running
15+
register: __is_system_running
16+
changed_when: false
17+
failed_when: false
18+
19+
- name: Require installed systemd
20+
fail:
21+
msg: "Error: This role requires systemd to be installed."
22+
when: '"No such file or directory" in __is_system_running.msg | d("")'
23+
24+
- name: Set flag to indicate that systemd runtime operations are available
25+
set_fact:
26+
# see https://www.man7.org/linux/man-pages/man1/systemctl.1.html#:~:text=is-system-running%20output
27+
__metrics_is_booted: "{{ __is_system_running.stdout != 'offline' }}"

tests/check_grafana.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@
22
---
33
- name: Check if Grafana works
44
uri:
5-
url: http://localhost:3000/login
5+
url: "{{ __metrics_grafana_protocol | d('http') }}://localhost:3000/login"
66
method: GET
77
status_code: 200
8+
validate_certs: false
89
when: __metrics_is_booted | bool

tests/restore_services_state.yml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
service_facts:
66
register: final_state
77

8-
# yamllint disable rule:line-length
98
- name: Restore state of services
109
tags: tests::cleanup
1110
service:
@@ -26,7 +25,6 @@
2625
- redis
2726
- valkey
2827
- grafana-server
29-
# yamllint enable rule:line-length
3028

3129
- name: Stop firewall
3230
service:

tests/tests_bz1855539.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
name: linux-system-roles.metrics
2626
public: true
2727

28+
- name: Flush handlers
29+
meta: flush_handlers
30+
2831
- name: >-
2932
Check if pmie configuration file on remote host is the secondary one
3033
command: |-
@@ -44,9 +47,6 @@
4447
grep -E '^\s*\S+\s+y\s+n\s+' {{ pcp_pmie_control_path }}/local
4548
changed_when: false
4649
47-
- name: Flush handlers
48-
meta: flush_handlers
49-
5050
rescue:
5151
- name: Handle failure case
5252
include_tasks: handle_test_failure.yml

tests/tests_bz1855544.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
name: linux-system-roles.metrics
2727
public: true
2828

29+
- name: Flush handlers
30+
meta: flush_handlers
31+
2932
- name: Check if all default datasources are configured
3033
include_tasks: check_default_datasources.yml
3134

@@ -38,9 +41,6 @@
3841
changed_when: false
3942
when: __metrics_is_booted | bool
4043

41-
- name: Flush handlers
42-
meta: flush_handlers
43-
4444
rescue:
4545
- name: Handle test failure
4646
include_tasks: handle_test_failure.yml

tests/tests_verify_auth.yml

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,15 @@
2828
name: linux-system-roles.metrics
2929
public: true
3030

31-
- name: Restart PMCD
32-
# noqa command-instead-of-module
33-
shell: systemctl restart pmcd && sleep 5
34-
changed_when: false
35-
when: __metrics_is_booted | bool
31+
- name: Flush handlers
32+
meta: flush_handlers
3633

3734
- name: Check if SASL works
3835
include_tasks: "{{ item }}"
3936
loop:
4037
- check_sasl.yml
4138
- check_firewall_selinux.yml
4239

43-
- name: Flush handlers
44-
meta: flush_handlers
45-
4640
rescue:
4741
- name: Handle failure case
4842
include_tasks: handle_test_failure.yml

tests/tests_verify_basic.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
name: linux-system-roles.metrics
2020
public: true
2121

22+
- name: Flush handlers
23+
meta: flush_handlers
24+
2225
- name: Check if basic metrics role setup works
2326
include_tasks: "{{ item }}"
2427
loop:
@@ -27,9 +30,6 @@
2730
- check_pmie.yml
2831
- check_firewall_selinux.yml
2932

30-
- name: Flush handlers
31-
meta: flush_handlers
32-
3333
rescue:
3434
- name: Handle failure case
3535
include_tasks: handle_test_failure.yml

0 commit comments

Comments
 (0)