Mercurial > repos > other > Puppet
view manifests/templates.pp @ 292:3e04f35dd0af
Turn Fail2ban setup into a module
We now:
* Don't have a large class outside a module
* Build "bad SSH users" config from a list
(easier to understand/see diffs in than a long line)
* Use modern EPP files
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 18 Jan 2020 15:17:03 +0000 |
parents | 1182a180085d |
children | 61e90445c899 |
line wrap: on
line source
# Make sure packages come after their repos File<| tag == 'repo-config' |> -> YumRepo<| |> -> Package<| |> # Make sure all files are in place before starting services File<| tag != 'post-service' |> -> Service<| |> # Set some shortcut variables #$os = $operatingsystem $osver = $operatingsystemmajrelease $server = '' class basenode { include sudo include defaultusers include logwatch file { '/etc/puppet/hiera.yaml': ensure => present, content => " # Let the system set defaults version: 5 ", } } class basevpsnode ( $primary_ip, $secondary_ip, $mailserver, $imapserver, $firewall_cmd = 'iptables', ) { if $firewall_cmd == 'iptables' { include vpsfirewall } #VPS is a self-mastered Puppet machine, so bodge a Hosts file file { '/etc/hosts': ensure => present, content => "127.0.0.1 localhost $primary_ip ${fqdn}", } require repos include basenode include privat include dnsresolver include ssh::server include vcs::server include vcs::client class { 'webserver': primary_ip => $primary_ip, secondary_ip => $secondary_ip, } include cronjobs include logrotate class { 'fail2ban': firewall_cmd => $firewall_cmd, } include tools class { 'email': mailserver => $mailserver, imapserver => $imapserver, } } ## Classes to allow facet behaviour using preconfigured setups of classes class vpsfirewall { resources { "firewall": purge => false, } firewallchain { 'INPUT:filter:IPv4': purge => true, ignore => [ '-j f2b-[^ ]+$', '^(:|-A )f2b-', '--comment "Great Firewall of China"', '--comment "Do not purge', ], } Firewall { before => Class['my_fw::post'], require => Class['my_fw::pre'], } class { ['my_fw::pre', 'my_fw::post']: } class { 'firewall': } firewall { '010 Whitelist Googlebot': source => '66.249.64.0/19', dport => [80,443], proto => tcp, action => accept, } # Block a spammer hitting our contact forms (also on StopForumSpam list A LOT) firewall { '099 Blacklist spammers 1': source => '107.181.78.172', dport => [80, 443], proto => tcp, action => 'reject', } firewall { '099 Blacklist IODC bot': # IODC bot makes too many bad requests, and contact form is broken # They don't publish a robots.txt name, so firewall it! source => '86.153.145.149', dport => [ 80, 443 ], proto => tcp, action => 'reject', } firewall { '099 Blacklist Baidu Brazil': #Baidu got a Brazilian netblock and are hitting us hard #Baidu doesn't honour "crawl-delay" in robots.txt #Baidu gets firewalled source => '131.161.8.0/22', dport => [ 80, 443 ], proto => tcp, action => 'reject', } firewallchain { 'GREATFIREWALLOFCHINA:filter:IPv4': ensure => present, } firewall { '050 Check our Great Firewall Against China': chain => 'INPUT', jump => 'GREATFIREWALLOFCHINA', } firewallchain { 'Fail2Ban:filter:IPv4': ensure => present, } firewall { '060 Check Fail2Ban': chain => 'INPUT', jump => 'Fail2Ban', } firewall { '100 allow https and http': dport => [80, 443], proto => tcp, action => accept, } firewall { '101 allow SMTP': dport => [25, 465], proto => tcp, action => accept, } firewall { '102 allow IMAPS': dport => 993, proto => tcp, action => accept, } # Note: SSH port will be managed separately as we # put it on a different port to hide from script kiddy noise } class dnsresolver { package { 'unbound': ensure => present, } package { 'named': ensure => absent, } service { 'named': ensure => stopped, enable => false, } service { 'unbound': ensure => running, enable => true, } file { '/etc/named.conf': ensure => absent, } file { '/etc/unbound/unbound.conf': ensure => present, source => [ "puppet:///common/unbound.conf-${::hostname}", "puppet:///common/unbound.conf", ], group => 'named', require => Package['unbound'], notify => Service['unbound'], } file { '/etc/NetworkManager/conf.d/local-dns-resolver.conf': ensure => present, content => "[main] dns=none", } file { '/etc/sysconfig/named': ensure => absent, } file { '/etc/resolv.conf': ensure => present, content => "nameserver 127.0.0.1", require => Service['unbound'], tag => 'post-service', } } class repos { yumrepo { 'epel': mirrorlist => 'https://mirrors.fedoraproject.org/metalink?repo=epel-$releasever&arch=$basearch', descr => "Extra Packages for Enterprise Linux", enabled => 1, failovermethod => 'priority', gpgcheck => 1, gpgkey => "file:///etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-$osver", } file { "/etc/pki/rpm-gpg/RPM-GPG-KEY-EPEL-$osver": ensure => present, source => "puppet:///common/RPM-GPG-KEY-EPEL-$osver", tag => 'repo-config', } yumrepo { 'ibboard': baseurl => 'https://download.opensuse.org/repositories/home:/IBBoard:/server/CentOS_$releasever/', descr => 'Extra packages from IBBoard', enabled => 1, gpgcheck => 1, gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-ibboard', } file { '/etc/pki/rpm-gpg/RPM-GPG-KEY-ibboard': ensure => present, source => 'puppet:///common/RPM-GPG-KEY-ibboard', tag => 'repo-config', } yumrepo { 'webtatic': ensure => absent, } file { '/etc/pki/rpm-gpg/RPM-GPG-KEY-webtatic-andy': ensure => absent, } file { '/etc/pki/rpm-gpg/RPM-GPG-KEY-webtatic-el7': ensure => absent, } # Install Pip and symlink it so we can use it as a package provider package { 'python2-pip': ensure => installed; } -> file { '/usr/bin/pip-python': ensure => link, target => '/usr/bin/pip', } -> Package <| provider == 'pip' |> } class tools { $packages = [ 'sqlite', 'bash-completion', 'nano', 'bzip2', 'mlocate', 'patch', 'tmux' ] package { $packages: ensure => installed; } } class logrotate { package { 'logrotate': ensure => installed; } file { '/etc/logrotate.d/httpd': ensure => present, source => 'puppet:///common/logrotate-httpd', require => Package['logrotate'], } file { '/etc/logrotate.d/trac': ensure => present, source => 'puppet:///common/logrotate-trac', require => Package['logrotate'], } } class logwatch { package { 'logwatch': ensure => installed; } File { ensure => present, require => Package['logwatch'], } file { '/etc/cron.daily/0logwatch': source => 'puppet:///common/0logwatch'; } file { '/etc/logwatch/scripts/shared/': ensure => directory, } file { '/etc/logwatch/scripts/services/fail2ban': source => 'puppet:///common/logwatch/services-fail2ban', } file { '/etc/logwatch/scripts/services/named': source => 'puppet:///common/logwatch/named', } file { '/etc/logwatch/scripts/services/http-error': source => 'puppet:///common/logwatch/http-error', } file { '/etc/logwatch/scripts/services/php': source => 'puppet:///common/logwatch/scripts_php', } file { '/etc/logwatch/scripts/services/mysql': source => 'puppet:///common/logwatch/scripts_mysql', } file { '/etc/logwatch/scripts/services/dovecot': source => 'puppet:///common/logwatch/dovecot', } file { '/etc/logwatch/scripts/services/postfix': source => 'puppet:///common/logwatch/postfix', } file { '/etc/logwatch/scripts/shared/applyhttperrordate': source => 'puppet:///common/logwatch/applyhttperrordate', } file { '/etc/logwatch/conf/logwatch.conf': content => 'Detail = Med', } file { '/etc/logwatch/conf/logfiles/http.conf': content => 'LogFile = apache/access_*.log', } file { '/etc/logwatch/conf/logfiles/http-error-24.conf': source => 'puppet:///common/logwatch/log-http-error.conf', } file { '/etc/logwatch/conf/logfiles/http-error.conf': ensure=> absent, } file { '/etc/logwatch/conf/services/http-error.conf': source => 'puppet:///common/logwatch/services-http-error.conf', } file { '/etc/logwatch/conf/logfiles/php.conf': source => 'puppet:///common/logwatch/logfiles_php.conf', } file { '/etc/logwatch/conf/services/php.conf': source => 'puppet:///common/logwatch/services_php.conf', } file { '/etc/logwatch/conf/logfiles/mysql.conf': source => 'puppet:///common/logwatch/logfiles_mysql.conf', } file { '/etc/logwatch/conf/services/mysql.conf': source => 'puppet:///common/logwatch/services_mysql.conf', } } #Our web server with our configs, not just a stock one class webserver ( $primary_ip, $secondary_ip, ) { #Setup base website parameters class { 'website': base_dir => '/srv/sites', primary_ip => $primary_ip, secondary_ip => $secondary_ip, default_owner => $defaultusers::default_user, default_group => $defaultusers::default_user, default_tld => 'co.uk', default_extra_tlds => [ 'com' ], } # Use Remi's PHP 7.3 for now - 7.4 is still VERY new $php_suffix = '' if $operatingsystem == 'CentOS' and versioncmp($operatingsystemrelease, '8') >= 0 { yumrepo { 'remirepo-safe': mirrorlist => 'http://cdn.remirepo.net/enterprise/$releasever/safe/$basearch/mirror', descr => "Extra CentOS packages from Remi", enabled => 1, failovermethod => 'priority', gpgcheck => 1, gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi', } yumrepo { 'remirepo-php': mirrorlist => 'http://cdn.remirepo.net/enterprise/$releasever/php73/$basearch/mirror', descr => "PHP7.3 for CentOS from Remi", enabled => 1, failovermethod => 'priority', gpgcheck => 1, gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi', } } else { yumrepo { 'remirepo-safe': mirrorlist => 'http://cdn.remirepo.net/enterprise/$releasever/safe/mirror', descr => "Extra CentOS packages from Remi", enabled => 1, failovermethod => 'priority', gpgcheck => 1, gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi', } yumrepo { 'remirepo-php': mirrorlist => 'http://cdn.remirepo.net/enterprise/$releasever/php73/mirror', descr => "PHP7.3 for CentOS from Remi", enabled => 1, failovermethod => 'priority', gpgcheck => 1, gpgkey => 'file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi', } } file { '/etc/pki/rpm-gpg/RPM-GPG-KEY-remi': ensure => present, source => 'puppet:///common/RPM-GPG-KEY-remi', before => YumRepo['remirepo-php'], } #Configure the PHP version to use class { 'website::php': suffix => $php_suffix, opcache => 'opcache', extras => [ 'process', 'intl', 'pecl-imagick', 'bcmath', 'pecl-zip' ], } #Setup MySQL, using (private) templates to make sure that we set non-std passwords and a default user if $operatingsystem == 'CentOS' and versioncmp($operatingsystemrelease, '7') >= 0 { $mysqlpackage = 'mariadb' $mysqlsuffix = '' # Required for SELinux rule setting/status checks if versioncmp($operatingsystemrelease, '8') >= 0 { $semanage_package_name = 'policycoreutils-python-utils' } else { $semanage_package_name = 'policycoreutils-python' } package { 'policycoreutils-python': name => $semanage_package_name, ensure => present, } $extra_packages = [ 'subversion-python', #Required for Trac 'perl-Sys-Syslog', #Required for Perl SPF checking ] package { $extra_packages: ensure => installed } } else { $mysqlpackage = 'mysql' $mysqlsuffix = '55w' } class { 'website::mysql': mysqluser => template('defaultusers/mysql-user'), mysqlpassword => template('defaultusers/mysql-password'), mysqlprefix => $mysqlpackage, mysqlsuffix => $mysqlsuffix, phpsuffix => $php_suffix, phpmysqlsuffix => 'nd' } } class ibboardvpsnode ( $primary_ip, $secondary_ip = $primary_ip, $mailserver, $imapserver, $firewall_cmd = 'iptables', ){ class { 'basevpsnode': primary_ip => $primary_ip, secondary_ip => $secondary_ip, mailserver => $mailserver, imapserver => $imapserver, firewall_cmd => $firewall_cmd, } # Set timezone to something sensible file { "/etc/localtime": ensure => 'link', target => '/usr/share/zoneinfo/Europe/London', } # Common modules used by multiple sites (mod_auth_basic is safe because we HTTPS all the things) $mods = [ 'auth_basic', 'authn_file', 'authz_user', 'deflate', 'xsendfile' ] apache::mod { $mods:; } if $operatingsystem == 'CentOS' and versioncmp($operatingsystemrelease, '7') >= 0 { apache::mod { 'authn_core':; } } #Configure our sites, using templates for the custom fragments where the extra content is too long include adminsite website::https::multitld { 'www.ibboard': custom_fragment => template("privat/apache/ibboard.fragment"), letsencrypt_name => 'ibboard.co.uk', csp_override => { "report-uri" => "https://ibboard.report-uri.com/r/d/csp/enforce", "default-src" => "'none'", "img-src" => "'self' https://live.staticflickr.com/", "script-src" => "'self'", "style-src" => "'self'", "font-src" => "'self'", "form-action" => "'self'", "connect-src" => "'self'", } } include hiveworldterrasite include bdstrikesite include devsite website::https::multitld { 'www.abiknight': custom_fragment => "$website::htmlphpfragment ErrorDocument 404 /error.php", letsencrypt_name => 'abiknight.co.uk', } include webmailpimsite } class adminsite{ apache::mod { 'info':; 'status':; 'cgi':; } website::https::multitld { 'admin.ibboard': force_no_index => false, ssl_ca_chain => '', custom_fragment => template("privat/apache/admin.fragment"), } cron { 'loadavg': command => '/usr/local/bin/run-loadavg-logger', user => apache, minute => '*/6' } cron { 'awstats': command => '/usr/local/bin/update-awstats > /srv/sites/admin/awstats.log', user => apache, hour => '*/6', minute => '0' } } class hiveworldterrasite { website::https::multitld { 'www.hiveworldterra': force_no_www => false, letsencrypt_name => 'hiveworldterra.co.uk', custom_fragment => template("privat/apache/hwt.fragment"), } website::https::multitld { 'forums.hiveworldterra': letsencrypt_name => 'hiveworldterra.co.uk', custom_fragment => template("privat/apache/forums.fragment"), } website::https::multitld { 'skins.hiveworldterra': letsencrypt_name => 'hiveworldterra.co.uk', custom_fragment => template("privat/apache/skins.fragment"), } website::https::redir { 'hiveworldterra.ibboard.co.uk': redir => 'https://www.hiveworldterra.co.uk/', docroot => "${website::basedir}/hiveworldterra", letsencrypt_name => 'hiveworldterra.co.uk', separate_log => true, } } class bdstrikesite { website::https::multitld { 'www.bdstrike': docroot_owner => $defaultusers::secondary_user, docroot_group => 'editors', letsencrypt_name => 'bdstrike.co.uk', custom_fragment => template("privat/apache/bdstrike.fragment"), csp_override => {"frame-ancestors" => "'self'"}, csp_report_override => { "font-src" => "'self' https://fonts.gstatic.com/", "img-src" => "'self' https://secure.gravatar.com/", "style-src" => "'self' https://fonts.googleapis.com/" }, } $aliases = [ 'strikecreations.co.uk', 'strikecreations.com', 'www.strikecreations.com' ] website::https::redir { 'www.strikecreations.co.uk': redir => 'https://bdstrike.co.uk/', serveraliases => $aliases, docroot => "${website::basedir}/bdstrike", docroot_owner => $defaultusers::secondary_user, docroot_group => 'editors', letsencrypt_name => 'bdstrike.co.uk', separate_log => true, } cron { 'wordpress_cron': # Run "php -f wp-cron.php" on a schedule so that we can auto-update # without giving Apache full write access! command => "/usr/local/bin/bdstrike-cron", user => $defaultusers::default_user, minute => '*/15', } } class devsite { if versioncmp($operatingsystemrelease, '8') >= 0 { # Apache::Mod doesn't map this correctly for CentOS 8 yet $mod_wsgi_lib = 'mod_wsgi_python3.so' } else { $mod_wsgi_lib = undef } apache::mod { # mod_wsgi for Python support 'wsgi': lib => $mod_wsgi_lib, } include python::venv # Create Python virtualenvs for the dev site apps python::venv::isolate { "/srv/rhodecode/virtualenv":; "/srv/trac/virtualenv":; } # Graphviz for Trac "master ticket" graphs package { 'graphviz': ensure => installed, } website::https::multitld { 'www.warfoundry': letsencrypt_name => 'warfoundry.co.uk', custom_fragment => template("privat/apache/warfoundry.fragment"), } website::https::multitld { 'dev.ibboard': #Make sure we're the first one hit for the tiny fraction of "no support" cases we care about (potentially Python for Mercurial!) # http://en.wikipedia.org/wiki/Server_Name_Indication#No_support priority => 1, letsencrypt_name => 'dev.ibboard.co.uk', custom_fragment => template("privat/apache/dev.fragment"), force_no_index => false, } } class webmailpimsite { # Webmail and Personal Information Management (PIM) sites website::https { 'webmail.ibboard.co.uk': force_no_index => false, ssl_ca_chain => '', custom_fragment => template("privat/apache/webmail.fragment"), } website::https { 'pim.ibboard.co.uk': docroot_owner => 'apache', docroot_group => 'editors', force_no_index => false, lockdown_requests => false, ssl_ca_chain => '', csp => false, csp_report => false, custom_fragment => template("privat/apache/pim.fragment"), } cron { 'owncloudcron': command => "/usr/local/bin/owncloud-cron", user => 'apache', minute => '*/15', } } class email ( $mailserver, $imapserver, ){ class { 'postfix': mailserver => $mailserver, protocols => 'ipv4', } class { 'dovecot': imapserver => $imapserver, } # Unspecified SpamAssassin config dependencies that started # showing up as errors in our logs package { ['perl-File-MimeInfo', 'perl-IO-Compress-Lzma']: ensure => installed, } package { [ 'amavisd-new' ]: ensure => installed, tag => 'av', } service { 'amavisd': ensure => 'running', enable => 'true', } file { '/etc/amavisd/amavisd.conf': ensure => present, source => 'puppet:///private/postfix/amavisd.conf', tag => 'av', } file { '/etc/mail/spamassassin/local.cf': ensure => present, source => 'puppet:///private/postfix/spamassassin-local.cf', tag => 'av', } file { '/etc/mail/spamassassin/ole2macro.cf': ensure => present, source => 'puppet:///common/ole2macro.cf', tag => 'av', } file { '/etc/mail/spamassassin/ole2macro.pm': ensure => present, source => 'puppet:///common/spamassassin-vba-macro-master/ole2macro.pm', tag => 'av', } Package<| tag == 'av' |> -> File<| tag == 'av' |> File<| tag == 'av' |> { notify => Service['amavisd'], } cron { 'Postwhite': command => "/usr/local/bin/postwhite 2>&1| grep -vE '^(Starting|Recursively|Getting|Querying|Removing|Sorting|$)'", user => 'root', weekday => 0, hour => 2, minute => 0, } } class cronjobs { # Add Mutt for scripts that send emails, but stop it clogging the disk by keeping copies of emails package { 'mutt': ensure => installed, } file { '/etc/Muttrc.local': content => 'set copy = no', require => Package['mutt'], } # General server-wide cron jobs Cron { user => 'root' } cron { 'backupalldbs': command => "/usr/local/bin/backupalldbs", monthday => "*/2", hour => "4", minute => "9" } cron { 'greatfirewallofchina': command => '/usr/local/bin/update-great-firewall-of-china', hour => 3, minute => 30 } cron { 'permissions': command => '/usr/local/bin/set-permissions', hour => 3, minute => 2 } # Since we're only managing the local server, use our script that wraps "puppet apply" instead of PuppetMaster cron { 'puppet': command => '/usr/local/bin/puppet-apply | grep -v "Compiled catalog for\|Finished catalog run in\|Applied catalog in"', hour => '*/6', minute => 5 } cron { 'purgecaches': command => "/usr/local/bin/purge-caches", hour => '4', minute => '15', weekday => '1', } # Notify of uncommitted files cron { 'check-mercurial-committed': command => "/usr/local/bin/check-hg-status", hour => '4', minute => '20', weekday => '0-6/3', #Sunday, Wednesday and Saturday morning } # Notify of available updates cron { 'check-yum-updates': command => '/usr/bin/yum check-updates | tail -2 | grep -Ev "^ \* [[:alnum:]-]+: [[:alnum:]\.]+$"', hour => '4', minute => '30', weekday => '0-6/3', #Sunday, Wednesday and Saturday morning } # And check whether anything needs restarting cron { 'check-needs-restarting': command => '/usr/bin/needs-restarting|grep -v "/usr/lib/systemd\|/usr/sbin/lvmetad\|/usr/lib/polkit-1/polkitd"', hour => '4', minute => '45', weekday => '0-6/3', #Sunday, Wednesday and Saturday morning } }