1
0
Fork 0
mirror of https://github.com/munin-monitoring/contrib.git synced 2025-07-21 18:41:03 +00:00

puppetdb: implement cert validation and client cert auth

It is very common for PuppetDB installs to run on a different host than
the puppetmaster. In such cases, a certificate file is normally used to
establish an encrypted communication to the server.

The most common setup for this server certificate is to use a
certificate that was signed by the puppetmaster's CA, so one would want
to verify the server cert against this same CA (it should be present on
puppet clients).

Moreover, to ensure that *only* puppet clients can communicate with
PuppetDB, a pair of client certificat/key files are usually used to
authenticate clients.
This commit is contained in:
Gabriel Filion 2020-01-19 19:40:29 -05:00 committed by Lars Kruse
parent 6a8fbf3bd3
commit 70f565c503

View file

@ -21,11 +21,33 @@ Plugin configuration parameters:
Time in seconds (int) to wait for a result when querying the REST API. By Time in seconds (int) to wait for a result when querying the REST API. By
default, wait for 2 seconds default, wait for 2 seconds
* ca:
Path to the Certificate Authority used for verifying a cert received from
the PuppetDB server during an https connection. This can be useful if the
cert used by PuppetDB was signed by the puppetmaster's CA. This option is
not necessary if a plaintext connection is used (e.g. if pdburl starts
with 'http://').
* cert:
Path to the TLS certificate file used for establishing client
communication for an https connection. This option should be paired with
the `key` option. This option is not necessary if a plaintext connection
is used (e.g. if pdburl starts with 'http://').
* key:
Path to the TLS private key used for establishing client communication for
an https connection. This option should be paired with the `cert` option.
This option is not necessary if a plaintext connection is used (e.g. if
pdburl starts with 'http://').
Example: Example:
[puppetdb] [puppetdb]
env.pdburl https://puppetdb.example.com:8080/metrics/v1/mbeans env.pdburl https://puppetdb.example.com:8080/metrics/v1/mbeans
env.timeout 5 env.timeout 5
env.ca /etc/puppetboard/ca.pem
env.cert /etc/puppetboard/client_cert.pem
env.key /etc/puppetboard/client_key.pem
=head1 DEPENDENCIES =head1 DEPENDENCIES
@ -54,14 +76,21 @@ class WrongStatusCode(Exception):
pass pass
def rest_request(url, timeout): def rest_request(url, timeout, ca, key_pair):
"""Make a GET request to URL. We expect a 200 response. """Make a GET request to URL. We expect a 200 response.
This function will let exceptions from requests raise through to indicate This function will let exceptions from requests raise through to indicate
request failure. request failure.
If response code is not 200, it will raise a WrongStatusCode exception. If response code is not 200, it will raise a WrongStatusCode exception.
""" """
headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'} headers = {'content-type': 'application/json', 'Accept-Charset': 'UTF-8'}
resp = requests.get(url, headers=headers, timeout=timeout)
ssl_options = {}
if ca:
ssl_options['verify'] = ca
if key_pair:
ssl_options['cert'] = key_pair
resp = requests.get(url, headers=headers, timeout=timeout, **ssl_options)
if resp.status_code != 200: if resp.status_code != 200:
err = f"GET Request to '{url}' returned code {resp.status_code}; expected 200." # noqa: E501 err = f"GET Request to '{url}' returned code {resp.status_code}; expected 200." # noqa: E501
raise WrongStatusCode(err) raise WrongStatusCode(err)
@ -93,12 +122,12 @@ def config():
print("jvm_mem_used.draw AREA") print("jvm_mem_used.draw AREA")
def fetch_field_values(mbeans_url, timeout): def fetch_field_values(mbeans_url, timeout, ca, key_pair):
"""Get values from PuppetDB and print them out.""" """Get values from PuppetDB and print them out."""
memory_url = f"{mbeans_url}/java.lang:type=Memory" memory_url = f"{mbeans_url}/java.lang:type=Memory"
try: try:
mem_req = rest_request(memory_url, timeout) mem_req = rest_request(memory_url, timeout, ca, key_pair)
except Exception as e: except Exception as e:
print(f"HTTP Request did not complete successfully: {e}", print(f"HTTP Request did not complete successfully: {e}",
file=sys.stderr) file=sys.stderr)
@ -134,9 +163,31 @@ if __name__ == '__main__':
print(f"Invalid value for timeout: {e}", file=sys.stderr) print(f"Invalid value for timeout: {e}", file=sys.stderr)
exit(1) exit(1)
ca = os.environ.get('ca', None)
if ca:
if not os.path.exists(ca):
print(f"CA file '{ca}' not found.", file=sys.stderr)
exit(1)
cert = os.environ.get('cert', None)
key = os.environ.get('key', None)
if cert or key:
if cert and key:
if not os.path.exists(cert):
print(f"Certificate file '{cert}' not found.", file=sys.stderr)
exit(1)
if not os.path.exists(key):
print(f"Key file '{key}' not found.", file=sys.stderr)
exit(1)
else:
print("Only one of 'cert' and 'key' supplied. "
"Both are needed for client authentication.",
file=sys.stderr)
exit(1)
if len(sys.argv) > 1 and sys.argv[1] == 'autoconf': if len(sys.argv) > 1 and sys.argv[1] == 'autoconf':
try: try:
dummy = rest_request(mbeans_url, timeout) dummy = rest_request(mbeans_url, timeout, ca, (cert, key))
except Exception as e: except Exception as e:
print(f"no ({e})") print(f"no ({e})")
exit(0) exit(0)
@ -148,4 +199,4 @@ if __name__ == '__main__':
config() config()
exit(0) exit(0)
fetch_field_values(mbeans_url, timeout) fetch_field_values(mbeans_url, timeout, ca, (cert, key))