summaryrefslogtreecommitdiff
path: root/sitemodules
diff options
context:
space:
mode:
Diffstat (limited to 'sitemodules')
-rw-r--r--sitemodules/profiles/files/gitea/gitea.service18
-rw-r--r--sitemodules/profiles/files/icinga2_external_commands/check_kernel_status.py66
-rw-r--r--sitemodules/profiles/files/icinga2_external_commands/check_ocsp56
-rw-r--r--sitemodules/profiles/files/icinga2_external_commands/ssl_cert-cacert-command.conf6
-rw-r--r--sitemodules/profiles/files/icinga2_master/check_puppetdb_nodes500
-rw-r--r--sitemodules/profiles/files/icinga2_master/icinga2-git-pull-hook16
-rw-r--r--sitemodules/profiles/manifests/base.pp111
-rw-r--r--sitemodules/profiles/manifests/gitea.pp162
-rw-r--r--sitemodules/profiles/manifests/icinga2_common.pp25
-rw-r--r--sitemodules/profiles/manifests/icinga2_master.pp76
-rw-r--r--sitemodules/profiles/manifests/icinga2_satellite.pp19
-rw-r--r--sitemodules/profiles/manifests/pootle.pp3
-rw-r--r--sitemodules/profiles/manifests/wordpress.pp3
-rw-r--r--sitemodules/profiles/manifests/x509cert_common.pp6
-rwxr-xr-xsitemodules/profiles/templates/base/update-crls.epp160
-rw-r--r--sitemodules/profiles/templates/cacert_boardvoting/config.yaml.epp3
-rw-r--r--sitemodules/profiles/templates/gitea/app.ini.epp101
-rw-r--r--sitemodules/profiles/templates/gitea/nginx.conf.epp82
-rw-r--r--sitemodules/profiles/templates/squid/squid.conf.epp3
-rw-r--r--sitemodules/profiles/templates/wordpress/wordpress-ssl.conf.epp4
-rw-r--r--sitemodules/roles/manifests/authserver.pp29
-rw-r--r--sitemodules/roles/manifests/code.pp28
-rw-r--r--sitemodules/roles/manifests/emailout.pp3
-rw-r--r--sitemodules/roles/manifests/git.pp3
-rw-r--r--sitemodules/roles/manifests/idp.pp29
-rw-r--r--sitemodules/roles/manifests/infra03.pp4
-rw-r--r--sitemodules/roles/manifests/ircserver.pp3
-rw-r--r--sitemodules/roles/manifests/issue.pp3
-rw-r--r--sitemodules/roles/manifests/monitor.pp3
-rw-r--r--sitemodules/roles/manifests/pgsql.pp2
-rw-r--r--sitemodules/roles/manifests/traininginstance.pp26
-rw-r--r--sitemodules/roles/manifests/translations.pp3
32 files changed, 1187 insertions, 369 deletions
diff --git a/sitemodules/profiles/files/gitea/gitea.service b/sitemodules/profiles/files/gitea/gitea.service
new file mode 100644
index 0000000..7cbab55
--- /dev/null
+++ b/sitemodules/profiles/files/gitea/gitea.service
@@ -0,0 +1,18 @@
+[Unit]
+Description=Gitea (Git with a cup of tea)
+After=syslog.target network.target
+Before=nginx.service
+
+[Service]
+RestartSec=2s
+Type=simple
+User=git
+Group=git
+WorkingDirectory=/var/lib/gitea
+ExecStartPre=+/usr/bin/install --directory --mode=0750 --owner=git --group=www-data /run/gitea
+ExecStart=/usr/local/bin/gitea web -c /etc/gitea/app.ini
+Restart=always
+Environment=USER=git HOME=/var/lib/gitea GITEA_WORK_DIR=/var/lib/gitea
+
+[Install]
+WantedBy=multi-user.target
diff --git a/sitemodules/profiles/files/icinga2_external_commands/check_kernel_status.py b/sitemodules/profiles/files/icinga2_external_commands/check_kernel_status.py
new file mode 100644
index 0000000..9236c9a
--- /dev/null
+++ b/sitemodules/profiles/files/icinga2_external_commands/check_kernel_status.py
@@ -0,0 +1,66 @@
+#!/usr/bin/env python3
+
+from apt import cache
+import argparse
+import nagiosplugin
+import logging
+
+_log = logging.getLogger("nagiosplugin")
+
+
+def get_running_kernel_version() -> str:
+ with open("/proc/version", "r") as proc_version:
+ return proc_version.read().split()[2]
+
+
+def get_installed_kernels() -> list[str]:
+ try:
+ pkg_cache = cache.FilteredCache()
+ pkg_cache.set_filter(cache.InstalledFilter())
+ pkg_cache.open()
+
+ return [
+ v
+ for v in [
+ k.name[len("linux-image-") :]
+ for k in pkg_cache
+ if k.name.startswith("linux-image")
+ ]
+ if "-" in v
+ ]
+ finally:
+ pkg_cache.close()
+
+
+class VersionsContext(nagiosplugin.ScalarContext):
+ def evaluate(self, metric, resource):
+ installed = get_installed_kernels()
+ latest = sorted(installed)[-1]
+ _log.info("current kernel version is %s", metric.value)
+ _log.info("installed kernel versions: %s", ",".join(installed))
+ _log.info("latest kernel version: %s", latest)
+
+ if latest == metric.value:
+ return self.result_cls(nagiosplugin.Ok)
+
+ return self.result_cls(nagiosplugin.Critical)
+
+
+class KernelVersion(nagiosplugin.Resource):
+ def probe(self):
+ current = get_running_kernel_version()
+ return [nagiosplugin.Metric("kernel version", current)]
+
+
+def main():
+ argp = argparse.ArgumentParser()
+ argp.add_argument(
+ "-v", "--verbose", action="count", default=0, help="verbose output"
+ )
+ args = argp.parse_args()
+ check = nagiosplugin.Check(KernelVersion(), VersionsContext("kernel version"))
+ check.main(args.verbose)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/sitemodules/profiles/files/icinga2_external_commands/check_ocsp b/sitemodules/profiles/files/icinga2_external_commands/check_ocsp
index be3f0f0..97885e2 100644
--- a/sitemodules/profiles/files/icinga2_external_commands/check_ocsp
+++ b/sitemodules/profiles/files/icinga2_external_commands/check_ocsp
@@ -93,38 +93,38 @@ case ${CLASS} in
;;
esac
+if [ ! -f "${ISSUER}" ]; then
+ echo "CRITICAL: issuer certificate file ${ISSUER} not found."
+ exit 2
+fi
+
TMP=$(mktemp)
ERR=${TMP}-err
trap 'rm -f ${TMP} ${ERR}' 0 1 2 3 15
-openssl ocsp -issuer "${ISSUER}" -serial "${SERIAL}" -CApath "${CAPATH}" -url "${RESPONDER}" >"${TMP}" 2>&1
-
-awk '
-NR == 1 {
- response = $0
- next
- }
-/This Update:/ {
- next
- }
-/Next Update:/ {
- next
- }
- {
- answer = answer " " $0;
- }
-END {
- if (response != "Response verify OK")
- exitcode = 2
- else
- exitcode = 0
- print response " " answer;
- exit(exitcode)
- }
-' "${TMP}"
-EXITCODE=$?
-rm -f "${TMP}"
-exit ${EXITCODE}
+if ! openssl ocsp -issuer "${ISSUER}" -serial "${SERIAL}" -CApath "${CAPATH}" -url "${RESPONDER}" -resp_text >"${TMP}" 2>&1; then
+ echo "CRITICAL: openssl ocsp command failed"
+ echo
+ echo "captured output:"
+ cat "${TMP}"
+ exit 2
+fi
+
+if grep -q "${SERIAL}: good" "${TMP}"; then
+ echo "OK: OCSP check successful, certificate OK"
+ exit 0
+fi
+
+if grep -q "${SERIAL}: revoked" "${TMP}"; then
+ echo "WARNING: OCSP check successful, certificate revoked"
+ exit 1
+fi
+
+echo "UNKNOWN: unexpected response"
+echo
+echo "captured output:"
+cat "${TMP}"
+exit 3
##Response Verify Failure
##17914:error:27069065:OCSP routines:OCSP_basic_verify:certificate verify error:ocsp_vfy.c:122:Verify error:certificate has expired
diff --git a/sitemodules/profiles/files/icinga2_external_commands/ssl_cert-cacert-command.conf b/sitemodules/profiles/files/icinga2_external_commands/ssl_cert-cacert-command.conf
index 0b47607..f4c6ea0 100644
--- a/sitemodules/profiles/files/icinga2_external_commands/ssl_cert-cacert-command.conf
+++ b/sitemodules/profiles/files/icinga2_external_commands/ssl_cert-cacert-command.conf
@@ -210,8 +210,14 @@ object CheckCommand "ssl_cert_cacert" {
set_if = "$ssl_cert_ignore_sct$"
description = "Do not check for signed certificate timestamps (SCT)"
}
+ "--first-element-only" = {
+ set_if = "$ssl_cert_first_element_only$"
+ description = "Verify just the first cert element, not the whole chain"
+ }
}
vars.ssl_cert_address = "$check_address$"
+ vars.ssl_cert_rootssl_certs = "/etc/ssl/certs/ca-certificates.crt"
+ vars.ssl_cert_first_element_only = true
vars.ssl_cert_port = 443
}
diff --git a/sitemodules/profiles/files/icinga2_master/check_puppetdb_nodes b/sitemodules/profiles/files/icinga2_master/check_puppetdb_nodes
index 727a328..1145e4e 100644
--- a/sitemodules/profiles/files/icinga2_master/check_puppetdb_nodes
+++ b/sitemodules/profiles/files/icinga2_master/check_puppetdb_nodes
@@ -1,253 +1,277 @@
-#!/usr/bin/perl
-
-# Copyright (c) 2014, Evgeni Golov
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without modification,
-# are permitted provided that the following conditions are met:
-#
-# * Redistributions of source code must retain the above copyright notice, this
-# list of conditions and the following disclaimer.
-#
-# * Redistributions in binary form must reproduce the above copyright notice, this
-# list of conditions and the following disclaimer in the documentation and/or
-# other materials provided with the distribution.
-#
-# * Neither the name of the {organization} nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
-# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
-# ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-use strict;
-use warnings;
-use JSON;
-use LWP;
-use Monitoring::Plugin;
-use Date::Parse;
-
-my $np = Monitoring::Plugin->new(
- usage => "Usage: %s [ -H|--hostname=<hostname>] "
- . "[ -p|--port=<port> ] [-s] [ -w|--warning=<minutes> ] "
- . "[ -c|--critical=<minutes> ] [ -W|--warnfails=<num> ] "
- . "[ -C|--critfails=<num> ] [ -n|--node=<node> ]"
- . "[ -a|--apiversion=<num> ]"
- . "[ -i|--ignore=<list> ]",
- shortname => 'Check last node runs from PuppetDB',
- url => 'https://github.com/evgeni/check_puppetdb_nodes',
- version => '1.0',
- license => 'This plugin is free software, and comes with ABSOLUTELY
-NO WARRANTY. It may be used, redistributed and/or modified under
-the terms of the BSD 3-clause license.',
-);
-
-$np->add_arg(
- spec => 'warning|w=i',
- help => "Exit with WARNING status if nodes did not update for "
- . "more than INTEGER minutes (default: %s)",
- default => 120,
-);
-
-$np->add_arg(
- spec => 'critical|c=i',
- help => "Exit with CRITICAL status if nodes did not update for "
- . "more than INTEGER minutes (default: %s)",
- default => 1440,
-);
-
-$np->add_arg(
- spec => 'warnfails|W=i',
- help => "Exit with WARNING status if nodes had at least INTEGER "
- . "failures in the last run (default: %s)",
- default => 1,
-);
-
-$np->add_arg(
- spec => 'critfails|C=i',
- help => "Exit with CRITICAL status if nodes had at least INTEGER "
- . "failures in the last run (default: %s)",
- default => 1,
-);
-
-$np->add_arg(
- spec => 'hostname|H=s',
- help => 'Hostname of the PuppetDB (default: %s)',
- default => 'localhost',
-);
-
-$np->add_arg(
- spec => 'port|p=i',
- help => 'Port PuppetDB is running on (default: %s)',
- default => 8080,
-);
-
-$np->add_arg(
- spec => 'node|n=s',
- help => 'Node name to check, if not given, all nodes will be checked',
-);
-
-$np->add_arg(
- spec => 'ssl|s',
- help => "Use HTTPS instead of HTTP",
-);
-
-$np->add_arg(
- spec => 'insecure|k',
- help => "Allow connections via HTTPS without checking certificates",
-);
-
-$np->add_arg(
- spec => 'apiversion|a=n',
- help => 'Specify PupppetDB API version (default: %s)',
- default => 3,
-);
-
-$np->add_arg(
- spec => 'ignore|i=s',
- help => 'Node names to ignore (comma-separated list) (default: %s)',
- default => '',
-);
-
-$np->getopts;
-
-my %apiurls = (
- 3 => { 'nodes' => 'v3/nodes', 'event-counts' => 'v3/event-counts' },
- 4 => { 'nodes' => 'pdb/query/v4/nodes', 'event-counts' => 'pdb/query/v4/event-counts', 'logs' => 'pdb/query/v4/reports/{hash}/logs' },
-);
-if ( !exists $apiurls{$np->opts->apiversion} ) {
- $np->nagios_exit( 'UNKNOWN', 'Unsupported PuppetDB API version ' . $np->opts->apiversion );
+#!/usr/bin/env python3
+"""Nagios/Icinga plugin to check puppetdb status of a node."""
+
+import argparse
+import logging
+from datetime import datetime
+from urllib.parse import urljoin
+
+import nagiosplugin
+import requests
+
+_log = logging.getLogger("check_puppetdb_nodes")
+
+api_urls = {
+ 3: {"nodes": "v3/nodes", "event-counts": "v3/event-counts"},
+ 4: {
+ "nodes": "pdb/query/v4/nodes",
+ "event-counts": "pdb/query/v4/event-counts",
+ "logs": "pdb/query/v4/reports/{hash}/logs",
+ },
}
-my @ignore_list = split( ',', $np->opts->ignore );
-my $url = sprintf( 'http%s://%s:%d/',
- defined( $np->opts->ssl ) ? 's' : '',
- $np->opts->hostname, $np->opts->port );
+class BoolContext(nagiosplugin.Context):
+ def evaluate(self, metric, resource):
+ if metric != 0:
+ return self.result_cls(nagiosplugin.Critical, metric)
-my $ua = new LWP::UserAgent;
-$ua->default_header( 'Accept' => 'application/json' );
-if ( defined( $np->opts->insecure ) ) {
- $ua->ssl_opts( verify_hostname => 0 ,SSL_verify_mode => 0x00);
-}
-my %parameters = ();
-if ( defined( $np->opts->node ) ) {
- %parameters = ( 'query' => '["=","certname","' . $np->opts->node . '"]' );
-}
-my $uri = URI->new( $url . $apiurls{$np->opts->apiversion}{'nodes'} );
-$uri->query_form(%parameters);
-my $response = $ua->get($uri);
+class PuppetDBReport(nagiosplugin.Resource):
+ """Domain model: last report for node.
-if ( !$response->is_success ) {
- $np->nagios_exit( 'UNKNOWN',
- $response->code . ": " . $response->status_line );
-}
+ Determines the age of the last puppetdb report for the given node.
+ """
-my $data = decode_json( $response->decoded_content );
+ def __init__(self, api_client, node_name, ignored):
+ self.api_client = api_client
+ self.node_name = node_name
+ self.ignored = ignored or []
-my $now = time();
+ def probe(self):
+ node_info = self.api_client.fetch_node_information(self.node_name)
-if ( defined( $np->opts->node ) and !@$data ) {
- $np->add_message( CRITICAL,
- $np->opts->node . " not found in puppetdb\n" );
-}
+ for node in node_info:
+ if "certname" in node:
+ certname = node["certname"]
+ else:
+ certname = node["name"]
+ deactivated = node["deactivated"]
+ catalog_timestamp = node["catalog_timestamp"]
+ report_hash = node["latest_report_hash"]
+ ts = datetime.strptime(catalog_timestamp, "%Y-%m-%dT%H:%M:%S.%fZ")
+ delta = datetime.utcnow() - ts
-foreach my $node (@$data) {
- my $certname = defined($node->{'certname'}) ? $node->{'certname'} : $node->{'name'} ;
- my $deactivated = $node->{'deactivated'};
- my $catalog_timestamp = $node->{'catalog_timestamp'};
- my $report_hash = $node->{'latest_report_hash'};
- my $ts = str2time($catalog_timestamp);
-
- next if grep { $certname eq $_ } @ignore_list;
-
- if ( !defined $deactivated and ( !length $catalog_timestamp or !length $report_hash )) {
- $np->add_message( CRITICAL,
- "$certname last run UNAVAILABLE\n" );
- }
- if ( !defined $deactivated and length $catalog_timestamp and $report_hash) {
- my $delta = ( $now - $ts );
- if ( $delta > ( $np->opts->critical * 60 ) ) {
- $np->add_message( CRITICAL,
- "$certname did not update since $catalog_timestamp\n" );
- }
- elsif ( $delta > ( $np->opts->warning * 60 ) ) {
- $np->add_message( WARNING,
- "$certname did not update since $catalog_timestamp\n" );
- }
-
- my %apiparameters = (
- 3 => {
- 'query' => '["and",["=","certname","'
- . $certname
- . '"],["=","latest-report?",true]]',
- 'summarize-by' => 'certname',
- 'count-by' => 'resource',
- },
- 4 => {
- 'query' => '["and",["=","certname","'
- . $certname
- . '"],["=","latest_report?",true]]',
- 'summarize_by' => 'certname',
- 'count_by' => 'resource',
- }
- );
- my $uri = URI->new( $url . $apiurls{$np->opts->apiversion}{'event-counts'} );
- $uri->query_form($apiparameters{$np->opts->apiversion});
- $response = $ua->get($uri);
-
- if ( $response->is_success ) {
- my $node_data = decode_json( $response->decoded_content );
-
- my $failures = 0;
- if ( defined( @$node_data[0] )
- and defined( @$node_data[0]->{'failures'} ) )
- {
- $failures = @$node_data[0]->{'failures'};
- }
+ if deactivated:
+ yield nagiosplugin.Metric(f"missing-{certname}", 1, min=0)
+ continue
- if ( $failures >= $np->opts->critfails ) {
- $np->add_message( CRITICAL,
- "$certname had $failures failures in the last run\n" );
- }
- elsif ( $failures >= $np->opts->warnfails ) {
- $np->add_message( WARNING,
- "$certname had $failures failures in the last run\n" );
- }
- elsif ( exists $apiurls{$np->opts->apiversion}{'logs'} ) {
- my $apiurl = $apiurls{$np->opts->apiversion}{'logs'};
- $apiurl =~ s/{hash}/$report_hash/;
- $uri = URI->new( $url . $apiurl );
- $response = $ua->get($uri);
- if ( $response->is_success ) {
- my $logs = decode_json( $response->decoded_content );
- foreach my $log (@$logs) {
- my $tags = $log->{'tags'};
- if ( grep(/^err$/, @$tags) ) {
- $np->add_message( WARNING, "$certname, $log->{'message'}" );
- }
- }
- }
- }
+ if certname in self.ignored:
+ continue
- } else {
- $np->nagios_exit( 'UNKNOWN', 'Unsupported query ' . $response->decoded_content);
- }
+ yield nagiosplugin.Metric(
+ f"last-{certname}", round(delta.total_seconds()), "s", min=0
+ )
- }
-}
+ failures = self.api_client.fetch_failure_report(certname, report_hash)
+ yield nagiosplugin.Metric(f"failed-{certname}", failures, min=0)
+
+
+def comma_separated(string):
+ result = []
+ for part in [item.trim for item in string.split(",")]:
+ if len(part) > 0:
+ result.append(part)
+ return result
-my $code;
-my $message;
-( $code, $message ) = $np->check_messages;
-$np->nagios_exit( $code, $message );
+class PuppetDBClient:
+ def __init__(self, hostname, port, tls, insecure, roots, api_version):
+ scheme = tls and "https" or "http"
+ self.base_url = f"{scheme}://{hostname}:{port}/"
+ self.allow_insecure = insecure
+ self.use_roots = roots
+ self.api_version = api_version
+
+ self.client = requests.Session()
+ self.client.headers = {"Accept": "application/json"}
+ self.node_info = []
+
+ def fetch_node_information(self, node_name):
+ if self.node_info:
+ return self.node_info
+
+ url = urljoin(self.base_url, api_urls[self.api_version]["nodes"])
+ if node_name:
+ r = self.client.get(
+ url, params={"query": f'["=","certname","{node_name}"]'}
+ )
+ else:
+ r = self.client.get(url)
+
+ r.raise_for_status()
+
+ self.node_info = r.json()
+
+ return self.node_info
+
+ def fetch_failure_report(self, cert_name, report_hash):
+ url = urljoin(self.base_url, api_urls[self.api_version]["event-counts"])
+
+ if self.api_version == 3:
+ query_args = {
+ "query": f'["and",["=","certname","{cert_name}"],["=","latest-report?",true]]',
+ "summarize-by": "certname",
+ "count-by": "resource",
+ }
+ else:
+ query_args = {
+ "query": f'["and",["=","certname","{cert_name}"],["=","latest_report?",true]]',
+ "summarize_by": "certname",
+ "count_by": "resource",
+ }
+
+ r = self.client.get(url, params=query_args)
+
+ r.raise_for_status()
+
+ report = r.json()
+
+ failures = 0
+
+ if report and "failures" in report[0]:
+ failures += report[0]["failures"]
+
+ if "logs" in api_urls[self.api_version]:
+ url = urljoin(self.base_url, api_urls[self.api_version]["logs"]).format(
+ hash=report_hash
+ )
+ self.client.get(url)
+
+ r.raise_for_status()
+
+ logs = r.json()
+
+ for log in logs:
+ if "tags" in log and "err" in log["tags"]:
+ failures += 1
+
+ return failures
+
+ def get_cert_names(self, node_name):
+ result = []
+
+ for node in self.fetch_node_information(node_name):
+ if "certname" in node:
+ result.append(node["certname"])
+ else:
+ result.append(node["name"])
+
+ return sorted(result)
+
+
+@nagiosplugin.guarded
+def main():
+ argp = argparse.ArgumentParser(description=__doc__)
+
+ argp.add_argument(
+ "-w",
+ "--warning",
+ type=int,
+ default=120,
+ help="Exit with WARNING status if nodes did not update for more then given minutes",
+ )
+ argp.add_argument(
+ "-c",
+ "--critical",
+ type=int,
+ default=1440,
+ help="Exit with CRITICAL status if nodes did not update for more then given minutes",
+ )
+ argp.add_argument(
+ "-W",
+ "--warnfails",
+ type=int,
+ default=1,
+ help="Exit with WARNING status if nodes had at least the given number of failures in the last run",
+ )
+ argp.add_argument(
+ "-C",
+ "--critfails",
+ type=int,
+ default=1,
+ help="Exit with CRITICAL status if nodes had at least the given number of failures in the last run",
+ )
+ argp.add_argument(
+ "-H",
+ "--hostname",
+ type=str,
+ default="localhost",
+ help="Hostname of the PuppetDB",
+ )
+ argp.add_argument(
+ "-p", "--port", type=int, default=8080, help="Port PuppetDB is running on"
+ )
+ argp.add_argument(
+ "-n",
+ "--node",
+ type=str,
+ help="Node name to check, if not given, all nodes will be checked",
+ )
+ argp.add_argument(
+ "-t",
+ "--tls",
+ action="store_true",
+ help="Use HTTPS instead of HTTP",
+ )
+ argp.add_argument(
+ "-k",
+ "--insecure",
+ action="store_true",
+ help="Allow connections via HTTPS without checking certificates",
+ )
+ argp.add_argument(
+ "-r",
+ "--roots",
+ type=str,
+ help="Use the given root certificate file for certificate validation",
+ )
+ argp.add_argument(
+ "-a",
+ "--apiversion",
+ dest="api_version",
+ type=int,
+ default=4,
+ choices=api_urls.keys(),
+ help="Specify PuppetDB API version",
+ )
+ argp.add_argument(
+ "-i",
+ "--ignore",
+ type=comma_separated,
+ help="Node names to ignore (comma-separated list)",
+ )
+ argp.add_argument(
+ "-v",
+ "--verbose",
+ action="count",
+ default=0,
+ help="increase output verbosity (use up to 3 times)",
+ )
+
+ args = argp.parse_args()
+
+ api_client = PuppetDBClient(
+ args.hostname, args.port, args.tls, args.insecure, args.roots, args.api_version
+ )
+
+ check = nagiosplugin.Check(PuppetDBReport(api_client, args.node, args.ignore))
+
+ for certname in api_client.get_cert_names(args.node):
+ check.add(BoolContext(f"missing-{certname}"))
+ check.add(
+ nagiosplugin.ScalarContext(
+ f"last-{certname}", args.warning * 60, args.critical * 60
+ )
+ )
+ check.add(
+ nagiosplugin.ScalarContext(
+ f"failed-{certname}", f"@{args.warnfails}:", f"@{args.critfails}:"
+ )
+ )
+
+ check.main(verbose=args.verbose)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/sitemodules/profiles/files/icinga2_master/icinga2-git-pull-hook b/sitemodules/profiles/files/icinga2_master/icinga2-git-pull-hook
index a0d3711..c786017 100644
--- a/sitemodules/profiles/files/icinga2_master/icinga2-git-pull-hook
+++ b/sitemodules/profiles/files/icinga2_master/icinga2-git-pull-hook
@@ -88,6 +88,17 @@ class GitHookRequestHandler(BaseHTTPRequestHandler):
self.wfile.write(("%s\r\n" % message).encode("UTF-8"))
def _handle_pull(self):
+ args = [
+ "sshpass",
+ "-e",
+ "-P",
+ "passphrase",
+ "git",
+ "pull",
+ GIT_REPOSITORY,
+ GIT_BRANCH,
+ ]
+ self.log.info("running '%s'", " ".join(args))
try:
git_proc = subprocess.run(
[
@@ -96,15 +107,12 @@ class GitHookRequestHandler(BaseHTTPRequestHandler):
"-P",
"passphrase",
"git",
- "subtree",
"pull",
- "--prefix",
- "icinga2/conf.d",
GIT_REPOSITORY,
GIT_BRANCH,
],
env=ENV_FOR_GIT,
- cwd="/etc",
+ cwd=GIT_DIRECTORY,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
check=True,
diff --git a/sitemodules/profiles/manifests/base.pp b/sitemodules/profiles/manifests/base.pp
index 719fe21..ff3d9e4 100644
--- a/sitemodules/profiles/manifests/base.pp
+++ b/sitemodules/profiles/manifests/base.pp
@@ -14,9 +14,7 @@
#
# @param rootalias alias that gets emails for root
#
-# @param crl_job_enable whether to setup the hourly CRL update job
-#
-# @param crl_job_services which services to reload after the CRL update
+# @param crl_job configure the hourly CRL update job
#
# @param is_external whether the node is outside of CAcert infrastructure
#
@@ -36,15 +34,21 @@
# Copyright
# ---------
#
-# Copyright 2016-2021 Jan Dittberner
+# Copyright 2016-2022 Jan Dittberner
#
class profiles::base (
- Array[String] $admins = [],
- Hash[String, Data] $users = {},
- String $rootalias = "${trusted['certname']}-admin@cacert.org",
- Boolean $crl_job_enable = false,
- Array[String] $crl_job_services = [],
- Boolean $is_external = false,
+ Array[String] $admins = [],
+ Hash[String, Data] $users = {},
+ String $rootalias = "${trusted['certname']}-admin@cacert.org",
+ Hash[String, Data] $crl_job = {
+ 'enable' => false,
+ 'hostname' => $trusted['certname'],
+ 'services' => [],
+ 'check_url' => 'https://monitor.infra.cacert.org:5665/v1/actions/process-check-result',
+ 'api_user' => '',
+ 'api_password' => '',
+ },
+ Boolean $is_external = false,
) {
# ensure admin users for this container
$admins.each |String $username| {
@@ -99,18 +103,37 @@ class profiles::base (
source => 'puppet:///modules/profiles/base/apt_periodic.conf',
}
- package { ['lsb-release', 'distro-info-data', 'sudo']:
+ package { ['lsb-release', 'distro-info-data']:
ensure => present,
}
+ class { 'sudo':
+ config_file_replace => false,
+ }
package { ['zsh', 'tmux', 'less', 'vim-nox']:
ensure => latest,
}
+ if $facts['virtual'] == 'lxc' {
+ file { '/etc/network/interfaces':
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ content => "auto lo\niface lo inet loopback\n",
+ }
+ }
+
Package['zsh'] -> User <| |>
- package { ['aptitude', 'apticron']:
- ensure => purged,
+ if !$is_external {
+ package { ['aptitude', 'apticron', 'isc-dhcp-client']:
+ ensure => purged,
+ }
+ } else {
+ package { ['aptitude', 'apticron']:
+ ensure => purged,
+ }
}
file { '/etc/zsh/newuser.zshrc.recommended':
@@ -153,10 +176,21 @@ class profiles::base (
repos => 'main',
release => "${::lsbdistcodename}-updates",
}
- apt::source { "security.debian.org-${::lsbdistcodename}-security":
- location => 'http://security.debian.org/debian-security',
- repos => 'main',
- release => "${::lsbdistcodename}/updates",
+
+ $os_major = Integer($facts['os']['release']['major'])
+
+ if $os_major < 11 {
+ apt::source { "security.debian.org-${::lsbdistcodename}-security":
+ location => 'http://security.debian.org/debian-security',
+ repos => 'main',
+ release => "${::lsbdistcodename}/updates",
+ }
+ } else {
+ apt::source { "security.debian.org-${::lsbdistcodename}-security":
+ location => 'http://security.debian.org/',
+ repos => 'main',
+ release => "${::lsbdistcodename}-security",
+ }
}
apt::source { "ftp.nl.debian.org-${::lsbdistcodename}-backports":
location => 'http://ftp.nl.debian.org/debian',
@@ -197,23 +231,42 @@ class profiles::base (
recipient => $rootalias,
}
- package { ['ca-certificates', 'ca-cacert']:
+ package { 'ca-certificates':
ensure => installed,
}
- file { '/usr/local/share/ca-certificates/cacert_class3_2021.crt':
+ $cacert_class1_file = '/usr/local/share/ca-certificates/cacert_class1_X0F.crt'
+ $cacert_class3_file = '/usr/local/share/ca-certificates/cacert_class3_2021.crt'
+
+ file { $cacert_class1_file:
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ source => 'puppet:///modules/profiles/base/cacert_class1_X0F.crt',
+ require => Package['ca-certificates'],
+ }
+
+ file { $cacert_class3_file:
ensure => file,
owner => 'root',
group => 'root',
mode => '0644',
source => 'puppet:///modules/profiles/base/cacert_class3_2021.crt',
require => Package['ca-certificates'],
- } ~>
+ }
+
exec { '/usr/sbin/update-ca-certificates':
- require => Package['ca-certificates'],
+ require => Package['ca-certificates'],
+ refreshonly => true,
+ subscribe => [File[$cacert_class1_file], File[$cacert_class3_file]],
}
- if ($crl_job_enable) {
+ if ($crl_job['enable']) {
+ package { 'python3-requests':
+ ensure => installed,
+ }
+
file { '/var/local/ssl':
ensure => directory,
owner => 'root',
@@ -236,12 +289,20 @@ class profiles::base (
mode => '0755',
content => epp(
'profiles/base/update-crls.epp',
- { 'services' => $crl_job_services }),
+ {
+ 'services' => $crl_job['services'],
+ 'check_url' => $crl_job['check_url'],
+ 'api_user' => $crl_job['api_user'],
+ 'api_password' => $crl_job['api_password'],
+ 'hostname' => $crl_job['hostname'],
+ },
+ ),
require => [
Package['ca-certificates'],
- Package['ca-cacert'],
+ Package['python3-requests'],
File['/var/local/ssl/crls'],
- File['/usr/local/share/ca-certificates/cacert_class3_2021.crt']
+ File[$cacert_class1_file],
+ File[$cacert_class3_file]
],
}
} else {
diff --git a/sitemodules/profiles/manifests/gitea.pp b/sitemodules/profiles/manifests/gitea.pp
new file mode 100644
index 0000000..7ae1576
--- /dev/null
+++ b/sitemodules/profiles/manifests/gitea.pp
@@ -0,0 +1,162 @@
+# Class: profiles::gitea
+#
+# This class installs and configures a Gitea server.
+#
+# Parameters
+# ----------
+#
+# @param database_host PostgreSQL database host name
+#
+# @param database_name PostgreSQL database name
+#
+# @param database_password PostgreSQL database password
+#
+# @param database_port PostgreSQL database port
+#
+# @param database_ssl_mode PostgreSQL database connection ssl mode
+#
+# @param database_user PostgreSQL database user name
+#
+# @param gitea_fqdn Gitea host name
+#
+# @param gitea_socket Gitea Unix domain socket path
+#
+# Examples
+# --------
+#
+# @example
+# class roles::myhost {
+# include profiles::gitea
+# }
+#
+# Authors
+# -------
+#
+# Jan Dittberner <jandd@cacert.org>
+#
+# Copyright
+# ---------
+#
+# Copyright 2022 Jan Dittberner
+#
+class profiles::gitea (
+ String $database_password,
+ String $database_host = 'pgsql',
+ Integer $database_port = 5432,
+ String $database_name = 'gitea',
+ String $database_user = 'gitea',
+ String $database_ssl_mode = 'require',
+ String $gitea_fqdn = 'code.cacert.org',
+ String $gitea_socket = '/run/gitea/gitea.sock',
+) {
+ $gitea_version = '1.18.0'
+ $gitea_checksum = 'b45b715d519a97086208c6b42528d291dd1c4dfdf40321dc940030e1cf3de6e6'
+ $gitea_url = "https://dl.gitea.io/gitea/${gitea_version}/gitea-${gitea_version}-linux-amd64"
+ $gitea_service = '/etc/systemd/system/gitea.service'
+
+ include profiles::systemd_reload
+ include profiles::x509cert_common
+
+ package { 'git':
+ ensure => installed,
+ }
+
+ user { 'git':
+ comment => 'Gitea user',
+ home => '/var/lib/gitea',
+ system => true,
+ }
+
+ file { [
+ '/etc/gitea', '/var/lib/gitea', '/var/lib/gitea/data',
+ '/var/lib/gitea/repositories', '/var/lib/gitea/data/lfs', '/var/log/gitea',
+ ]:
+ ensure => directory,
+ owner => 'git',
+ group => 'git',
+ mode => '0750',
+ }
+
+ file { '/var/lib/gitea/.ssh':
+ ensure => directory,
+ owner => 'git',
+ group => 'git',
+ mode => '0700',
+ }
+
+ file { '/usr/local/bin/gitea':
+ ensure => file,
+ source => $gitea_url,
+ checksum => 'sha256',
+ checksum_value => $gitea_checksum,
+ owner => 'root',
+ group => 'git',
+ mode => '0750',
+ }
+
+ file { '/etc/gitea/app.ini':
+ ensure => file,
+ owner => 'git',
+ group => 'git',
+ mode => '0640',
+ content => epp('profiles/gitea/app.ini.epp', {
+ database_host => $database_host,
+ database_port => $database_port,
+ database_ssl_mode => $database_ssl_mode,
+ database_name => $database_name,
+ database_user => $database_user,
+ database_password => $database_password,
+ gitea_fqdn => $gitea_fqdn,
+ gitea_socket => $gitea_socket,
+ gitea_user => 'git',
+ }),
+ }
+
+ file { $gitea_service:
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ source => 'puppet:///modules/profiles/gitea/gitea.service',
+ notify => Exec['reload systemd configuration'],
+ }
+
+ service { 'gitea':
+ ensure => running,
+ enable => true,
+ require => [
+ User['git'],
+ ],
+ subscribe => [
+ File[$gitea_service],
+ File['/etc/gitea/app.ini'],
+ ],
+ }
+
+ file { '/etc/nginx':
+ ensure => directory,
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ } -> file { '/etc/nginx/nginx.conf':
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0644',
+ content => epp('profiles/gitea/nginx.conf.epp',
+ {
+ hostname => $gitea_fqdn,
+ gitea_socket => $gitea_socket,
+ }
+ ),
+ require => [
+ Concat["/etc/ssl/public/${gitea_fqdn}.chain.pem"],
+ File["/etc/ssl/private/${gitea_fqdn}.key.pem"],
+ ],
+ } -> package { 'nginx-light':
+ ensure => present,
+ } -> service { 'nginx':
+ ensure => running,
+ enable => true,
+ }
+}
diff --git a/sitemodules/profiles/manifests/icinga2_common.pp b/sitemodules/profiles/manifests/icinga2_common.pp
index e0c204a..66c946f 100644
--- a/sitemodules/profiles/manifests/icinga2_common.pp
+++ b/sitemodules/profiles/manifests/icinga2_common.pp
@@ -19,7 +19,7 @@
# Copyright
# ---------
#
-# Copyright 2019-2021 Jan Dittberner
+# Copyright 2019-2022 Jan Dittberner
class profiles::icinga2_common (
) {
include profiles::icinga2_certificates
@@ -47,4 +47,27 @@ class profiles::icinga2_common (
ensure => latest,
}
}
+
+ file { ['/usr/local/lib/nagios', '/usr/local/lib/nagios/plugins']:
+ ensure => directory,
+ owner => 'root',
+ group => 'staff',
+ mode => '0755',
+ }
+
+ if Integer($facts['os']['release']['major']) >= 9 {
+ package { ['python3-nagiosplugin', 'python3-apt' ]:
+ ensure => present,
+ }
+ }
+
+ if $facts['virtual'] in ['physical', 'kvm'] {
+ file { '/usr/local/lib/nagios/plugins/check_kernel_status':
+ ensure => file,
+ owner => 'root',
+ group => 'staff',
+ mode => '0755',
+ source => 'puppet:///modules/profiles/icinga2_external_commands/check_kernel_status.py',
+ }
+ }
}
diff --git a/sitemodules/profiles/manifests/icinga2_master.pp b/sitemodules/profiles/manifests/icinga2_master.pp
index e8f4968..221a3cb 100644
--- a/sitemodules/profiles/manifests/icinga2_master.pp
+++ b/sitemodules/profiles/manifests/icinga2_master.pp
@@ -50,7 +50,7 @@
# Copyright
# ---------
#
-# Copyright 2019-2021 Jan Dittberner
+# Copyright 2019-2022 Jan Dittberner
class profiles::icinga2_master (
String $ido_database_password,
String $web2_database_password,
@@ -69,7 +69,7 @@ class profiles::icinga2_master (
include profiles::systemd_reload
include postgresql::server
- class { '::icinga2':
+ class { 'icinga2':
manage_repo => false,
features => ['mainlog', 'checker', 'notification'],
constants => {
@@ -78,7 +78,7 @@ class profiles::icinga2_master (
},
}
- class { '::icinga2::pki::ca':
+ class { 'icinga2::pki::ca':
ca_cert => $ca_certificate,
ca_key => $ca_key,
}
@@ -88,7 +88,7 @@ class profiles::icinga2_master (
password => postgresql_password('icinga2', $ido_database_password),
}
- class { '::icinga2::feature::idopgsql':
+ class { 'icinga2::feature::idopgsql':
user => 'icinga2',
password => $ido_database_password,
database => 'icinga2',
@@ -96,7 +96,7 @@ class profiles::icinga2_master (
require => Postgresql::Server::Db['icinga2'],
}
- class { '::icinga2::feature::api':
+ class { 'icinga2::feature::api':
pki => 'none',
}
@@ -123,7 +123,7 @@ class profiles::icinga2_master (
),
}
- class { '::icingaweb2':
+ class { 'icingaweb2':
manage_repo => false,
import_schema => true,
db_type => 'pgsql',
@@ -134,7 +134,7 @@ class profiles::icinga2_master (
require => Postgresql::Server::Db['icingaweb2'],
}
- class { '::icingaweb2::module::monitoring':
+ class { 'icingaweb2::module::monitoring':
ido_type => 'pgsql',
ido_host => 'localhost',
ido_port => 5432,
@@ -146,19 +146,19 @@ class profiles::icinga2_master (
transport => 'api',
username => 'root',
password => $api_users['root']['password'],
- }
- }
+ },
+ },
}
icingaweb2::config::authmethod { 'external-authentication':
backend => 'external',
- require => Class['::icingaweb2'],
+ require => Class['icingaweb2'],
}
icingaweb2::config::role { 'admin':
users => join($icingaweb_admins, ','),
permissions => '*',
- require => Class['::icingaweb2'],
+ require => Class['icingaweb2'],
}
package { ['sshpass', 'git']:
@@ -206,14 +206,7 @@ class profiles::icinga2_master (
notify => Exec['reload systemd configuration'],
}
- file { '/usr/local/lib/nagios-plugins':
- ensure => directory,
- owner => 'root',
- group => 'staff',
- mode => '0755'
- }
-
- file { '/usr/local/lib/nagios-plugins/check_puppetdb_nodes':
+ file { '/usr/local/lib/nagios/plugins/check_puppetdb_nodes':
ensure => file,
owner => 'root',
group => 'staff',
@@ -221,6 +214,19 @@ class profiles::icinga2_master (
source => 'puppet:///modules/profiles/icinga2_master/check_puppetdb_nodes',
}
+ package {['rsync', 'python3-cryptography']:
+ ensure => present,
+ }
+
+ file { '/usr/local/lib/nagios/plugins/check_cacert_crl':
+ ensure => file,
+ owner => 'root',
+ group => 'root',
+ mode => '0755',
+ source => 'puppet:///modules/profiles/icinga2_external_commands/cacert_check_crl.py',
+ require => [Package['rsync'], Package['python3-nagiosplugin'], Package['python3-cryptography']],
+ }
+
service { 'icinga2-git-pull-hook':
ensure => running,
enable => true,
@@ -231,7 +237,7 @@ class profiles::icinga2_master (
],
}
- include ::icinga2
+ include icinga2
file { '/etc/icinga2/zones.d/global-templates':
ensure => directory,
@@ -262,12 +268,38 @@ class profiles::icinga2_master (
target => '/etc/icinga2/zones.d/global-templates/ocsp-command.conf',
}
::icinga2::object::checkcommand { 'cacert_crl':
- ensure => present,
- command => [
+ ensure => present,
+ command => [
'/usr/local/lib/nagios/plugins/check_cacert_crl',
],
+ arguments => {
+ '--rsync-url' => {
+ 'value' => '$cacert_crl_rsync_url$',
+ 'description' => 'rsync URL to check',
+ },
+ '--warning-last-age' => {
+ 'value' => '$cacert_crl_warning_last_age$',
+ 'description' => 'warning if last age is more than that many minutes',
+ },
+ '--critical-last-age' => {
+ 'value' => '$cacert_crl_critical_last_age$',
+ 'description' => 'critical if last age is more than that many minutes',
+ },
+ },
+ vars => {
+ 'cacert_crl_rsync_url' => 'rsync://crl.cacert.org/crl/',
+ 'cacert_crl_warning_last_age' => '1500', # 25h
+ 'cacert_crl_critical_last_age' => '2160', # 36h
+ },
target => '/etc/icinga2/zones.d/global-templates/cacert_crl-command.conf',
}
+ ::icinga2::object::checkcommand { 'kernel_status':
+ ensure => present,
+ command => [
+ '/usr/local/lib/nagios/plugins/check_kernel_status',
+ ],
+ target => '/etc/icinga2/zones.d/global-templates/kernel-status-command.conf',
+ }
file { '/etc/icinga2/zones.d/global-templates/ssl_cert-cacert-command.conf':
ensure => file,
diff --git a/sitemodules/profiles/manifests/icinga2_satellite.pp b/sitemodules/profiles/manifests/icinga2_satellite.pp
index 82ff7f7..5c6df6f 100644
--- a/sitemodules/profiles/manifests/icinga2_satellite.pp
+++ b/sitemodules/profiles/manifests/icinga2_satellite.pp
@@ -24,27 +24,28 @@
# Copyright
# ---------
#
-# Copyright 2021 Jan Dittberner
+# Copyright 2021-2022 Jan Dittberner
class profiles::icinga2_satellite {
include 'profiles::icinga2_common'
include 'profiles::icinga2_agent'
- file { ['/usr/local/lib/nagios', '/usr/local/lib/nagios/plugins']:
- ensure => directory,
- owner => 'root',
- group => 'root',
- mode => '0755',
- }
+ $cacert_class1_file = '/usr/local/share/ca-certificates/cacert_class1_X0F.crt'
+ $cacert_class3_file = '/usr/local/share/ca-certificates/cacert_class3_2021.crt'
+
file { '/usr/local/lib/nagios/plugins/check_ocsp':
ensure => file,
owner => 'root',
group => 'root',
mode => '0755',
source => 'puppet:///modules/profiles/icinga2_external_commands/check_ocsp',
- require => Package['ca-cacert'],
+ require => [
+ Package['ca-certificates'],
+ File[$cacert_class1_file],
+ File[$cacert_class3_file]
+ ],
}
- package {['rsync', 'python3-nagiosplugin', 'python3-cryptography']:
+ package {['rsync', 'python3-cryptography']:
ensure => present,
}
diff --git a/sitemodules/profiles/manifests/pootle.pp b/sitemodules/profiles/manifests/pootle.pp
index 7bf6a90..0d724d3 100644
--- a/sitemodules/profiles/manifests/pootle.pp
+++ b/sitemodules/profiles/manifests/pootle.pp
@@ -45,9 +45,6 @@ class profiles::pootle {
gid => 200,
system => true,
}
- class { 'sudo':
- config_file_replace => false,
- }
file { '/usr/local/bin/pootle-update':
ensure => file,
source => 'puppet:///modules/profiles/pootle/pootle-update',
diff --git a/sitemodules/profiles/manifests/wordpress.pp b/sitemodules/profiles/manifests/wordpress.pp
index f38eee7..1047188 100644
--- a/sitemodules/profiles/manifests/wordpress.pp
+++ b/sitemodules/profiles/manifests/wordpress.pp
@@ -33,7 +33,6 @@ class profiles::wordpress (
) {
include profiles::x509cert_common
- $server_cert = "/etc/ssl/public/${external_name}.crt.pem"
$server_key = "/etc/ssl/private/${external_name}.key.pem"
$server_chain = "/etc/ssl/public/${external_name}.chain.pem"
$client_ca_certificates = "/etc/ssl/public/${external_name}_client_cas.pem"
@@ -52,7 +51,6 @@ class profiles::wordpress (
mode => '0644',
content => epp('profiles/wordpress/wordpress-ssl.conf.epp', {
server_name => $external_name,
- server_cert => $server_cert,
server_key => $server_key,
server_chain => $server_chain,
client_ca_certificates => $client_ca_certificates,
@@ -70,7 +68,6 @@ class profiles::wordpress (
ensure => running,
enable => true,
subscribe => [
- File[$server_cert],
File[$server_key],
Concat[$server_chain],
Concat[$client_ca_certificates],
diff --git a/sitemodules/profiles/manifests/x509cert_common.pp b/sitemodules/profiles/manifests/x509cert_common.pp
index 380b505..88edace 100644
--- a/sitemodules/profiles/manifests/x509cert_common.pp
+++ b/sitemodules/profiles/manifests/x509cert_common.pp
@@ -41,6 +41,10 @@
class profiles::x509cert_common (
Hash[String, Data] $certificates,
) {
+ group { 'ssl-cert':
+ ensure => present,
+ system => true,
+ }
file { '/etc/ssl/public':
ensure => directory,
owner => 'root',
@@ -58,7 +62,7 @@ class profiles::x509cert_common (
file { "/etc/ssl/private/${name}.key.pem":
ensure => file,
owner => pick($cert_info['key_owner'], 'root'),
- group => pick($cert_info['key_group'], 'root'),
+ group => pick($cert_info['key_group'], 'ssl-cert'),
mode => pick($cert_info['key_mode'], '0640'),
content => $cert_info['private_key'],
}
diff --git a/sitemodules/profiles/templates/base/update-crls.epp b/sitemodules/profiles/templates/base/update-crls.epp
index eefdfe2..def45ab 100755
--- a/sitemodules/profiles/templates/base/update-crls.epp
+++ b/sitemodules/profiles/templates/base/update-crls.epp
@@ -1,30 +1,148 @@
-<%- | Array[String] $services | -%>
-#!/bin/sh
+<%- |
+ Array[String] $services,
+ String $check_url,
+ String $api_user,
+ String $api_password,
+ String $hostname,
+| -%>
+#!/usr/bin/env python3
# THIS FILE IS MANAGED BY PUPPET, MANUAL CHANGES WILL BE OVERWRITTEN AT THE
# NEXT PUPPET RUN.
-set -e
+import glob
+import subprocess
+import sys
+from datetime import datetime
+from os import path
-CRL_PATH='/var/local/ssl/crls/'
-CA_CERT='/etc/ssl/certs/ca-certificates.crt'
-RSYNC_LOCATION='crl.cacert.org::crl'
+import requests
-rsync -aqz "$RSYNC_LOCATION" "$CRL_PATH"
+CRL_PATH = "/var/local/ssl/crls/"
+CA_CERT = "/etc/ssl/certs/ca-certificates.crt"
+RSYNC_LOCATION = "crl2.intra.cacert.org::crl"
+ICINGA_CA = "/var/lib/icinga2/certs/ca.crt"
-for crl in "$CRL_PATH"*.crl
-do
- if openssl crl -noout -inform DER -in "$crl" -CAfile "$CA_CERT" 2>/dev/null
- then
- openssl crl -inform DER -in "$crl" -out "$crl".pem
- else
- echo "Error: Could not validate the CRL at $crl" >&2
- fi
-done
-c_rehash "$CRL_PATH" 2>/dev/null >&2
-<% $services.each |$service| { -%>
-service <%= $service %> reload > /dev/null
-<% } %>
+def json_timestamp(ts):
+ return int(ts.timestamp())
-exit 0
+
+def report_result(success, output, start):
+ data = {
+ "type": "Service",
+ "filter": 'host.name=="<%= $hostname %>" && service.name=="crl-sync"',
+ "exit_status": 0 if success else 2,
+ "plugin_output": "OK" if success else f"CRITICAL CRL sync failed\n{output}",
+ "check_source": "<%= $hostname %>",
+ "execution_start": json_timestamp(start),
+ "execution_end": json_timestamp(datetime.utcnow()),
+ "ttl": 3720,
+ }
+
+ r = requests.post(
+ "<%= $check_url %>",
+ auth=("<%= $api_user %>", "<%= $api_password %>"),
+ headers={"Accept": "application/json"},
+ json=data,
+ verify=ICINGA_CA,
+ )
+
+ if not r.ok:
+ print("could not submit passive check")
+ print(r.status_code, r.reason)
+ print(r.text)
+ sys.exit(1)
+
+
+def run_command(args, timeout=10):
+ try:
+ res = subprocess.run(args, capture_output=True, timeout=timeout, text=True)
+ except subprocess.TimeoutExpired:
+ return False, "timeout of {} expired running '{}'".format(
+ timeout, " ".join(args)
+ )
+
+ return res.returncode == 0, res.stderr
+
+
+def verify_crl(crl, ca_certificates=CA_CERT):
+ return run_command(
+ [
+ "openssl",
+ "crl",
+ "-noout",
+ "-inform",
+ "DER",
+ "-in",
+ crl,
+ "-CAfile",
+ ca_certificates,
+ ],
+ )
+
+
+def convert_to_pem(crl):
+ return run_command(
+ ["openssl", "crl", "-inform", "DER", "-in", crl, "-out", f"{crl}.pem"]
+ )
+
+
+def rehash_crls(crl_path=CRL_PATH):
+ return run_command(["c_rehash", crl_path])
+
+
+def run_rsync(rsync_source, dest_path):
+ return run_command(["rsync", "-aqz", rsync_source, dest_path], 60)
+
+
+def restart_service(service):
+ return run_command(["systemctl", "restart", f"{service}.service"], 30)
+
+
+def main():
+ start = datetime.utcnow()
+
+ ok, output = run_rsync(RSYNC_LOCATION, CRL_PATH)
+ if not ok:
+ report_result(False, f"rsync run failed:\n{output}", start)
+ return
+
+ error_output = []
+
+ for crl in glob.glob(path.join(CRL_PATH, "*.crl")):
+ ok, output = verify_crl(crl)
+ if not ok:
+ error_output.append(f"crl validation for {crl} failed:\n{output}")
+ continue
+
+ ok, output = convert_to_pem(crl)
+ if not ok:
+ error_output.append(f"pem conversion for {crl} failed:\n{output}")
+
+ if error_output:
+ report_result(False, "\n\n".join(error_output), start)
+ return
+
+ ok, output = rehash_crls(CRL_PATH)
+ if not ok:
+ report_result(False, f"c_rehash for {CRL_PATH} failed:\n{output}", start)
+ return
+
+ services = [<% if $services { %>"<%= $services.join(", ") %>"<% } %>]
+
+ for service in services:
+ ok, output = restart_service(service)
+ if not ok:
+ error_output.append(f"service restart failed for {service}:\n{output}")
+ continue
+
+ if error_output:
+ report_result(False, "\n\n".join(error_output), start)
+ return
+
+ report_result(True, "", start)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/sitemodules/profiles/templates/cacert_boardvoting/config.yaml.epp b/sitemodules/profiles/templates/cacert_boardvoting/config.yaml.epp
index 1e3309d..29eb2c9 100644
--- a/sitemodules/profiles/templates/cacert_boardvoting/config.yaml.epp
+++ b/sitemodules/profiles/templates/cacert_boardvoting/config.yaml.epp
@@ -18,7 +18,8 @@ database_file: /srv/cacert-boardvoting/data/database.sqlite
client_ca_certificates: <%= $client_ca_certs %>
server_certificate: <%= $server_cert %>
server_key: <%= $server_key %>
-https_address: ":8443"
+http_address: ":80"
+https_address: ":443"
cookie_secret: <%= $cookie_secret %>
csrf_key: <%= $csrf_key %>
base_url: <%= $base_url %>
diff --git a/sitemodules/profiles/templates/gitea/app.ini.epp b/sitemodules/profiles/templates/gitea/app.ini.epp
new file mode 100644
index 0000000..a1854f2
--- /dev/null
+++ b/sitemodules/profiles/templates/gitea/app.ini.epp
@@ -0,0 +1,101 @@
+<%- | String $gitea_fqdn,
+ String $gitea_user,
+ String $gitea_socket,
+ String $database_host,
+ String $database_name,
+ String $database_password,
+ String $database_user,
+ String $database_ssl_mode,
+ Integer $database_port,
+| -%>
+# THIS FILE IS MANAGED BY PUPPET, MANUAL CHANGES WILL BE OVERWRITTEN BY THE
+# NEXT PUPPET RUN
+APP_NAME = <%= $gitea_fqdn %> :: CAcert code hosting
+RUN_USER = <%= $gitea_user %>
+RUN_MODE = prod
+
+[repository]
+ROOT = /var/lib/gitea/repositories
+MAX_CREATION_LIMIT = 0
+
+[database]
+DB_TYPE = postgres
+HOST = <%= $database_host %>:<%= $database_port %>
+NAME = <%= $database_name %>
+USER = <%= $database_user %>
+PASSWD = <%= $database_password %>
+SSL_MODE = <%= $database_ssl_mode %>
+LOG_SQL = false
+
+[security]
+INSTALL_LOCK = true
+SECRET_KEY = <%= seeded_rand_string(20, 'gitea::secret_key', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') %>
+INTERNAL_TOKEN_URI = file:/etc/gitea/internal_token
+
+[server]
+DOMAIN = <%= $gitea_fqdn %>
+PROTOCOL = http+unix
+HTTP_ADDR = <%= $gitea_socket %>
+ROOT_URL = https://<%= $gitea_fqdn %>/
+DISABLE_SSH = true
+LFS_START_SERVER = true
+LFS_CONTENT_PATH = /var/lib/gitea/data/lfs
+LFS_JWT_SECRET = <%= seeded_rand_string(43, 'gitea::lfs_jwt_secret', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789') %>
+OFFLINE_MODE = false
+LANDING_PAGE = explore
+
+[mailer]
+ENABLED = true
+FROM = git@<%= $gitea_fqdn %>
+MAILER_TYPE = sendmail
+
+[service]
+REGISTER_EMAIL_CONFIRM = true
+ENABLE_NOTIFY_MAIL = true
+DISABLE_REGISTRATION = true
+ENABLE_CAPTCHA = false
+REQUIRE_SIGNIN_VIEW = false
+DEFAULT_KEEP_EMAIL_PRIVATE = false
+DEFAULT_ALLOW_CREATE_ORGANIZATION = false
+DEFAULT_ENABLE_TIMETRACKING = true
+NO_REPLY_ADDRESS = noreply.<%= $gitea_fqdn %>
+SHOW_REGISTRATION_BUTTON = false
+
+[picture]
+DISABLE_GRAVATAR = true
+ENABLE_FEDERATED_AVATAR = false
+
+[oauth2]
+ENABLE = false
+
+[federation]
+ENABLE = false
+
+[openid]
+ENABLE_OPENID_SIGNIN = false
+ENABLE_OPENID_SIGNUP = false
+
+[session]
+PROVIDER = file
+COOKIE_SECURE = true
+DOMAIN = <%= $gitea_fqdn %>
+SAME_SITE = strict
+
+[migrations]
+ALLOW_LOCALNETWORKS = true
+
+[webhook]
+ALLOWED_HOST_LIST = external,private
+
+[log]
+MODE = file,console
+ENABLE_ACCESS_LOG = true
+ACCESS = file
+ROUTER = file
+ROOT_PATH = /var/log/gitea/
+
+[log.file]
+LEVEL = Info
+
+[log.console]
+LEVEL = Warn
diff --git a/sitemodules/profiles/templates/gitea/nginx.conf.epp b/sitemodules/profiles/templates/gitea/nginx.conf.epp
new file mode 100644
index 0000000..bfc084e
--- /dev/null
+++ b/sitemodules/profiles/templates/gitea/nginx.conf.epp
@@ -0,0 +1,82 @@
+<%- | String $hostname,
+ String $gitea_socket,
+| -%>
+# THIS FILE IS MANAGED BY PUPPET, MANUAL CHANGES WILL BE OVERWRITTEN BY THE
+# NEXT PUPPET RUN
+user www-data;
+worker_processes auto;
+pid /run/nginx.pid;
+include /etc/nginx/modules-enabled/*.conf;
+
+events {
+ worker_connections 768;
+ # multi_accept on;
+}
+
+http {
+
+ ##
+ # Basic Settings
+ ##
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+ server_tokens off;
+
+ server_names_hash_bucket_size 64;
+ # server_name_in_redirect off;
+
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ ssl_protocols TLSv1.2 TLSv1.3;
+ ssl_prefer_server_ciphers on;
+
+ access_log /var/log/nginx/access.log;
+ error_log /var/log/nginx/error.log;
+
+ gzip on;
+
+ # gzip_vary on;
+ # gzip_proxied any;
+ # gzip_comp_level 6;
+ # gzip_buffers 16 8k;
+ # gzip_http_version 1.1;
+ # gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
+
+ include /etc/nginx/conf.d/*.conf;
+
+ upstream gitea {
+ server unix:<%= $gitea_socket %>;
+ }
+
+ server {
+ listen 80;
+ listen [::]:80;
+
+ listen 443 ssl;
+ listen [::]:443 ssl;
+
+ if ($https != "on") {
+ return 301 https://$host$uri;
+ }
+
+ ssl_certificate /etc/ssl/public/<%= $hostname %>.chain.pem;
+ ssl_certificate_key /etc/ssl/private/<%= $hostname %>.key.pem;
+
+ server_name <%= $hostname %>;
+
+ location / {
+ client_max_body_size 128M;
+
+ proxy_pass http://gitea;
+ proxy_set_header Host $host;
+ proxy_set_header X-Real-IP $remote_addr;
+ proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+ proxy_set_header X-Forwarded-Proto $scheme;
+ }
+ }
+}
diff --git a/sitemodules/profiles/templates/squid/squid.conf.epp b/sitemodules/profiles/templates/squid/squid.conf.epp
index a064368..d071279 100644
--- a/sitemodules/profiles/templates/squid/squid.conf.epp
+++ b/sitemodules/profiles/templates/squid/squid.conf.epp
@@ -12,4 +12,5 @@ acl <%= $acl -%>
http_access <%= $access_rule -%>
<% } %>
-maximum_object_size 50 MB
+maximum_object_size 500 MB
+cache_dir aufs /var/spool/squid 20000 16 256
diff --git a/sitemodules/profiles/templates/wordpress/wordpress-ssl.conf.epp b/sitemodules/profiles/templates/wordpress/wordpress-ssl.conf.epp
index 7eeb31e..66eaa30 100644
--- a/sitemodules/profiles/templates/wordpress/wordpress-ssl.conf.epp
+++ b/sitemodules/profiles/templates/wordpress/wordpress-ssl.conf.epp
@@ -1,5 +1,4 @@
<%- | String $server_name,
- String $server_cert,
String $server_key,
String $server_chain,
String $client_ca_certificates,
@@ -10,9 +9,8 @@
ServerName <%= $server_name %>
SSLEngine on
- SSLCertificateFile <%= $server_cert %>
+ SSLCertificateFile <%= $server_chain %>
SSLCertificateKeyFile <%= $server_key %>
- SSLCertificateChainFile <%= $server_chain %>
SSLCACertificateFile <%= $client_ca_certificates %>
SSLProtocol ALL -SSLv2 -SSLv3 -TLSv1.1 -TLSv1
diff --git a/sitemodules/roles/manifests/authserver.pp b/sitemodules/roles/manifests/authserver.pp
new file mode 100644
index 0000000..792bc71
--- /dev/null
+++ b/sitemodules/roles/manifests/authserver.pp
@@ -0,0 +1,29 @@
+# Class: roles::authserver
+# ========================
+#
+# This class defines the authserver role for a Hydra OAuth2/OpenID connect API
+# server used for authentication/authorization.
+# You should assign this class using hiera or via an ENC.
+#
+# Examples
+# --------
+#
+# @example
+# class { 'roles::authserver': }
+#
+# Authors
+# -------
+#
+# Jan Dittberner <jandd@cacert.org>
+#
+# Copyright
+# ---------
+#
+# Copyright 2022 Jan Dittberner
+#
+class roles::authserver {
+ include profiles::base
+ include profiles::rsyslog
+ include profiles::icinga2_agent
+}
+
diff --git a/sitemodules/roles/manifests/code.pp b/sitemodules/roles/manifests/code.pp
new file mode 100644
index 0000000..f88761a
--- /dev/null
+++ b/sitemodules/roles/manifests/code.pp
@@ -0,0 +1,28 @@
+# Class: roles::code
+# ==================
+#
+# This class defines the code role for a Gitea server used for code hosting.
+# You should assign this class using hiera or via an ENC.
+#
+# Examples
+# --------
+#
+# @example
+# class { 'roles::code': }
+#
+# Authors
+# -------
+#
+# Jan Dittberner <jandd@cacert.org>
+#
+# Copyright
+# ---------
+#
+# Copyright 2022 Jan Dittberner
+#
+class roles::code {
+ include profiles::base
+ include profiles::rsyslog
+ include profiles::icinga2_agent
+ include profiles::gitea
+}
diff --git a/sitemodules/roles/manifests/emailout.pp b/sitemodules/roles/manifests/emailout.pp
index d6bd462..72901d4 100644
--- a/sitemodules/roles/manifests/emailout.pp
+++ b/sitemodules/roles/manifests/emailout.pp
@@ -18,11 +18,12 @@
# Copyright
# ---------
#
-# Copyright 2018-2019 Jan Dittberner
+# Copyright 2018-2022 Jan Dittberner
#
class roles::emailout {
include profiles::base
include profiles::rsyslog
include profiles::purge_nrpe_agent
include profiles::icinga2_agent
+ include profiles::x509cert_common
}
diff --git a/sitemodules/roles/manifests/git.pp b/sitemodules/roles/manifests/git.pp
index 55d81b9..ef2393d 100644
--- a/sitemodules/roles/manifests/git.pp
+++ b/sitemodules/roles/manifests/git.pp
@@ -18,11 +18,12 @@
# Copyright
# ---------
#
-# Copyright 2020 Jan Dittberner
+# Copyright 2020-2022 Jan Dittberner
#
class roles::git {
include profiles::base
include profiles::rsyslog
include profiles::purge_nrpe_agent
include profiles::icinga2_agent
+ include profiles::x509cert_common
}
diff --git a/sitemodules/roles/manifests/idp.pp b/sitemodules/roles/manifests/idp.pp
new file mode 100644
index 0000000..2878931
--- /dev/null
+++ b/sitemodules/roles/manifests/idp.pp
@@ -0,0 +1,29 @@
+# Class: roles::idp
+# ========================
+#
+# This class defines the idp role for an OAuth2/OpenID identity provider
+# used for authentication/authorization.
+# You should assign this class using hiera or via an ENC.
+#
+# Examples
+# --------
+#
+# @example
+# class { 'roles::idp': }
+#
+# Authors
+# -------
+#
+# Jan Dittberner <jandd@cacert.org>
+#
+# Copyright
+# ---------
+#
+# Copyright 2022 Jan Dittberner
+#
+class roles::idp {
+ include profiles::base
+ include profiles::rsyslog
+ include profiles::icinga2_agent
+}
+
diff --git a/sitemodules/roles/manifests/infra03.pp b/sitemodules/roles/manifests/infra03.pp
index f1f6fe7..6ceb0d6 100644
--- a/sitemodules/roles/manifests/infra03.pp
+++ b/sitemodules/roles/manifests/infra03.pp
@@ -18,10 +18,10 @@
# Copyright
# ---------
#
-# Copyright 2021 Jan Dittberner
+# Copyright 2021-2022 Jan Dittberner
#
class roles::infra03 {
include profiles::base
include profiles::lxc_host
- #include profiles::icinga2_satellite
+ include profiles::icinga2_agent
}
diff --git a/sitemodules/roles/manifests/ircserver.pp b/sitemodules/roles/manifests/ircserver.pp
index f1ba1a9..c1f627c 100644
--- a/sitemodules/roles/manifests/ircserver.pp
+++ b/sitemodules/roles/manifests/ircserver.pp
@@ -17,11 +17,12 @@
# Copyright
# ---------
#
-# Copyright 2018-2019 Jan Dittberner
+# Copyright 2018-2022 Jan Dittberner
#
class roles::ircserver {
include profiles::base
include profiles::rsyslog
include profiles::purge_nrpe_agent
include profiles::icinga2_agent
+ include profiles::x509cert_common
}
diff --git a/sitemodules/roles/manifests/issue.pp b/sitemodules/roles/manifests/issue.pp
index eb3b86b..5a6a14d 100644
--- a/sitemodules/roles/manifests/issue.pp
+++ b/sitemodules/roles/manifests/issue.pp
@@ -18,11 +18,12 @@
# Copyright
# ---------
#
-# Copyright 2018-2019 Jan Dittberner
+# Copyright 2018-2022 Jan Dittberner
#
class roles::issue {
include profiles::base
include profiles::rsyslog
include profiles::purge_nrpe_agent
include profiles::icinga2_agent
+ include profiles::x509cert_common
}
diff --git a/sitemodules/roles/manifests/monitor.pp b/sitemodules/roles/manifests/monitor.pp
index 32f8c96..eea702c 100644
--- a/sitemodules/roles/manifests/monitor.pp
+++ b/sitemodules/roles/manifests/monitor.pp
@@ -18,11 +18,12 @@
# Copyright
# ---------
#
-# Copyright 2018-2019 Jan Dittberner
+# Copyright 2018-2022 Jan Dittberner
#
class roles::monitor {
include profiles::base
include profiles::rsyslog
include profiles::purge_nrpe_agent
include profiles::icinga2_master
+ include profiles::x509cert_common
}
diff --git a/sitemodules/roles/manifests/pgsql.pp b/sitemodules/roles/manifests/pgsql.pp
index 7b953d0..6e07288 100644
--- a/sitemodules/roles/manifests/pgsql.pp
+++ b/sitemodules/roles/manifests/pgsql.pp
@@ -8,7 +8,7 @@
# --------
#
# @example
-# class { 'roles::mariadb': }
+# class { 'roles::pgsql': }
#
# Authors
# -------
diff --git a/sitemodules/roles/manifests/traininginstance.pp b/sitemodules/roles/manifests/traininginstance.pp
new file mode 100644
index 0000000..9cacf78
--- /dev/null
+++ b/sitemodules/roles/manifests/traininginstance.pp
@@ -0,0 +1,26 @@
+# Class: roles::traininginstance
+# ==============================
+#
+# This class defines the traininginstance role for servers providing training
+# environments for CAcert sytem administration volunteers. You should assign
+# this class using hiera or via an ENC.
+#
+# Examples
+# --------
+#
+# @example
+# class { 'roles::traininginstance': }
+#
+# Authors
+# -------
+#
+# Jan Dittberner <jandd@cacert.org>
+#
+# Copyright
+# ---------
+#
+# Copyright 2020 Jan Dittberner
+#
+class roles::traininginstance {
+ include profiles::base
+}
diff --git a/sitemodules/roles/manifests/translations.pp b/sitemodules/roles/manifests/translations.pp
index 006dd6f..e8d2998 100644
--- a/sitemodules/roles/manifests/translations.pp
+++ b/sitemodules/roles/manifests/translations.pp
@@ -18,7 +18,7 @@
# Copyright
# ---------
#
-# Copyright 2018-2019 Jan Dittberner
+# Copyright 2018-2022 Jan Dittberner
#
class roles::translations {
include profiles::base
@@ -26,4 +26,5 @@ class roles::translations {
include profiles::purge_nrpe_agent
include profiles::icinga2_agent
include profiles::pootle
+ include profiles::x509cert_common
}