├── .gitignore ├── site ├── profiles │ ├── .puppet-lint.rc │ ├── .sync.yml │ ├── .rubocop.yml │ ├── manifests │ │ ├── libvirt.pp │ │ ├── ssh_keys │ │ │ ├── people │ │ │ │ ├── genebean.pp │ │ │ │ ├── rwaffen.pp │ │ │ │ ├── binford2k.pp │ │ │ │ ├── nmburgan.pp │ │ │ │ ├── smortex.pp │ │ │ │ ├── sebastianrakel.pp │ │ │ │ ├── ekohl.pp │ │ │ │ └── bastelfreak.pp │ │ │ ├── pmc.pp │ │ │ └── additional_keys.pp │ │ ├── node_exporter.pp │ │ ├── lets_encrypt.pp │ │ ├── docker.pp │ │ ├── certbot.pp │ │ ├── postgres_exporter.pp │ │ ├── puppet │ │ │ ├── db.pp │ │ │ ├── server_firewalling.pp │ │ │ └── code.pp │ │ ├── redis.pp │ │ ├── nginx.pp │ │ ├── ssh.pp │ │ ├── nftables.pp │ │ ├── certbot │ │ │ └── nginx.pp │ │ ├── download_server │ │ │ ├── rsync.pp │ │ │ ├── rclone.pp │ │ │ └── nginx.pp │ │ ├── download_server.pp │ │ ├── postgresql.pp │ │ ├── postfix.pp │ │ ├── foreman.pp │ │ ├── vpt.pp │ │ ├── puppet.pp │ │ ├── github_runners │ │ │ └── ruby.pp │ │ ├── prometheus.pp │ │ ├── borg.pp │ │ ├── github_runners.pp │ │ ├── base.pp │ │ ├── puppetmodule.pp │ │ └── grafana.pp │ ├── files │ │ ├── puppetmodule-daily.timer │ │ ├── puppetmodule-hourly.timer │ │ ├── certbot.service │ │ ├── puppetmodule.socket │ │ ├── puppetmodule-daily.service │ │ ├── puppetmodule-hourly.service │ │ ├── kibana.voxpupu.li.conf │ │ ├── shell_setup.sh │ │ ├── sentry.voxpupu.li.conf │ │ ├── voxpupu.li.conf │ │ └── puppetmodule.service │ ├── spec │ │ ├── spec_helper_acceptance.rb │ │ ├── hiera.yaml │ │ ├── acceptance │ │ │ ├── prometheus_spec.rb │ │ │ ├── puppetcode_spec.rb │ │ │ ├── node_exporter_spec.rb │ │ │ ├── postgres_exporter_spec.rb │ │ │ ├── vpt_spec.rb │ │ │ ├── nginx_spec.rb │ │ │ ├── postfix_spec.rb │ │ │ ├── grafana_spec.rb │ │ │ ├── postgresql_spec.rb │ │ │ └── base_spec.rb │ │ ├── classes │ │ │ ├── postgres_exporter_spec.rb │ │ │ ├── prometheus_spec.rb │ │ │ ├── node_exporter_spec.rb │ │ │ ├── borg_spec.rb │ │ │ ├── nginx_spec.rb │ │ │ ├── postfix_spec.rb │ │ │ ├── certbot_spec.rb │ │ │ ├── puppet_spec.rb │ │ │ ├── postgresql_spec.rb │ │ │ ├── grafana_spec.rb │ │ │ ├── vpt_spec.rb │ │ │ └── base_spec.rb │ │ ├── defines │ │ │ └── certbot │ │ │ │ └── nginx_spec.rb │ │ └── spec_helper.rb │ ├── Rakefile │ ├── .gitignore │ ├── functions │ │ └── update_ssh_authorized_keys.pp │ ├── .fixtures-deps.yml │ ├── Gemfile │ ├── .fixtures.yml │ ├── metadata.json │ └── REFERENCE.md └── roles │ ├── .rubocop.yml │ ├── manifests │ ├── github_runner.pp │ ├── voxpupuli.pp │ └── download_server.pp │ ├── .gitignore │ └── metadata.json ├── data ├── nodes │ ├── ci04.voxpupu.li.yaml │ ├── ci03.voxpupu.li.yaml │ ├── puppetserver.voxpupu.li.yaml │ ├── voxpupu.li.yaml │ ├── mirror.voxpupu.li.yaml │ ├── ci02.voxpupu.li.yaml │ └── ci01.voxpupu.li.yaml ├── roles │ └── puppetserver.yaml └── global.yaml ├── environment.conf ├── Gemfile ├── renovate.json ├── encrypt-secret ├── hiera.yaml ├── Rakefile ├── bin └── config_script.sh ├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── manifests └── site.pp ├── public_key.pkcs7.pem ├── Puppetfile ├── .yamllint ├── README.md └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | .vendor 2 | .bundle 3 | Gemfile.lock 4 | -------------------------------------------------------------------------------- /site/profiles/.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --fail-on-warnings 2 | -------------------------------------------------------------------------------- /site/profiles/.sync.yml: -------------------------------------------------------------------------------- 1 | hiera_config: 'spec/hiera.yaml' 2 | -------------------------------------------------------------------------------- /data/nodes/ci04.voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | profiles::base::manage_borg: false 3 | -------------------------------------------------------------------------------- /data/roles/puppetserver.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | classes: 3 | - profiles::puppet 4 | -------------------------------------------------------------------------------- /data/nodes/ci03.voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | 3 | profiles::base::manage_borg: false 4 | -------------------------------------------------------------------------------- /environment.conf: -------------------------------------------------------------------------------- 1 | config_version = 'bin/config_script.sh $environment' 2 | modulepath = site:modules:$basemodulepath 3 | -------------------------------------------------------------------------------- /site/profiles/.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AllCops: 3 | NewCops: enable 4 | inherit_gem: 5 | voxpupuli-test: rubocop.yml 6 | -------------------------------------------------------------------------------- /data/nodes/puppetserver.voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | profiles::puppet::server: true 3 | profiles::postgresql::version: '17' 4 | profiles::base::manage_borg: false 5 | -------------------------------------------------------------------------------- /site/profiles/manifests/libvirt.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary installs libvirt 3 | # 4 | # @author Tim Meusel 5 | # 6 | class profiles::libvirt { 7 | contain libvirt 8 | } 9 | -------------------------------------------------------------------------------- /site/roles/.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | inherit_gem: 6 | voxpupuli-test: rubocop.yml 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source ENV['GEM_SOURCE'] || 'https://rubygems.org' 2 | 3 | gem 'rake', require: false 4 | gem 'ra10ke', require: false 5 | gem 'metadata_json_deps' 6 | gem 'openvox', require: false 7 | gem 'syslog' 8 | -------------------------------------------------------------------------------- /site/profiles/files/puppetmodule-daily.timer: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | [Unit] 3 | Description=partial module update each day 4 | 5 | [Timer] 6 | OnCalendar=daily 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /site/roles/manifests/github_runner.pp: -------------------------------------------------------------------------------- 1 | # @summary Configures a node as a GitHub Actions Runner 2 | # 3 | # Configures a node as a GitHub Actions Runner 4 | # 5 | class roles::github_runner { 6 | include profiles::github_runners 7 | } 8 | -------------------------------------------------------------------------------- /site/profiles/files/puppetmodule-hourly.timer: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | [Unit] 3 | Description=partial module update each hour 4 | 5 | [Timer] 6 | OnCalendar=hourly 7 | 8 | [Install] 9 | WantedBy=multi-user.target 10 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/genebean.pp: -------------------------------------------------------------------------------- 1 | # @summary configure key from genebean from GitHubs in the authorized_keys file 2 | # 3 | class profiles::ssh_keys::people::genebean { 4 | profiles::update_ssh_authorized_keys(['genebean']) 5 | } 6 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/rwaffen.pp: -------------------------------------------------------------------------------- 1 | # @summary configure key from rwaffen from GitHubs in the authorized_keys file 2 | # 3 | class profiles::ssh_keys::people::rwaffen { 4 | profiles::update_ssh_authorized_keys(['rwaffen']) 5 | } 6 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/binford2k.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary Configure key from binford2k from GitHubs in the authorized_keys file 3 | # 4 | class profiles::ssh_keys::people::binford2k { 5 | profiles::update_ssh_authorized_keys(['binford2k']) 6 | } 7 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/nmburgan.pp: -------------------------------------------------------------------------------- 1 | # @summary configure key from nmburgan from GitHubs in the authorized_keys file 2 | # 3 | # 4 | class profiles::ssh_keys::people::nmburgan { 5 | profiles::update_ssh_authorized_keys(['nmburgan']) 6 | } 7 | -------------------------------------------------------------------------------- /site/profiles/spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'voxpupuli/acceptance/spec_helper_acceptance' 4 | 5 | configure_beaker(modules: :fixtures) 6 | 7 | Dir['./spec/support/acceptance/**/*.rb'].sort.each { |f| require f } 8 | -------------------------------------------------------------------------------- /site/profiles/files/certbot.service: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | # By default, certbot runs with -q and doesn't reload anything we need 3 | [Service] 4 | ExecStart= 5 | ExecStart=/usr/bin/certbot renew 6 | ExecStartPost=/bin/systemctl reload-or-restart postfix nginx 7 | -------------------------------------------------------------------------------- /site/profiles/manifests/node_exporter.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary install node_exporter 3 | # 4 | class profiles::node_exporter { 5 | class { 'prometheus::node_exporter': 6 | version => '1.4.0', 7 | extra_options => '--web.listen-address="127.0.0.1:9100"', 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /site/profiles/manifests/lets_encrypt.pp: -------------------------------------------------------------------------------- 1 | # @summary Common Let's Encrypt settings 2 | # 3 | # Common Let's Encrypt settings 4 | # 5 | class profiles::lets_encrypt { 6 | class { 'letsencrypt': 7 | email => 'pmc@voxpupuli.org', 8 | package_ensure => 'latest', 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /site/profiles/spec/hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 5 3 | defaults: 4 | datadir: "../../../data" 5 | data_hash: yaml_data 6 | 7 | hierarchy: 8 | - name: "Per-node data" 9 | path: "nodes/%{facts.networking.fqdn}.yaml" 10 | - name: "one file to rule them all" 11 | path: "global.yaml" 12 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "enabledManagers": ["puppet"], 4 | "addLabels": ["renovate", "puppet", "dependencies"], 5 | "assigneesFromCodeOwners": true, 6 | "dependencyDashboard": true, 7 | "automerge": true, 8 | "automergeType": "pr" 9 | } 10 | -------------------------------------------------------------------------------- /site/profiles/Rakefile: -------------------------------------------------------------------------------- 1 | begin 2 | require 'voxpupuli/test/rake' 3 | rescue LoadError 4 | # only available if gem group test is installed 5 | end 6 | 7 | begin 8 | require 'voxpupuli/acceptance/rake' 9 | rescue LoadError 10 | # only available if gem group acceptance is installed 11 | end 12 | 13 | # vim: syntax=ruby 14 | -------------------------------------------------------------------------------- /site/profiles/manifests/docker.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary installs docker 3 | # 4 | # @author Tim Meusel 5 | # 6 | class profiles::docker { 7 | class { 'docker': 8 | use_upstream_package_source => true, 9 | iptables => false, 10 | } 11 | contain docker 12 | include nftables::rules::docker_ce 13 | } 14 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/prometheus_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::prometheus' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::prometheus' 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/puppetcode_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::puppetcode' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::puppetcode' 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /encrypt-secret: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Use this script to encrypt fragments when submitting PRs to .eyaml files 4 | # Secret goes to stdin, encrypted body goes to stdout 5 | # Multiline secrets not supported 6 | 7 | IFS= read -s -r SECRET 8 | printf "ENC[PKCS7,%s]\n" "$(printf "%s" "$SECRET" | openssl smime -aes256 -encrypt -outform der -binary public_key.pkcs7.pem | openssl base64 -A)" 9 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/node_exporter_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::node_exporter' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::node_exporter' 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/postgres_exporter_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::postgres_exporter' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::postgres_exporter' 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /site/roles/manifests/voxpupuli.pp: -------------------------------------------------------------------------------- 1 | # 2 | # This role encompasses the various applications that makeup the host at voxpupu.li 3 | # 4 | class roles::voxpupuli { 5 | include profiles::grafana 6 | include profiles::node_exporter 7 | include profiles::postgres_exporter 8 | include profiles::prometheus 9 | include profiles::puppetcode 10 | include profiles::puppetmodule 11 | include profiles::vpt 12 | } 13 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/vpt_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::vpt' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'class { "profiles::vpt": deploy_kibana => false, deploy_vpt => false, deploy_sentry => false }' 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/postgres_exporter_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::base' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/prometheus_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::prometheus' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/node_exporter_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::node_exporter' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /site/roles/.gitignore: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | pkg/ 5 | Gemfile.lock 6 | Gemfile.local 7 | vendor/ 8 | .vendor/ 9 | spec/fixtures/manifests/ 10 | spec/fixtures/modules/ 11 | .vagrant/ 12 | .bundle/ 13 | .ruby-version 14 | coverage/ 15 | log/ 16 | .idea/ 17 | .dependencies/ 18 | .librarian/ 19 | Puppetfile.lock 20 | *.iml 21 | .*.sw? 22 | .yardoc/ 23 | Guardfile 24 | -------------------------------------------------------------------------------- /site/profiles/.gitignore: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | pkg/ 5 | Gemfile.lock 6 | Gemfile.local 7 | vendor/ 8 | .vendor/ 9 | spec/fixtures/manifests/ 10 | spec/fixtures/modules/ 11 | .vagrant/ 12 | .bundle/ 13 | .ruby-version 14 | coverage/ 15 | log/ 16 | .idea/ 17 | .dependencies/ 18 | .librarian/ 19 | Puppetfile.lock 20 | *.iml 21 | .*.sw? 22 | .yardoc/ 23 | Guardfile 24 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/pmc.pp: -------------------------------------------------------------------------------- 1 | # @summary Configure keys from GitHub of PMC members in the authorized_keys file 2 | # 3 | # Configure keys from GitHub of PMC members in the authorized_keys file. 4 | # The PMC's member list is maintained in global.yaml and looked up directly. 5 | # 6 | class profiles::ssh_keys::pmc { 7 | $_pmc_members = lookup('pmc_members') 8 | $_pmc_members.each |$member| { 9 | include "profiles::ssh_keys::people::${member}" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /site/profiles/files/puppetmodule.socket: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | [Unit] 3 | Description=Puma HTTP Server Accept Sockets 4 | 5 | [Socket] 6 | ListenStream=127.0.0.1:8080 7 | 8 | # AF_UNIX domain socket 9 | # SocketUser, SocketGroup, etc. may be needed for Unix domain sockets 10 | # ListenStream=/run/puma.sock 11 | 12 | # Socket options matching Puma defaults 13 | NoDelay=true 14 | ReusePort=true 15 | Backlog=1024 16 | 17 | [Install] 18 | WantedBy=sockets.target 19 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/borg_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::borg' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_class('borg') } 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/nginx_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::nginx' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_class('nginx') } 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 5 3 | defaults: 4 | datadir: "data" 5 | lookup_key: "eyaml_lookup_key" 6 | options: 7 | pkcs7_private_key: "/etc/puppetlabs/puppet/keys/private_key.pkcs7.pem" 8 | pkcs7_public_key: "/etc/puppetlabs/puppet/keys/public_key.pkcs7.pem" 9 | 10 | hierarchy: 11 | - name: "Per-node data" 12 | path: "nodes/%{facts.networking.fqdn}.yaml" 13 | - name: "Role data" 14 | path: "roles/%{trusted.extensions.pp_role}.yaml" 15 | - name: "one file to rule them all" 16 | path: "global.yaml" 17 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'ra10ke' 2 | require 'metadata_json_deps' 3 | 4 | Ra10ke::RakeTask.new do |t| 5 | # install modules into site/, so they are next to our profiles 6 | t.moduledir = File.join(Dir.pwd, 'site') 7 | end 8 | 9 | desc 'Run metadata-json-deps' 10 | task :metadata_deps do 11 | files = FileList['site/*/metadata.json'] 12 | # pull modules if they do not exist already 13 | Rake::Task['r10k:install'].invoke if files.count <= 2 14 | files = FileList['site/profiles/metadata.json'] 15 | MetadataJsonDeps::run(files) 16 | end 17 | -------------------------------------------------------------------------------- /site/profiles/files/puppetmodule-daily.service: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | [Unit] 3 | Documentation=https://github.com/voxpupuli/puppetmodule.info#rake-tasks 4 | Description=Import modules from forge 5 | After=puppetmodule.service 6 | Wants=puppetmodule.service 7 | 8 | [Service] 9 | Type=oneshot 10 | User=puppetmodule 11 | Group=puppetmodule 12 | WorkingDirectory=/srv/puppetmodule/puppetmodule.info 13 | ExecStart=/usr/bin/bundle exec rake modules:update 14 | SyslogIdentifier=puppetmodule-daily 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/postfix_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::postfix' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_package('postfix') } 15 | it { is_expected.to contain_service('postfix.service') } 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /bin/config_script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | CODEDIR='/etc/puppetlabs/code' # better: $(puppet master --configprint codedir) 3 | CODESTAGEDIR='/etc/puppetlabs/code-staging' # better: "($puppet master --configprint codedir)-staging" 4 | if [ -x /usr/bin/git ]; then 5 | if [ -d $CODESTAGEDIR ]; then 6 | ENVGITDIR="${CODESTAGEDIR}/environments/$1/.git" 7 | else 8 | ENVGITDIR="${CODEDIR}/environments/$1/.git" 9 | fi 10 | /usr/bin/git --git-dir $ENVGITDIR log --pretty=format:"%h - %an, %ad : %s" -1 11 | else 12 | echo "no git - environment $1" 13 | fi 14 | exit 0 15 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/nginx_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::nginx' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::nginx' 10 | end 11 | end 12 | describe package('nginx') do 13 | it { is_expected.to be_installed } 14 | end 15 | 16 | describe service('nginx') do 17 | it { is_expected.to be_enabled } 18 | it { is_expected.to be_running } 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /site/profiles/files/puppetmodule-hourly.service: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | [Unit] 3 | Documentation=https://github.com/voxpupuli/puppetmodule.info#rake-tasks 4 | Description=Import modules from forge 5 | After=puppetmodule.service 6 | Wants=puppetmodule.service 7 | 8 | [Service] 9 | Type=oneshot 10 | Environment='MODULE_UPDATER_PARTIAL=true' 11 | User=puppetmodule 12 | Group=puppetmodule 13 | WorkingDirectory=/srv/puppetmodule/puppetmodule.info 14 | ExecStart=/usr/bin/bundle exec rake modules:update 15 | SyslogIdentifier=puppetmodule-hourly 16 | 17 | [Install] 18 | WantedBy=multi-user.target 19 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/additional_keys.pp: -------------------------------------------------------------------------------- 1 | # @summary Allow additional admins' keys to be pulled in via Hiera 2 | # 3 | # Allow additional admins' keys to be pulled in via Hiera 4 | # 5 | # @param user_list [Array[String[1]]] 6 | # The list of users whose ssh keys should be pulled in. Each listed user will 7 | # need to be represented by a manifest under `site/profiles/manifests/ssh_keys/people`. 8 | # 9 | class profiles::ssh_keys::additional_keys ( 10 | Array[String[1]] $user_list = [], 11 | ) { 12 | $user_list.each |$user| { 13 | include "profiles::ssh_keys::people::${user}" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/smortex.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary Configure key from smortex from GitHubs in the authorized_keys file along with supplemental keys 3 | # 4 | # Configure key from smortex from GitHubs in the authorized_keys file along with supplemental keys 5 | # 6 | class profiles::ssh_keys::people::smortex { 7 | profiles::update_ssh_authorized_keys(['smortex']) 8 | 9 | ssh_authorized_key { 'romain@fenchurch': 10 | ensure => 'present', 11 | user => 'root', 12 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAILvGP9clA62A6cTrc68sqRp1m2MWVrpBy1EigRnMpSfG', 13 | type => 'ssh-ed25519', 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | # raise PRs for gem updates 4 | - package-ecosystem: bundler 5 | directory: "/" 6 | schedule: 7 | interval: daily 8 | time: "13:00" 9 | open-pull-requests-limit: 10 10 | # raise PRs for gem updates 11 | - package-ecosystem: bundler 12 | directory: "/site/profile" 13 | schedule: 14 | interval: daily 15 | time: "13:00" 16 | open-pull-requests-limit: 10 17 | # Maintain dependencies for GitHub Actions 18 | - package-ecosystem: github-actions 19 | directory: "/" 20 | schedule: 21 | interval: daily 22 | time: "13:00" 23 | open-pull-requests-limit: 10 24 | -------------------------------------------------------------------------------- /data/nodes/voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | role: roles::voxpupuli 3 | 4 | prometheus::scrape_configs: 5 | - job_name: 'postgres_exporter' 6 | scrape_interval: '10s' 7 | scrape_timeout: '10s' 8 | static_configs: 9 | - targets: 10 | - '127.0.0.1:9187' 11 | - job_name: 'node_exporter' 12 | scrape_interval: '10s' 13 | scrape_timeout: '10s' 14 | static_configs: 15 | - targets: 16 | - '127.0.0.1:9100' 17 | 18 | profiles::borg::absolutebackupdestdir: 'voxpupuli' 19 | profiles::borg::borg_excludes: 20 | - /var/lib/docker/overlay2 21 | - /var/lib/docker/containers 22 | - /var/lib/lxcfs 23 | -------------------------------------------------------------------------------- /site/profiles/manifests/certbot.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configures the certbot foo. Doesn't create certificates! 3 | # 4 | # @author Tim Meusel 5 | class profiles::certbot { 6 | package { 'certbot': 7 | ensure => 'installed', 8 | } 9 | service { 'certbot.timer': 10 | ensure => 'running', 11 | enable => true, 12 | require => Package['certbot'], 13 | } 14 | systemd::dropin_file { 'verbose.conf': 15 | unit => 'certbot.service', 16 | content => file("${module_name}/certbot.service"), 17 | require => Package['certbot'], 18 | notify => Service['certbot.timer'], 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /site/profiles/manifests/postgres_exporter.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary installs a postgres exporter 3 | # 4 | class profiles::postgres_exporter { 5 | class { 'prometheus::postgres_exporter': 6 | version => '0.11.1', 7 | user => 'postgres', 8 | group => 'postgres', 9 | postgres_auth_method => 'custom', 10 | manage_user => false, 11 | manage_group => false, 12 | options => '--web.listen-address="127.0.0.1:9187"', 13 | data_source_custom => { 'DATA_SOURCE_NAME' => 'user=postgres host=/var/run/postgresql/ sslmode=disable', }, 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/certbot_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::certbot' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_package('certbot') } 15 | it { is_expected.to contain_service('certbot.timer') } 16 | it { is_expected.to contain_systemd__dropin_file('verbose.conf') } 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/puppet_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::puppet' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_class('r10k') } 15 | it { is_expected.to contain_ssh_keygen('root_github') } 16 | it { is_expected.to contain_ssh__client__config__user('root_github') } 17 | end 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/postgresql_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::postgresql' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_file('/srv/pg_dumps') } 15 | it { is_expected.to contain_class('postgresql::globals') } 16 | it { is_expected.to contain_class('postgresql::server') } 17 | it { is_expected.to contain_class('dbbackup') } 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /site/profiles/functions/update_ssh_authorized_keys.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary generate ssh_authorized_key root entries for a list of github users 3 | # 4 | # @param github_users the list of users 5 | # 6 | # @author Tim Meusel 7 | # 8 | function profiles::update_ssh_authorized_keys(Array[String[1]] $github_users) { 9 | $github_users.each |$user| { 10 | $keys = extlib::read_url("https://github.com/${user}.keys") 11 | $keys.split("\n").each |$index, $key| { 12 | $keyparts = $key.split(' ') 13 | ssh_authorized_key { "${user}-${index}": 14 | user => 'root', 15 | type => $keyparts[0], 16 | key => $keyparts[1], 17 | } 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /site/profiles/.fixtures-deps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | fixtures: 3 | repositories: 4 | archive: https://github.com/voxpupuli/puppet-archive 5 | concat: https://github.com/puppetlabs/puppetlabs-concat 6 | ssh_keys: https://github.com/puppetlabs/puppetlabs-sshkeys_core 7 | yumrepo: https://github.com/puppetlabs/puppetlabs-yumrepo_core 8 | mount: https://github.com/puppetlabs/puppetlabs-mount_core 9 | cron: https://github.com/puppetlabs/puppetlabs-cron_core 10 | augeasproviders_core: https://github.com/voxpupuli/puppet-augeasproviders_core.git 11 | augeas_core: https://github.com/puppetlabs/puppetlabs-augeas_core.git 12 | selinux_core: https://github.com/puppetlabs/puppetlabs-selinux_core 13 | -------------------------------------------------------------------------------- /data/global.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | lookup_options: 3 | profiles::ssh_keys::additional_keys::user_list: 4 | merge: deep 5 | 6 | # current PMC members 7 | pmc_members: 8 | - 'bastelfreak' 9 | - 'ekohl' 10 | - 'rwaffen' 11 | - 'sebastianrakel' 12 | - 'smortex' 13 | - 'binford2k' 14 | 15 | # config for theforeman/puppet 16 | puppet::puppetmaster: 'voxpupu.li' 17 | puppet::show_diff: true 18 | puppet::splay: true 19 | puppet::agent: true 20 | puppet::server: false 21 | 22 | # config for saz/ssh 23 | ssh::storeconfigs_enabled: false 24 | ssh::server_options: 25 | PasswordAuthentication: 'no' 26 | PermitRootLogin: 'yes' 27 | X11Forwarding: 'no' 28 | PrintMotd: 'yes' 29 | AllowAgentForwarding: 'no' 30 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/postfix_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::postfix' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::postfix' 10 | end 11 | end 12 | describe package('postfix') do 13 | it { is_expected.to be_installed } 14 | end 15 | 16 | describe service('postfix') do 17 | it { is_expected.to be_enabled } 18 | it { is_expected.to be_running } 19 | end 20 | 21 | describe port(25) do 22 | it { is_expected.to be_listening.on('0.0.0.0').with('tcp') } 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /site/profiles/spec/defines/certbot/nginx_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::certbot::nginx' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | let(:title) { 'example.com' } 13 | 14 | context 'with all defaults' do 15 | it { is_expected.to compile.with_all_deps } 16 | it { is_expected.to contain_nginx__resource__location('= /.well-known/acme-challenge/ - example.com') } 17 | it { is_expected.to contain_nginx__resource__location('^~ /.well-known/acme-challenge/ - example.com') } 18 | end 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/grafana_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::grafana' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::grafana' 10 | end 11 | end 12 | describe package('grafana') do 13 | it { is_expected.to be_installed } 14 | end 15 | 16 | describe service('grafana-server') do 17 | it { is_expected.to be_enabled } 18 | it { is_expected.to be_running } 19 | end 20 | 21 | describe port(3001) do 22 | it { is_expected.to be_listening.on('127.0.0.1').with('tcp') } 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/postgresql_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::postgresql' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::postgresql' 10 | end 11 | end 12 | describe package('postgresql-13') do 13 | it { is_expected.to be_installed } 14 | end 15 | 16 | describe service('postgresql@13-main.service') do 17 | it { is_expected.to be_enabled } 18 | it { is_expected.to be_running } 19 | end 20 | 21 | describe port(5432) do 22 | it { is_expected.to be_listening.on('127.0.0.1').with('tcp') } 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /site/profiles/files/kibana.voxpupu.li.conf: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | server { 3 | listen 443; 4 | listen [::]:443; 5 | server_name kibana.voxpupu.li; 6 | 7 | 8 | location / { 9 | auth_basic "Kibana"; 10 | auth_basic_user_file .htpasswd; 11 | proxy_pass http://localhost:5601; 12 | } 13 | 14 | ssl_certificate /etc/letsencrypt/live/kibana.voxpupu.li/fullchain.pem; # managed by Certbot 15 | ssl_certificate_key /etc/letsencrypt/live/kibana.voxpupu.li/privkey.pem; # managed by Certbot 16 | } 17 | server { 18 | if ($host = kibana.voxpupu.li) { 19 | return 301 https://$host$request_uri; 20 | } # managed by Certbot 21 | 22 | listen 80; 23 | listen [::]:80; 24 | server_name kibana.voxpupu.li; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /site/profiles/Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Managed by modulesync - DO NOT EDIT 4 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 5 | 6 | source ENV['GEM_SOURCE'] || 'https://rubygems.org' 7 | 8 | group :test do 9 | gem 'voxpupuli-test', '~> 13.2', require: false 10 | gem 'puppet_metadata', '~> 5.3', require: false 11 | end 12 | 13 | group :system_tests do 14 | gem 'voxpupuli-acceptance', '~> 4.2', require: false 15 | end 16 | 17 | gem 'openfact', ENV['FACTER_GEM_VERSION'], require: false, groups: [:test] 18 | gem 'rake', require: false 19 | 20 | puppetversion = ENV['PUPPET_GEM_VERSION'] || '>= 7.24' 21 | gem 'metadata_json_deps', require: false 22 | gem 'openvox', puppetversion, require: false, groups: [:test] 23 | # vim: syntax=ruby 24 | -------------------------------------------------------------------------------- /site/profiles/manifests/puppet/db.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary installs puppetdb *on a puppetserver that also runs foreman* 3 | # 4 | # @api private 5 | # 6 | # @author Tim Meusel 7 | # 8 | class profiles::puppet::db { 9 | assert_private() 10 | require profiles::postgresql 11 | include postgresql::server::contrib 12 | postgresql::server::extension { 'pg_trgm': 13 | database => 'puppetdb', 14 | require => Postgresql::Server::Db['puppetdb'], 15 | before => Service['puppetdb'], 16 | } 17 | class { 'puppetdb': 18 | manage_dbserver => false, 19 | manage_firewall => false, 20 | } 21 | contain puppetdb 22 | class { 'puppet::server::puppetdb': 23 | server => $facts['networking']['fqdn'], 24 | } 25 | contain puppet::server::puppetdb 26 | } 27 | -------------------------------------------------------------------------------- /site/roles/manifests/download_server.pp: -------------------------------------------------------------------------------- 1 | # @summary Configures a system to be the public facing aspect of Vox Pupuli's repos 2 | # 3 | # Configures a system to be the public facing aspect of Vox Pupuli's repos. 4 | # This includes the repositories maintained in support of the OpenVox project 5 | # such as {artifacts,apt,downloads,rsync,yum}.voxpupuli.org. Different repos 6 | # are presented in different ways. For example, the artifacts and downloads 7 | # ones are simple directory listings while apt, rsync, and rsync are presented 8 | # in an applications-specific way. 9 | # 10 | class roles::download_server { 11 | include nftables::rules::http 12 | include nftables::rules::https 13 | include nftables::rules::rsync 14 | include profiles::download_server 15 | include profiles::lets_encrypt 16 | } 17 | -------------------------------------------------------------------------------- /site/profiles/manifests/redis.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configures redis on different platforms 3 | # 4 | class profiles::redis { 5 | if $facts['os']['name'] == 'Archlinux' { 6 | fail('profiles::redis does not work on Archlinux, because puppet/redis does not support Archlinux') 7 | } 8 | # manage_repo pulls in the epel module, but that's broken on CentOS 8 9 | # https://github.com/voxpupuli/puppet-epel/issues/108 10 | elsif $facts['os']['family'] == 'RedHat' { 11 | $params = { 'require' => Package['epel-release'], 'manage_repo' => false } 12 | require profiles::centos 13 | } elsif $facts['os']['family'] == 'Debian' { 14 | $params = { 'redis_apt_repo' => true, 'manage_repo' => true } 15 | } else { 16 | $params = {} 17 | } 18 | class { 'redis': 19 | * => $params, 20 | } 21 | contain redis 22 | } 23 | -------------------------------------------------------------------------------- /site/profiles/spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Managed by modulesync - DO NOT EDIT 4 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 5 | 6 | # puppetlabs_spec_helper will set up coverage if the env variable is set. 7 | # We want to do this if lib exists and it hasn't been explicitly set. 8 | ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../lib', __dir__)) 9 | 10 | require 'voxpupuli/test/spec_helper' 11 | 12 | RSpec.configure do |c| 13 | c.hiera_config = 'spec/hiera.yaml' 14 | end 15 | 16 | add_mocked_facts! 17 | 18 | if File.exist?(File.join(__dir__, 'default_module_facts.yml')) 19 | facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) 20 | facts&.each do |name, value| 21 | add_custom_fact name.to_sym, value 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /site/profiles/manifests/nginx.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary multiple profiles requires nginx vhosts, this profile pulls in the nginx class/package/service setup 3 | # 4 | # @author Tim Meusel 5 | # 6 | class profiles::nginx { 7 | # do not contain it because it triggers also apt, which is triggerd by other profiles as well 8 | require profiles::certbot 9 | $manage_repo = $facts['os']['name'] ? { 10 | 'Archlinux' => false, 11 | default => true, 12 | } 13 | class { 'nginx': 14 | confd_purge => true, 15 | service_config_check => true, 16 | ssl_protocols => 'TLSv1.2 TLSv1.3', 17 | ssl_prefer_server_ciphers => 'on', 18 | worker_processes => 'auto', 19 | ssl_ciphers => 'ECDHE+AESGCM:DHE+AESGCM:ECDHE+ECDSA+AES+SHA256', 20 | manage_repo => $manage_repo, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/grafana_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::grafana' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | let :node do 13 | 'voxpupu.li' 14 | end 15 | 16 | context 'with all defaults' do 17 | it { is_expected.to compile.with_all_deps } 18 | it { is_expected.to contain_class('grafana').with_require('Package[toml]') } 19 | it { is_expected.to contain_class('profiles::nginx') } 20 | it { is_expected.to contain_package('toml').with_provider('puppet_gem') } 21 | it { is_expected.to contain_nginx__resource__server('grafana.voxpupu.li') } 22 | it { is_expected.to contain_grafana_plugin('grafana-bigquery-datasource') } 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary ssh profile to manage sshd + ssh keys 3 | # 4 | # @author Tim Meusel 5 | # 6 | class profiles::ssh { 7 | contain ssh 8 | 9 | # /root/.ssh/config entry to access github.com fast 10 | ssh::client::config::user { 'root_github': 11 | ensure => present, 12 | user => 'root', 13 | user_home_dir => '/root', 14 | options => { 15 | 'Host github.secureserver.net' => { 16 | 'User' => 'git', 17 | 'IdentityFile' => '~/.ssh/id_ed25519_github', 18 | 'ControlMaster' => 'auto', 19 | 'ControlPath' => '~/.ssh/ssh-%r@%h:%p', 20 | 'ControlPersist' => '10m', 21 | }, 22 | }, 23 | } 24 | # setup ssh key exchange 25 | ssh_keygen { 'root_github': 26 | type => 'ed25519', 27 | user => 'root', 28 | filename => '/root/.ssh/id_ed25519_github', 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /site/roles/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voxpupuli-roles", 3 | "version": "0.1.0", 4 | "summary": "a list of roles for our cloud instances", 5 | "author": "Vox Pupuli", 6 | "source": "https://github.com/voxpupuli/voxpupu.li.git", 7 | "project_page": "https://github.com/voxpupuli/voxpupu.li", 8 | "issues_url": "https://github.com/voxpupuli/voxpupu.li/issues", 9 | "license": "AGPL-3.0", 10 | "tags": [ 11 | "control repo", 12 | "controlrepo", 13 | "roles", 14 | "vox pupuli", 15 | "Vox Pupuli" 16 | ], 17 | "requirements": [ 18 | { 19 | "name": "puppet", 20 | "version_requirement": ">= 6.17.0 < 7.0.0" 21 | } 22 | ], 23 | "operatingsystem_support": [ 24 | { 25 | "operatingsystem": "Ubuntu", 26 | "operatingsystemrelease": [ 27 | "18.04" 28 | ] 29 | } 30 | ], 31 | "dependencies": [ 32 | { 33 | "name": "voxpupuli/profiles", 34 | "version_requirement": ">= 0.1.0 < 2.0.0" 35 | } 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /site/profiles/files/shell_setup.sh: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | 3 | # colorized tail 4 | ctail() { 5 | tail "$@" | ccze -A -o nolookups 6 | } 7 | # colorized journalctl 8 | cj() { 9 | journalctl -f "$@" | ccze -A -o nolookups 10 | } 11 | 12 | if [ $(command -v dircolors) ]; then 13 | eval "$(dircolors)" 14 | fi 15 | 16 | # workaround for broken systemd/kernel sync 17 | alias reboot='sync; reboot' 18 | alias poweroff='sync; poweroff' 19 | 20 | export LS_OPTIONS='--color=auto -h' 21 | export EDITOR='vim' 22 | 23 | # colorize iostat 24 | export S_COLORS=auto 25 | 26 | # colorized PS1 27 | export PS1='\[\033[01;31m\]\u\[\033[01;33m\]@\[\033[01;36m\]\h \[\033[01;33m\]\w \[\033[01;35m\]\$ \[\033[00m\]' 28 | 29 | alias ncdu='ncdu --color dark' 30 | alias dmesg='dmesg -T --color' 31 | alias r10k='r10k --color' 32 | alias ip='ip -c' 33 | alias grep='grep --color' 34 | alias ls='ls $LS_OPTIONS' 35 | alias ll='ls -l' 36 | alias l='ls $LS_OPTIONS -lA' 37 | 38 | if [ $TERM == "alacritty" ]; then export TERM=xterm-256color; fi 39 | -------------------------------------------------------------------------------- /site/profiles/manifests/nftables.pp: -------------------------------------------------------------------------------- 1 | # @summary configure certain nftable rules 2 | # 3 | # @param in_ssh allows incoming ssh connections 4 | # @param icmp allow all ICMP traffic 5 | # @param nat decide if the box should be allowed to handle NAT traffic 6 | # @param out_all Allow all outbound connections 7 | # 8 | class profiles::nftables ( 9 | Boolean $in_ssh = true, 10 | Boolean $icmp = true, 11 | Boolean $nat = false, 12 | Boolean $out_all = false 13 | ) { 14 | class { 'nftables': 15 | in_ssh => $in_ssh, 16 | in_icmp => $icmp, 17 | out_icmp => $icmp, 18 | in_out_conntrack => true, 19 | inet_filter => true, 20 | nat => $nat, 21 | reject_with => false, 22 | out_all => $out_all, 23 | } 24 | include nftables::rules::out::ssh 25 | include nftables::rules::out::whois 26 | include nftables::rules::out::hkp 27 | class { 'nftables::rules::out::puppet': 28 | puppetserver => ['116.202.97.65', '2a01:4f8:c013:359b::1',], 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /manifests/site.pp: -------------------------------------------------------------------------------- 1 | # hack pluginsync as file resource. only required for `puppet apply` usage 2 | # this works by accident with puppet agent, but only on the puppetserver 3 | # it breaks puppet agent on other systems, so we need to guard it 4 | if $trusted['authenticated'] == 'local' { 5 | file { $settings::libdir: 6 | ensure => directory, 7 | source => 'puppet:///plugins', # lint:ignore:puppet_url_without_modules 8 | recurse => true, 9 | purge => true, 10 | backup => false, 11 | noop => false, 12 | tag => 'hacked_pluginsync', 13 | } 14 | } 15 | 16 | # include base profile that every node gets 17 | contain profiles::base 18 | 19 | # Look in Hiera for the role to be included. This method will only permit a 20 | # single role to be assigned to a node. 21 | $_node_role = lookup('role', String, 'first', '') 22 | unless empty($_node_role) { 23 | include($_node_role) 24 | } 25 | 26 | # include node specific profiles 27 | lookup('classes', Array[String[1]], 'unique', []).each |$c| { 28 | contain $c 29 | } 30 | -------------------------------------------------------------------------------- /site/profiles/manifests/certbot/nginx.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configures location blocks for nginx 3 | # 4 | # @param domain the domain for the location blocks 5 | # 6 | # @author Tim Meusel 7 | # 8 | define profiles::certbot::nginx ( 9 | Stdlib::Fqdn $domain = $title, 10 | ) { 11 | include nginx 12 | nginx::resource::location { "^~ /.well-known/acme-challenge/ - ${domain}": 13 | server => $domain, 14 | www_root => '/var/lib/letsencrypt/', 15 | index_files => [], 16 | location => '^~ /.well-known/acme-challenge/', 17 | location_custom_cfg => { 'default_type' => 'text/plain', }, 18 | ssl => false, 19 | ssl_only => false, 20 | } 21 | nginx::resource::location { "= /.well-known/acme-challenge/ - ${domain}": 22 | server => $domain, 23 | location_cfg_append => { 'return' => '404', }, 24 | index_files => [], 25 | location => '= /.well-known/acme-challenge/', 26 | ssl => false, 27 | ssl_only => false, 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/vpt_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::vpt' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_file('/etc/nginx/sites-available/kibana.voxpupu.li.conf').with_ensure('file') } 15 | it { is_expected.to contain_file('/etc/nginx/sites-available/voxpupu.li.conf').with_ensure('file') } 16 | it { is_expected.to contain_file('/etc/nginx/sites-available/sentry.voxpupu.li.conf').with_ensure('file') } 17 | it { is_expected.to contain_file('/etc/nginx/sites-enabled/kibana.voxpupu.li.conf').with_ensure('link') } 18 | it { is_expected.to contain_file('/etc/nginx/sites-enabled/voxpupu.li.conf').with_ensure('link') } 19 | it { is_expected.to contain_file('/etc/nginx/sites-enabled/sentry.voxpupu.li.conf').with_ensure('link') } 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /data/nodes/mirror.voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | role: roles::download_server 3 | 4 | profiles::base::manage_borg: true 5 | profiles::borg::borg_excludes: 6 | - /var/mirror/downloads 7 | profiles::borg::borg_includes: 8 | - /var/mirror/yum 9 | - /var/mirror/apt 10 | 11 | profiles::download_server::server_names: 12 | 'apt.voxpupuli.org': 13 | locations: 14 | '/': 15 | autoindex: 'on' 16 | www_root: /var/mirror/apt 17 | 'artifacts.voxpupuli.org': 18 | locations: 19 | '/': 20 | autoindex: 'on' 21 | www_root: /var/mirror/artifacts 22 | 'downloads.voxpupuli.org': 23 | locations: 24 | '/': 25 | autoindex: 'on' 26 | www_root: /var/mirror/downloads 27 | 'rsync.voxpupuli.org': 28 | aliases: 29 | - 'mirror.voxpupu.li' 30 | locations: 31 | '/': 32 | autoindex: 'on' 33 | www_root: /var/mirror 34 | 'yum.voxpupuli.org': 35 | locations: 36 | '/': 37 | autoindex: 'on' 38 | www_root: /var/mirror/yum 39 | 40 | profiles::ssh_keys::additional_keys::user_list: 41 | - genebean 42 | - nmburgan 43 | -------------------------------------------------------------------------------- /site/profiles/manifests/download_server/rsync.pp: -------------------------------------------------------------------------------- 1 | # @summary Configures an rsync server to present the files under /var/mirror 2 | # 3 | # Configures an rsync server to present the files under /var/mirror 4 | # 5 | # @api private 6 | # 7 | class profiles::download_server::rsync { 8 | assert_private() 9 | 10 | class { 'rsync': 11 | package_ensure => 'latest', 12 | } 13 | 14 | class { 'rsync::server': 15 | use_xinetd => false, 16 | uid => 'nobody', 17 | gid => 'nogroup', 18 | address => '*', 19 | } 20 | 21 | rsync::server::module { 22 | default: 23 | path => '/var/mirror', 24 | read_only => 'yes', 25 | max_connections => '60', 26 | incoming_chmod => false, 27 | outgoing_chmod => false, 28 | uid => 'nobody', 29 | gid => 'nogroup', 30 | ; 31 | 'all': 32 | comment => 'Vox Pupuli Artifacts, Downloads, and Repositories', 33 | ; 34 | 'packages': 35 | comment => 'Vox Pupuli Downloads and Repositories', 36 | exclude => ['/artifacts/'], 37 | ; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /public_key.pkcs7.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC2TCCAcGgAwIBAgIBATANBgkqhkiG9w0BAQsFADAAMCAXDTI0MDgxNzIxNTIz 3 | NVoYDzIwNzQwODA1MjE1MjM1WjAAMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB 4 | CgKCAQEAxReadYp/GYwzjA0aoPPJbmS2QJ6V8VNJYWa1F2V87MFTyddVfWmkFBEp 5 | QWytH/2CezlP3H1h1qXgV8vZlLl34cYElbL9KSfPJIDQer1X5HzBkZ2fiQM1OgPK 6 | adqa/b/WP/9iWH0PE+poCLP5gvRzWk+PvRES54W0x+aXfoQZJclkFuvaAbMiBaF6 7 | lIc9QOh1f+N43/OAcMgo1PypNUya1ZDTFv4B4CxtgzNAhuzFm4qd4klxO/YDJsbE 8 | 9WcpaS6j4iTe2ui5MPcommHf/HqzTfDdgLR9L2nugozWT+C2CPmSD4znsSaA0+ww 9 | B2ImwnFWd8mtmR4CjVRlRSM8WRlDEwIDAQABo1wwWjAPBgNVHRMBAf8EBTADAQH/ 10 | MB0GA1UdDgQWBBQqvulJfRPDOheDKNdYBX04ZFYiqzAoBgNVHSMEITAfgBQqvulJ 11 | fRPDOheDKNdYBX04ZFYiq6EEpAIwAIIBATANBgkqhkiG9w0BAQsFAAOCAQEATyOy 12 | V6spplbcGLQ4UsvK+az7KMNdWwABkI/7vq5ENyic5nvl2fMyeSR9For3eNZrSucp 13 | SSK4b5cVpnhj6szfKmVJ38mPtoO3D1s382GodcvpgTxvZ1clAGIfLzTnwA+6/6Rc 14 | 4fX3jkxAXZQFqs/uA+UPWlWdKCIIkC4yioDUwBP+AVr2/i4DaAJ4zXMYrZF1NcnQ 15 | 3gZOKKahwPjw1YgQdNrLVCizny8vcoycWAfsJqcWUxF6qmKS83Jz+7eTFBmP8YMm 16 | 267ARxM/mCg8WTIkrpuMgC5uKbAwAFA2wYSsvKSM3FCLDhl/31sXWiHBReSUpe9n 17 | QkJFqVbUWn1v5WqIPg== 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /data/nodes/ci02.voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | role: roles::github_runner 3 | 4 | profiles::base::manage_borg: false 5 | 6 | profiles::github_runners::setup_ruby: false 7 | profiles::github_runners::setup_docker: true 8 | profiles::github_runners::instances: ['first', 'second'] 9 | profiles::github_runners::runner_group: 'Hetzner-ARM' 10 | profiles::github_runners::labels: ['self-hosted', 'hetzner-arm'] 11 | profiles::github_runners::org_name: 'openvoxproject' 12 | 13 | github_actions_runner::personal_access_token: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAV1E1ok8FbLt6IeeKsmdoazXV/ZI87iSiRpt29FUG1giIgrxPEscowQAzug7Q/wSjTAh36ZEVIpnq+uBxoWPRJ9hyyQDxbUB4K9AcjyZ4Ih7qWzhNCBx/PKStFw2Pa4O1Y1yyILfxmYg9SIpDxaCmuXCxlsT9BiIjOjWBHTtK8hOEClpF8w7SuzHIukhZ4CPvOGM0chVFNk6sjgSkyBNaAZmjprdmkkhj6gZewGgVctsQWhHalnDt5iAEsmZQ8gWd2PxQHhmCPUtO4feC3zaD8C88Aj9FaITnkitp+ZAwEvtMccJGMnrUtzd0yRQoo/V57/WHv3MszNvfeRcU1TLaYzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDMNfPlQuSt2+pyCVZBlc2WgDDvB9RwoqrLzdksiVFAD+I2pP+FG+CUi+p7bHxiZZqc8CRrSB+VO+nSCvb8oyaP2nk=] 14 | 15 | profiles::ssh_keys::additional_keys::user_list: 16 | - nmburgan 17 | -------------------------------------------------------------------------------- /data/nodes/ci01.voxpupu.li.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | role: roles::github_runner 3 | 4 | profiles::base::manage_borg: false 5 | profiles::github_runners::setup_ruby: true 6 | profiles::github_runners::setup_docker: true 7 | profiles::github_runners::setup_libvirt: true 8 | profiles::github_runners::instances: ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'senventh', 'eighth', 'ninth', 'tenth', 'eleventh', 'twelfth', 'thirteenth', 'fourteenth', 'fifteenth', 'sixteenth'] 9 | profiles::github_runners::runner_group: 'Macarne-runners' 10 | profiles::github_runners::labels: ['self-hosted', 'macarne'] 11 | 12 | github_actions_runner::personal_access_token: ENC[PKCS7,MIIBmQYJKoZIhvcNAQcDoIIBijCCAYYCAQAxggEhMIIBHQIBADAFMAACAQEwDQYJKoZIhvcNAQEBBQAEggEAV1E1ok8FbLt6IeeKsmdoazXV/ZI87iSiRpt29FUG1giIgrxPEscowQAzug7Q/wSjTAh36ZEVIpnq+uBxoWPRJ9hyyQDxbUB4K9AcjyZ4Ih7qWzhNCBx/PKStFw2Pa4O1Y1yyILfxmYg9SIpDxaCmuXCxlsT9BiIjOjWBHTtK8hOEClpF8w7SuzHIukhZ4CPvOGM0chVFNk6sjgSkyBNaAZmjprdmkkhj6gZewGgVctsQWhHalnDt5iAEsmZQ8gWd2PxQHhmCPUtO4feC3zaD8C88Aj9FaITnkitp+ZAwEvtMccJGMnrUtzd0yRQoo/V57/WHv3MszNvfeRcU1TLaYzBcBgkqhkiG9w0BBwEwHQYJYIZIAWUDBAEqBBDMNfPlQuSt2+pyCVZBlc2WgDDvB9RwoqrLzdksiVFAD+I2pP+FG+CUi+p7bHxiZZqc8CRrSB+VO+nSCvb8oyaP2nk=] 13 | -------------------------------------------------------------------------------- /site/profiles/manifests/download_server.pp: -------------------------------------------------------------------------------- 1 | # @summary Setup a server to present Vox Pupuli's files and packages for download over http and rsync 2 | # 3 | # Setup a server to present Vox Pupuli's files and packages for download over 4 | # http and rsync. The files being served are sourced from OSL's S3-compatible 5 | # bucket via rclone. 6 | # 7 | # @param server_names 8 | # A hash of Nginx server names to create. Each top-level key is a FQDN and 9 | # its value is made up of a location block and, optionally, a list of 10 | # aliases by which the server should also be known. The value of the 11 | # locations block is passed directly to a splat within an 12 | # `nginx::resource::location` resource. 13 | # 14 | class profiles::download_server ( 15 | Hash[Stdlib::Fqdn, Struct[ 16 | { 17 | locations => Hash, 18 | aliases => Optional[Array[Stdlib::Fqdn]] 19 | } 20 | ] 21 | ] $server_names, 22 | ) { 23 | # This mainfest was getting unweildy so I broke it down into some subclasses 24 | # -- GeneBean 2025-03-03 25 | contain profiles::download_server::nginx 26 | contain profiles::download_server::rclone 27 | contain profiles::download_server::rsync 28 | } 29 | -------------------------------------------------------------------------------- /site/profiles/manifests/postgresql.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary install latest postgresql with upstream repositories 3 | # 4 | # @param version desired postgresql version 5 | # 6 | # @author Tim Meusel 7 | # 8 | class profiles::postgresql ( 9 | Enum['11', '12', '13', '14', '15', '16', '17', '18'] $version = '13', 10 | ) { 11 | class { 'postgresql::globals': 12 | encoding => 'UTF-8', 13 | locale => 'en_US.UTF-8', 14 | version => $version, 15 | manage_package_repo => true, 16 | } 17 | # don't contain it. causes apt cycles 18 | class { 'postgresql::server': 19 | listen_addresses => '127.0.0.1', 20 | } 21 | file { '/srv/pg_dumps': 22 | ensure => 'directory', 23 | } 24 | class { 'dbbackup': 25 | destination => '/srv/pg_dumps', 26 | backuphistory => 21, 27 | manage_dependencies => true, 28 | require => File['/srv/pg_dumps'], 29 | } 30 | contain dbbackup 31 | $activity = $facts['os']['family'] ? { 32 | 'RedHat' => 'pg_activity', 33 | 'Debian' => 'pg-activity', 34 | default => undef, 35 | } 36 | package { ['pgbadger', $activity,]: 37 | ensure => 'installed', 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /site/profiles/spec/classes/base_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'profiles::base' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let :facts do 9 | os_facts 10 | end 11 | 12 | context 'with all defaults' do 13 | it { is_expected.to compile.with_all_deps } 14 | it { is_expected.to contain_package('build-essential') } 15 | it { is_expected.to contain_package('make') } 16 | it { is_expected.to contain_package('gcc') } 17 | it { is_expected.to contain_package('htop') } 18 | it { is_expected.to contain_package('lsb-release') } 19 | it { is_expected.to contain_package('ca-certificates') } 20 | it { is_expected.to contain_package('dfc') } 21 | it { is_expected.to contain_package('ccze') } 22 | it { is_expected.to contain_package('apt-file') } 23 | it { is_expected.to contain_package('tree') } 24 | it { is_expected.to contain_exec('refresh apt-file cache') } 25 | it { is_expected.to contain_class('ssh') } 26 | it { is_expected.to contain_class('profiles::borg') } 27 | end 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /site/profiles/files/sentry.voxpupu.li.conf: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | server { 3 | server_name sentry.voxpupu.li; 4 | 5 | 6 | listen [::]:443 ssl; # managed by Certbot 7 | listen 443 ssl; # managed by Certbot 8 | ssl_certificate /etc/letsencrypt/live/sentry.voxpupu.li/fullchain.pem; # managed by Certbot 9 | ssl_certificate_key /etc/letsencrypt/live/sentry.voxpupu.li/privkey.pem; # managed by Certbot 10 | include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot 11 | ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot 12 | 13 | location / { 14 | proxy_set_header Host $http_host; 15 | proxy_set_header X-Real-IP $remote_addr; 16 | proxy_set_header Client-IP $remote_addr; 17 | proxy_set_header X-Forwarded-For $remote_addr; 18 | proxy_set_header X-Forwarded-Proto https; 19 | proxy_set_header X-Forwarded-Port 443; 20 | proxy_pass http://localhost:9001; 21 | } 22 | 23 | } 24 | server { 25 | if ($host = sentry.voxpupu.li) { 26 | return 301 https://$host$request_uri; 27 | } # managed by Certbot 28 | 29 | listen 80; 30 | listen [::]:80; 31 | server_name sentry.voxpupu.li; 32 | return 404; # managed by Certbot 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Puppetfile: -------------------------------------------------------------------------------- 1 | mod 'cirrax-libvirt', '6.0.0' 2 | mod 'puppet-archive', '8.1.0' 3 | mod 'puppet-borg', '5.0.0' 4 | mod 'puppet-cron', '5.0.0' 5 | mod 'puppet-dbbackup', '2.2.0' 6 | mod 'puppet-extlib', '7.5.1' 7 | mod 'puppet-github_actions_runner', '1.2.0' 8 | mod 'puppet-grafana', '14.2.0' 9 | mod 'puppet-letsencrypt', '13.1.0' 10 | mod 'puppet-nftables', '7.0.0' 11 | mod 'puppet-nginx', '7.0.1' 12 | mod 'puppet-prometheus', '17.0.0' 13 | mod 'puppet-r10k', '15.0.0' 14 | mod 'puppet-redis', '12.0.0' 15 | mod 'puppet-ssh_keygen', '6.0.0' 16 | mod 'puppet-systemd', '9.4.0' 17 | mod 'puppetlabs-apache', '13.1.0' 18 | mod 'puppetlabs-apt', '11.2.0' 19 | mod 'puppetlabs-concat', '9.1.0' 20 | mod 'puppetlabs-docker', '10.3.0' 21 | mod 'puppetlabs-inifile', '6.2.0' 22 | mod 'puppetlabs-mount_core', '2.0.1' 23 | mod 'puppetlabs-postgresql', '10.6.0' 24 | mod 'puppetlabs-puppetdb', '8.1.0' 25 | mod 'puppetlabs-rsync', '1.2.0' 26 | mod 'puppetlabs-stdlib', '9.7.0' 27 | mod 'puppetlabs-vcsrepo', '7.0.0' 28 | mod 'richardc-datacat', '0.6.2' 29 | mod 'saz-ssh', '14.1.0' 30 | mod 'saz-sudo', '9.0.2' 31 | mod 'smarteon-rclone', '0.0.2' 32 | mod 'theforeman-dns', '12.0.0' 33 | mod 'theforeman-foreman', '28.1.0' 34 | mod 'theforeman-foreman_proxy', '29.1.0' 35 | mod 'theforeman-puppet', '22.0.1' 36 | mod 'theforeman-puppetserver_foreman', '4.3.0' 37 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/sebastianrakel.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary Configure key from sebastianrakel from GitHubs in the authorized_keys file along with supplemental keys 3 | # 4 | # Configure key from sebastianrakel from GitHubs in the authorized_keys file along with supplemental keys 5 | # 6 | class profiles::ssh_keys::people::sebastianrakel { 7 | profiles::update_ssh_authorized_keys(['sebastianrakel']) 8 | 9 | ssh_authorized_key { 10 | default: 11 | ensure => 'present', 12 | user => 'root', 13 | type => 'ssh-rsa', 14 | ; 15 | 'Spritzgebaeck-ed25519': 16 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAINo6u1C58Gc4ZzpgxsDSPK49i+bnvPZv/p5Tyw2/NwyP', 17 | type => 'ssh-ed25519', 18 | ; 19 | 'spritzgebaeck-rsa': 20 | key => 'AAAAB3NzaC1yc2EAAAADAQABAAABgQDS2uZyrP/EFeakDm9/GRRj/K3CknCwd8Z30Yj/zP0HTfvQyjCSMLCkavX6P8rhMy8UggsWkgWWGT8OAZmSfWChmlmtSypGR2W9/HYdWTqE/XbJxgplwExo7/s9qjZ5XQd4Htyt75egU0ZX7Ag1p2jvY3G+UMOHgHxrddtJgufIEaUYJRd76wpseP6hC2BojR0FR3WtOnYyG78PUzNF/7FYCRLSsSN7TS7dwObZjsxRcwv+jurGag8tbgcGqaF8XgtngLje5AFmaL1G7uuGJYWFjCAk3Ha1sjaqivyDvvirouU0Ma/ZlxTVx31wQTqa7FO24+FaEBerO9gxlSplRNSo7CB7WQTAdGGJOAEqDoug2H14DUPGiACO6sQ5vAqMUc9NLxyWucKVeCEW2T50brHrAsXZUWioSU26y8MANwV5Oy5lrxl48igXlxq6uDSkGf/jiNAHW59emnHfiMSjN9nrXfeYmuGX1YFe9CvWakSIXxqFh1/l3ySc1KxQwzR8Lyk=', 21 | type => 'ssh-rsa', 22 | ; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | #-------------------------------------------------# 2 | # Validator: https://www.yamllint.com/ # 3 | # Docu: https://yamllint.readthedocs.io/ # 4 | #-------------------------------------------------# 5 | --- 6 | yaml-files: 7 | - '*.yaml' 8 | - '*.yml' 9 | - '.yamllint' 10 | 11 | rules: 12 | braces: 13 | min-spaces-inside: 0 14 | max-spaces-inside: 1 15 | min-spaces-inside-empty: 0 16 | max-spaces-inside-empty: 1 17 | brackets: 18 | min-spaces-inside: 0 19 | max-spaces-inside: 1 20 | min-spaces-inside-empty: 0 21 | max-spaces-inside-empty: 1 22 | colons: 23 | max-spaces-before: 0 24 | max-spaces-after: 1 25 | commas: 26 | max-spaces-before: 0 27 | min-spaces-after: 1 28 | max-spaces-after: 1 29 | comments: 30 | level: warning 31 | require-starting-space: false 32 | min-spaces-from-content: 1 33 | comments-indentation: disable 34 | document-end: disable 35 | document-start: 36 | level: warning 37 | present: true 38 | empty-lines: 39 | max: 1 40 | max-start: 0 41 | max-end: 1 42 | hyphens: 43 | max-spaces-after: 1 44 | indentation: 45 | spaces: 2 46 | indent-sequences: whatever 47 | check-multi-line-strings: true 48 | key-duplicates: enable 49 | line-length: disable 50 | new-line-at-end-of-file: enable 51 | new-lines: 52 | type: unix 53 | trailing-spaces: enable 54 | truthy: 55 | level: warning 56 | -------------------------------------------------------------------------------- /site/profiles/manifests/postfix.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary installs postfix 3 | # 4 | class profiles::postfix { 5 | # provides /etc/ssl/certs/ca-certificates.crt for smtpd_tls_CAfile ini_setting 6 | require profiles::base 7 | require profiles::certbot 8 | # todo: use puppet/postfix 9 | package { 'postfix': 10 | ensure => 'installed', 11 | } 12 | -> service { 'postfix.service': 13 | ensure => 'running', 14 | enable => true, 15 | } 16 | if fact('letsencrypt_directory."voxpupu.li"') { 17 | $path = fact('letsencrypt_directory."voxpupu.li"') 18 | ini_setting { 'smtpd_tls_cert_file': 19 | ensure => 'present', 20 | path => '/etc/postfix/main.cf', 21 | section => '', 22 | setting => 'smtpd_tls_cert_file', 23 | value => "${path}/fullchain.pem", 24 | notify => Service['postfix.service'], 25 | } 26 | ini_setting { 'smtpd_tls_key_file': 27 | ensure => 'present', 28 | path => '/etc/postfix/main.cf', 29 | section => '', 30 | setting => 'smtpd_tls_key_file', 31 | value => "${path}/privkey.pem", 32 | notify => Service['postfix.service'], 33 | } 34 | ini_setting { 'smtpd_tls_CAfile': 35 | ensure => 'present', 36 | path => '/etc/postfix/main.cf', 37 | section => '', 38 | setting => 'smtpd_tls_CAfile', 39 | value => '/etc/ssl/certs/ca-certificates.crt', 40 | notify => Service['postfix.service'], 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /site/profiles/spec/acceptance/base_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'profiles::base' do 6 | context 'with defaults' do 7 | it_behaves_like 'an idempotent resource' do 8 | let(:manifest) do 9 | 'include profiles::base' 10 | end 11 | end 12 | # rubocop:disable RSpec/RepeatedExampleGroupBody 13 | describe package('htop') do 14 | it { is_expected.to be_installed } 15 | end 16 | 17 | describe package('make') do 18 | it { is_expected.to be_installed } 19 | end 20 | 21 | describe package('gcc') do 22 | it { is_expected.to be_installed } 23 | end 24 | 25 | describe package('build-essential') do 26 | it { is_expected.to be_installed } 27 | end 28 | 29 | describe package('lsb-release') do 30 | it { is_expected.to be_installed } 31 | end 32 | 33 | describe package('ccze') do 34 | it { is_expected.to be_installed } 35 | end 36 | 37 | describe package('ca-certificates') do 38 | it { is_expected.to be_installed } 39 | end 40 | 41 | describe package('apt-file') do 42 | it { is_expected.to be_installed } 43 | end 44 | 45 | describe package('dfc') do 46 | it { is_expected.to be_installed } 47 | end 48 | 49 | describe package('tree') do 50 | it { is_expected.to be_installed } 51 | end 52 | # rubocop:enable RSpec/RepeatedExampleGroupBody 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /site/profiles/manifests/puppet/server_firewalling.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary manages nft rules on Puppetserver/PuppetDB 3 | # 4 | # @author Tim Meusel 5 | # 6 | # @api private 7 | class profiles::puppet::server_firewalling { 8 | assert_private() 9 | include profiles::nftables 10 | nftables::simplerule { 'allow_puppet_4': 11 | action => 'accept', 12 | proto => 'tcp', 13 | dport => 8140, 14 | saddr => ['95.217.246.117/32', '77.42.36.83/32', "${facts['networking']['ip']}/32",], 15 | } 16 | nftables::simplerule { 'allow_puppet_6': 17 | action => 'accept', 18 | proto => 'tcp', 19 | dport => 8140, 20 | saddr => ['2a01:4f9:c01f:9f8a::/64','2a01:4f9:c011:bcee::1', "${facts['networking']['ip6']}/128",], 21 | } 22 | nftables::simplerule { 'allow_puppetdb_4': 23 | action => 'accept', 24 | proto => 'tcp', 25 | dport => 8081, 26 | saddr => "${facts['networking']['ip']}/32", 27 | } 28 | nftables::simplerule { 'allow_puppetdb_6': 29 | action => 'accept', 30 | proto => 'tcp', 31 | dport => 8081, 32 | saddr => "${facts['networking']['ip6']}/128", 33 | } 34 | # allow connections from the agent/curl to reach the PuppetDB via http/https 35 | nftables::rule { 'default_out-puppetdbv6': 36 | content => "tcp dport { 8080, 8081 } ip6 daddr ${facts['networking']['ip6']}/128 accept", 37 | } 38 | nftables::rule { 'default_out-puppetdbv4': 39 | content => "tcp dport { 8080, 8081 } ip daddr ${facts['networking']['ip']}/32 accept", 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /site/profiles/files/voxpupu.li.conf: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | server { 3 | 4 | server_name voxpupu.li; 5 | 6 | listen 159.69.85.37:443 ssl; 7 | listen [2a01:4f8:c2c:7501::1]:443 ssl ; 8 | ssl_certificate /etc/letsencrypt/live/voxpupu.li/fullchain.pem; # managed by Certbot 9 | ssl_certificate_key /etc/letsencrypt/live/voxpupu.li/privkey.pem; # managed by Certbot 10 | ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot 11 | 12 | location / { 13 | proxy_set_header Host $http_host; 14 | proxy_set_header X-Real-IP $remote_addr; 15 | proxy_set_header Client-IP $remote_addr; 16 | proxy_set_header X-Forwarded-For $remote_addr; 17 | proxy_set_header X-Forwarded-Proto https; 18 | proxy_set_header X-Forwarded-Port 443; 19 | proxy_pass http://localhost:3000; 20 | } 21 | access_log /var/log/nginx/ssl-voxpupu.li.access.log; 22 | error_log /var/log/nginx/ssl-voxpupu.li.error.log; 23 | } 24 | server { 25 | listen 159.69.85.37:80; 26 | listen [2a01:4f8:c2c:7501::1]:80 ; 27 | server_name voxpupu.li; 28 | access_log /var/log/nginx/voxpupu.li.access.log; 29 | error_log /var/log/nginx/voxpupu.li.error.log; 30 | 31 | location / { 32 | return 301 https://voxpupu.li$request_uri; 33 | } 34 | 35 | location ^~ /.well-known/acme-challenge/ { 36 | root /var/lib/letsencrypt/; 37 | default_type text/plain; 38 | } 39 | 40 | location = /.well-known/acme-challenge/ { 41 | return 404; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /site/profiles/manifests/puppet/code.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary some resources to manage puppete code 3 | # 4 | # @author Tim Meusel 5 | # 6 | # @api private 7 | class profiles::puppet::code { 8 | assert_private() 9 | ssh_keygen { 'root_github': 10 | type => 'ed25519', 11 | filename => '/root/.ssh/id_ed25519_github', 12 | home => '/root', 13 | user => 'root', 14 | } 15 | # /root/.ssh/config entry for the backup server 16 | ssh::client::config::user { 'root_github': 17 | ensure => present, 18 | user => 'root', 19 | user_home_dir => '/root', 20 | options => { 21 | 'Host github.com' => { 22 | 'User' => 'git', 23 | 'IdentityFile' => '~/.ssh/id_ed25519_github', 24 | 'ControlMaster' => 'auto', 25 | 'ControlPath' => '~/.ssh/ssh-%r@%h:%p', 26 | 'ControlPersist' => 'yes', 27 | }, 28 | }, 29 | } 30 | if $facts['os']['name'] == 'Archlinux' { 31 | $deploy = { 'generate_types' => true, 'exclude_spec' => true, 'puppet_path' => '/usr/bin/puppet' } 32 | $version = 'installed' 33 | } else { 34 | $deploy = { 'generate_types' => true, 'exclude_spec' => true, } 35 | # we hardcode this and update it from time to time. 36 | # agent runs faster compared to ensure latest 37 | $version = '3.16.0' 38 | } 39 | class { 'r10k': 40 | pool_size => $facts['processors']['count']*2, 41 | sources => { 42 | 'puppet' => { 43 | 'remote' => 'https://github.com/voxpupuli/controlrepo.git', 44 | 'basedir' => '/etc/puppetlabs/code/environments', 45 | }, 46 | }, 47 | version => $version, 48 | deploy_settings => $deploy, 49 | } 50 | contain r10k 51 | } 52 | -------------------------------------------------------------------------------- /site/profiles/manifests/foreman.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configure foreman + plugins 3 | # 4 | # @see `cat /opt/puppetlabs/puppet/cache/foreman_cache_data/admin_password` provides the admin password 5 | # 6 | class profiles::foreman { 7 | require profiles::redis 8 | require profiles::postgresql 9 | require profiles::nftables # ensures hkp access is working to download the apt key 10 | 11 | class { 'foreman::repo': 12 | repo => '3.17', 13 | } 14 | 15 | class { 'foreman': 16 | foreman_service_puma_workers => 3, 17 | foreman_service_puma_threads_min => 3, 18 | foreman_service_puma_threads_max => 3, 19 | logging_type => 'journald', 20 | initial_admin_username => 'admin', 21 | initial_admin_first_name => 'Vox', 22 | initial_admin_last_name => 'Pupuli', 23 | initial_admin_email => 'pmc@voxpupuli.org', 24 | register_in_foreman => true, # is a foreman 3.1+ feature 25 | rails_cache_store => { 26 | 'type' => 'redis', 27 | 'urls' => ['localhost:6379/0'], 28 | 'options' => { 29 | 'compress' => 'true', 30 | 'namespace' => 'foreman', 31 | }, 32 | }, 33 | } 34 | include foreman::plugin::puppet 35 | include foreman::plugin::puppetdb 36 | 37 | class { 'foreman_proxy': 38 | register_in_foreman => true, # is a foreman 3.1+ feature 39 | puppet => true, 40 | puppetca => true, 41 | tftp => false, 42 | dhcp => false, 43 | dns => false, 44 | bmc => false, 45 | realm => false, 46 | } 47 | # open http/https in firewall 48 | require nftables::rules::http 49 | require nftables::rules::https 50 | } 51 | -------------------------------------------------------------------------------- /site/profiles/manifests/vpt.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary this profile will, in the future, instal Vox Pupuli Tasks 3 | # 4 | # @param deploy_sentry manage the sentry nginx config 5 | # @param deploy_vpt manage the VPT nginx config 6 | # @param deploy_kibana manage the kibana nginx config 7 | # 8 | # @see https://github.com/voxpupuli/vox-pupuli-tasks 9 | # 10 | # @author Tim Meusel 11 | # 12 | class profiles::vpt ( 13 | Boolean $deploy_sentry = true, 14 | Boolean $deploy_vpt = true, 15 | Boolean $deploy_kibana = true 16 | ) { 17 | require profiles::nginx 18 | # deploy vhosts by hand for now 19 | if $deploy_sentry { 20 | file { '/etc/nginx/sites-available/sentry.voxpupu.li.conf': 21 | ensure => 'file', 22 | content => file("${module_name}/sentry.voxpupu.li.conf"), 23 | notify => Service['nginx'], 24 | } 25 | file { '/etc/nginx/sites-enabled/sentry.voxpupu.li.conf': 26 | ensure => 'link', 27 | target => '/etc/nginx/sites-available/sentry.voxpupu.li.conf', 28 | notify => Service['nginx'], 29 | } 30 | } 31 | if $deploy_vpt { 32 | file { '/etc/nginx/sites-available/voxpupu.li.conf': 33 | ensure => 'file', 34 | content => file("${module_name}/voxpupu.li.conf"), 35 | notify => Service['nginx'], 36 | } 37 | file { '/etc/nginx/sites-enabled/voxpupu.li.conf': 38 | ensure => 'link', 39 | target => '/etc/nginx/sites-available/voxpupu.li.conf', 40 | notify => Service['nginx'], 41 | } 42 | } 43 | if $deploy_kibana { 44 | file { '/etc/nginx/sites-available/kibana.voxpupu.li.conf': 45 | ensure => 'file', 46 | content => file("${module_name}/kibana.voxpupu.li.conf"), 47 | notify => Service['nginx'], 48 | } 49 | file { '/etc/nginx/sites-enabled/kibana.voxpupu.li.conf': 50 | ensure => 'link', 51 | target => '/etc/nginx/sites-available/kibana.voxpupu.li.conf', 52 | notify => Service['nginx'], 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/ekohl.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary Configure key from ekohl from GitHubs in the authorized_keys file along with supplemental keys 3 | # 4 | # Configure key from ekohl from GitHubs in the authorized_keys file along with supplemental keys 5 | # 6 | class profiles::ssh_keys::people::ekohl { 7 | profiles::update_ssh_authorized_keys(['ekohl']) 8 | 9 | ssh_authorized_key { 10 | default: 11 | ensure => 'present', 12 | user => 'root', 13 | type => 'ssh-rsa', 14 | ; 15 | 'ewoud-rsa-1': 16 | key => 'AAAAB3NzaC1yc2EAAAADAQABAAACAQCYUCD5BVc5ILSXhBh+Kj8gC/x7ctPhpk6GGrB/nJCyoY8hBXW6mspaVaQeLwDmjFFH+YwpfJZhCI1GFVsNKZWcLB319stoQkDEdYFcZZ6WMD4dc3kBVdEUqVBbe51pQlHIfuaROXAI42cjbJTuYy2AIVCKYdasg3ztFNuDE/RPuscbn9Kv7u8bi0CYjEYeSTML8YXX0n4xS7k1v6r0ksRnUxvNhgiEi73pSboRIeyizX3iaHB+vqWy9EoM8YjnJOUDjtMsflkHoV3UHWmcKOPjjvKNJeuUJs4rCksiXGM6iGdHx7jjcx9iW5v3L0mCuetV6AoZNw2bkXx7/6o6jao6vyJATAKQbHu31oJiPWjbtSgKzg2OepGcrpSm3fuDGncXqPSbf+RGYm0+6gMyt3Wbfl0q0DsB5nixbUqtx6BlG0zZyWhOD7FDmMh4L+fxM/pndh3rSOwLk7DlR9UiDWGvn7jxPXiinTn6aoef20IF+i9sRImpGYQaGDlpCIYI+3+V4MYm5KcgGFVOk4TSxqISvzKr9Rb19VxUV0QBd3v/U4TJBjLjmXlN5feSmEROWFURemvlH1nZfZOxPgAnR0S2YlW22q1Zoz6Ts/uq6lZTl5b1sBFFD1mCKS4aB4/R2GE3DWcieCpdZVLaHOpT4DBvKMovyMOp+dHBearmWs7NeQ==', 17 | ; 18 | 'ewoud-rsa-2': 19 | key => 'AAAAB3NzaC1yc2EAAAADAQABAAACAQDUeRHvXLF+3vIhI50zsM7FIc7QW0O36ZY2RYluc+MiPDfTDJlGUb8eBU+jxpkohOVys0llbuC01M8nRzJzOQne+LyGurFOYc3/SE0Q4BlaJqYAoF2GW5aYm+0TNHxCVWWnSSZWJZuLi4P0Zv5egSfzXG898vErLUwaHUybi4S230DdSECgtrWyc4NhMAma7Zh9zkoJ2SuYDdQWrnk2hWA4AWNn1LKRIYATg4cT+BucqqkNglFZutPWcdoL2WqD3pNNrJaA5bIdjUpkNghNsN9IbGLnQK+txsnwyI4bkqeI51JGPtZbdQ44WEA0I2wqGuLF2p76grSDi9y6I7dlk91yAqBwZ73iwGJoFdhtjgiQ0BRS3hTNP/lpsQVYrZmUj6IN/76gvKWwqCEKMpoJZ8hO64KS9HgnpTBvbtZehJVqC1SPtuBmW+PK1Z3snJ34uV6amvXtKs/WQjW94GLhlWss3ySJrgQjgpKPu9Lw1IYT3Ypei0Mo0iT3ZUsTciIJZ6STeK6WVTHTCiuhVoC7K0JCqcBk8t7sdSA5NIObpjyu4zv4MvrZY0T0C1DyzoptkFRKOnsYEVNOkZJaXle/RZNwFFzEKgBeWJiA7FVgK5I0q+8F6paXFyHkc4P16cVa4sL+Ue1mGYq0c9zaVw2Tr+58rGeS++ODnscxV60bGZrWGw==', 20 | ; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /site/profiles/manifests/puppet.pp: -------------------------------------------------------------------------------- 1 | # @summary configure puppet agent and server 2 | # 3 | # @param server decide if the server should be configured as well 4 | # @param manage_msgpack configure if we should install msgpack on the agent 5 | # 6 | # @author Tim Meusel 7 | # 8 | class profiles::puppet ( 9 | Boolean $server = ($trusted['pp_role'] == 'puppetserver'), 10 | Boolean $manage_msgpack = ($facts['os']['name'] != 'gentoo'), 11 | ) { 12 | include profiles::puppet::code 13 | if $server { 14 | require profiles::foreman 15 | include profiles::puppet::db 16 | $params = { 17 | server => true, 18 | server_reports => 'puppetdb,foreman', 19 | server_storeconfigs => true, 20 | server_foreman => true, 21 | # don't create /etc/puppetlabs/code/environments/common 22 | server_common_modules_path => [], 23 | server_jvm_min_heap_size => '1G', 24 | server_jvm_max_heap_size => '1G', 25 | #server_jvm_extra_args => ['-Djruby.logger.class=com.puppetlabs.jruby_utils.jruby.Slf4jLogger', '-XX:+UseParallelGC'], 26 | server_multithreaded => true, 27 | } 28 | package { 'msgpack-server': 29 | ensure => 'installed', 30 | provider => 'puppetserver_gem', 31 | name => 'msgpack', 32 | require => [Package['make'],Package['gcc'],Class['puppet']], 33 | } 34 | contain profiles::puppet::server_firewalling 35 | } else { 36 | $params = {} 37 | } 38 | class { 'puppet': 39 | runmode => 'unmanaged', 40 | unavailable_runmodes => ['cron', 'systemd.timer'], 41 | * => $params, 42 | } 43 | if $manage_msgpack { 44 | if $facts['os']['name'] == 'Archlinux' { 45 | $provider = undef 46 | $package = 'ruby-msgpack' 47 | } else { 48 | $provider = 'puppet_gem' 49 | $package = 'msgpack' 50 | } 51 | package { $package: 52 | ensure => 'installed', 53 | provider => $provider, 54 | require => Class['puppet'], 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /site/profiles/manifests/github_runners/ruby.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @api private 3 | # 4 | # @summary install ruby for GitHub self hosted runners 5 | # 6 | # @author Tim Meusel 7 | # 8 | class profiles::github_runners::ruby ( 9 | String[1] $user, 10 | String[1] $version, 11 | String[1] $group = $user, 12 | Stdlib::Absolutepath $home = '/', 13 | Hash $instances = {}, 14 | ) { 15 | assert_private() 16 | 17 | # workaround for the too old ruby-build package in Debian bookworm... 18 | # afterwards we need to run PREFIX=~/.local/ ./install.sh 19 | vcsrepo { "${home}/.rbenv/plugins/ruby-build": 20 | ensure => 'present', 21 | provider => 'git', 22 | source => 'https://github.com/rbenv/ruby-build.git', 23 | user => $user, 24 | notify => Exec['install-ruby-build'], 25 | } 26 | exec { 'install-ruby-build': 27 | command => ["${home}/.rbenv/plugins/ruby-build/install.sh"], 28 | refreshonly => true, 29 | user => $user, 30 | cwd => "${home}/.rbenv/plugins/ruby-build", 31 | path => $facts['path'], 32 | environment => ["PREFIX=${home}/.local/"], 33 | provider => 'shell', 34 | } 35 | 36 | ['2.7.8', '3.2.5', '3.3.4'].each |$ruby| { 37 | # $ ruby-build 3.2.2 /scratch/actions/try/_work/_tool/Ruby/3.2.2/x64 38 | # Once that completes successfully, mark it as complete with: 39 | # $ touch /scratch/actions/try/_work/_tool/Ruby/3.2.2/x64.complete 40 | 41 | $instances.each |$key, $data| { 42 | $_work = "${home}/actions-runner-${version}/${key}/_work" 43 | $_dest = "${_work}/_tool/Ruby/${ruby}/x64" 44 | exec { "ruby_build_${ruby}_${key}": 45 | user => $user, 46 | group => $group, 47 | cwd => $home, 48 | command => "ruby-build ${ruby} ${_dest} && /usr/bin/touch ${_dest}.complete", 49 | creates => "${_dest}.complete", 50 | timeout => 600, # 10 minutes 51 | require => Exec['install-ruby-build'], 52 | path => "${home}/.local/bin/:${facts['path']}", 53 | provider => 'shell', 54 | } 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /site/profiles/.fixtures.yml: -------------------------------------------------------------------------------- 1 | --- 2 | fixtures: 3 | repositories: 4 | stdlib: https://github.com/puppetlabs/puppetlabs-stdlib 5 | nginx: https://github.com/voxpupuli/puppet-nginx.git 6 | ferm: https://github.com/voxpupuli/puppet-ferm.git 7 | ssh: https://github.com/saz/puppet-ssh.git 8 | ssh_keygen: https://github.com/voxpupuli/puppet-ssh_keygen.git 9 | r10k: https://github.com/voxpupuli/puppet-r10k 10 | grafana: https://github.com/voxpupuli/puppet-grafana.git 11 | letsencrypt: https://github.com/voxpupuli/puppet-letsencrypt 12 | dbbackup: https://github.com/voxpupuli/puppet-dbbackup.git 13 | apt: https://github.com/puppetlabs/puppetlabs-apt 14 | inifile: https://github.com/puppetlabs/puppetlabs-inifile 15 | systemd: https://github.com/voxpupuli/puppet-systemd 16 | postgresql: https://github.com/puppetlabs/puppetlabs-postgresql 17 | puppetdb: https://github.com/puppetlabs/puppetlabs-puppetdb.git 18 | prometheus: https://github.com/voxpupuli/puppet-prometheus.git 19 | borg: https://github.com/voxpupuli/puppet-borg.git 20 | puppet: https://github.com/theforeman/puppet-puppet 21 | foreman: https://github.com/theforeman/puppet-foreman 22 | foreman_proxy: https://github.com/theforeman/puppet-foreman_proxy 23 | extlib: https://github.com/voxpupuli/puppet-extlib.git 24 | nftables: https://github.com/voxpupuli/puppet-nftables.git 25 | docker: https://github.com/puppetlabs/puppetlabs-docker 26 | libvirt: https://github.com/cirrax/puppet-libvirt 27 | redis: https://github.com/voxpupuli/puppet-redis.git 28 | archive: https://github.com/voxpupuli/puppet-archive 29 | concat: https://github.com/puppetlabs/puppetlabs-concat 30 | ssh_keys: https://github.com/puppetlabs/puppetlabs-sshkeys_core 31 | yumrepo: https://github.com/puppetlabs/puppetlabs-yumrepo_core 32 | mount: https://github.com/puppetlabs/puppetlabs-mount_core 33 | cron: https://github.com/puppetlabs/puppetlabs-cron_core 34 | augeasproviders_core: https://github.com/voxpupuli/puppet-augeasproviders_core.git 35 | augeas_core: https://github.com/puppetlabs/puppetlabs-augeas_core.git 36 | selinux_core: https://github.com/puppetlabs/puppetlabs-selinux_core 37 | -------------------------------------------------------------------------------- /site/profiles/manifests/prometheus.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary install Prometheus 3 | # 4 | class profiles::prometheus { 5 | class { 'prometheus': 6 | version => '2.39.1', 7 | web_listen_address => '127.0.0.1:9090', 8 | manage_prometheus_server => true, 9 | } 10 | require profiles::nginx 11 | require profiles::certbot 12 | 13 | # setup vhost 14 | # how to get a cert 15 | # certbot certonly --webroot --email pmc@voxpupuli.org --rsa-key-size 4096 --domain prometheus.voxpupu.li \ 16 | # --webroot-path=/var/lib/letsencrypt/ --renew-by-default --keep --agree-tos --text --non-interactive 17 | $domain = 'prometheus.voxpupu.li' 18 | if fact('letsencrypt_directory."prometheus.voxpupu.li"') { 19 | $path = fact('letsencrypt_directory."prometheus.voxpupu.li"') 20 | $ssl = { 21 | ssl_key => "${path}/privkey.pem", 22 | ssl_cert => "${path}/fullchain.pem", 23 | ssl => true, 24 | ssl_redirect => true, 25 | } 26 | $ssl_location = true 27 | } else { 28 | $ssl = {} 29 | $ssl_location = false 30 | } 31 | nginx::resource::server { $domain: 32 | listen_port => 80, 33 | proxy => 'http://127.0.0.1:9090', 34 | server_name => [$domain, 'prometheus.puppet.community', 'prometheus.voxpupuli.org'], 35 | proxy_http_version => '1.1', 36 | ipv6_enable => true, 37 | http2 => 'on', 38 | ipv6_listen_options => '', 39 | proxy_set_header => [ 40 | 'Host $host', 41 | 'X-Real-IP $remote_addr', 42 | 'X-Forwarded-For $proxy_add_x_forwarded_for', 43 | 'X-Forwarded-Proto $scheme', 44 | 'X-Forwarded-Ssl on', 45 | 'Proxy ""', 46 | ], 47 | * => $ssl, 48 | } 49 | nginx::resource::location { "^~ /.well-known/acme-challenge/ - ${domain}": 50 | server => $domain, 51 | www_root => '/var/lib/letsencrypt/', 52 | index_files => [], 53 | location => '^~ /.well-known/acme-challenge/', 54 | ssl => $ssl_location, 55 | } 56 | nginx::resource::location { "= /.well-known/acme-challenge/ - ${domain}": 57 | server => $domain, 58 | location_cfg_append => { 'return' => '404', }, 59 | index_files => [], 60 | location => '= /.well-known/acme-challenge/', 61 | ssl => $ssl_location, 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /site/profiles/files/puppetmodule.service: -------------------------------------------------------------------------------- 1 | # THIS FILE IS MANAGED BY PUPPET 2 | 3 | [Install] 4 | WantedBy=multi-user.target 5 | 6 | 7 | [Unit] 8 | Description=puppetmodule.info 9 | Documentation=https://github.com/puma/puma/blob/master/docs/systemd.md https://ruderich.org/simon/notes/systemd-service-hardening https://www.freedesktop.org/software/systemd/man/systemd.exec.html 10 | Requires=puppetmodule.socket 11 | After=network.target 12 | 13 | [Service] 14 | #Puma supports systemd's `Type=notify` and watchdog service 15 | # monitoring, if the [sd_notify](https://github.com/agis/ruby-sdnotify) gem is installed, 16 | # as of Puma 5.1 or later. 17 | # On earlier versions of Puma or JRuby, change this to `Type=simple` and remove 18 | # the `WatchdogSec` line. 19 | Type=notify 20 | # If your Puma process locks up, systemd's watchdog will restart it within seconds. 21 | WatchdogSec=10 22 | Restart=always 23 | SyslogIdentifier=puppetmodule.info 24 | User=puppetmodule 25 | Group=puppetmodule 26 | WorkingDirectory=/srv/puppetmodule/puppetmodule.info 27 | ExecStart=/usr/bin/bundle exec --keep-file-descriptors puma --config config/puma.rb 28 | PrivateTmp=true 29 | RestrictSUIDSGID=true 30 | NoNewPrivileges=true 31 | RemoveIPC=true 32 | PrivateDevices=true 33 | PrivateMounts=true 34 | ProtectProc=invisible 35 | ProcSubset=pid 36 | ProtectHostname=true 37 | ProtectClock=true 38 | ProtectKernelTunables=true 39 | ProtectKernelModules=true 40 | ProtectKernelLogs=true 41 | ProtectControlGroups=true 42 | RestrictAddressFamilies=AF_UNIX 43 | ProtectHome=true 44 | ProtectSystem=strict 45 | # allow the app to write into the database and create a socket 46 | ReadWritePaths=/srv/puppetmodule/puppetmodule.info/sockets /srv/puppetmodule/puppetmodule.info/data /srv/puppetmodule/puppetmodule.info/public/js /srv/puppetmodule/puppetmodule.info/public/css /srv/puppetmodule/puppetmodule.info/public/images /srv/puppetmodule/puppetmodule.info/repos /srv/puppetmodule/puppetmodule.info/tmp 47 | RestrictRealtime=true 48 | SystemCallArchitectures=native 49 | MemoryDenyWriteExecute=true 50 | LockPersonality=true 51 | CapabilityBoundingSet= 52 | SystemCallFilter= 53 | SystemCallFilter=@network-io @file-system @basic-io @system-service 54 | SystemCallFilter=~@aio @chown @clock @cpu-emulation @debug @keyring @memlock @module @mount @obsolete @privileged @raw-io @reboot @resources @setuid @swap userfaultfd mincore 55 | RestrictNamespaces=~user pid net uts mnt cgroup ipc 56 | UMask=0077 57 | Environment='RACK_ENV=production' 58 | -------------------------------------------------------------------------------- /site/profiles/manifests/download_server/rclone.pp: -------------------------------------------------------------------------------- 1 | # @summary Configures a server with rclone and mirrors data from OSL's S3-compatible buckets 2 | # 3 | # Configures a server with rclone and mirrors data from OSL's S3-compatible buckets 4 | # 5 | # @api private 6 | # 7 | class profiles::download_server::rclone { 8 | assert_private() 9 | 10 | include rclone 11 | 12 | # unzip is pulled in by profiles::base but we need to make sure it gets installed first: 13 | Package <| title == 'unzip' |> { 14 | before => Class['rclone::install'], 15 | } 16 | 17 | $_rclone_config = @("EOF") 18 | [OpenVox] 19 | type = s3 20 | provider = Other 21 | env_auth = false 22 | endpoint = https://s3.osuosl.org 23 | | EOF 24 | 25 | file { 26 | default: 27 | ensure => directory, 28 | owner => 'www-data', 29 | group => 'www-data', 30 | require => Class['nginx'], 31 | ; 32 | '/var/mirror': ; 33 | '/var/www': ; 34 | '/var/www/.rclone.conf': 35 | ensure => file, 36 | content => $_rclone_config, 37 | ; 38 | } 39 | 40 | file { 41 | default: 42 | ensure => directory, 43 | owner => 'www-data', 44 | group => 'www-data', 45 | require => File['/var/mirror'], 46 | ; 47 | '/var/mirror/downloads': ; 48 | '/var/mirror/apt': ; 49 | '/var/mirror/yum': ; 50 | '/var/mirror/artifacts': ; 51 | } 52 | 53 | mount { '/var/mirror/downloads': 54 | ensure => mounted, 55 | atboot => true, 56 | device => '/var/mirror/artifacts/downloads', 57 | fstype => 'none', 58 | options => 'bind', 59 | require => File['/var/mirror/downloads'], 60 | } 61 | 62 | cron::hourly { 63 | default: 64 | user => 'www-data', 65 | environment => ['MAILTO=root', 'PATH="/usr/bin:/bin"',], 66 | require => [ 67 | Class['rclone::install'], 68 | File[ 69 | '/var/mirror', 70 | '/var/www/.rclone.conf', 71 | ], 72 | ], 73 | ; 74 | 'sync-apt-from-OSL': 75 | minute => '20', 76 | command => 'rclone sync --exclude index.html OpenVox:openvox-apt /var/mirror/apt', 77 | ; 78 | 'sync-artifacts-from-OSL': 79 | minute => '25', 80 | command => 'rclone sync --exclude index.html OpenVox:openvox-artifacts /var/mirror/artifacts', 81 | ; 82 | 'sync-yum-from-OSL': 83 | minute => '30', 84 | command => 'rclone sync --exclude index.html OpenVox:openvox-yum /var/mirror/yum', 85 | ; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /site/profiles/manifests/borg.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configures borg backups 3 | # 4 | # @param borg_excludes paths you do not want to backup 5 | # @param borg_includes additional dirs you want to backup 6 | # @param borg_compression the desired compression algo for borg 7 | # @param borg_keep_yearly how many years to backup 8 | # @param borg_keep_monthly how many months to backup 9 | # @param borg_keep_weekly how many weeks to backup 10 | # @param borg_keep_daily how many days to backup 11 | # @param borg_keep_within how many days to keep all created backups 12 | # @param absolutebackupdestdir the subdir on the backupserver where borg places backups 13 | # 14 | # @author Tim Meusel 15 | # 16 | class profiles::borg ( 17 | Array[Stdlib::Absolutepath] $borg_excludes = [], 18 | Array[Stdlib::Absolutepath] $borg_includes = [], 19 | String[1] $borg_compression = 'auto,zstd,6', 20 | Integer[0] $borg_keep_yearly = 3, 21 | Integer[0] $borg_keep_monthly = 6, 22 | Integer[0] $borg_keep_weekly = 4, 23 | Integer[0] $borg_keep_daily = 14, 24 | Integer[0] $borg_keep_within = 14, 25 | String[1] $absolutebackupdestdir = $facts['networking']['hostname'], 26 | ) { 27 | # setup borg. Requires powertools and epel repo on RedHat os family 28 | $borg_require = if $facts['os']['family'] == 'RedHat' { 29 | [Yumrepo['powertools'],Package['epel-release']] 30 | } else { 31 | undef 32 | } 33 | class { 'borg': 34 | create_prometheus_metrics => false, 35 | update_borg_restore_db_after_backuprun => false, 36 | install_restore_script => false, 37 | backupserver => 'u477156.your-storagebox.de', 38 | username => 'u477156', 39 | ssh_port => 23, 40 | absolutebackupdestdir => $absolutebackupdestdir, 41 | additional_excludes => $borg_excludes, 42 | additional_includes => $borg_includes, 43 | keep_yearly => $borg_keep_yearly, 44 | keep_monthly => $borg_keep_monthly, 45 | keep_weekly => $borg_keep_weekly, 46 | keep_daily => $borg_keep_daily, 47 | keep_within => $borg_keep_within, 48 | compression => $borg_compression, 49 | additional_exclude_pattern => ['sh:/opt/puppetlabs/puppet/cache/r10k/*'], 50 | require => $borg_require, 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Puppet Code to manage [voxpupu.li](https://voxpupu.li) 2 | 3 | [Hetzner cloud](https://www.hetzner.com/cloud) sponsors us a cloud instance. 4 | It's currently running Ubuntu 18.04. This repository works as a control 5 | repository for [r10k](https://github.com/puppetlabs/r10k#r10k). 6 | 7 | The main purpose of this cloud instance is to run [Vox Pupuli Tasks](https://github.com/voxpupuli/vox-pupuli-tasks#vox-pupuli-tasks---the-webapp-for-community-management). 8 | 9 | ## Bootstrap 10 | 11 | Assume we've got a new Ubuntu 18.04 cloud instance: 12 | 13 | ```sh 14 | wget https://apt.puppet.com/puppet7-release-bionic.deb 15 | dpkg -i puppet7-release-bionic.deb 16 | apt update 17 | apt --yes upgrade 18 | apt --yes install puppet-agent 19 | source /etc/profile.d/puppet-agent.sh 20 | puppet module install puppet-r10k 21 | puppet apply -e 'include r10k' 22 | sed -i 's#remote:.*#remote: https://github.com/voxpupuli/controlrepo.git#' /etc/puppetlabs/r10k/r10k.yaml 23 | puppet resource package toml ensure=installed provider=puppet_gem 24 | r10k deploy environment --modules --verbose --exclude-spec 25 | puppet apply /etc/puppetlabs/code/environments/production/manifests/site.pp --show_diff 26 | ``` 27 | 28 | ## Hetzner Cloud cloud-init userdata: 29 | 30 | ```yaml 31 | #cloud-config 32 | --- 33 | package_reboot_if_required: true 34 | package_upgrade: true 35 | packages: 36 | - git 37 | - ca-certificates 38 | repo_update: true 39 | repo_upgrade: all 40 | puppet: 41 | install_type: aio 42 | collection: puppet8 43 | cleanup: false 44 | package_name: puppet-agent 45 | csr_attributes: 46 | extension_requests: 47 | pp_role: puppetserver 48 | runcmd: 49 | - systemctl disable --now puppet 50 | - /opt/puppetlabs/puppet/bin/gem install --no-document r10k toml 51 | - cd /root && git clone https://github.com/voxpupuli/controlrepo 52 | - cd /root/controlrepo && /opt/puppetlabs/puppet/bin/r10k puppetfile install --verbose 53 | - /opt/puppetlabs/puppet/bin/puppet apply /root/controlrepo/manifests/site.pp --modulepath /root/controlrepo/modules:/root/controlrepo/site --show_diff --write_catalog_summary --hiera_config /root/controlrepo/hiera.yaml --summarize --graph --tags r10k,hacked_pluginsync 54 | - /opt/puppetlabs/puppet/bin/r10k deploy environment --modules --verbose 55 | - /opt/puppetlabs/puppet/bin/puppet apply /etc/puppetlabs/code/environments/production/manifests/site.pp --show_diff --environment production --write_catalog_summary --summarize --graph 56 | - /opt/puppetlabs/puppet/bin/puppet agent -t 57 | - /opt/puppetlabs/puppet/bin/puppet agent -t 58 | ``` 59 | 60 | ## ToDos 61 | 62 | * setup csr_attributes (cloud-inits supports that as well) 63 | * write the r10k config so we can do the initial provisioning into `/etc/puppetlabs/code/environments` and not `/root` 64 | 65 | ## metadata.json and dependencies 66 | 67 | the `site/profiles/metadata.json` only tracks modules that are direct 68 | dependencies to profiles. The `.fixtures.yml` can be autogenerated with the 69 | `generate_fixtures` rake task. 70 | -------------------------------------------------------------------------------- /site/profiles/metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "voxpupuli-profiles", 3 | "version": "0.1.0", 4 | "summary": "a list of profiles for our cloud instances", 5 | "author": "Vox Pupuli", 6 | "source": "https://github.com/voxpupuli/voxpupu.li.git", 7 | "project_page": "https://github.com/voxpupuli/voxpupu.li", 8 | "issues_url": "https://github.com/voxpupuli/voxpupu.li/issues", 9 | "license": "AGPL-3.0", 10 | "tags": [ 11 | "control repo", 12 | "controlrepo", 13 | "profiles", 14 | "vox pupuli", 15 | "voxpupuli" 16 | ], 17 | "requirements": [ 18 | { 19 | "name": "openvox", 20 | "version_requirement": ">= 8.19.0 < 9.0.0" 21 | } 22 | ], 23 | "operatingsystem_support": [ 24 | { 25 | "operatingsystem": "Ubuntu", 26 | "operatingsystemrelease": [ 27 | "22.04", 28 | "24.04" 29 | ] 30 | } 31 | ], 32 | "dependencies": [ 33 | { 34 | "name": "puppetlabs/stdlib", 35 | "version_requirement": ">= 9.2.0 < 10.0.0" 36 | }, 37 | { 38 | "name": "puppet/nginx", 39 | "version_requirement": ">= 4.2.0 < 6.0.0" 40 | }, 41 | { 42 | "name": "puppet/ferm", 43 | "version_requirement": ">= 7.0.1 < 10.0.0" 44 | }, 45 | { 46 | "name": "saz/ssh", 47 | "version_requirement": ">= 10.0.0 < 12.0.0" 48 | }, 49 | { 50 | "name": "puppet/ssh_keygen", 51 | "version_requirement": ">= 5.0.2 < 6.0.0" 52 | }, 53 | { 54 | "name": "puppet/r10k", 55 | "version_requirement": ">= 10.1.1 < 12.0.0" 56 | }, 57 | { 58 | "name": "puppet/grafana", 59 | "version_requirement": ">= 10.0.1 < 14.0.0" 60 | }, 61 | { 62 | "name": "puppet/letsencrypt", 63 | "version_requirement": ">= 8.0.2 < 10.0.0" 64 | }, 65 | { 66 | "name": "puppet/dbbackup", 67 | "version_requirement": ">= 1.1.1 < 2.0.0" 68 | }, 69 | { 70 | "name": "puppetlabs/apt", 71 | "version_requirement": ">= 8.3.0 < 10.0.0" 72 | }, 73 | { 74 | "name": "puppetlabs/inifile", 75 | "version_requirement": ">= 5.2.0 < 7.0.0" 76 | }, 77 | { 78 | "name": "puppet/systemd", 79 | "version_requirement": ">= 3.8.0 < 6.0.0" 80 | }, 81 | { 82 | "name": "puppetlabs/postgresql", 83 | "version_requirement": ">= 8.0.0 < 14.0.0" 84 | }, 85 | { 86 | "name": "puppetlabs/puppetdb", 87 | "version_requirement": ">= 7.13.0 < 8.0.0" 88 | }, 89 | { 90 | "name": "puppet/prometheus", 91 | "version_requirement": ">= 12.3.0 < 14.0.0" 92 | }, 93 | { 94 | "name": "puppet/borg", 95 | "version_requirement": ">= 3.1.0 < 4.0.0" 96 | }, 97 | { 98 | "name": "theforeman/puppet", 99 | "version_requirement": ">= 20.0.0 < 21.0.0" 100 | }, 101 | { 102 | "name": "theforeman/foreman", 103 | "version_requirement": ">= 23.0.0 < 24.0.0" 104 | }, 105 | { 106 | "name": "theforeman/foreman_proxy", 107 | "version_requirement": ">= 25.0.0 < 26.0.0" 108 | }, 109 | { 110 | "name": "puppet/extlib", 111 | "version_requirement": ">= 7.2.0 < 8.0.0" 112 | }, 113 | { 114 | "name": "puppet/nftables", 115 | "version_requirement": ">= 4.0.0 < 5.0.0" 116 | }, 117 | { 118 | "name": "puppetlabs/docker", 119 | "version_requirement": ">= 10.0.1 < 11.0.0" 120 | }, 121 | { 122 | "name": "cirrax/libvirt", 123 | "version_requirement": ">= 5.0.4 < 6.0.0" 124 | }, 125 | { 126 | "name": "puppet/redis", 127 | "version_requirement": ">= 9.0.0 < 10.0.0" 128 | } 129 | ] 130 | } 131 | -------------------------------------------------------------------------------- /site/profiles/manifests/ssh_keys/people/bastelfreak.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary Configure key from bastelfreak from GitHubs in the authorized_keys file along with supplemental keys 3 | # 4 | # Configure key from bastelfreak from GitHubs in the authorized_keys file along with supplemental keys 5 | # 6 | class profiles::ssh_keys::people::bastelfreak { 7 | profiles::update_ssh_authorized_keys(['bastelfreak']) 8 | 9 | ssh_authorized_key { 10 | default: 11 | ensure => 'present', 12 | user => 'root', 13 | ; 14 | 'personal key 2021-07-02': 15 | type => 'ssh-ed25519', 16 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAIKO6DUyFoPn/euUQq+G7H49ESrT/28BhFbbjRl4wzPi7', 17 | ; 18 | 'bastelfreak': 19 | type => 'ssh-ed25519', 20 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAIKC4uaKuYzMGK4jlTvPlbnMP9n+gdac65480/eDTMWRw', 21 | ; 22 | 'bastelfreak-nb-c vox-pupuli-tasks 2021-08-30': 23 | type => 'ssh-ed25519', 24 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAIF7O2iuxjShCg0MNugyYjWTrjKmXd6tC7FIJPsejD8SB', 25 | ; 26 | 'bastelfreak@bastelfreak-nb': 27 | type => 'ssh-rsa', 28 | key => 'AAAAB3NzaC1yc2EAAAADAQABAAAEAQDEsooTj/ktA/0E1TA9wT3ekNNKGfdD1m9pOar4tgcQmXtafIeYBrmcSI7x1k4YkmvAInRpKTEKTGFq0GJg4/Q28lhO0lh52I2kg5xt6kEVWGGWvfHLXnZzRPuCBUKzy0cgTNr1mdo+bvSU08YFRAt3TAwID3uQ/AcQ4g+5xcFjFW4yDDdZMiBJKKPJB3EJmXTuMBBgD+Hd9nU9BYshqWnS6vubLIxHvLetisj62BRaI42p/9bMkqRxU/2uZQK1D9h4xTX18OZ6+Qo+xTOBBM3ZQ2vsKCiKD60b+bcEi2mNJLyzCe2fyusAeyjuEEcnE8gG0a30+eYurHZB6QUbkStVcrL555vN2ADGIXvFB3wvS52+2Y/Rx1bG+1B5LU7x/ykNeHiJnm9H25DbkmNH1RPmYr82u2U7Y0BMQQpxe3NB2+RKKFod4925ISXBxenQdZ/oGFK3VZnw0Xh9HjKiPAkHymP/jACQm4i8TEkWbBAzhlqOjOc5Exe6r/6dmJgTfjAJ2SvrzdQPil9JmmKH6w3DNTZrq9p6tuU2YpUtuAt4sy/IEnlBPVkHV8xovJjEEwY9DRfIgRbhGzlm1cgyyOaQVPUNyKnOK8LqQY/gBTMCnskx9F+65Ah5HjczCLki2u6eN8D6GZE7EqkrREUyh0meP9YPDQbSS+uI4j1WNu/qGEp2HuLGjdG8PbsM5MPhQOEsS7PNT+3NGUCcFVP/6n1QACMHhRBf7HU9HTKz9MjLPakq9q5V+bBs1DQ/t3Sziz32wseFZXHSCyfiCrM0PMp/KCuPLkEnTHOrV9QLCtgc4CV8ziWP483daRVJErEATBmvyvyJYylrsdqwuvj0UIcJuKJToU+cuBlFuykfCRLUQG7bfCGyeuf2188eHFAGkBRhU5GcAcWyRcj5kN7z/WcXTuSOC2emIYkoU/6ptoDdSmIxVbUxkdU5KQhnI2gN1l6CbMUIzsZ94hD20t1OEXUB7YL1JH8sD91jrfgRqWITs1tGHNol+dmXVSj/95lG1l+XxggnmdtP+lLSliiQrHrkURu7BuPV4IeHKxX2OaGFSGeCtC7eJhlc5BSNp6qjomZWhBMqaYGBvES5nG8UIOF9MsymxaBf7GWusYwwPTGa5zRBxHX6CRUOJaEx/d7vxB7GBcAwWoO37J3Ns801PZA+WCEqLv21gunMEgSVYX33jRDW3oZV/zO5a+bZlXksc27JMlqT6EXYnHRG/fUYaM7n1PqRxJ3boD7Z1EwJrlheCf83zQn+I4MDuB7tBN8xLx2MPLFse/wbRK1u1gW1qP3LVWqCoTevajtoUHeN3/JGvwnMLrygJck1HXLJEUfm4NN1m2Ag0vFvp+hJfRAoSUtT', 29 | ; 30 | 'bastelfreak@basteles-bastelknecht.bastelfreak.org': 31 | type => 'ssh-rsa', 32 | key => 'AAAAB3NzaC1yc2EAAAADAQABAAAEAQCs5EkKmqsL4eqUt09WTbvxAYYyChIw8Gok1Y7V15T42vezwRVlZ2h6F6vpa/AX+usukn4+dBFo+bJyD6Qy6EkWzaY96hGX3NziOxaCeueSXdc+b4xCEkfMOHJY1+pVm0cidQUozV/RWp/pQDzm9wUEMTYn6gB5XxDvAsGzUqKViLFeUKcZBsrbhbVvXCelgJWSd/k83KBfPtDHHcqpYJMuIWuGY9PPjwBkvokXRcWwZStsH60Q33sz+zsI1ujhnRxJRSBTn2BdfPHdiHDk6aO6SDglUqhdrq2fvIS5WOiptwSmvAAnG3+LprPiWrvppnBwuoGVOtwIspAVmCpyzamK/d7WoFMDTDPPhOmtIJyNqfNhPLhuIBlUjJLPgh4JjGnpMhnBmi0F+nciEfqYyK/KJYunfaYV6PRYUOKwEQ55nEpvq7tVKr2UWwLg7kzg+RvCBLFQ4qBkVUf4p44NRazLVMQDnSKXBH6dhwH3x4zHQ+sCsexXFAKfOGjnQGWBz2XvKFl/ml6aOHJnxqiBmpjmm34ULCYXhUi/dUlBV94+fsuO0qULei57F37LhMFeZocaxeD7+M4H2T6+cQCSTdxjr6Uj6BWK5FCHreQHgdL5Nq5kW/zCuoTtBoW9kp5fcn6ZjHCYprCThOIPRwINfznpuGg6LRyPnRgD2sVqSwLr2Rwt9zg3hS7ZBfXJYX28r1q4G+r7KuGLGEoLLVjl7RBWUUhXdF6GZ+6X5g6On9GBSDLpFHV0lC2NGKh+M6vcXJHeFh62dNOTMaC7H/JQcmB6G89y/OsT0S0v8q/k8i4Swaeh+iTNnv/tfC5ScDZY8gvUP2Zowd6HYwyvDHZ28vu812SAdWN99I4fIMGDpT5OLQGxArAV5VQeyRgaJHDX2umSdU+QN0fTaOL401ZDGOi9RQWuwldJC2D+MWTFhcM9yXtC6MOoM/b3F8nlbxhISvaHsrs37COEZQRxN7k3P9XMkmb/W2B5F2oEh4ggDlpJT+wtRHvKriGMtq1l23WZBsbcNEmxz7Uy/n4I8szuX2xATfmRf1z/GRk6IuudM/BfJBmQb77+ZFk9bN6w/es+EbsldatNdB3veJrL2bXVgB4CbsXx6M0sb3XAeJQq9AANY5vTsF3IY/weB84yTwaPLZyNsIJgy4AXG8xydbUNGeZExrddlpmPcE/AWkX9XqWGZPl/SLjdTMVUwhxRYn0FuhwZ1iQhZlwjuGcYFI8M2UJzIdTtDYAYMS8ekbNTwtex9EeLv63YrM2OMNqYchZTvyngifk6md/ZZhGxMnLvqe5zgh227jR+lAbGJ+gC1E4/jgQbiF93Ucp0eX8I9pzH0cc0GyDk5Fd4o+0CsKNsvLfx', 33 | ; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /site/profiles/manifests/download_server/nginx.pp: -------------------------------------------------------------------------------- 1 | # @summary Configures an nginx server to present the files under /var/mirror 2 | # 3 | # Configures an nginx server to present the files under /var/mirror. This class 4 | # takes two puppet runs to fully apply because the needed certificates from 5 | # Let's Encrypt are obtained during the first run and utilized in subsequent 6 | # runs. 7 | # 8 | # @api private 9 | # 10 | class profiles::download_server::nginx { 11 | assert_private() 12 | 13 | class { 'nginx': 14 | server_purge => true, 15 | confd_purge => true, 16 | } 17 | 18 | $_server_names = $profiles::download_server::server_names 19 | 20 | $_server_names.keys().each |$domain| { 21 | # During the first puppet run for a new domain the certificate will not yet exist. 22 | # This conditional makes it so that the bits that rely on the certificate files 23 | # existing are not appled until after the cert has been issued and installed. 24 | # The result is that on the second puppet run all the SSL/TLS related bits 25 | # get applied to Nginx. 26 | if fact("letsencrypt_directory.\"${domain}\"") { 27 | $path = fact("letsencrypt_directory.\"${domain}\"") 28 | $ssl = { 29 | ssl_key => "${path}/privkey.pem", 30 | ssl_cert => "${path}/fullchain.pem", 31 | ssl => true, 32 | ssl_port => 443, 33 | ssl_session_timeout => '1d', 34 | ssl_session_tickets => 'off', 35 | ssl_protocols => 'TLSv1.2 TLSv1.3', 36 | ssl_prefer_server_ciphers => 'off', 37 | add_header => { 38 | 'Strict-Transport-Security' => { 39 | 'max-age=63072000' => 'always', 40 | }, 41 | }, 42 | } 43 | } else { 44 | $ssl = {} 45 | } 46 | 47 | $_server_names_array = $_server_names[$domain]['aliases'] ? { 48 | undef => [$domain], 49 | default => [$domain] + $_server_names[$domain]['aliases'] 50 | } 51 | 52 | nginx::resource::server { $domain: 53 | listen_port => 80, 54 | server_name => $_server_names_array, 55 | ipv6_enable => true, 56 | ipv6_listen_options => '', # when using IPv4 & IPv6 the default options break Nginx 57 | http2 => 'on', 58 | access_log => "/var/log/nginx/${domain}.access.log", 59 | error_log => "/var/log/nginx/${domain}.error.log", 60 | format_log => 'combined', 61 | use_default_location => false, 62 | index_files => [], 63 | * => $ssl, 64 | } 65 | 66 | nginx::resource::location { "${domain} Let's Encrypt challenges": 67 | location => '^~ /.well-known/acme-challenge/', 68 | server => $domain, 69 | www_root => '/var/lib/letsencrypt/', 70 | index_files => [], 71 | } 72 | 73 | nginx::resource::location { "${domain} acme-challenge directory": 74 | location => '= /.well-known/acme-challenge/', 75 | server => $domain, 76 | location_cfg_append => { 'return' => '404', }, 77 | index_files => [], 78 | } 79 | 80 | letsencrypt::certonly { $domain: 81 | domains => [$domain], 82 | plugin => 'nginx', 83 | manage_cron => true, 84 | additional_args => [ 85 | '--no-redirect', 86 | ], 87 | require => [ 88 | Nginx::Resource::Server[$domain], 89 | Nginx::Resource::Location[ 90 | "${domain} Let's Encrypt challenges", 91 | "${domain} acme-challenge directory", 92 | ], 93 | ], 94 | } 95 | 96 | # This conditional keeps location blocks that rely on SSL/TLS bits being in place 97 | # are not appled before the certificates have been issued. 98 | if fact("letsencrypt_directory.\"${domain}\"") { 99 | $_server_names[$domain]['locations'].each |$orig_path, $settings| { 100 | nginx::resource::location { "${domain} ${orig_path}": 101 | ensure => present, 102 | location => $orig_path, 103 | ssl => true, 104 | server => $domain, 105 | index_files => [], 106 | * => $settings, 107 | } 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /site/profiles/manifests/github_runners.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configures a self-hosted github runner 3 | # 4 | # @param labels the labels that we will assign 5 | # @param user the user that runs the runner 6 | # @param group the group that runs the runner 7 | # @param version version of the runner, matches their upstream github release names 8 | # @param instances amount (and names) for all runners we create within one group 9 | # @param repo_name set it to configure an repo-specific and not org specific runner 10 | # @param setup_ruby installs ruby for rspec-puppet unit tests 11 | # @param setup_docker installs docker for beaker jobs 12 | # @param setup_libvirt installs libvirt and adds the user to the group 13 | # @param runner_group the group that we will assign to the runners. Needs to exist 14 | # @param org_name name of the github org 15 | # 16 | # @see code provided by CERN 17 | # 18 | # @author Tim Meusel 19 | # 20 | class profiles::github_runners ( 21 | Array[String[1]] $labels = ['self-hosted',], 22 | String[1] $user = 'runner', 23 | String[1] $group = $user, 24 | String[1] $version = '2.321.0', 25 | Optional[String[1]] $repo_name = undef, 26 | Array[String[1]] $instances = [], 27 | Boolean $setup_ruby = false, 28 | Boolean $setup_docker = false, 29 | Boolean $setup_libvirt = false, 30 | Optional[String[1]] $runner_group = undef, 31 | Enum['voxpupuli', 'openvoxproject'] $org_name = 'voxpupuli', 32 | ) { 33 | package { ['jq', 'libffi-dev', 'libyaml-dev', 'libreadline-dev', 'zlib1g-dev', 'libssl-dev',]: 34 | ensure => 'installed', 35 | } 36 | $home = "/opt/${user}" 37 | $groups_d = if $setup_docker { 38 | ['docker'] 39 | } else { 40 | [] 41 | } 42 | $groups_l = if $setup_libvirt { 43 | ['libvirt'] 44 | } else { 45 | [] 46 | } 47 | 48 | user { $user: 49 | ensure => 'present', 50 | managehome => true, 51 | purge_ssh_keys => true, 52 | system => true, 53 | home => $home, 54 | forcelocal => true, 55 | shell => '/usr/sbin/nologin', 56 | groups => $groups_d + $groups_l, 57 | # Notify the class to reload the runner 58 | # require when the docker integration is added later on 59 | notify => Class['github_actions_runner'], 60 | } 61 | group { $group: 62 | ensure => 'present', 63 | system => true, 64 | forcelocal => true, 65 | } 66 | 67 | $_instances = $instances.map | $inst | { 68 | { 69 | $inst => { 70 | 'labels' => $labels, 71 | 'repo_name' => $repo_name, 72 | 'repo_token' => lookup("runner_${$inst}_${repo_name}", Optional[String[1]], 'first', undef), 73 | 'runner_group' => $runner_group, 74 | }.delete_undef_values 75 | } 76 | }.reduce | $_memo, $_kv | { $_memo + $_kv } 77 | 78 | class { 'github_actions_runner': 79 | ensure => present, 80 | package_ensure => $version, 81 | base_dir_name => "${home}/actions-runner", 82 | repository_url => 'https://github.com/actions/runner/releases/download', 83 | #personal_access_token => Deferred('teigi::get',['pat']), 84 | org_name => $org_name, 85 | user => $user, 86 | group => $group, 87 | instances => $_instances, 88 | } 89 | contain github_actions_runner 90 | 91 | if $setup_ruby { 92 | class { 'profiles::github_runners::ruby': 93 | home => $home, 94 | instances => $_instances, 95 | user => $user, 96 | version => $version, 97 | } 98 | } 99 | 100 | if $setup_docker { 101 | # setup a docker daemon 102 | require profiles::docker 103 | } 104 | 105 | if $setup_libvirt { 106 | require profiles::libvirt 107 | } 108 | 109 | # some github actions want to configure repos 110 | # ideally we update gha-puppet and our self hosted runners already have the packages installed 111 | include sudo 112 | sudo::conf { 'runner-ppa': 113 | priority => 10, 114 | content => 'runner ALL=(ALL) NOPASSWD: /usr/bin/add-apt-repository', 115 | } 116 | sudo::conf { 'runner-aptupdate': 117 | priority => 10, 118 | content => 'runner ALL=(ALL) NOPASSWD: /usr/bin/apt-get update', 119 | } 120 | sudo::conf { 'runner-aptinstall': 121 | priority => 10, 122 | content => 'runner ALL=(ALL) NOPASSWD: /usr/bin/apt-get install -y *', 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /site/profiles/manifests/base.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary ssh profile to manage basic stuff that doesn't fit into a dedicated profile 3 | # 4 | # @param manage_borg whether borg should be installed or not 5 | # 6 | # @author Tim Meusel 7 | # 8 | class profiles::base ( 9 | Boolean $manage_borg = true, 10 | ) { 11 | include profiles::ssh_keys::additional_keys 12 | include profiles::ssh_keys::pmc 13 | 14 | if $manage_borg { 15 | contain profiles::borg 16 | } 17 | 18 | $_base_packages = [ 19 | "linux-generic-hwe-${facts['os']['release']['major']}", 20 | 'apt-file', 21 | 'build-essential', 22 | 'ca-certificates', 23 | 'ccze', 24 | 'dfc', 25 | 'file', 26 | 'gcc', 27 | 'htop', 28 | 'lsb-release', 29 | 'make', 30 | 'ncdu', 31 | 'tree', 32 | 'unzip', 33 | 'uptimed', 34 | 'whois', 35 | ] 36 | 37 | package { $_base_packages: 38 | ensure => 'installed', 39 | } 40 | 41 | package { 'snapd': 42 | ensure => 'absent', 43 | } 44 | 45 | # disable services that talk to Canonical 46 | service { ['update-notifier-motd.timer', 'apt-news.service', 'esm-cache.service']: 47 | ensure => 'stopped', 48 | enable => 'mask', 49 | } 50 | 51 | # remove apt hook that talks to canonical 52 | file { '/etc/apt/apt.conf.d/20apt-esm-hook.conf': 53 | ensure => 'absent', 54 | } 55 | 56 | exec { 'refresh apt-file cache': 57 | refreshonly => true, 58 | command => '/usr/bin/apt-file update', 59 | subscribe => Package['apt-file'], 60 | } 61 | 62 | service { 'uptimed': 63 | ensure => 'running', 64 | enable => true, 65 | require => Package['uptimed'], 66 | } 67 | 68 | # do an apt update daily, don't log it, run it before packages 69 | class { 'apt': 70 | update => { 71 | frequency => 'daily', 72 | loglevel => 'debug', 73 | }, 74 | } 75 | # ensure update runs before installing packages 76 | Class['apt::update'] -> Package <| provider == 'apt' |> 77 | 78 | # https://www.sshaudit.com/hardening_guides.html 79 | class { 'ssh': 80 | storeconfigs_enabled => false, 81 | validate_sshd_file => true, 82 | server_options => { 83 | 'PasswordAuthentication' => 'no', 84 | 'PermitRootLogin' => 'without-password', 85 | 'X11Forwarding' => 'no', 86 | 'PrintMotd' => 'yes', 87 | 'AllowAgentForwarding' => 'no', 88 | 'Protocol' => 2, 89 | 'Port' => 22, 90 | 'MaxStartups' => '100:10:300', 91 | }, 92 | client_options => { 93 | 'Host *' => { 94 | 'Ciphers' => 'chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr', 95 | 'KexAlgorithms' => 'curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256', 96 | 'MACs' => 'hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com', 97 | 'HostKeyAlgorithms' => 'ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com', 98 | }, 99 | }, 100 | } 101 | contain ssh 102 | 103 | # add vox-pupuli-tasks admin keys 104 | ssh_authorized_key { 105 | default: 106 | ensure => 'present', 107 | user => 'root', 108 | type => 'ssh-ed25519', 109 | ; 110 | 'robert@Roberts-MBP.fritz.box': 111 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAIKpAtp1I07CyFhixqy97toXzv2cuhRJZj22YorhhH7Ds', 112 | ; 113 | 'robert@pc-mueller-2016-07-15': 114 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAIGEVvWqFedfEkG63cWq5iwdkptC/lXr/jWjpqW0EktU3', 115 | ; 116 | 'robert@DESKTOP-EV17QP6': 117 | key => 'AAAAC3NzaC1lZDI1NTE5AAAAIHwJ9FqCygbcCLNNqKlyN9nflIcHrxfxWmgEz08+EEUY', 118 | ; 119 | } 120 | 121 | # manage root so we can purge unknown keys 122 | user { 'root': 123 | ensure => 'present', 124 | purge_ssh_keys => true, 125 | } 126 | 127 | # install sensors if we are on a physical system 128 | if $facts['virtual'] == 'physical' { 129 | package { 'lm-sensors': 130 | ensure => 'installed', 131 | } 132 | } 133 | 134 | # install nvme tools if we have an nvme 135 | if $facts['disks'].keys.any |$disk| { $disk =~ /nvme/ } { 136 | package { 'nvme-cli': 137 | ensure => 'installed', 138 | } 139 | } 140 | 141 | include profiles::nftables 142 | 143 | # colourize the shell 144 | file { '/etc/profile.d/shell_setup.sh': 145 | ensure => 'file', 146 | content => file("${module_name}/shell_setup.sh"), 147 | } 148 | 149 | # configure puppet agent/server 150 | contain profiles::puppet 151 | 152 | # ensure we've the correct FQDN set 153 | if $trusted['certname'] { 154 | file { '/etc/hostname': 155 | content => "${trusted['certname']}\n", 156 | } 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: CI 3 | 4 | on: pull_request 5 | 6 | permissions: 7 | contents: read 8 | 9 | defaults: 10 | run: 11 | working-directory: ./site/profiles 12 | 13 | concurrency: 14 | group: ${{ github.head_ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | setup_matrix: 19 | defaults: 20 | run: 21 | working-directory: './site/profiles' 22 | name: 'Setup Test Matrix' 23 | runs-on: ubuntu-latest 24 | timeout-minutes: 40 25 | outputs: 26 | puppet_unit_test_matrix: ${{ steps.get-outputs.outputs.puppet_unit_test_matrix }} 27 | github_action_test_matrix: ${{ steps.get-outputs.outputs.github_action_test_matrix }} 28 | steps: 29 | - uses: actions/checkout@v6 30 | - name: Setup ruby 31 | uses: ruby/setup-ruby@v1 32 | with: 33 | ruby-version: '3.4' 34 | bundler-cache: true 35 | working-directory: ./site/profiles 36 | - name: Display Ruby environment 37 | run: bundle env 38 | - name: Run static validations 39 | run: bundle exec rake validate lint check 40 | - name: Run rake rubocop 41 | run: bundle exec rake rubocop 42 | - name: Setup Test Matrix 43 | id: get-outputs 44 | run: bundle exec metadata2gha --pidfile-workaround false 45 | 46 | unit: 47 | needs: setup_matrix 48 | runs-on: ubuntu-latest 49 | timeout-minutes: 40 50 | strategy: 51 | fail-fast: false 52 | matrix: 53 | include: ${{fromJson(needs.setup_matrix.outputs.puppet_unit_test_matrix)}} 54 | env: 55 | BUNDLE_WITHOUT: development:system_tests:release 56 | PUPPET_VERSION: "~> ${{ matrix.puppet }}.0" 57 | name: Puppet ${{ matrix.puppet }} (Ruby ${{ matrix.ruby }}) 58 | steps: 59 | - uses: actions/checkout@v6 60 | - name: Setup ruby 61 | uses: ruby/setup-ruby@v1 62 | with: 63 | ruby-version: ${{ matrix.ruby }} 64 | bundler-cache: true 65 | working-directory: ./site/profiles 66 | - name: Run tests 67 | run: bundle exec rake parallel_spec 68 | 69 | acceptance: 70 | needs: setup_matrix 71 | runs-on: ubuntu-latest 72 | env: 73 | BUNDLE_WITHOUT: development:release 74 | strategy: 75 | fail-fast: false 76 | matrix: 77 | include: ${{fromJson(needs.setup_matrix.outputs.github_action_test_matrix)}} 78 | name: ${{ matrix.puppet.name }} - ${{ matrix.setfile.name }} 79 | steps: 80 | - uses: actions/checkout@v6 81 | - name: Setup ruby 82 | uses: ruby/setup-ruby@v1 83 | with: 84 | ruby-version: '3.4' 85 | bundler-cache: true 86 | working-directory: ./site/profiles 87 | - name: Prepare fixtures 88 | run: bundle exec rake spec_prep 89 | - name: Run tests 90 | run: bundle exec rake beaker 91 | env: 92 | LANG: en_US 93 | LC_ALL: en_US.UTF-8 94 | BEAKER_PUPPET_COLLECTION: ${{ matrix.puppet.collection }} 95 | BEAKER_setfile: ${{ matrix.setfile.value }} 96 | 97 | puppetfile: 98 | defaults: 99 | run: 100 | working-directory: . 101 | runs-on: ubuntu-latest 102 | name: Validate Puppetfile + all modules 103 | steps: 104 | - uses: actions/checkout@v6 105 | - name: Setup ruby 106 | uses: ruby/setup-ruby@v1 107 | with: 108 | ruby-version: '3.4' 109 | bundler-cache: true 110 | - name: Check for puppetfile syntax correctness 111 | run: bundle exec rake r10k:syntax 112 | - name: validate URLs in Puppetfile 113 | run: bundle exec rake r10k:validate 114 | - name: Check for puppetfile duplicates 115 | run: bundle exec rake r10k:duplicates 116 | - name: Check for puppetfile module deprecations 117 | run: bundle exec rake r10k:deprecation 118 | - name: Check for outdated modules 119 | run: bundle exec rake r10k:dependencies 120 | - name: Validate all dependencies in metadata.json 121 | run: bundle exec rake metadata_deps 122 | - name: validate all dependencies 123 | run: bundle exec puppet module list --modulepath site 124 | 125 | Hiera: 126 | defaults: 127 | run: 128 | working-directory: . 129 | runs-on: ubuntu-latest 130 | name: Validate Hiera 131 | steps: 132 | - uses: actions/checkout@v6 133 | - name: Set up Python ${{ matrix.python-version }} 134 | uses: actions/setup-python@v6 135 | with: 136 | python-version: '3.10' 137 | - name: Install dependencies 138 | run: | 139 | pip install --upgrade wheel setuptools pip 140 | pip install yamllint 141 | - name: Run yamllint 142 | run: yamllint --format github --strict data/ 143 | tests: 144 | needs: 145 | - unit 146 | - acceptance 147 | - puppetfile 148 | - Hiera 149 | runs-on: ubuntu-latest 150 | name: Test suite 151 | steps: 152 | - run: echo Test suite completed 153 | # overwrite global working-directory because this step does not checkout the git repo 154 | working-directory: . 155 | -------------------------------------------------------------------------------- /site/profiles/manifests/puppetmodule.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary configures puppetmodule.info 3 | # 4 | # @param domain the url to the site *without www* 5 | # @param postgresql_password the database password 6 | # @param postgresql_database the database name 7 | # @param postgresql_user the database user 8 | # 9 | # @author Tim Meusel 10 | # 11 | # @see https://puppetmodule.info 12 | # @see https://github.com/puma/puma/blob/master/docs/nginx.md 13 | # 14 | class profiles::puppetmodule ( 15 | Stdlib::Fqdn $domain = 'puppetmodule.info', 16 | Variant[String[1],Sensitive] $postgresql_password = 'oehr384yhg034y5oreihu04y5', 17 | String[1] $postgresql_user = 'puppetmodule', 18 | String[1] $postgresql_database = $postgresql_user, 19 | ) { 20 | # setup database 21 | require profiles::postgresql 22 | postgresql::server::db { $postgresql_database: 23 | user => $postgresql_user, 24 | password => postgresql::postgresql_password($postgresql_user, $postgresql_password), 25 | } 26 | postgresql_conn_validator { 'validate postgres connection for puppetmodule.info': 27 | host => '127.0.0.1', 28 | port => 5432, 29 | db_username => $postgresql_user, 30 | db_password => $postgresql_password, 31 | db_name => $postgresql_database, 32 | psql_path => '/usr/bin/psql', 33 | before => Service['puppetmodule.service'], 34 | } 35 | # setup all the webfoo 36 | require profiles::nginx 37 | profiles::certbot::nginx { $domain: } 38 | profiles::certbot::nginx { "www.${domain}": } 39 | # generate the cert 40 | # certbot certonly --webroot --webroot-path=/var/lib/letsencrypt/ --renew-by-default --keep --agree-tos --email tim@bastelfreak.de --key-type ecdsa --elliptic-curve secp384r1 --non-interactive --text -d puppetmodule.info -d www.puppetmodule.info 41 | if fact('letsencrypt_directory."puppetmodule.info"') { 42 | nginx::resource::server { $domain: 43 | listen_ip => $facts['networking']['ip'], 44 | ipv6_listen_ip => $facts['networking']['ip6'], 45 | ipv6_enable => true, 46 | ipv6_listen_options => '', 47 | server_name => [$domain], 48 | ssl => true, 49 | ssl_cert => "/etc/letsencrypt/live/${domain}/fullchain.pem", 50 | ssl_key => "/etc/letsencrypt/live/${domain}/privkey.pem", 51 | ssl_redirect => true, 52 | proxy => 'http://127.0.0.1:8080', 53 | proxy_http_version => '1.1', 54 | rewrite_non_www_to_www => true, 55 | } 56 | } else { 57 | nginx::resource::server { $domain: 58 | listen_ip => $facts['networking']['ip'], 59 | ipv6_listen_ip => $facts['networking']['ip6'], 60 | ipv6_enable => true, 61 | ipv6_listen_options => '', 62 | ssl_redirect => true, 63 | server_name => [$domain], 64 | } 65 | } 66 | # Setup user and code 67 | group { 'puppetmodule': 68 | ensure => 'present', 69 | gid => 51, 70 | system => true, 71 | } 72 | user { 'puppetmodule': 73 | ensure => 'present', 74 | managehome => true, 75 | purge_ssh_keys => true, 76 | uid => 51, 77 | system => true, 78 | require => Group['puppetmodule'], 79 | home => '/srv/puppetmodule', 80 | groups => 'rvm', 81 | } 82 | # Puppet creates the home for us, but we need to update the mode, so nginx can access it 83 | file { '/srv/puppetmodule': 84 | ensure => 'directory', 85 | owner => 'puppetmodule', 86 | group => 'puppetmodule', 87 | mode => '0755', 88 | } 89 | # that user runs nginx on Ubuntu 90 | user { $nginx::daemon_user: 91 | groups => ['puppetmodule'], 92 | } 93 | 94 | package { ['ruby', 'ruby-bundler', 'ruby-dev', 'libpq-dev']: 95 | ensure => 'installed', 96 | } 97 | -> exec { 'install deps': 98 | command => 'bundle install --path .vendor/ --jobs 8', 99 | provider => 'shell', 100 | cwd => '/srv/puppetmodule/puppetmodule.info', 101 | require => Vcsrepo['/srv/puppetmodule/puppetmodule.info'], 102 | refreshonly => true, 103 | user => 'puppetmodule', 104 | environment => ['HOME=/srv/puppetmodule/'], 105 | } 106 | vcsrepo { '/srv/puppetmodule/puppetmodule.info': 107 | ensure => 'present', 108 | provider => 'git', 109 | source => 'https://github.com/voxpupuli/puppetmodule.info.git', 110 | user => 'puppetmodule', 111 | require => User['puppetmodule'], 112 | keep_local_changes => true, 113 | notify => Exec['install deps'], 114 | } 115 | -> file { '/srv/puppetmodule/puppetmodule.info/sockets': 116 | ensure => 'directory', 117 | owner => 'puppetmodule', 118 | group => 'puppetmodule', 119 | } 120 | 121 | systemd::unit_file { 'puppetmodule.socket': 122 | enable => true, 123 | active => true, 124 | content => file("${module_name}/puppetmodule.socket"), 125 | require => File['/srv/puppetmodule/puppetmodule.info/sockets'], 126 | } 127 | -> systemd::unit_file { 'puppetmodule.service': 128 | enable => true, 129 | active => true, 130 | content => file("${module_name}/puppetmodule.service"), 131 | require => File['/srv/puppetmodule/puppetmodule.info/sockets'], 132 | } 133 | systemd::timer { 'puppetmodule-hourly.timer': 134 | active => true, 135 | enable => true, 136 | timer_content => file("${module_name}/puppetmodule-hourly.timer"), 137 | service_content => file("${module_name}/puppetmodule-hourly.service"), 138 | } 139 | systemd::timer { 'puppetmodule-daily.timer': 140 | active => true, 141 | enable => true, 142 | timer_content => file("${module_name}/puppetmodule-daily.timer"), 143 | service_content => file("${module_name}/puppetmodule-daily.service"), 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /site/profiles/manifests/grafana.pp: -------------------------------------------------------------------------------- 1 | # 2 | # @summary installs grafana to display stats from dropsonde about Vox Pupuli modules 3 | # 4 | # @param postgresql_password 5 | # @param postgresql_user 6 | # @param postgresql_database 7 | # 8 | # @see https://dev.to/puppet/help-us-understand-how-you-use-open-source-puppet-51aj 9 | # @see https://grafana.com/tutorials/run-grafana-behind-a-proxy/ 10 | # 11 | # @author Tim Meusel 12 | # 13 | class profiles::grafana ( 14 | Variant[String[1],Sensitive] $postgresql_password = '023745uoehr98325yrsehrw023y4', 15 | String[1] $postgresql_user = 'grafana', 16 | String[1] $postgresql_database = $postgresql_user, 17 | ) { 18 | require profiles::base 19 | $domain = "grafana.${facts['networking']['fqdn']}" 20 | require profiles::nginx 21 | require profiles::certbot 22 | require profiles::postgresql 23 | require profiles::postfix 24 | package { 'toml': 25 | ensure => 'installed', 26 | provider => 'puppet_gem', 27 | } 28 | postgresql::server::db { $postgresql_database: 29 | user => $postgresql_user, 30 | password => postgresql::postgresql_password($postgresql_user, $postgresql_password), 31 | } 32 | postgresql_conn_validator { 'validate my postgres connection': 33 | host => '127.0.0.1', 34 | port => 5432, 35 | db_username => $postgresql_user, 36 | db_password => $postgresql_password, 37 | db_name => $postgresql_database, 38 | psql_path => '/usr/bin/psql', 39 | before => Service['grafana'], 40 | } 41 | class { 'grafana': 42 | install_method => 'repo', 43 | cfg => { 44 | server => { 45 | http_addr => '127.0.0.1', 46 | http_port => 3001, 47 | domain => $domain, 48 | }, 49 | security => { 50 | disable_gravatar => true, 51 | }, 52 | analytics => { 53 | reporting_enabled => false, 54 | check_for_updates => false, 55 | }, 56 | # wait with TLS setup until we've proper puppet certificates 57 | database => { 58 | type => 'postgres', 59 | host => '127.0.0.1', 60 | port => 5432, 61 | user => $postgresql_user, 62 | password => $postgresql_password, 63 | }, 64 | smtp => { 65 | enabled => true, 66 | }, 67 | 'auth.anonymous' => { 68 | enabled => true, 69 | hide_version => true, 70 | org_name => 'Vox Pupuli', 71 | org_role => 'Viewer', 72 | }, 73 | }, 74 | require => Package['toml'], 75 | } 76 | contain grafana 77 | grafana_plugin { 'grafana-bigquery-datasource': 78 | ensure => 'present', 79 | } 80 | 81 | # setup vhost 82 | # how to get a cert 83 | # certbot certonly --webroot --email pmc@voxpupuli.org --rsa-key-size 4096 --domain grafana.voxpupu.li \ 84 | # --webroot-path=/var/lib/letsencrypt/ --renew-by-default --keep --agree-tos --text --non-interactive 85 | if fact('letsencrypt_directory."grafana.voxpupu.li"') { 86 | $path = fact('letsencrypt_directory."grafana.voxpupu.li"') 87 | $ssl = { 88 | ssl_key => "${path}/privkey.pem", 89 | ssl_cert => "${path}/fullchain.pem", 90 | ssl => true, 91 | ssl_redirect => true, 92 | } 93 | } else { 94 | $ssl = {} 95 | } 96 | nginx::resource::server { $domain: 97 | listen_port => 80, 98 | proxy => 'http://127.0.0.1:3001', 99 | server_name => [$domain, 'grafana.puppet.community', 'grafana.voxpupuli.org'], 100 | proxy_http_version => '1.1', 101 | ipv6_enable => true, 102 | http2 => 'on', 103 | ipv6_listen_options => '', 104 | add_header => { 105 | 'X-Frame-Options' => 'DENY', 106 | 'Content-Security-Policy' => "upgrade-insecure-requests; default-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' blob:; img-src 'self' data: grafana.com; connect-src 'self' grafana.com; font-src 'self'; object-src 'none'; media-src 'none'; worker-src 'none'; frame-src 'none'; form-action 'self'; frame-ancestors 'none'; base-uri 'self';", 107 | }, 108 | proxy_set_header => [ 109 | 'Host $host', 110 | 'X-Real-IP $remote_addr', 111 | 'X-Forwarded-For $proxy_add_x_forwarded_for', 112 | 'X-Forwarded-Proto $scheme', 113 | 'X-Forwarded-Ssl on', 114 | 'Proxy ""', 115 | ], 116 | * => $ssl, 117 | } 118 | if fact('letsencrypt_directory."grafana.voxpupu.li"') { 119 | nginx::resource::location { '/api/live': 120 | server => $domain, 121 | rewrite_rules => ['^/(.*) /$1 break'], 122 | proxy_http_version => '1.1', 123 | proxy_set_header => [ 124 | 'Host $http_host', 125 | 'X-Real-IP $remote_addr', 126 | 'X-Forwarded-For $proxy_add_x_forwarded_for', 127 | 'X-Forwarded-Proto $scheme', 128 | 'X-Forwarded-Ssl on', 129 | 'Proxy ""', 130 | 'Upgrade $http_upgrade', 131 | 'Connection $connection_upgrade', 132 | ], 133 | proxy => 'http://127.0.0.1:3001', 134 | ssl => true, 135 | ssl_only => true, 136 | } 137 | } 138 | nginx::resource::map { 'connection_upgrade': 139 | string => '$http_upgrade', 140 | mappings => { 141 | 'default' => 'upgrade', 142 | "''" => 'close', 143 | }, 144 | } 145 | nginx::resource::location { "^~ /.well-known/acme-challenge/ - ${domain}": 146 | server => $domain, 147 | www_root => '/var/lib/letsencrypt/', 148 | index_files => [], 149 | location => '^~ /.well-known/acme-challenge/', 150 | } 151 | nginx::resource::location { "= /.well-known/acme-challenge/ - ${domain}": 152 | server => $domain, 153 | location_cfg_append => { 'return' => '404', }, 154 | index_files => [], 155 | location => '= /.well-known/acme-challenge/', 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /site/profiles/REFERENCE.md: -------------------------------------------------------------------------------- 1 | # Reference 2 | 3 | 4 | 5 | ## Table of Contents 6 | 7 | ### Classes 8 | 9 | #### Public Classes 10 | 11 | * [`profiles::base`](#profiles--base): ssh profile to manage basic stuff that doesn't fit into a dedicated profile 12 | * [`profiles::borg`](#profiles--borg): configures borg backups 13 | * [`profiles::certbot`](#profiles--certbot): configures the certbot foo. Doesn't create certificates! 14 | * [`profiles::docker`](#profiles--docker): installs docker 15 | * [`profiles::download_server`](#profiles--download_server): Setup a server to present Vox Pupuli's files and packages for download over http and rsync 16 | * [`profiles::foreman`](#profiles--foreman): configure foreman + plugins 17 | * [`profiles::github_runners`](#profiles--github_runners): configures a self-hosted github runner 18 | * [`profiles::grafana`](#profiles--grafana): installs grafana to display stats from dropsonde about Vox Pupuli modules 19 | * [`profiles::lets_encrypt`](#profiles--lets_encrypt): Common Let's Encrypt settings 20 | * [`profiles::libvirt`](#profiles--libvirt): installs libvirt 21 | * [`profiles::nftables`](#profiles--nftables): configure certain nftable rules 22 | * [`profiles::nginx`](#profiles--nginx): multiple profiles requires nginx vhosts, this profile pulls in the nginx class/package/service setup 23 | * [`profiles::node_exporter`](#profiles--node_exporter): install node_exporter 24 | * [`profiles::postfix`](#profiles--postfix): installs postfix 25 | * [`profiles::postgres_exporter`](#profiles--postgres_exporter): installs a postgres exporter 26 | * [`profiles::postgresql`](#profiles--postgresql): install latest postgresql with upstream repositories 27 | * [`profiles::prometheus`](#profiles--prometheus): install Prometheus 28 | * [`profiles::puppet`](#profiles--puppet): configure puppet agent and server 29 | * [`profiles::puppetmodule`](#profiles--puppetmodule): configures puppetmodule.info 30 | * [`profiles::redis`](#profiles--redis): configures redis on different platforms 31 | * [`profiles::ssh`](#profiles--ssh): ssh profile to manage sshd + ssh keys 32 | * [`profiles::ssh_keys::additional_keys`](#profiles--ssh_keys--additional_keys): Allow additional admins' keys to be pulled in via Hiera 33 | * [`profiles::ssh_keys::people::bastelfreak`](#profiles--ssh_keys--people--bastelfreak): Configure key from bastelfreak from GitHubs in the authorized_keys file along with supplemental keys 34 | * [`profiles::ssh_keys::people::binford2k`](#profiles--ssh_keys--people--binford2k): Configure key from binford2k from GitHubs in the authorized_keys file 35 | * [`profiles::ssh_keys::people::ekohl`](#profiles--ssh_keys--people--ekohl): Configure key from ekohl from GitHubs in the authorized_keys file along with supplemental keys 36 | * [`profiles::ssh_keys::people::genebean`](#profiles--ssh_keys--people--genebean): configure key from genebean from GitHubs in the authorized_keys file 37 | * [`profiles::ssh_keys::people::nmburgan`](#profiles--ssh_keys--people--nmburgan): configure key from nmburgan from GitHubs in the authorized_keys file 38 | * [`profiles::ssh_keys::people::rwaffen`](#profiles--ssh_keys--people--rwaffen): configure key from rwaffen from GitHubs in the authorized_keys file 39 | * [`profiles::ssh_keys::people::sebastianrakel`](#profiles--ssh_keys--people--sebastianrakel): Configure key from sebastianrakel from GitHubs in the authorized_keys file along with supplemental keys 40 | * [`profiles::ssh_keys::people::smortex`](#profiles--ssh_keys--people--smortex): Configure key from smortex from GitHubs in the authorized_keys file along with supplemental keys 41 | * [`profiles::ssh_keys::pmc`](#profiles--ssh_keys--pmc): Configure keys from GitHub of PMC members in the authorized_keys file 42 | * [`profiles::vpt`](#profiles--vpt): this profile will, in the future, instal Vox Pupuli Tasks 43 | 44 | #### Private Classes 45 | 46 | * `profiles::download_server::nginx`: Configures an nginx server to present the files under /var/mirror 47 | * `profiles::download_server::rclone`: Configures a server with rclone and mirrors data from OSL's S3-compatible buckets 48 | * `profiles::download_server::rsync`: Configures an rsync server to present the files under /var/mirror 49 | * `profiles::github_runners::ruby`: install ruby for GitHub self hosted runners 50 | * `profiles::puppet::code`: some resources to manage puppete code 51 | * `profiles::puppet::db`: installs puppetdb *on a puppetserver that also runs foreman* 52 | * `profiles::puppet::server_firewalling`: manages nft rules on Puppetserver/PuppetDB 53 | 54 | ### Defined types 55 | 56 | * [`profiles::certbot::nginx`](#profiles--certbot--nginx): configures location blocks for nginx 57 | 58 | ### Functions 59 | 60 | * [`profiles::update_ssh_authorized_keys`](#profiles--update_ssh_authorized_keys): generate ssh_authorized_key root entries for a list of github users 61 | 62 | ## Classes 63 | 64 | ### `profiles::base` 65 | 66 | ssh profile to manage basic stuff that doesn't fit into a dedicated profile 67 | 68 | #### Parameters 69 | 70 | The following parameters are available in the `profiles::base` class: 71 | 72 | * [`manage_borg`](#-profiles--base--manage_borg) 73 | 74 | ##### `manage_borg` 75 | 76 | Data type: `Boolean` 77 | 78 | whether borg should be installed or not 79 | 80 | Default value: `true` 81 | 82 | ### `profiles::borg` 83 | 84 | configures borg backups 85 | 86 | #### Parameters 87 | 88 | The following parameters are available in the `profiles::borg` class: 89 | 90 | * [`borg_excludes`](#-profiles--borg--borg_excludes) 91 | * [`borg_includes`](#-profiles--borg--borg_includes) 92 | * [`borg_compression`](#-profiles--borg--borg_compression) 93 | * [`borg_keep_yearly`](#-profiles--borg--borg_keep_yearly) 94 | * [`borg_keep_monthly`](#-profiles--borg--borg_keep_monthly) 95 | * [`borg_keep_weekly`](#-profiles--borg--borg_keep_weekly) 96 | * [`borg_keep_daily`](#-profiles--borg--borg_keep_daily) 97 | * [`borg_keep_within`](#-profiles--borg--borg_keep_within) 98 | * [`absolutebackupdestdir`](#-profiles--borg--absolutebackupdestdir) 99 | 100 | ##### `borg_excludes` 101 | 102 | Data type: `Array[Stdlib::Absolutepath]` 103 | 104 | paths you do not want to backup 105 | 106 | Default value: `[]` 107 | 108 | ##### `borg_includes` 109 | 110 | Data type: `Array[Stdlib::Absolutepath]` 111 | 112 | additional dirs you want to backup 113 | 114 | Default value: `[]` 115 | 116 | ##### `borg_compression` 117 | 118 | Data type: `String[1]` 119 | 120 | the desired compression algo for borg 121 | 122 | Default value: `'auto,zstd,6'` 123 | 124 | ##### `borg_keep_yearly` 125 | 126 | Data type: `Integer[0]` 127 | 128 | how many years to backup 129 | 130 | Default value: `3` 131 | 132 | ##### `borg_keep_monthly` 133 | 134 | Data type: `Integer[0]` 135 | 136 | how many months to backup 137 | 138 | Default value: `6` 139 | 140 | ##### `borg_keep_weekly` 141 | 142 | Data type: `Integer[0]` 143 | 144 | how many weeks to backup 145 | 146 | Default value: `4` 147 | 148 | ##### `borg_keep_daily` 149 | 150 | Data type: `Integer[0]` 151 | 152 | how many days to backup 153 | 154 | Default value: `14` 155 | 156 | ##### `borg_keep_within` 157 | 158 | Data type: `Integer[0]` 159 | 160 | how many days to keep all created backups 161 | 162 | Default value: `14` 163 | 164 | ##### `absolutebackupdestdir` 165 | 166 | Data type: `String[1]` 167 | 168 | the subdir on the backupserver where borg places backups 169 | 170 | Default value: `$facts['networking']['hostname']` 171 | 172 | ### `profiles::certbot` 173 | 174 | configures the certbot foo. Doesn't create certificates! 175 | 176 | ### `profiles::docker` 177 | 178 | installs docker 179 | 180 | ### `profiles::download_server` 181 | 182 | Setup a server to present Vox Pupuli's files and packages for download over 183 | http and rsync. The files being served are sourced from OSL's S3-compatible 184 | bucket via rclone. 185 | 186 | #### Parameters 187 | 188 | The following parameters are available in the `profiles::download_server` class: 189 | 190 | * [`server_names`](#-profiles--download_server--server_names) 191 | 192 | ##### `server_names` 193 | 194 | Data type: 195 | 196 | ```puppet 197 | Hash[Stdlib::Fqdn, Struct[ 198 | { 199 | locations => Hash, 200 | aliases => Optional[Array[Stdlib::Fqdn]] 201 | } 202 | ] 203 | ] 204 | ``` 205 | 206 | A hash of Nginx server names to create. Each top-level key is a FQDN and 207 | its value is made up of a location block and, optionally, a list of 208 | aliases by which the server should also be known. The value of the 209 | locations block is passed directly to a splat within an 210 | `nginx::resource::location` resource. 211 | 212 | ### `profiles::foreman` 213 | 214 | configure foreman + plugins 215 | 216 | * **See also** 217 | * `cat 218 | * /opt/puppetlabs/puppet/cache/foreman_cache_data/admin_password` provides the admin password 219 | 220 | ### `profiles::github_runners` 221 | 222 | configures a self-hosted github runner 223 | 224 | * **See also** 225 | * code 226 | * provided by CERN 227 | 228 | #### Parameters 229 | 230 | The following parameters are available in the `profiles::github_runners` class: 231 | 232 | * [`labels`](#-profiles--github_runners--labels) 233 | * [`user`](#-profiles--github_runners--user) 234 | * [`group`](#-profiles--github_runners--group) 235 | * [`version`](#-profiles--github_runners--version) 236 | * [`instances`](#-profiles--github_runners--instances) 237 | * [`repo_name`](#-profiles--github_runners--repo_name) 238 | * [`setup_ruby`](#-profiles--github_runners--setup_ruby) 239 | * [`setup_docker`](#-profiles--github_runners--setup_docker) 240 | * [`setup_libvirt`](#-profiles--github_runners--setup_libvirt) 241 | * [`runner_group`](#-profiles--github_runners--runner_group) 242 | * [`org_name`](#-profiles--github_runners--org_name) 243 | 244 | ##### `labels` 245 | 246 | Data type: `Array[String[1]]` 247 | 248 | the labels that we will assign 249 | 250 | Default value: `['self-hosted',]` 251 | 252 | ##### `user` 253 | 254 | Data type: `String[1]` 255 | 256 | the user that runs the runner 257 | 258 | Default value: `'runner'` 259 | 260 | ##### `group` 261 | 262 | Data type: `String[1]` 263 | 264 | the group that runs the runner 265 | 266 | Default value: `$user` 267 | 268 | ##### `version` 269 | 270 | Data type: `String[1]` 271 | 272 | version of the runner, matches their upstream github release names 273 | 274 | Default value: `'2.321.0'` 275 | 276 | ##### `instances` 277 | 278 | Data type: `Array[String[1]]` 279 | 280 | amount (and names) for all runners we create within one group 281 | 282 | Default value: `[]` 283 | 284 | ##### `repo_name` 285 | 286 | Data type: `Optional[String[1]]` 287 | 288 | set it to configure an repo-specific and not org specific runner 289 | 290 | Default value: `undef` 291 | 292 | ##### `setup_ruby` 293 | 294 | Data type: `Boolean` 295 | 296 | installs ruby for rspec-puppet unit tests 297 | 298 | Default value: `false` 299 | 300 | ##### `setup_docker` 301 | 302 | Data type: `Boolean` 303 | 304 | installs docker for beaker jobs 305 | 306 | Default value: `false` 307 | 308 | ##### `setup_libvirt` 309 | 310 | Data type: `Boolean` 311 | 312 | installs libvirt and adds the user to the group 313 | 314 | Default value: `false` 315 | 316 | ##### `runner_group` 317 | 318 | Data type: `Optional[String[1]]` 319 | 320 | the group that we will assign to the runners. Needs to exist 321 | 322 | Default value: `undef` 323 | 324 | ##### `org_name` 325 | 326 | Data type: `Enum['voxpupuli', 'openvoxproject']` 327 | 328 | name of the github org 329 | 330 | Default value: `'voxpupuli'` 331 | 332 | ### `profiles::grafana` 333 | 334 | installs grafana to display stats from dropsonde about Vox Pupuli modules 335 | 336 | * **See also** 337 | * https://dev.to/puppet/help-us-understand-how-you-use-open-source-puppet-51aj 338 | * https://grafana.com/tutorials/run-grafana-behind-a-proxy/ 339 | 340 | #### Parameters 341 | 342 | The following parameters are available in the `profiles::grafana` class: 343 | 344 | * [`postgresql_password`](#-profiles--grafana--postgresql_password) 345 | * [`postgresql_user`](#-profiles--grafana--postgresql_user) 346 | * [`postgresql_database`](#-profiles--grafana--postgresql_database) 347 | 348 | ##### `postgresql_password` 349 | 350 | Data type: `Variant[String[1],Sensitive]` 351 | 352 | 353 | 354 | Default value: `'023745uoehr98325yrsehrw023y4'` 355 | 356 | ##### `postgresql_user` 357 | 358 | Data type: `String[1]` 359 | 360 | 361 | 362 | Default value: `'grafana'` 363 | 364 | ##### `postgresql_database` 365 | 366 | Data type: `String[1]` 367 | 368 | 369 | 370 | Default value: `$postgresql_user` 371 | 372 | ### `profiles::lets_encrypt` 373 | 374 | Common Let's Encrypt settings 375 | 376 | ### `profiles::libvirt` 377 | 378 | installs libvirt 379 | 380 | ### `profiles::nftables` 381 | 382 | configure certain nftable rules 383 | 384 | #### Parameters 385 | 386 | The following parameters are available in the `profiles::nftables` class: 387 | 388 | * [`in_ssh`](#-profiles--nftables--in_ssh) 389 | * [`icmp`](#-profiles--nftables--icmp) 390 | * [`nat`](#-profiles--nftables--nat) 391 | * [`out_all`](#-profiles--nftables--out_all) 392 | 393 | ##### `in_ssh` 394 | 395 | Data type: `Boolean` 396 | 397 | allows incoming ssh connections 398 | 399 | Default value: `true` 400 | 401 | ##### `icmp` 402 | 403 | Data type: `Boolean` 404 | 405 | allow all ICMP traffic 406 | 407 | Default value: `true` 408 | 409 | ##### `nat` 410 | 411 | Data type: `Boolean` 412 | 413 | decide if the box should be allowed to handle NAT traffic 414 | 415 | Default value: `false` 416 | 417 | ##### `out_all` 418 | 419 | Data type: `Boolean` 420 | 421 | Allow all outbound connections 422 | 423 | Default value: `false` 424 | 425 | ### `profiles::nginx` 426 | 427 | multiple profiles requires nginx vhosts, this profile pulls in the nginx class/package/service setup 428 | 429 | ### `profiles::node_exporter` 430 | 431 | install node_exporter 432 | 433 | ### `profiles::postfix` 434 | 435 | installs postfix 436 | 437 | ### `profiles::postgres_exporter` 438 | 439 | installs a postgres exporter 440 | 441 | ### `profiles::postgresql` 442 | 443 | install latest postgresql with upstream repositories 444 | 445 | #### Parameters 446 | 447 | The following parameters are available in the `profiles::postgresql` class: 448 | 449 | * [`version`](#-profiles--postgresql--version) 450 | 451 | ##### `version` 452 | 453 | Data type: `Enum['11', '12', '13', '14', '15', '16', '17', '18']` 454 | 455 | desired postgresql version 456 | 457 | Default value: `'13'` 458 | 459 | ### `profiles::prometheus` 460 | 461 | install Prometheus 462 | 463 | ### `profiles::puppet` 464 | 465 | configure puppet agent and server 466 | 467 | #### Parameters 468 | 469 | The following parameters are available in the `profiles::puppet` class: 470 | 471 | * [`server`](#-profiles--puppet--server) 472 | * [`manage_msgpack`](#-profiles--puppet--manage_msgpack) 473 | 474 | ##### `server` 475 | 476 | Data type: `Boolean` 477 | 478 | decide if the server should be configured as well 479 | 480 | Default value: `($trusted['pp_role'] == 'puppetserver'` 481 | 482 | ##### `manage_msgpack` 483 | 484 | Data type: `Boolean` 485 | 486 | configure if we should install msgpack on the agent 487 | 488 | Default value: `($facts['os']['name'] != 'gentoo'` 489 | 490 | ### `profiles::puppetmodule` 491 | 492 | configures puppetmodule.info 493 | 494 | * **See also** 495 | * https://puppetmodule.info 496 | * https://github.com/puma/puma/blob/master/docs/nginx.md 497 | 498 | #### Parameters 499 | 500 | The following parameters are available in the `profiles::puppetmodule` class: 501 | 502 | * [`domain`](#-profiles--puppetmodule--domain) 503 | * [`postgresql_password`](#-profiles--puppetmodule--postgresql_password) 504 | * [`postgresql_database`](#-profiles--puppetmodule--postgresql_database) 505 | * [`postgresql_user`](#-profiles--puppetmodule--postgresql_user) 506 | 507 | ##### `domain` 508 | 509 | Data type: `Stdlib::Fqdn` 510 | 511 | the url to the site *without www* 512 | 513 | Default value: `'puppetmodule.info'` 514 | 515 | ##### `postgresql_password` 516 | 517 | Data type: `Variant[String[1],Sensitive]` 518 | 519 | the database password 520 | 521 | Default value: `'oehr384yhg034y5oreihu04y5'` 522 | 523 | ##### `postgresql_database` 524 | 525 | Data type: `String[1]` 526 | 527 | the database name 528 | 529 | Default value: `$postgresql_user` 530 | 531 | ##### `postgresql_user` 532 | 533 | Data type: `String[1]` 534 | 535 | the database user 536 | 537 | Default value: `'puppetmodule'` 538 | 539 | ### `profiles::redis` 540 | 541 | configures redis on different platforms 542 | 543 | ### `profiles::ssh` 544 | 545 | ssh profile to manage sshd + ssh keys 546 | 547 | ### `profiles::ssh_keys::additional_keys` 548 | 549 | Allow additional admins' keys to be pulled in via Hiera 550 | 551 | #### Parameters 552 | 553 | The following parameters are available in the `profiles::ssh_keys::additional_keys` class: 554 | 555 | * [`user_list`](#-profiles--ssh_keys--additional_keys--user_list) 556 | 557 | ##### `user_list` 558 | 559 | Data type: `Array[String[1]]` 560 | 561 | The list of users whose ssh keys should be pulled in. Each listed user will 562 | need to be represented by a manifest under `site/profiles/manifests/ssh_keys/people`. 563 | 564 | Default value: `[]` 565 | 566 | ### `profiles::ssh_keys::people::bastelfreak` 567 | 568 | Configure key from bastelfreak from GitHubs in the authorized_keys file along with supplemental keys 569 | 570 | ### `profiles::ssh_keys::people::binford2k` 571 | 572 | Configure key from binford2k from GitHubs in the authorized_keys file 573 | 574 | ### `profiles::ssh_keys::people::ekohl` 575 | 576 | Configure key from ekohl from GitHubs in the authorized_keys file along with supplemental keys 577 | 578 | ### `profiles::ssh_keys::people::genebean` 579 | 580 | configure key from genebean from GitHubs in the authorized_keys file 581 | 582 | ### `profiles::ssh_keys::people::nmburgan` 583 | 584 | configure key from nmburgan from GitHubs in the authorized_keys file 585 | 586 | ### `profiles::ssh_keys::people::rwaffen` 587 | 588 | configure key from rwaffen from GitHubs in the authorized_keys file 589 | 590 | ### `profiles::ssh_keys::people::sebastianrakel` 591 | 592 | Configure key from sebastianrakel from GitHubs in the authorized_keys file along with supplemental keys 593 | 594 | ### `profiles::ssh_keys::people::smortex` 595 | 596 | Configure key from smortex from GitHubs in the authorized_keys file along with supplemental keys 597 | 598 | ### `profiles::ssh_keys::pmc` 599 | 600 | Configure keys from GitHub of PMC members in the authorized_keys file. 601 | The PMC's member list is maintained in global.yaml and looked up directly. 602 | 603 | ### `profiles::vpt` 604 | 605 | this profile will, in the future, instal Vox Pupuli Tasks 606 | 607 | * **See also** 608 | * https://github.com/voxpupuli/vox-pupuli-tasks 609 | 610 | #### Parameters 611 | 612 | The following parameters are available in the `profiles::vpt` class: 613 | 614 | * [`deploy_sentry`](#-profiles--vpt--deploy_sentry) 615 | * [`deploy_vpt`](#-profiles--vpt--deploy_vpt) 616 | * [`deploy_kibana`](#-profiles--vpt--deploy_kibana) 617 | 618 | ##### `deploy_sentry` 619 | 620 | Data type: `Boolean` 621 | 622 | manage the sentry nginx config 623 | 624 | Default value: `true` 625 | 626 | ##### `deploy_vpt` 627 | 628 | Data type: `Boolean` 629 | 630 | manage the VPT nginx config 631 | 632 | Default value: `true` 633 | 634 | ##### `deploy_kibana` 635 | 636 | Data type: `Boolean` 637 | 638 | manage the kibana nginx config 639 | 640 | Default value: `true` 641 | 642 | ## Defined types 643 | 644 | ### `profiles::certbot::nginx` 645 | 646 | configures location blocks for nginx 647 | 648 | #### Parameters 649 | 650 | The following parameters are available in the `profiles::certbot::nginx` defined type: 651 | 652 | * [`domain`](#-profiles--certbot--nginx--domain) 653 | 654 | ##### `domain` 655 | 656 | Data type: `Stdlib::Fqdn` 657 | 658 | the domain for the location blocks 659 | 660 | Default value: `$title` 661 | 662 | ## Functions 663 | 664 | ### `profiles::update_ssh_authorized_keys` 665 | 666 | Type: Puppet Language 667 | 668 | generate ssh_authorized_key root entries for a list of github users 669 | 670 | #### `profiles::update_ssh_authorized_keys(Array[String[1]] $github_users)` 671 | 672 | The profiles::update_ssh_authorized_keys function. 673 | 674 | Returns: `Any` 675 | 676 | ##### `github_users` 677 | 678 | Data type: `Array[String[1]]` 679 | 680 | the list of users 681 | 682 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU AFFERO GENERAL PUBLIC LICENSE 2 | Version 3, 19 November 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | Preamble 9 | 10 | The GNU Affero General Public License is a free, copyleft license for 11 | software and other kinds of works, specifically designed to ensure 12 | cooperation with the community in the case of network server software. 13 | 14 | The licenses for most software and other practical works are designed 15 | to take away your freedom to share and change the works. By contrast, 16 | our General Public Licenses are intended to guarantee your freedom to 17 | share and change all versions of a program--to make sure it remains free 18 | software for all its users. 19 | 20 | When we speak of free software, we are referring to freedom, not 21 | price. Our General Public Licenses are designed to make sure that you 22 | have the freedom to distribute copies of free software (and charge for 23 | them if you wish), that you receive source code or can get it if you 24 | want it, that you can change the software or use pieces of it in new 25 | free programs, and that you know you can do these things. 26 | 27 | Developers that use our General Public Licenses protect your rights 28 | with two steps: (1) assert copyright on the software, and (2) offer 29 | you this License which gives you legal permission to copy, distribute 30 | and/or modify the software. 31 | 32 | A secondary benefit of defending all users' freedom is that 33 | improvements made in alternate versions of the program, if they 34 | receive widespread use, become available for other developers to 35 | incorporate. Many developers of free software are heartened and 36 | encouraged by the resulting cooperation. However, in the case of 37 | software used on network servers, this result may fail to come about. 38 | The GNU General Public License permits making a modified version and 39 | letting the public access it on a server without ever releasing its 40 | source code to the public. 41 | 42 | The GNU Affero General Public License is designed specifically to 43 | ensure that, in such cases, the modified source code becomes available 44 | to the community. It requires the operator of a network server to 45 | provide the source code of the modified version running there to the 46 | users of that server. Therefore, public use of a modified version, on 47 | a publicly accessible server, gives the public access to the source 48 | code of the modified version. 49 | 50 | An older license, called the Affero General Public License and 51 | published by Affero, was designed to accomplish similar goals. This is 52 | a different license, not a version of the Affero GPL, but Affero has 53 | released a new version of the Affero GPL which permits relicensing under 54 | this license. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | TERMS AND CONDITIONS 60 | 61 | 0. Definitions. 62 | 63 | "This License" refers to version 3 of the GNU Affero General Public License. 64 | 65 | "Copyright" also means copyright-like laws that apply to other kinds of 66 | works, such as semiconductor masks. 67 | 68 | "The Program" refers to any copyrightable work licensed under this 69 | License. Each licensee is addressed as "you". "Licensees" and 70 | "recipients" may be individuals or organizations. 71 | 72 | To "modify" a work means to copy from or adapt all or part of the work 73 | in a fashion requiring copyright permission, other than the making of an 74 | exact copy. The resulting work is called a "modified version" of the 75 | earlier work or a work "based on" the earlier work. 76 | 77 | A "covered work" means either the unmodified Program or a work based 78 | on the Program. 79 | 80 | To "propagate" a work means to do anything with it that, without 81 | permission, would make you directly or secondarily liable for 82 | infringement under applicable copyright law, except executing it on a 83 | computer or modifying a private copy. Propagation includes copying, 84 | distribution (with or without modification), making available to the 85 | public, and in some countries other activities as well. 86 | 87 | To "convey" a work means any kind of propagation that enables other 88 | parties to make or receive copies. Mere interaction with a user through 89 | a computer network, with no transfer of a copy, is not conveying. 90 | 91 | An interactive user interface displays "Appropriate Legal Notices" 92 | to the extent that it includes a convenient and prominently visible 93 | feature that (1) displays an appropriate copyright notice, and (2) 94 | tells the user that there is no warranty for the work (except to the 95 | extent that warranties are provided), that licensees may convey the 96 | work under this License, and how to view a copy of this License. If 97 | the interface presents a list of user commands or options, such as a 98 | menu, a prominent item in the list meets this criterion. 99 | 100 | 1. Source Code. 101 | 102 | The "source code" for a work means the preferred form of the work 103 | for making modifications to it. "Object code" means any non-source 104 | form of a work. 105 | 106 | A "Standard Interface" means an interface that either is an official 107 | standard defined by a recognized standards body, or, in the case of 108 | interfaces specified for a particular programming language, one that 109 | is widely used among developers working in that language. 110 | 111 | The "System Libraries" of an executable work include anything, other 112 | than the work as a whole, that (a) is included in the normal form of 113 | packaging a Major Component, but which is not part of that Major 114 | Component, and (b) serves only to enable use of the work with that 115 | Major Component, or to implement a Standard Interface for which an 116 | implementation is available to the public in source code form. A 117 | "Major Component", in this context, means a major essential component 118 | (kernel, window system, and so on) of the specific operating system 119 | (if any) on which the executable work runs, or a compiler used to 120 | produce the work, or an object code interpreter used to run it. 121 | 122 | The "Corresponding Source" for a work in object code form means all 123 | the source code needed to generate, install, and (for an executable 124 | work) run the object code and to modify the work, including scripts to 125 | control those activities. However, it does not include the work's 126 | System Libraries, or general-purpose tools or generally available free 127 | programs which are used unmodified in performing those activities but 128 | which are not part of the work. For example, Corresponding Source 129 | includes interface definition files associated with source files for 130 | the work, and the source code for shared libraries and dynamically 131 | linked subprograms that the work is specifically designed to require, 132 | such as by intimate data communication or control flow between those 133 | subprograms and other parts of the work. 134 | 135 | The Corresponding Source need not include anything that users 136 | can regenerate automatically from other parts of the Corresponding 137 | Source. 138 | 139 | The Corresponding Source for a work in source code form is that 140 | same work. 141 | 142 | 2. Basic Permissions. 143 | 144 | All rights granted under this License are granted for the term of 145 | copyright on the Program, and are irrevocable provided the stated 146 | conditions are met. This License explicitly affirms your unlimited 147 | permission to run the unmodified Program. The output from running a 148 | covered work is covered by this License only if the output, given its 149 | content, constitutes a covered work. This License acknowledges your 150 | rights of fair use or other equivalent, as provided by copyright law. 151 | 152 | You may make, run and propagate covered works that you do not 153 | convey, without conditions so long as your license otherwise remains 154 | in force. You may convey covered works to others for the sole purpose 155 | of having them make modifications exclusively for you, or provide you 156 | with facilities for running those works, provided that you comply with 157 | the terms of this License in conveying all material for which you do 158 | not control copyright. Those thus making or running the covered works 159 | for you must do so exclusively on your behalf, under your direction 160 | and control, on terms that prohibit them from making any copies of 161 | your copyrighted material outside their relationship with you. 162 | 163 | Conveying under any other circumstances is permitted solely under 164 | the conditions stated below. Sublicensing is not allowed; section 10 165 | makes it unnecessary. 166 | 167 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law. 168 | 169 | No covered work shall be deemed part of an effective technological 170 | measure under any applicable law fulfilling obligations under article 171 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or 172 | similar laws prohibiting or restricting circumvention of such 173 | measures. 174 | 175 | When you convey a covered work, you waive any legal power to forbid 176 | circumvention of technological measures to the extent such circumvention 177 | is effected by exercising rights under this License with respect to 178 | the covered work, and you disclaim any intention to limit operation or 179 | modification of the work as a means of enforcing, against the work's 180 | users, your or third parties' legal rights to forbid circumvention of 181 | technological measures. 182 | 183 | 4. Conveying Verbatim Copies. 184 | 185 | You may convey verbatim copies of the Program's source code as you 186 | receive it, in any medium, provided that you conspicuously and 187 | appropriately publish on each copy an appropriate copyright notice; 188 | keep intact all notices stating that this License and any 189 | non-permissive terms added in accord with section 7 apply to the code; 190 | keep intact all notices of the absence of any warranty; and give all 191 | recipients a copy of this License along with the Program. 192 | 193 | You may charge any price or no price for each copy that you convey, 194 | and you may offer support or warranty protection for a fee. 195 | 196 | 5. Conveying Modified Source Versions. 197 | 198 | You may convey a work based on the Program, or the modifications to 199 | produce it from the Program, in the form of source code under the 200 | terms of section 4, provided that you also meet all of these conditions: 201 | 202 | a) The work must carry prominent notices stating that you modified 203 | it, and giving a relevant date. 204 | 205 | b) The work must carry prominent notices stating that it is 206 | released under this License and any conditions added under section 207 | 7. This requirement modifies the requirement in section 4 to 208 | "keep intact all notices". 209 | 210 | c) You must license the entire work, as a whole, under this 211 | License to anyone who comes into possession of a copy. This 212 | License will therefore apply, along with any applicable section 7 213 | additional terms, to the whole of the work, and all its parts, 214 | regardless of how they are packaged. This License gives no 215 | permission to license the work in any other way, but it does not 216 | invalidate such permission if you have separately received it. 217 | 218 | d) If the work has interactive user interfaces, each must display 219 | Appropriate Legal Notices; however, if the Program has interactive 220 | interfaces that do not display Appropriate Legal Notices, your 221 | work need not make them do so. 222 | 223 | A compilation of a covered work with other separate and independent 224 | works, which are not by their nature extensions of the covered work, 225 | and which are not combined with it such as to form a larger program, 226 | in or on a volume of a storage or distribution medium, is called an 227 | "aggregate" if the compilation and its resulting copyright are not 228 | used to limit the access or legal rights of the compilation's users 229 | beyond what the individual works permit. Inclusion of a covered work 230 | in an aggregate does not cause this License to apply to the other 231 | parts of the aggregate. 232 | 233 | 6. Conveying Non-Source Forms. 234 | 235 | You may convey a covered work in object code form under the terms 236 | of sections 4 and 5, provided that you also convey the 237 | machine-readable Corresponding Source under the terms of this License, 238 | in one of these ways: 239 | 240 | a) Convey the object code in, or embodied in, a physical product 241 | (including a physical distribution medium), accompanied by the 242 | Corresponding Source fixed on a durable physical medium 243 | customarily used for software interchange. 244 | 245 | b) Convey the object code in, or embodied in, a physical product 246 | (including a physical distribution medium), accompanied by a 247 | written offer, valid for at least three years and valid for as 248 | long as you offer spare parts or customer support for that product 249 | model, to give anyone who possesses the object code either (1) a 250 | copy of the Corresponding Source for all the software in the 251 | product that is covered by this License, on a durable physical 252 | medium customarily used for software interchange, for a price no 253 | more than your reasonable cost of physically performing this 254 | conveying of source, or (2) access to copy the 255 | Corresponding Source from a network server at no charge. 256 | 257 | c) Convey individual copies of the object code with a copy of the 258 | written offer to provide the Corresponding Source. This 259 | alternative is allowed only occasionally and noncommercially, and 260 | only if you received the object code with such an offer, in accord 261 | with subsection 6b. 262 | 263 | d) Convey the object code by offering access from a designated 264 | place (gratis or for a charge), and offer equivalent access to the 265 | Corresponding Source in the same way through the same place at no 266 | further charge. You need not require recipients to copy the 267 | Corresponding Source along with the object code. If the place to 268 | copy the object code is a network server, the Corresponding Source 269 | may be on a different server (operated by you or a third party) 270 | that supports equivalent copying facilities, provided you maintain 271 | clear directions next to the object code saying where to find the 272 | Corresponding Source. Regardless of what server hosts the 273 | Corresponding Source, you remain obligated to ensure that it is 274 | available for as long as needed to satisfy these requirements. 275 | 276 | e) Convey the object code using peer-to-peer transmission, provided 277 | you inform other peers where the object code and Corresponding 278 | Source of the work are being offered to the general public at no 279 | charge under subsection 6d. 280 | 281 | A separable portion of the object code, whose source code is excluded 282 | from the Corresponding Source as a System Library, need not be 283 | included in conveying the object code work. 284 | 285 | A "User Product" is either (1) a "consumer product", which means any 286 | tangible personal property which is normally used for personal, family, 287 | or household purposes, or (2) anything designed or sold for incorporation 288 | into a dwelling. In determining whether a product is a consumer product, 289 | doubtful cases shall be resolved in favor of coverage. For a particular 290 | product received by a particular user, "normally used" refers to a 291 | typical or common use of that class of product, regardless of the status 292 | of the particular user or of the way in which the particular user 293 | actually uses, or expects or is expected to use, the product. A product 294 | is a consumer product regardless of whether the product has substantial 295 | commercial, industrial or non-consumer uses, unless such uses represent 296 | the only significant mode of use of the product. 297 | 298 | "Installation Information" for a User Product means any methods, 299 | procedures, authorization keys, or other information required to install 300 | and execute modified versions of a covered work in that User Product from 301 | a modified version of its Corresponding Source. The information must 302 | suffice to ensure that the continued functioning of the modified object 303 | code is in no case prevented or interfered with solely because 304 | modification has been made. 305 | 306 | If you convey an object code work under this section in, or with, or 307 | specifically for use in, a User Product, and the conveying occurs as 308 | part of a transaction in which the right of possession and use of the 309 | User Product is transferred to the recipient in perpetuity or for a 310 | fixed term (regardless of how the transaction is characterized), the 311 | Corresponding Source conveyed under this section must be accompanied 312 | by the Installation Information. But this requirement does not apply 313 | if neither you nor any third party retains the ability to install 314 | modified object code on the User Product (for example, the work has 315 | been installed in ROM). 316 | 317 | The requirement to provide Installation Information does not include a 318 | requirement to continue to provide support service, warranty, or updates 319 | for a work that has been modified or installed by the recipient, or for 320 | the User Product in which it has been modified or installed. Access to a 321 | network may be denied when the modification itself materially and 322 | adversely affects the operation of the network or violates the rules and 323 | protocols for communication across the network. 324 | 325 | Corresponding Source conveyed, and Installation Information provided, 326 | in accord with this section must be in a format that is publicly 327 | documented (and with an implementation available to the public in 328 | source code form), and must require no special password or key for 329 | unpacking, reading or copying. 330 | 331 | 7. Additional Terms. 332 | 333 | "Additional permissions" are terms that supplement the terms of this 334 | License by making exceptions from one or more of its conditions. 335 | Additional permissions that are applicable to the entire Program shall 336 | be treated as though they were included in this License, to the extent 337 | that they are valid under applicable law. If additional permissions 338 | apply only to part of the Program, that part may be used separately 339 | under those permissions, but the entire Program remains governed by 340 | this License without regard to the additional permissions. 341 | 342 | When you convey a copy of a covered work, you may at your option 343 | remove any additional permissions from that copy, or from any part of 344 | it. (Additional permissions may be written to require their own 345 | removal in certain cases when you modify the work.) You may place 346 | additional permissions on material, added by you to a covered work, 347 | for which you have or can give appropriate copyright permission. 348 | 349 | Notwithstanding any other provision of this License, for material you 350 | add to a covered work, you may (if authorized by the copyright holders of 351 | that material) supplement the terms of this License with terms: 352 | 353 | a) Disclaiming warranty or limiting liability differently from the 354 | terms of sections 15 and 16 of this License; or 355 | 356 | b) Requiring preservation of specified reasonable legal notices or 357 | author attributions in that material or in the Appropriate Legal 358 | Notices displayed by works containing it; or 359 | 360 | c) Prohibiting misrepresentation of the origin of that material, or 361 | requiring that modified versions of such material be marked in 362 | reasonable ways as different from the original version; or 363 | 364 | d) Limiting the use for publicity purposes of names of licensors or 365 | authors of the material; or 366 | 367 | e) Declining to grant rights under trademark law for use of some 368 | trade names, trademarks, or service marks; or 369 | 370 | f) Requiring indemnification of licensors and authors of that 371 | material by anyone who conveys the material (or modified versions of 372 | it) with contractual assumptions of liability to the recipient, for 373 | any liability that these contractual assumptions directly impose on 374 | those licensors and authors. 375 | 376 | All other non-permissive additional terms are considered "further 377 | restrictions" within the meaning of section 10. If the Program as you 378 | received it, or any part of it, contains a notice stating that it is 379 | governed by this License along with a term that is a further 380 | restriction, you may remove that term. If a license document contains 381 | a further restriction but permits relicensing or conveying under this 382 | License, you may add to a covered work material governed by the terms 383 | of that license document, provided that the further restriction does 384 | not survive such relicensing or conveying. 385 | 386 | If you add terms to a covered work in accord with this section, you 387 | must place, in the relevant source files, a statement of the 388 | additional terms that apply to those files, or a notice indicating 389 | where to find the applicable terms. 390 | 391 | Additional terms, permissive or non-permissive, may be stated in the 392 | form of a separately written license, or stated as exceptions; 393 | the above requirements apply either way. 394 | 395 | 8. Termination. 396 | 397 | You may not propagate or modify a covered work except as expressly 398 | provided under this License. Any attempt otherwise to propagate or 399 | modify it is void, and will automatically terminate your rights under 400 | this License (including any patent licenses granted under the third 401 | paragraph of section 11). 402 | 403 | However, if you cease all violation of this License, then your 404 | license from a particular copyright holder is reinstated (a) 405 | provisionally, unless and until the copyright holder explicitly and 406 | finally terminates your license, and (b) permanently, if the copyright 407 | holder fails to notify you of the violation by some reasonable means 408 | prior to 60 days after the cessation. 409 | 410 | Moreover, your license from a particular copyright holder is 411 | reinstated permanently if the copyright holder notifies you of the 412 | violation by some reasonable means, this is the first time you have 413 | received notice of violation of this License (for any work) from that 414 | copyright holder, and you cure the violation prior to 30 days after 415 | your receipt of the notice. 416 | 417 | Termination of your rights under this section does not terminate the 418 | licenses of parties who have received copies or rights from you under 419 | this License. If your rights have been terminated and not permanently 420 | reinstated, you do not qualify to receive new licenses for the same 421 | material under section 10. 422 | 423 | 9. Acceptance Not Required for Having Copies. 424 | 425 | You are not required to accept this License in order to receive or 426 | run a copy of the Program. Ancillary propagation of a covered work 427 | occurring solely as a consequence of using peer-to-peer transmission 428 | to receive a copy likewise does not require acceptance. However, 429 | nothing other than this License grants you permission to propagate or 430 | modify any covered work. These actions infringe copyright if you do 431 | not accept this License. Therefore, by modifying or propagating a 432 | covered work, you indicate your acceptance of this License to do so. 433 | 434 | 10. Automatic Licensing of Downstream Recipients. 435 | 436 | Each time you convey a covered work, the recipient automatically 437 | receives a license from the original licensors, to run, modify and 438 | propagate that work, subject to this License. You are not responsible 439 | for enforcing compliance by third parties with this License. 440 | 441 | An "entity transaction" is a transaction transferring control of an 442 | organization, or substantially all assets of one, or subdividing an 443 | organization, or merging organizations. If propagation of a covered 444 | work results from an entity transaction, each party to that 445 | transaction who receives a copy of the work also receives whatever 446 | licenses to the work the party's predecessor in interest had or could 447 | give under the previous paragraph, plus a right to possession of the 448 | Corresponding Source of the work from the predecessor in interest, if 449 | the predecessor has it or can get it with reasonable efforts. 450 | 451 | You may not impose any further restrictions on the exercise of the 452 | rights granted or affirmed under this License. For example, you may 453 | not impose a license fee, royalty, or other charge for exercise of 454 | rights granted under this License, and you may not initiate litigation 455 | (including a cross-claim or counterclaim in a lawsuit) alleging that 456 | any patent claim is infringed by making, using, selling, offering for 457 | sale, or importing the Program or any portion of it. 458 | 459 | 11. Patents. 460 | 461 | A "contributor" is a copyright holder who authorizes use under this 462 | License of the Program or a work on which the Program is based. The 463 | work thus licensed is called the contributor's "contributor version". 464 | 465 | A contributor's "essential patent claims" are all patent claims 466 | owned or controlled by the contributor, whether already acquired or 467 | hereafter acquired, that would be infringed by some manner, permitted 468 | by this License, of making, using, or selling its contributor version, 469 | but do not include claims that would be infringed only as a 470 | consequence of further modification of the contributor version. For 471 | purposes of this definition, "control" includes the right to grant 472 | patent sublicenses in a manner consistent with the requirements of 473 | this License. 474 | 475 | Each contributor grants you a non-exclusive, worldwide, royalty-free 476 | patent license under the contributor's essential patent claims, to 477 | make, use, sell, offer for sale, import and otherwise run, modify and 478 | propagate the contents of its contributor version. 479 | 480 | In the following three paragraphs, a "patent license" is any express 481 | agreement or commitment, however denominated, not to enforce a patent 482 | (such as an express permission to practice a patent or covenant not to 483 | sue for patent infringement). To "grant" such a patent license to a 484 | party means to make such an agreement or commitment not to enforce a 485 | patent against the party. 486 | 487 | If you convey a covered work, knowingly relying on a patent license, 488 | and the Corresponding Source of the work is not available for anyone 489 | to copy, free of charge and under the terms of this License, through a 490 | publicly available network server or other readily accessible means, 491 | then you must either (1) cause the Corresponding Source to be so 492 | available, or (2) arrange to deprive yourself of the benefit of the 493 | patent license for this particular work, or (3) arrange, in a manner 494 | consistent with the requirements of this License, to extend the patent 495 | license to downstream recipients. "Knowingly relying" means you have 496 | actual knowledge that, but for the patent license, your conveying the 497 | covered work in a country, or your recipient's use of the covered work 498 | in a country, would infringe one or more identifiable patents in that 499 | country that you have reason to believe are valid. 500 | 501 | If, pursuant to or in connection with a single transaction or 502 | arrangement, you convey, or propagate by procuring conveyance of, a 503 | covered work, and grant a patent license to some of the parties 504 | receiving the covered work authorizing them to use, propagate, modify 505 | or convey a specific copy of the covered work, then the patent license 506 | you grant is automatically extended to all recipients of the covered 507 | work and works based on it. 508 | 509 | A patent license is "discriminatory" if it does not include within 510 | the scope of its coverage, prohibits the exercise of, or is 511 | conditioned on the non-exercise of one or more of the rights that are 512 | specifically granted under this License. You may not convey a covered 513 | work if you are a party to an arrangement with a third party that is 514 | in the business of distributing software, under which you make payment 515 | to the third party based on the extent of your activity of conveying 516 | the work, and under which the third party grants, to any of the 517 | parties who would receive the covered work from you, a discriminatory 518 | patent license (a) in connection with copies of the covered work 519 | conveyed by you (or copies made from those copies), or (b) primarily 520 | for and in connection with specific products or compilations that 521 | contain the covered work, unless you entered into that arrangement, 522 | or that patent license was granted, prior to 28 March 2007. 523 | 524 | Nothing in this License shall be construed as excluding or limiting 525 | any implied license or other defenses to infringement that may 526 | otherwise be available to you under applicable patent law. 527 | 528 | 12. No Surrender of Others' Freedom. 529 | 530 | If conditions are imposed on you (whether by court order, agreement or 531 | otherwise) that contradict the conditions of this License, they do not 532 | excuse you from the conditions of this License. If you cannot convey a 533 | covered work so as to satisfy simultaneously your obligations under this 534 | License and any other pertinent obligations, then as a consequence you may 535 | not convey it at all. For example, if you agree to terms that obligate you 536 | to collect a royalty for further conveying from those to whom you convey 537 | the Program, the only way you could satisfy both those terms and this 538 | License would be to refrain entirely from conveying the Program. 539 | 540 | 13. Remote Network Interaction; Use with the GNU General Public License. 541 | 542 | Notwithstanding any other provision of this License, if you modify the 543 | Program, your modified version must prominently offer all users 544 | interacting with it remotely through a computer network (if your version 545 | supports such interaction) an opportunity to receive the Corresponding 546 | Source of your version by providing access to the Corresponding Source 547 | from a network server at no charge, through some standard or customary 548 | means of facilitating copying of software. This Corresponding Source 549 | shall include the Corresponding Source for any work covered by version 3 550 | of the GNU General Public License that is incorporated pursuant to the 551 | following paragraph. 552 | 553 | Notwithstanding any other provision of this License, you have 554 | permission to link or combine any covered work with a work licensed 555 | under version 3 of the GNU General Public License into a single 556 | combined work, and to convey the resulting work. The terms of this 557 | License will continue to apply to the part which is the covered work, 558 | but the work with which it is combined will remain governed by version 559 | 3 of the GNU General Public License. 560 | 561 | 14. Revised Versions of this License. 562 | 563 | The Free Software Foundation may publish revised and/or new versions of 564 | the GNU Affero General Public License from time to time. Such new versions 565 | will be similar in spirit to the present version, but may differ in detail to 566 | address new problems or concerns. 567 | 568 | Each version is given a distinguishing version number. If the 569 | Program specifies that a certain numbered version of the GNU Affero General 570 | Public License "or any later version" applies to it, you have the 571 | option of following the terms and conditions either of that numbered 572 | version or of any later version published by the Free Software 573 | Foundation. If the Program does not specify a version number of the 574 | GNU Affero General Public License, you may choose any version ever published 575 | by the Free Software Foundation. 576 | 577 | If the Program specifies that a proxy can decide which future 578 | versions of the GNU Affero General Public License can be used, that proxy's 579 | public statement of acceptance of a version permanently authorizes you 580 | to choose that version for the Program. 581 | 582 | Later license versions may give you additional or different 583 | permissions. However, no additional obligations are imposed on any 584 | author or copyright holder as a result of your choosing to follow a 585 | later version. 586 | 587 | 15. Disclaimer of Warranty. 588 | 589 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY 590 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT 591 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY 592 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, 593 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 594 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM 595 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF 596 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 597 | 598 | 16. Limitation of Liability. 599 | 600 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 601 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS 602 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 603 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE 604 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF 605 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD 606 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), 607 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF 608 | SUCH DAMAGES. 609 | 610 | 17. Interpretation of Sections 15 and 16. 611 | 612 | If the disclaimer of warranty and limitation of liability provided 613 | above cannot be given local legal effect according to their terms, 614 | reviewing courts shall apply local law that most closely approximates 615 | an absolute waiver of all civil liability in connection with the 616 | Program, unless a warranty or assumption of liability accompanies a 617 | copy of the Program in return for a fee. 618 | 619 | END OF TERMS AND CONDITIONS 620 | 621 | How to Apply These Terms to Your New Programs 622 | 623 | If you develop a new program, and you want it to be of the greatest 624 | possible use to the public, the best way to achieve this is to make it 625 | free software which everyone can redistribute and change under these terms. 626 | 627 | To do so, attach the following notices to the program. It is safest 628 | to attach them to the start of each source file to most effectively 629 | state the exclusion of warranty; and each file should have at least 630 | the "copyright" line and a pointer to where the full notice is found. 631 | 632 | 633 | Copyright (C) 634 | 635 | This program is free software: you can redistribute it and/or modify 636 | it under the terms of the GNU Affero General Public License as published 637 | by the Free Software Foundation, either version 3 of the License, or 638 | (at your option) any later version. 639 | 640 | This program is distributed in the hope that it will be useful, 641 | but WITHOUT ANY WARRANTY; without even the implied warranty of 642 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 643 | GNU Affero General Public License for more details. 644 | 645 | You should have received a copy of the GNU Affero General Public License 646 | along with this program. If not, see . 647 | 648 | Also add information on how to contact you by electronic and paper mail. 649 | 650 | If your software can interact with users remotely through a computer 651 | network, you should also make sure that it provides a way for users to 652 | get its source. For example, if your program is a web application, its 653 | interface could display a "Source" link that leads users to an archive 654 | of the code. There are many ways you could offer source, and different 655 | solutions will be better for different programs; see section 13 for the 656 | specific requirements. 657 | 658 | You should also get your employer (if you work as a programmer) or school, 659 | if any, to sign a "copyright disclaimer" for the program, if necessary. 660 | For more information on this, and how to apply and follow the GNU AGPL, see 661 | . 662 | --------------------------------------------------------------------------------