Secure Prometheus https scrapper by TLS with mutual TLS authentication
This post was inspired and parly based on Julien Pivotto’s blog postlocal copy
Main difference from Julen’s approach that I’m going to use 2 way TLS authentication between Prometheus and node-exporter instead of one way password authentication in Julien’s post.
Generating TLS certificates.
We will need a self signed cert for https scrapper and exporters. Standard stuff, you have done it million times.
Create directories to store certificates
mkdir -p /usr/local/etc/prometheus/certs
chown -R prometheus:prometheus /usr/local/etc/prometheus
chmod og= /usr/local/etc/prometheus/certs
cd /usr/local/etc/prometheus/certs
Creating CA
- Create CA_openssl.cnf file for CA with following content
# cat CA_openssl.cnf subjectKeyIdentifier=hash basicConstraints=critical, CA:TRUE keyUsage = keyCertSign,digitalSignature,cRLSign nsCertType = sslCA
- Create a private key and self-signed certificate for CA. Cert will expire in 10 years, so do not forget to set a reminder.
- CSR and private key in same command
# openssl req -newkey rsa:2048 -keyout CA.key -out CA.csr -nodes Generating a RSA private key ............................................................................+++++ ............+++++ writing new private key to 'CA.key' ----- You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter '.', the field will be left blank. ----- Country Name (2 letter code) [AU]: State or Province Name (full name) [Some-State]:NSW Locality Name (eg, city) []: Organization Name (eg, company) [Internet Widgits Pty Ltd]:Example CA Organizational Unit Name (eg, section) []: Common Name (e.g. server FQDN or YOUR name) []:monitoring.example.com Email Address []: Please enter the following 'extra' attributes to be sent with your certificate request A challenge password []: An optional company name []:
# ls monitoring:/usr/local/etc/prometheus/certs/test@[20:59] # ls CA.csr CA_openssl.cnf CA.key
- Sign that CSR
# openssl x509 -req -days 3650 -in CA.csr -signkey CA.key -out CA.cert -extfile CA_openssl.cnf Signature ok subject=C = AU, ST = NSW, O = Example CA, CN = monitoring.example.com Getting Private key
# ls CA* CA.cert CA.csr CA.key CA_openssl.cnf
-
Check results. Cert must have
CA:TRUE
andDigital Signature, Certificate Sign
openssl x509 -in CA.cert -text -noout Certificate: Data: Version: 3 (0x2) Serial Number: 39:35:3c:18:ed:70:53:c6:43:fa:69:20:f0:f0:b6:ba:78:a8:30:04 Signature Algorithm: sha256WithRSAEncryption Issuer: C = AU, ST = NSW, O = Example CA, CN = monitoring.example.com Validity Not Before: Feb 7 10:05:06 2022 GMT Not After : Feb 5 10:05:06 2032 GMT Subject: C = AU, ST = NSW, O = Example CA, CN = monitoring.example.com Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:a2:66:5b:30:ad:61:10:b2:9f:16:b2:7a:ac:62: === CUT === 55:83 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 30:F9:C0:EB:90:66:3D:0F:8A:7F:01:7A:11:3E:2B:C2:22:87:7F:F6 X509v3 Basic Constraints: critical CA:TRUE X509v3 Key Usage: Digital Signature, Certificate Sign, CRL Sign Netscape Cert Type: SSL CA Signature Algorithm: sha256WithRSAEncryption 61:f2:1f:90:cf:03:0c:c4:d6:f8:f6:81:7d:66:90:a7:df:12: === CUT === 73:a3:f2:3b
- Delete CSR, we no longer need it
# rm CA.csr
- CSR and private key in same command
Generate certificate(s) for prometheus AKA scrapper
- Create config file. scrapper.
# cat monitoring.example.com.cnf [req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name ] CN = monitoring.example.com [v3_req] # authorityKeyIdentifier=keyid,issuer:always basicConstraints=critical,CA:false keyUsage = digitalSignature,keyEncipherment, dataEncipherment extendedKeyUsage = clientAuth nsCertType = client
Replace values for CN with FDQN of your prometheuth host (or just fake domain, it does not mater what is there)
- Generate private key and CSR
monitoring:/usr/local/etc/prometheus/certs/test@[21:31] # openssl req -newkey rsa:2048 -keyout monitoring.example.com.key -out monitoring.example.com.csr -config monitoring.example.com.cnf -nodes Generating a RSA private key .......+++++ .....................+++++ writing new private key to 'monitoring.example.com.key' -----
- Signef CSR by CA key
# openssl x509 -req -days 3650 -CA CA.cert -CAkey CA.key -CAcreateserial -CAserial CA.serial -extensions v3_req -out monitoring.example.com.cert -extfile monitoring.example.com.cnf -in monitoring.example.com.csr Signature ok subject=CN = monitoring.example.com Getting CA Private Key
- Check that cert has following extensions.
TLS Web Client Authentication
is what impotent there.# openssl x509 -text -noout -in monitoring.example.com.cert === CUT === X509v3 extensions: X509v3 Basic Constraints: critical CA:FALSE X509v3 Key Usage: Digital Signature, Key Encipherment, Data Encipherment X509v3 Extended Key Usage: TLS Web Client Authentication Netscape Cert Type: SSL Client === CUT ===
Generate certificates for exporters
There are 2 approaches to exporters certificates
- Configure Prometheuth to do not verify exporters certificates. In this case you can use any certs on exportes you find fancy. You event can uses same self-signed cert for all exporters in your network.
- Use “proper” certs for exporters signed by CA. This way you will need individual cert for each exporter with matching FDQN so Prometheus will be able to verify that it is talking to right host. I did not find a way to use prevent prometheuth from checking that target and CN in certificate match.
It may be a good idea to generate exporters cert on other machine not where Prometheus server runs. But we are trying to protect server so if bad guys get access to exporters certs it is already too late.
Following pretty much same steps as for scrapper certificate to create exporters certificates.
-
Create CNF files You will need one file for each exporter you have. Please put FDQN of exporter to CN and DNS.1 fields inside CNF file.
!!! These FDQN have to match hostnames you put under target list in Prometheus config file prometheus.yaml. !!!
Also notice that extendedKeyUsage is serverAuth for exporter’s certs.
# cat host1.example.com.cnf [req] distinguished_name = req_distinguished_name req_extensions = v3_req prompt = no [req_distinguished_name ] CN = host1.example.com [v3_req] # authorityKeyIdentifier=keyid,issuer:always basicConstraints=critical,CA:false keyUsage = digitalSignature,keyEncipherment, dataEncipherment extendedKeyUsage = serverAuth nsCertType = client subjectAltName = @alt_names [alt_names] DNS.1 = host1.example.com
- Generate CSR and sign it by CA
# openssl req -newkey rsa:2048 -keyout host1.example.com.key -out host1.example.com.csr -config host1.example.com.cnf -nodes Generating a RSA private key ..............................................+++++ ....+++++ writing new private key to 'host1.example.com.key' ----- # openssl x509 -req -days 3650 -CA CA.cert -CAkey CA.key -CAcreateserial -CAserial CA.serial -extensions v3_req -out host1.example.com.cert -extfile host1.example.com.cnf -in host1.example.com.csr Signature ok subject=CN = host1.example.com Getting CA Private Key # rm host1.example.com.csr
- Repeat for each host with exporter(s). Multiple exporters on same host but different ports may share certificate
Configuring scrapper.
Add following under scrape_config in prometheuth.yaml
scrape_configs:
- job_name: 'node_exporter'
scheme: https
tls_config:
cert_file: /usr/local/etc/prometheus/certs/monitoring.example.com.cert
key_file: /usr/local/etc/prometheus/certs/monitoring.example.com.key
ca_file: /usr/local/etc/prometheus/certs/CA.cert
# insecure_skip_verify: true
static_configs:
- targets:
- host1.example.com
- host2..example.com
- host3..example.com
relabel_configs:
- source_labels: [__address__] # populate list of hosts to scan from targets list
target_label: __param_target
- source_labels: [__param_target] # Add label 'instance' to identify targets.
target_label: instance
- source_labels: [__param_target]
target_label: __address__ # Add port number to targets automatically.
replacement: $1:9010
Uncomment insecure_skip_verify: true
if you use self-signed certificates for exporters.
Otherwise FQDN names under - targets:
must match CN/DNS.1 names in scrapper certificates. You probaly can use IP addresses there, but in this case certificates should have appropriate IP.1 entry under alt_names in cnf files. I did not tested it.
Do not forget to reload or restart prometheus to pick up config change.
Configure node_exporter
I’m using FreeBSD,but it should be pretty much same for linux.
-
Create a user for node_exporter.
TLS support in node_exporter is still experimental. So bugs are expected. One problem I found with TLS that node_exporter first drop root privileges and then attempts read certificates. By default node_exporter runs as nobody:nobody, so to allow it read certs I will need make certs readable for user nobody, which is very bad idea. So let’s create a dedicated unprivileged user to run node_exporter.
# adduser -D -w no -s /usr/sbin/nologin Username: node_exporter Full name: node_exporter Uid (Leave empty for default): Login group [node_exporter]: Login group is node_exporter. Invite node_exporter into other groups? []: Login class [default]: Shell (sh csh tcsh bash rbash nologin) [nologin]: Home directory [/home/node_exporter]: Home directory permissions (Leave empty for default): Use password-based authentication? [no]: Lock out the account after creation? [no]: Username : node_exporter Password : <disabled> Full Name : node_exporter Uid : 1007 Class : Groups : node_exporter Home : /home/node_exporter Home Mode : Shell : /usr/sbin/nologin Locked : no OK? (yes/no): yes adduser: INFO: Successfully added (node_exporter) to the user database. Add another user? (yes/no): no Goodbye!
- Install node_exporter
# pkg install node_exporter
- Create directory to store certificate and config.
mkdir -p /usr/local/etc/node_exporter/certs chown -R node_exporter:node_exporter /usr/local/etc/node_exporter chmod og= /usr/local/etc/node_exporter/certs
- Copy certificates from whatever system you created them.
3 files are required
- host specific private key
host1.example.com.key
- host specific certificate
host1.example.com.cert
- CA certificate
CA.cert
/usr/local/etc/node_exporter/certs# ls CA.cert host1.example.com.cert host1.example.com.key
- host specific private key
-
create web-config.yaml
# cat /usr/local/etc/node_exporter/web-config.yaml tls_server_config: # Certificate and key files for server to use to authenticate to client. cert_file: /usr/local/etc/node_exporter/certs/host1.example.com.cert key_file: /usr/local/etc/node_exporter/certs/host1.example.com.key # Server policy for client authentication. Maps to ClientAuth Policies. # client_auth_type: "NoClientCert" client_auth_type: "RequireAndVerifyClientCert" # CA certificate for client certificate authentication to the server. client_ca_file: /usr/local/etc/node_exporter/certs/CA.cert
-
Add parameters to /etc/rc.config
# cat /etc/rc.config | grep node_exporter_ node_exporter_enable="YES" node_exporter_user=node_exporter node_exporter_group=node_exporter node_exporter_args="--web.config=/usr/local/etc/node_exporter/web-config.yaml" node_exporter_listen_address="a.b.c.d:9010"
- Start node_exporter
# /usr/local/etc/rc.d/node_exporter start
It is done. your prometheuth metrics are more secure than before. Enjoy!!