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:
parent
6a8fbf3bd3
commit
70f565c503
1 changed files with 57 additions and 6 deletions
|
@ -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))
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue