diff options
Diffstat (limited to 'sitemodules')
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 } |