From eaa6b99c5e9c3d03937ee950cd2976486290a553 Mon Sep 17 00:00:00 2001 From: Paul Lecuq Date: Fri, 10 Jul 2020 00:58:55 +0200 Subject: [PATCH] initial commit --- .gitignore | 6 + README.md | 117 +++ cloud/cloud | 3 + cloud/cloud.profiles | 20 + cloud/cloud.providers | 14 + cloud/scaleway/scw01-ams.paulbsd.com | 6 + cloud/scaleway/scw_lamp.map | 7 + cloud/scaleway/scw_web.map | 16 + config/master | 80 ++ config/master.sample | 867 ++++++++++++++++++ config/minion | 10 + config/minion.sample | 781 ++++++++++++++++ config/roster | 17 + config/roster.sample | 11 + scripts/encrypt_password | 10 + scripts/salt-test.sh | 2 + states/_modules/custom.py | 19 + states/_modules/dkron.py | 28 + states/_modules/ovhapi.py | 153 ++++ states/_modules/syncthing.py | 59 ++ states/_runners/process_minion_data.py | 51 ++ states/_states/custom.py | 74 ++ states/_states/dkron.py | 24 + states/_states/ovhapi.py | 65 ++ states/_states/syncthing.py | 27 + states/acme/defaults.yaml | 20 + states/acme/init.sls | 50 + states/acme/map.jinja | 5 + states/androidstudio/defaults.yaml | 5 + states/androidstudio/init.sls | 24 + .../androidstudio/jetbrains-studio.desktop.j2 | 12 + states/androidstudio/map.jinja | 5 + states/apparmor/defaults.yaml | 4 + states/apparmor/init.sls | 21 + states/apparmor/map.jinja | 5 + states/apparmor/opt.kingsoft.j2 | 27 + .../opt.sublime_text_3.sublime_text.j2 | 37 + states/apparmor/usr.bin.skype.j2 | 77 ++ states/apparmor/usr.bin.spotify.j2 | 24 + states/appimagekit/init.sls | 8 + states/apt/10proxy.j2 | 3 + states/apt/init.sls | 28 + states/arduino/arduino-arduinoide.desktop.j2 | 14 + states/arduino/defaults.yaml | 4 + states/arduino/init.sls | 40 + states/arduino/map.jinja | 5 + states/bareos/bareos-fd.conf.j2 | 20 + states/bareos/bareos-fd.service.j2 | 21 + states/bareos/bareos-fd.sls | 59 ++ states/bareos/config.sls | 21 + states/bareos/defaults.yaml | 8 + states/bareos/init.sls | 5 + states/bareos/install.sls | 17 + states/bareos/kernelmap.yaml | 3 + states/bareos/map.jinja | 14 + states/bareos/osarchmap.yaml | 21 + states/bareos/service.sls | 20 + states/burp/burp.conf.j2 | 4 + states/burp/burp.conf.j2.sample | 131 +++ states/burp/client/burp-backup.service.j2 | 12 + states/burp/client/burp-backup.timer.j2 | 12 + states/burp/client/config.sls | 11 + states/burp/client/service.sls | 19 + states/burp/defaults.yaml | 66 ++ states/burp/init.sls | 12 + states/burp/install.sls | 7 + states/burp/map.jinja | 8 + states/burp/pkg.sls | 4 + states/burp/server/burp.service.j2 | 10 + states/burp/server/config.sls | 11 + states/burp/server/service.sls | 11 + states/cds/config.sls | 17 + states/cds/defaults.yaml | 7 + states/cds/init.sls | 6 + states/cds/install.sls | 12 + states/cds/map.jinja | 5 + states/cds/service.sls | 15 + states/cds/telegraf.service.j2 | 16 + states/config/defaults.yaml | 6 + states/config/init.sls | 19 + states/config/map.jinja | 3 + states/coronafana/defaults.yaml | 9 + states/coronafana/init.sls | 3 + states/coronafana/install.sls | 16 + states/coronafana/kernelmap.yaml | 3 + states/coronafana/map.jinja | 14 + states/coronafana/osarchmap.yaml | 21 + states/cron/defaults.yaml | 10 + states/cron/init.sls | 8 + states/cron/map.jinja | 8 + states/custom.sls | 6 + states/dkron/config.sls | 15 + states/dkron/defaults.yaml | 20 + states/dkron/dkron.service.j2 | 15 + states/dkron/dkron.yml.j2 | 4 + states/dkron/init.sls | 6 + states/dkron/install.sls | 30 + states/dkron/jobs.sls | 8 + states/dkron/kernelmap.yaml | 3 + states/dkron/map.jinja | 14 + states/dkron/osarchmap.yaml | 21 + states/dkron/service.sls | 16 + states/docker/config.sls | 16 + states/docker/daemon.json.j2 | 2 + states/docker/defaults.yaml | 13 + states/docker/init.sls | 5 + states/docker/install.sls | 11 + states/docker/map.jinja | 6 + states/docker/service.sls | 5 + states/dokuwiki/config.sls | 9 + states/dokuwiki/defaults.yaml | 15 + states/dokuwiki/init.sls | 5 + states/dokuwiki/install.sls | 23 + states/dokuwiki/map.jinja | 5 + states/dokuwiki/templates/local.php.j2 | 9 + states/dovecot/config.sls | 35 + states/dovecot/defaults.yaml | 132 +++ states/dovecot/files/config.j2 | 23 + states/dovecot/init.sls | 6 + states/dovecot/install.sls | 8 + states/dovecot/map.jinja | 5 + states/dovecot/service.sls | 5 + states/drone/defaults.yaml | 4 + states/drone/init.sls | 4 + states/drone/map.jinja | 5 + states/drone/server.sls | 7 + states/fuelprices/defaults.yaml | 9 + states/fuelprices/init.sls | 3 + states/fuelprices/install.sls | 16 + states/fuelprices/kernelmap.yaml | 3 + states/fuelprices/map.jinja | 14 + states/fuelprices/osarchmap.yaml | 21 + states/g2g/defaults.yaml | 9 + states/g2g/init.sls | 3 + states/g2g/install.sls | 16 + states/g2g/kernelmap.yaml | 3 + states/g2g/map.jinja | 14 + states/g2g/osarchmap.yaml | 21 + states/gitea/config.sls | 15 + states/gitea/defaults.yaml | 65 ++ states/gitea/files/app.ini.j2 | 106 +++ states/gitea/files/gitea.service | 23 + states/gitea/init.sls | 7 + states/gitea/install.sls | 27 + states/gitea/map.jinja | 19 + states/gitea/service.sls | 20 + states/golang/defaults.yaml | 8 + states/golang/init.sls | 3 + states/golang/install.sls | 18 + states/golang/kernelmap.yaml | 12 + states/golang/map.jinja | 14 + states/golang/osarchmap.yaml | 21 + states/grafana/archive/clean.sls | 11 + states/grafana/archive/init.sls | 5 + states/grafana/archive/install.sls | 51 ++ states/grafana/clean.sls | 8 + states/grafana/config/alternatives/clean.sls | 34 + states/grafana/config/alternatives/init.sls | 5 + .../grafana/config/alternatives/install.sls | 71 ++ states/grafana/config/clean.sls | 28 + states/grafana/config/environ.sls | 29 + states/grafana/config/file.sls | 36 + states/grafana/config/init.sls | 7 + states/grafana/defaults.yaml | 48 + .../grafana/files/default/grafana.ini.jinja | 17 + states/grafana/files/default/grafana.sh.jinja | 8 + states/grafana/init.sls | 11 + states/grafana/jinja/macros.jinja | 10 + states/grafana/libtofs.jinja | 100 ++ states/grafana/map.jinja | 49 + states/grafana/osarchmap.yaml | 35 + states/grafana/osfamilymap.yaml | 91 ++ states/grafana/osfingermap.yaml | 14 + states/grafana/osmap.yaml | 25 + states/grafana/package/clean.sls | 21 + states/grafana/package/init.sls | 5 + states/grafana/package/install.sls | 16 + states/grafana/package/repo/clean.sls | 10 + states/grafana/package/repo/init.sls | 5 + states/grafana/package/repo/install.sls | 15 + states/grafana/service/clean.sls | 11 + states/grafana/service/init.sls | 5 + states/grafana/service/running.sls | 27 + states/gufw/Desktop.profile | 95 ++ states/gufw/gufw.cfg | 5 + states/gufw/init.sls | 16 + states/influxdb/config.sls | 20 + states/influxdb/defaults.yaml | 20 + states/influxdb/influxdb.conf.j2 | 39 + states/influxdb/influxdb.service.j2 | 17 + states/influxdb/init.sls | 5 + states/influxdb/install.sls | 55 ++ states/influxdb/kernelmap.yaml | 3 + states/influxdb/map.jinja | 14 + states/influxdb/osarchmap.yaml | 21 + states/influxdb/service.sls | 15 + states/influxdb/v2/influxdb.service_v2.j2 | 16 + states/influxdb/v2/init_v2.sls | 58 ++ states/ipfs/init.sls | 89 ++ states/ipfs/ipfs.service | 20 + states/iptables/init.sls | 96 ++ states/iptables/ip6tables.conf.j2 | 18 + states/iptables/ip6tables.conf.reset.j2 | 18 + states/iptables/iptables-service.j2 | 35 + states/iptables/iptables.conf.j2 | 25 + states/iptables/iptables.conf.reset.j2 | 18 + states/iptables/iptables.service.j2 | 14 + states/iptables/iptables.sls | 92 ++ states/java/defaults.yaml | 8 + states/java/init.sls | 3 + states/java/install.sls | 39 + states/java/kernelmap.yaml | 3 + states/java/map.jinja | 14 + states/java/osarchmap.yaml | 21 + states/java/update-alternatives-java.sh.j2 | 7 + states/maildb/defaults.yaml | 13 + states/maildb/init.sls | 53 ++ states/maildb/maildb.ini.j2 | 6 + states/maildb/maildb.py | 88 ++ states/maildb/map.jinja | 5 + states/mariadb/config.sls | 15 + states/mariadb/databases.sls | 9 + states/mariadb/defaults.yaml | 42 + states/mariadb/init.sls | 7 + states/mariadb/install.sls | 15 + states/mariadb/map.jinja | 5 + states/mariadb/server.cnf.j2 | 9 + states/mariadb/service.sls | 5 + states/mariadb/users.sls | 23 + states/misc/init.sls | 35 + states/molotov/init.sls | 28 + states/molotov/molotov.desktop.j2 | 12 + states/molotov/molotov.png | Bin 0 -> 62400 bytes states/motd/init.sls | 6 + states/motd/motd.j2 | 2 + states/netbox/defaults.yaml | 97 ++ states/netbox/init.sls | 6 + states/netbox/install.sls | 72 ++ states/netbox/map.jinja | 8 + states/netbox/pkgs.sls | 8 + states/netbox/service.sls | 14 + states/netbox/templates/configuration.py.j2 | 47 + states/netbox/templates/gunicorn.py.j2 | 10 + states/netbox/templates/netbox.service.j2 | 18 + states/netboxinventory/defaults.yaml | 6 + states/netboxinventory/init.sls | 31 + states/netboxinventory/map.jinja | 8 + states/nextcloud/config.sls | 9 + states/nextcloud/defaults.yaml | 148 +++ states/nextcloud/init.sls | 6 + states/nextcloud/install.sls | 25 + states/nextcloud/map.jinja | 5 + states/nextcloud/templates/config.php.j2 | 26 + states/nextcloud_desktop/init.sls | 27 + states/nextcloud_desktop/nextcloud.desktop | 188 ++++ states/nextcloud_desktop/nextcloud.png | Bin 0 -> 17488 bytes states/nftables/defaults.yaml | 4 + states/nftables/init.sls | 22 + states/nftables/map.jinja | 7 + states/nftables/nftables.conf.j2 | 48 + states/nginx/auth.sls | 25 + states/nginx/config.sls | 73 ++ states/nginx/defaults.yaml | 29 + states/nginx/init.sls | 7 + states/nginx/install.sls | 5 + states/nginx/map.jinja | 5 + states/nginx/service.sls | 6 + states/nginx/templates/access.j2 | 8 + states/nginx/templates/fastcgi_params.j2 | 27 + states/nginx/templates/http.j2 | 4 + states/nginx/templates/https.j2 | 4 + states/nginx/templates/mime.types.j2 | 87 ++ states/nginx/templates/nginx.conf.j2 | 57 ++ states/nginx/templates/proxy_params.j2 | 17 + states/nginx/templates/scgi_params.j2 | 18 + states/nginx/templates/ssl_params.j2 | 23 + states/nginx/templates/uwsgi_params.j2 | 18 + states/nginx/vhost.j2 | 34 + states/npf/defaults.yaml | 3 + states/npf/init.sls | 14 + states/npf/map.jinja | 6 + states/npf/npf.conf.j2 | 104 +++ states/oldstates/clamav/clamd.conf.j2 | 4 + states/oldstates/clamav/config.sls | 11 + states/oldstates/clamav/defaults.yaml | 91 ++ states/oldstates/clamav/init.sls | 5 + states/oldstates/clamav/install.sls | 6 + states/oldstates/clamav/map.jinja | 5 + states/oldstates/clamav/service.sls | 7 + states/oldstates/collectd/collectd.conf.j2 | 83 ++ states/oldstates/collectd/config.sls | 13 + states/oldstates/collectd/init.sls | 5 + states/oldstates/collectd/install.sls | 6 + states/oldstates/collectd/service.sls | 5 + states/oldstates/snmp/init.sls | 16 + states/oldstates/snmp/snmpd.conf.j2 | 29 + states/opendkim/TrustedHosts.j2 | 9 + states/opendkim/config.sls | 52 ++ states/opendkim/defaults.yaml | 49 + states/opendkim/init.sls | 6 + states/opendkim/install.sls | 7 + states/opendkim/key.j2 | 2 + states/opendkim/map.jinja | 8 + states/opendkim/opendkim.conf.j2 | 23 + states/opendkim/service.sls | 6 + states/opendkim/service_defaults.j2 | 2 + states/openvpn_client/config.sls | 32 + states/openvpn_client/defaults.yaml | 3 + states/openvpn_client/init.sls | 5 + states/openvpn_client/install.sls | 6 + states/openvpn_client/map.jinja | 5 + states/openvpn_client/pass.j2 | 7 + states/openvpn_client/service.sls | 5 + states/openvpn_client/vpn.conf.j2 | 29 + states/ovh/defaults.yaml | 6 + states/ovh/domain.sls | 14 + states/ovh/init.sls | 5 + states/ovh/map.jinja | 5 + states/ovh/pkg.sls | 5 + states/packer/defaults.yaml | 8 + states/packer/init.sls | 3 + states/packer/install.sls | 8 + states/packer/kernelmap.yaml | 3 + states/packer/map.jinja | 14 + states/packer/osarchmap.yaml | 21 + states/php/config.sls | 29 + states/php/defaults.yaml | 148 +++ states/php/init.sls | 6 + states/php/install.sls | 7 + states/php/map.jinja | 5 + states/php/service.sls | 6 + states/pkg/bootstrap.sls | 9 + states/pkg/defaults.yaml | 5 + states/pkg/init.sls | 8 + states/pkg/install.sls | 13 + states/pkg/manual.sls | 12 + states/pkg/map.jinja | 5 + states/pkg/remove.sls | 10 + states/pkg/tasks.sls | 8 + states/postfix/defaults.yaml | 46 + states/postfix/init.sls | 73 ++ states/postfix/main.cf.j2 | 6 + states/postfix/map.jinja | 8 + states/postfix/master.cf.j2 | 40 + states/postfix/sender_access.j2 | 6 + states/postfix/transport.j2 | 6 + states/postgresql/databases.sls | 15 + states/postgresql/defaults.yaml | 6 + states/postgresql/init.sls | 6 + states/postgresql/install.sls | 4 + states/postgresql/map.jinja | 5 + states/postgresql/service.sls | 5 + states/postgresql/users.sls | 13 + states/provision/defaults.yaml | 12 + states/provision/init.sls | 31 + states/provision/map.jinja | 5 + states/pycharm/defaults.yaml | 6 + states/pycharm/init.sls | 29 + .../pycharm/jetbrains-pycharm-ce.desktop.j2 | 12 + states/pycharm/map.jinja | 5 + states/qrz/defaults.yaml | 10 + states/qrz/init.sls | 4 + states/qrz/install.sls | 20 + states/qrz/kernelmap.yaml | 3 + states/qrz/map.jinja | 14 + states/qrz/osarchmap.yaml | 21 + states/qrz/qrz.service.j2 | 11 + states/qrz/service.sls | 14 + states/rclone/defaults.yaml | 9 + states/rclone/init.sls | 3 + states/rclone/install.sls | 19 + states/rclone/kernelmap.yaml | 3 + states/rclone/map.jinja | 14 + states/rclone/osarchmap.yaml | 21 + states/reactor/auth.sls | 15 + states/reactor/email-on-failure.sls | 8 + states/redis/defaults.yaml | 2 + states/redis/init.sls | 4 + states/redis/install.sls | 4 + states/redis/map.jinja | 5 + states/redis/service.sls | 5 + states/repos/init.sls | 33 + states/rspamd/config.sls | 14 + states/rspamd/defaults.yaml | 5 + states/rspamd/init.sls | 5 + states/rspamd/install.sls | 6 + states/rspamd/map.jinja | 5 + states/rspamd/service.sls | 5 + states/rsync/config.sls | 13 + states/rsync/defaults.yaml | 16 + states/rsync/init.sls | 5 + states/rsync/install.sls | 4 + states/rsync/map.jinja | 5 + states/rsync/rsyncd.conf.j2 | 12 + states/rsync/service.sls | 7 + states/rsyslog/config.sls | 10 + states/rsyslog/init.sls | 5 + states/rsyslog/install.sls | 4 + states/rsyslog/rsyslog.conf.j2 | 46 + states/rsyslog/rsyslog.conf.j2.orig | 60 ++ states/rsyslog/service.sls | 5 + states/salt_minion/defaults.yaml | 14 + states/salt_minion/init.sls | 19 + states/salt_minion/map.jinja | 5 + states/salt_minion/minion.j2 | 5 + states/salt_minion/minion.sample | 781 ++++++++++++++++ states/salt_minion/salt-minion.service.j2 | 15 + states/samba/config.sls | 6 + states/samba/defaults.yaml | 4 + states/samba/init.sls | 5 + states/samba/install.sls | 8 + states/samba/map.jinja | 6 + states/samba/users.sls | 18 + states/sensu/agent.sls | 76 ++ states/sensu/agent.yml.j2 | 4 + states/sensu/defaults.yaml | 28 + states/sensu/init.sls | 6 + states/sensu/map.jinja | 5 + states/sensu/sensu-agent.service | 17 + states/services/init.sls | 23 + states/ssh/config.sls | 10 + states/ssh/defaults.yaml | 15 + states/ssh/init.sls | 5 + states/ssh/install.sls | 7 + states/ssh/map.jinja | 5 + states/ssh/old.txt | 15 + states/ssh/service.sls | 6 + states/ssh/sshd_config.j2 | 20 + states/states/defaults.yaml | 2 + states/states/init.sls | 13 + states/states/map.jinja | 5 + states/sublimetext/defaults.yaml | 5 + states/sublimetext/init.sls | 37 + states/sublimetext/kernelmap.yaml | 3 + states/sublimetext/map.jinja | 14 + states/sublimetext/osarchmap.yaml | 21 + states/sublimetext/sublime_text.desktop.j2 | 24 + states/sublimetext/sublime_text_keys.txt.j2 | 29 + states/sudo/config.sls | 11 + states/sudo/defaults.yaml | 25 + states/sudo/init.sls | 4 + states/sudo/install.sls | 5 + states/sudo/map.jinja | 5 + states/sudo/sudoers.j2 | 16 + states/syncthing/config.sls | 8 + states/syncthing/config_old.sls | 24 + states/syncthing/defaults.yaml | 17 + states/syncthing/init.sls | 6 + states/syncthing/install.sls | 19 + states/syncthing/kernelmap.yaml | 3 + states/syncthing/map.jinja | 14 + states/syncthing/osarchmap.yaml | 21 + states/syncthing/service.sls | 16 + states/syncthing/syncthing.service.j2 | 15 + states/telegraf/config.sls | 18 + states/telegraf/defaults.yaml | 75 ++ states/telegraf/init.sls | 5 + states/telegraf/install.sls | 26 + states/telegraf/kernelmap.yaml | 3 + states/telegraf/map.jinja | 14 + states/telegraf/osarchmap.yaml | 21 + states/telegraf/service.sls | 16 + states/telegraf/telegraf.conf.j2 | 30 + states/telegraf/telegraf.service.j2 | 17 + states/telegraf/telegraf_default.j2 | 3 + states/telegram/defaults.yaml | 7 + states/telegram/init.sls | 3 + states/telegram/install.sls | 33 + states/telegram/map.jinja | 5 + states/telegram/telegram.desktop.j2 | 15 + states/tests/repo.sls | 22 + states/tests/tmp.sls | 10 + states/time/config.sls | 12 + states/time/defaults.yaml | 4 + states/time/init.sls | 8 + states/time/map.jinja | 5 + states/time/ntp.conf.j2 | 9 + states/time/service.sls | 6 + states/time/timeupdate.sls | 5 + states/time/timezone.sls | 6 + states/tmux/defaults.yaml | 13 + states/tmux/init.sls | 15 + states/tmux/map.jinja | 5 + states/tmux/tmux.conf.j2 | 15 + states/top.sls | 7 + states/tor/defaults.yaml | 10 + states/tor/init.sls | 29 + states/tor/map.jinja | 11 + states/tor/torrc.j2 | 8 + states/transmission/config.sls | 34 + states/transmission/defaults.yaml | 73 ++ states/transmission/init.sls | 7 + states/transmission/install.sls | 9 + states/transmission/map.jinja | 5 + states/transmission/service.sls | 5 + states/transmission/settings.json.j2 | 2 + states/users/defaults.yaml | 3 + states/users/groups.sls | 12 + states/users/init.sls | 5 + states/users/map.jinja | 6 + states/users/ssh-keys.sls | 16 + states/users/users.sls | 31 + states/vim/config.sls | 12 + states/vim/defaults.yaml | 61 ++ states/vim/init.sls | 6 + states/vim/install.sls | 15 + states/vim/map.jinja | 5 + states/vim/plugins.sls | 13 + states/vim/themes.sls | 15 + states/vim/vimrc.j2 | 24 + states/vsftpd/defaults.yaml | 18 + states/vsftpd/init.sls | 16 + states/vsftpd/map.jinja | 5 + states/vsftpd/vsftpd.conf.j2 | 6 + states/weather/defaults.yaml | 9 + states/weather/init.sls | 3 + states/weather/install.sls | 16 + states/weather/kernelmap.yaml | 3 + states/weather/map.jinja | 14 + states/weather/osarchmap.yaml | 21 + states/website/defaults.yaml | 7 + states/website/init.sls | 3 + states/website/install.sls | 16 + states/website/map.jinja | 5 + states/winpkg/bootstrap.sls | 4 + states/winpkg/defaults.yaml | 3 + states/winpkg/init.sls | 4 + states/winpkg/install.sls | 9 + states/winpkg/map.jinja | 5 + states/wintse/init.sls | 12 + states/youtubedl/defaults.yaml | 4 + states/youtubedl/init.sls | 11 + states/youtubedl/map.jinja | 6 + states/zsh/config.sls | 18 + states/zsh/defaults.yaml | 4 + states/zsh/init.sls | 6 + states/zsh/misc.txt | 4 + states/zsh/pkg.sls | 6 + states/zsh/users.sls | 17 + states/zsh/zprofile.j2 | 35 + states/zsh/zshrc.j2 | 3 + 541 files changed, 12486 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 cloud/cloud create mode 100644 cloud/cloud.profiles create mode 100644 cloud/cloud.providers create mode 100644 cloud/scaleway/scw01-ams.paulbsd.com create mode 100644 cloud/scaleway/scw_lamp.map create mode 100644 cloud/scaleway/scw_web.map create mode 100644 config/master create mode 100644 config/master.sample create mode 100755 config/minion create mode 100644 config/minion.sample create mode 100644 config/roster create mode 100644 config/roster.sample create mode 100755 scripts/encrypt_password create mode 100755 scripts/salt-test.sh create mode 100755 states/_modules/custom.py create mode 100644 states/_modules/dkron.py create mode 100644 states/_modules/ovhapi.py create mode 100644 states/_modules/syncthing.py create mode 100755 states/_runners/process_minion_data.py create mode 100755 states/_states/custom.py create mode 100644 states/_states/dkron.py create mode 100644 states/_states/ovhapi.py create mode 100644 states/_states/syncthing.py create mode 100644 states/acme/defaults.yaml create mode 100644 states/acme/init.sls create mode 100644 states/acme/map.jinja create mode 100644 states/androidstudio/defaults.yaml create mode 100644 states/androidstudio/init.sls create mode 100644 states/androidstudio/jetbrains-studio.desktop.j2 create mode 100644 states/androidstudio/map.jinja create mode 100644 states/apparmor/defaults.yaml create mode 100644 states/apparmor/init.sls create mode 100644 states/apparmor/map.jinja create mode 100644 states/apparmor/opt.kingsoft.j2 create mode 100644 states/apparmor/opt.sublime_text_3.sublime_text.j2 create mode 100644 states/apparmor/usr.bin.skype.j2 create mode 100644 states/apparmor/usr.bin.spotify.j2 create mode 100644 states/appimagekit/init.sls create mode 100644 states/apt/10proxy.j2 create mode 100644 states/apt/init.sls create mode 100644 states/arduino/arduino-arduinoide.desktop.j2 create mode 100644 states/arduino/defaults.yaml create mode 100644 states/arduino/init.sls create mode 100644 states/arduino/map.jinja create mode 100644 states/bareos/bareos-fd.conf.j2 create mode 100644 states/bareos/bareos-fd.service.j2 create mode 100644 states/bareos/bareos-fd.sls create mode 100644 states/bareos/config.sls create mode 100644 states/bareos/defaults.yaml create mode 100644 states/bareos/init.sls create mode 100644 states/bareos/install.sls create mode 100644 states/bareos/kernelmap.yaml create mode 100644 states/bareos/map.jinja create mode 100644 states/bareos/osarchmap.yaml create mode 100644 states/bareos/service.sls create mode 100644 states/burp/burp.conf.j2 create mode 100644 states/burp/burp.conf.j2.sample create mode 100644 states/burp/client/burp-backup.service.j2 create mode 100644 states/burp/client/burp-backup.timer.j2 create mode 100644 states/burp/client/config.sls create mode 100644 states/burp/client/service.sls create mode 100644 states/burp/defaults.yaml create mode 100644 states/burp/init.sls create mode 100644 states/burp/install.sls create mode 100644 states/burp/map.jinja create mode 100644 states/burp/pkg.sls create mode 100644 states/burp/server/burp.service.j2 create mode 100644 states/burp/server/config.sls create mode 100644 states/burp/server/service.sls create mode 100644 states/cds/config.sls create mode 100644 states/cds/defaults.yaml create mode 100644 states/cds/init.sls create mode 100644 states/cds/install.sls create mode 100644 states/cds/map.jinja create mode 100644 states/cds/service.sls create mode 100644 states/cds/telegraf.service.j2 create mode 100644 states/config/defaults.yaml create mode 100644 states/config/init.sls create mode 100644 states/config/map.jinja create mode 100644 states/coronafana/defaults.yaml create mode 100644 states/coronafana/init.sls create mode 100644 states/coronafana/install.sls create mode 100644 states/coronafana/kernelmap.yaml create mode 100644 states/coronafana/map.jinja create mode 100644 states/coronafana/osarchmap.yaml create mode 100644 states/cron/defaults.yaml create mode 100644 states/cron/init.sls create mode 100644 states/cron/map.jinja create mode 100644 states/custom.sls create mode 100644 states/dkron/config.sls create mode 100644 states/dkron/defaults.yaml create mode 100644 states/dkron/dkron.service.j2 create mode 100644 states/dkron/dkron.yml.j2 create mode 100644 states/dkron/init.sls create mode 100644 states/dkron/install.sls create mode 100644 states/dkron/jobs.sls create mode 100644 states/dkron/kernelmap.yaml create mode 100644 states/dkron/map.jinja create mode 100644 states/dkron/osarchmap.yaml create mode 100644 states/dkron/service.sls create mode 100644 states/docker/config.sls create mode 100644 states/docker/daemon.json.j2 create mode 100644 states/docker/defaults.yaml create mode 100644 states/docker/init.sls create mode 100644 states/docker/install.sls create mode 100644 states/docker/map.jinja create mode 100644 states/docker/service.sls create mode 100644 states/dokuwiki/config.sls create mode 100644 states/dokuwiki/defaults.yaml create mode 100644 states/dokuwiki/init.sls create mode 100644 states/dokuwiki/install.sls create mode 100644 states/dokuwiki/map.jinja create mode 100644 states/dokuwiki/templates/local.php.j2 create mode 100644 states/dovecot/config.sls create mode 100644 states/dovecot/defaults.yaml create mode 100644 states/dovecot/files/config.j2 create mode 100644 states/dovecot/init.sls create mode 100644 states/dovecot/install.sls create mode 100644 states/dovecot/map.jinja create mode 100644 states/dovecot/service.sls create mode 100644 states/drone/defaults.yaml create mode 100644 states/drone/init.sls create mode 100644 states/drone/map.jinja create mode 100644 states/drone/server.sls create mode 100644 states/fuelprices/defaults.yaml create mode 100644 states/fuelprices/init.sls create mode 100644 states/fuelprices/install.sls create mode 100644 states/fuelprices/kernelmap.yaml create mode 100644 states/fuelprices/map.jinja create mode 100644 states/fuelprices/osarchmap.yaml create mode 100644 states/g2g/defaults.yaml create mode 100644 states/g2g/init.sls create mode 100644 states/g2g/install.sls create mode 100644 states/g2g/kernelmap.yaml create mode 100644 states/g2g/map.jinja create mode 100644 states/g2g/osarchmap.yaml create mode 100644 states/gitea/config.sls create mode 100644 states/gitea/defaults.yaml create mode 100644 states/gitea/files/app.ini.j2 create mode 100644 states/gitea/files/gitea.service create mode 100644 states/gitea/init.sls create mode 100644 states/gitea/install.sls create mode 100644 states/gitea/map.jinja create mode 100644 states/gitea/service.sls create mode 100644 states/golang/defaults.yaml create mode 100644 states/golang/init.sls create mode 100644 states/golang/install.sls create mode 100644 states/golang/kernelmap.yaml create mode 100644 states/golang/map.jinja create mode 100644 states/golang/osarchmap.yaml create mode 100644 states/grafana/archive/clean.sls create mode 100644 states/grafana/archive/init.sls create mode 100644 states/grafana/archive/install.sls create mode 100644 states/grafana/clean.sls create mode 100644 states/grafana/config/alternatives/clean.sls create mode 100644 states/grafana/config/alternatives/init.sls create mode 100644 states/grafana/config/alternatives/install.sls create mode 100644 states/grafana/config/clean.sls create mode 100644 states/grafana/config/environ.sls create mode 100644 states/grafana/config/file.sls create mode 100644 states/grafana/config/init.sls create mode 100644 states/grafana/defaults.yaml create mode 100644 states/grafana/files/default/grafana.ini.jinja create mode 100644 states/grafana/files/default/grafana.sh.jinja create mode 100644 states/grafana/init.sls create mode 100644 states/grafana/jinja/macros.jinja create mode 100644 states/grafana/libtofs.jinja create mode 100644 states/grafana/map.jinja create mode 100644 states/grafana/osarchmap.yaml create mode 100644 states/grafana/osfamilymap.yaml create mode 100644 states/grafana/osfingermap.yaml create mode 100644 states/grafana/osmap.yaml create mode 100644 states/grafana/package/clean.sls create mode 100644 states/grafana/package/init.sls create mode 100644 states/grafana/package/install.sls create mode 100644 states/grafana/package/repo/clean.sls create mode 100644 states/grafana/package/repo/init.sls create mode 100644 states/grafana/package/repo/install.sls create mode 100644 states/grafana/service/clean.sls create mode 100644 states/grafana/service/init.sls create mode 100644 states/grafana/service/running.sls create mode 100644 states/gufw/Desktop.profile create mode 100644 states/gufw/gufw.cfg create mode 100644 states/gufw/init.sls create mode 100644 states/influxdb/config.sls create mode 100644 states/influxdb/defaults.yaml create mode 100644 states/influxdb/influxdb.conf.j2 create mode 100644 states/influxdb/influxdb.service.j2 create mode 100644 states/influxdb/init.sls create mode 100644 states/influxdb/install.sls create mode 100644 states/influxdb/kernelmap.yaml create mode 100644 states/influxdb/map.jinja create mode 100644 states/influxdb/osarchmap.yaml create mode 100644 states/influxdb/service.sls create mode 100644 states/influxdb/v2/influxdb.service_v2.j2 create mode 100644 states/influxdb/v2/init_v2.sls create mode 100644 states/ipfs/init.sls create mode 100644 states/ipfs/ipfs.service create mode 100644 states/iptables/init.sls create mode 100644 states/iptables/ip6tables.conf.j2 create mode 100644 states/iptables/ip6tables.conf.reset.j2 create mode 100644 states/iptables/iptables-service.j2 create mode 100644 states/iptables/iptables.conf.j2 create mode 100644 states/iptables/iptables.conf.reset.j2 create mode 100644 states/iptables/iptables.service.j2 create mode 100644 states/iptables/iptables.sls create mode 100644 states/java/defaults.yaml create mode 100644 states/java/init.sls create mode 100644 states/java/install.sls create mode 100644 states/java/kernelmap.yaml create mode 100644 states/java/map.jinja create mode 100644 states/java/osarchmap.yaml create mode 100644 states/java/update-alternatives-java.sh.j2 create mode 100644 states/maildb/defaults.yaml create mode 100644 states/maildb/init.sls create mode 100644 states/maildb/maildb.ini.j2 create mode 100644 states/maildb/maildb.py create mode 100644 states/maildb/map.jinja create mode 100644 states/mariadb/config.sls create mode 100644 states/mariadb/databases.sls create mode 100644 states/mariadb/defaults.yaml create mode 100644 states/mariadb/init.sls create mode 100644 states/mariadb/install.sls create mode 100644 states/mariadb/map.jinja create mode 100644 states/mariadb/server.cnf.j2 create mode 100644 states/mariadb/service.sls create mode 100644 states/mariadb/users.sls create mode 100644 states/misc/init.sls create mode 100644 states/molotov/init.sls create mode 100644 states/molotov/molotov.desktop.j2 create mode 100644 states/molotov/molotov.png create mode 100644 states/motd/init.sls create mode 100644 states/motd/motd.j2 create mode 100644 states/netbox/defaults.yaml create mode 100644 states/netbox/init.sls create mode 100644 states/netbox/install.sls create mode 100644 states/netbox/map.jinja create mode 100644 states/netbox/pkgs.sls create mode 100644 states/netbox/service.sls create mode 100644 states/netbox/templates/configuration.py.j2 create mode 100644 states/netbox/templates/gunicorn.py.j2 create mode 100644 states/netbox/templates/netbox.service.j2 create mode 100644 states/netboxinventory/defaults.yaml create mode 100644 states/netboxinventory/init.sls create mode 100644 states/netboxinventory/map.jinja create mode 100644 states/nextcloud/config.sls create mode 100644 states/nextcloud/defaults.yaml create mode 100644 states/nextcloud/init.sls create mode 100644 states/nextcloud/install.sls create mode 100644 states/nextcloud/map.jinja create mode 100644 states/nextcloud/templates/config.php.j2 create mode 100644 states/nextcloud_desktop/init.sls create mode 100644 states/nextcloud_desktop/nextcloud.desktop create mode 100644 states/nextcloud_desktop/nextcloud.png create mode 100644 states/nftables/defaults.yaml create mode 100644 states/nftables/init.sls create mode 100644 states/nftables/map.jinja create mode 100644 states/nftables/nftables.conf.j2 create mode 100644 states/nginx/auth.sls create mode 100644 states/nginx/config.sls create mode 100644 states/nginx/defaults.yaml create mode 100644 states/nginx/init.sls create mode 100644 states/nginx/install.sls create mode 100644 states/nginx/map.jinja create mode 100644 states/nginx/service.sls create mode 100644 states/nginx/templates/access.j2 create mode 100644 states/nginx/templates/fastcgi_params.j2 create mode 100644 states/nginx/templates/http.j2 create mode 100644 states/nginx/templates/https.j2 create mode 100644 states/nginx/templates/mime.types.j2 create mode 100644 states/nginx/templates/nginx.conf.j2 create mode 100644 states/nginx/templates/proxy_params.j2 create mode 100644 states/nginx/templates/scgi_params.j2 create mode 100644 states/nginx/templates/ssl_params.j2 create mode 100644 states/nginx/templates/uwsgi_params.j2 create mode 100644 states/nginx/vhost.j2 create mode 100644 states/npf/defaults.yaml create mode 100644 states/npf/init.sls create mode 100644 states/npf/map.jinja create mode 100644 states/npf/npf.conf.j2 create mode 100644 states/oldstates/clamav/clamd.conf.j2 create mode 100644 states/oldstates/clamav/config.sls create mode 100644 states/oldstates/clamav/defaults.yaml create mode 100644 states/oldstates/clamav/init.sls create mode 100644 states/oldstates/clamav/install.sls create mode 100644 states/oldstates/clamav/map.jinja create mode 100644 states/oldstates/clamav/service.sls create mode 100644 states/oldstates/collectd/collectd.conf.j2 create mode 100644 states/oldstates/collectd/config.sls create mode 100644 states/oldstates/collectd/init.sls create mode 100644 states/oldstates/collectd/install.sls create mode 100644 states/oldstates/collectd/service.sls create mode 100644 states/oldstates/snmp/init.sls create mode 100644 states/oldstates/snmp/snmpd.conf.j2 create mode 100644 states/opendkim/TrustedHosts.j2 create mode 100644 states/opendkim/config.sls create mode 100644 states/opendkim/defaults.yaml create mode 100644 states/opendkim/init.sls create mode 100644 states/opendkim/install.sls create mode 100644 states/opendkim/key.j2 create mode 100644 states/opendkim/map.jinja create mode 100644 states/opendkim/opendkim.conf.j2 create mode 100644 states/opendkim/service.sls create mode 100644 states/opendkim/service_defaults.j2 create mode 100644 states/openvpn_client/config.sls create mode 100644 states/openvpn_client/defaults.yaml create mode 100644 states/openvpn_client/init.sls create mode 100644 states/openvpn_client/install.sls create mode 100644 states/openvpn_client/map.jinja create mode 100644 states/openvpn_client/pass.j2 create mode 100644 states/openvpn_client/service.sls create mode 100644 states/openvpn_client/vpn.conf.j2 create mode 100644 states/ovh/defaults.yaml create mode 100644 states/ovh/domain.sls create mode 100644 states/ovh/init.sls create mode 100644 states/ovh/map.jinja create mode 100644 states/ovh/pkg.sls create mode 100644 states/packer/defaults.yaml create mode 100644 states/packer/init.sls create mode 100644 states/packer/install.sls create mode 100644 states/packer/kernelmap.yaml create mode 100644 states/packer/map.jinja create mode 100644 states/packer/osarchmap.yaml create mode 100644 states/php/config.sls create mode 100644 states/php/defaults.yaml create mode 100644 states/php/init.sls create mode 100644 states/php/install.sls create mode 100644 states/php/map.jinja create mode 100644 states/php/service.sls create mode 100644 states/pkg/bootstrap.sls create mode 100644 states/pkg/defaults.yaml create mode 100644 states/pkg/init.sls create mode 100644 states/pkg/install.sls create mode 100644 states/pkg/manual.sls create mode 100644 states/pkg/map.jinja create mode 100644 states/pkg/remove.sls create mode 100644 states/pkg/tasks.sls create mode 100644 states/postfix/defaults.yaml create mode 100644 states/postfix/init.sls create mode 100644 states/postfix/main.cf.j2 create mode 100644 states/postfix/map.jinja create mode 100644 states/postfix/master.cf.j2 create mode 100644 states/postfix/sender_access.j2 create mode 100644 states/postfix/transport.j2 create mode 100644 states/postgresql/databases.sls create mode 100644 states/postgresql/defaults.yaml create mode 100644 states/postgresql/init.sls create mode 100644 states/postgresql/install.sls create mode 100644 states/postgresql/map.jinja create mode 100644 states/postgresql/service.sls create mode 100644 states/postgresql/users.sls create mode 100644 states/provision/defaults.yaml create mode 100644 states/provision/init.sls create mode 100644 states/provision/map.jinja create mode 100644 states/pycharm/defaults.yaml create mode 100644 states/pycharm/init.sls create mode 100644 states/pycharm/jetbrains-pycharm-ce.desktop.j2 create mode 100644 states/pycharm/map.jinja create mode 100644 states/qrz/defaults.yaml create mode 100644 states/qrz/init.sls create mode 100644 states/qrz/install.sls create mode 100644 states/qrz/kernelmap.yaml create mode 100644 states/qrz/map.jinja create mode 100644 states/qrz/osarchmap.yaml create mode 100644 states/qrz/qrz.service.j2 create mode 100644 states/qrz/service.sls create mode 100644 states/rclone/defaults.yaml create mode 100644 states/rclone/init.sls create mode 100644 states/rclone/install.sls create mode 100644 states/rclone/kernelmap.yaml create mode 100644 states/rclone/map.jinja create mode 100644 states/rclone/osarchmap.yaml create mode 100644 states/reactor/auth.sls create mode 100644 states/reactor/email-on-failure.sls create mode 100644 states/redis/defaults.yaml create mode 100644 states/redis/init.sls create mode 100644 states/redis/install.sls create mode 100644 states/redis/map.jinja create mode 100644 states/redis/service.sls create mode 100644 states/repos/init.sls create mode 100644 states/rspamd/config.sls create mode 100644 states/rspamd/defaults.yaml create mode 100644 states/rspamd/init.sls create mode 100644 states/rspamd/install.sls create mode 100644 states/rspamd/map.jinja create mode 100644 states/rspamd/service.sls create mode 100644 states/rsync/config.sls create mode 100644 states/rsync/defaults.yaml create mode 100644 states/rsync/init.sls create mode 100644 states/rsync/install.sls create mode 100644 states/rsync/map.jinja create mode 100644 states/rsync/rsyncd.conf.j2 create mode 100644 states/rsync/service.sls create mode 100644 states/rsyslog/config.sls create mode 100644 states/rsyslog/init.sls create mode 100644 states/rsyslog/install.sls create mode 100644 states/rsyslog/rsyslog.conf.j2 create mode 100644 states/rsyslog/rsyslog.conf.j2.orig create mode 100644 states/rsyslog/service.sls create mode 100644 states/salt_minion/defaults.yaml create mode 100644 states/salt_minion/init.sls create mode 100644 states/salt_minion/map.jinja create mode 100644 states/salt_minion/minion.j2 create mode 100644 states/salt_minion/minion.sample create mode 100644 states/salt_minion/salt-minion.service.j2 create mode 100644 states/samba/config.sls create mode 100644 states/samba/defaults.yaml create mode 100644 states/samba/init.sls create mode 100644 states/samba/install.sls create mode 100644 states/samba/map.jinja create mode 100644 states/samba/users.sls create mode 100644 states/sensu/agent.sls create mode 100644 states/sensu/agent.yml.j2 create mode 100644 states/sensu/defaults.yaml create mode 100644 states/sensu/init.sls create mode 100644 states/sensu/map.jinja create mode 100644 states/sensu/sensu-agent.service create mode 100644 states/services/init.sls create mode 100644 states/ssh/config.sls create mode 100644 states/ssh/defaults.yaml create mode 100644 states/ssh/init.sls create mode 100644 states/ssh/install.sls create mode 100644 states/ssh/map.jinja create mode 100644 states/ssh/old.txt create mode 100644 states/ssh/service.sls create mode 100644 states/ssh/sshd_config.j2 create mode 100644 states/states/defaults.yaml create mode 100644 states/states/init.sls create mode 100644 states/states/map.jinja create mode 100644 states/sublimetext/defaults.yaml create mode 100644 states/sublimetext/init.sls create mode 100644 states/sublimetext/kernelmap.yaml create mode 100644 states/sublimetext/map.jinja create mode 100644 states/sublimetext/osarchmap.yaml create mode 100644 states/sublimetext/sublime_text.desktop.j2 create mode 100644 states/sublimetext/sublime_text_keys.txt.j2 create mode 100644 states/sudo/config.sls create mode 100644 states/sudo/defaults.yaml create mode 100644 states/sudo/init.sls create mode 100644 states/sudo/install.sls create mode 100644 states/sudo/map.jinja create mode 100644 states/sudo/sudoers.j2 create mode 100644 states/syncthing/config.sls create mode 100644 states/syncthing/config_old.sls create mode 100644 states/syncthing/defaults.yaml create mode 100644 states/syncthing/init.sls create mode 100644 states/syncthing/install.sls create mode 100644 states/syncthing/kernelmap.yaml create mode 100644 states/syncthing/map.jinja create mode 100644 states/syncthing/osarchmap.yaml create mode 100644 states/syncthing/service.sls create mode 100644 states/syncthing/syncthing.service.j2 create mode 100644 states/telegraf/config.sls create mode 100644 states/telegraf/defaults.yaml create mode 100644 states/telegraf/init.sls create mode 100644 states/telegraf/install.sls create mode 100644 states/telegraf/kernelmap.yaml create mode 100644 states/telegraf/map.jinja create mode 100644 states/telegraf/osarchmap.yaml create mode 100644 states/telegraf/service.sls create mode 100644 states/telegraf/telegraf.conf.j2 create mode 100644 states/telegraf/telegraf.service.j2 create mode 100644 states/telegraf/telegraf_default.j2 create mode 100644 states/telegram/defaults.yaml create mode 100644 states/telegram/init.sls create mode 100644 states/telegram/install.sls create mode 100644 states/telegram/map.jinja create mode 100644 states/telegram/telegram.desktop.j2 create mode 100644 states/tests/repo.sls create mode 100644 states/tests/tmp.sls create mode 100644 states/time/config.sls create mode 100644 states/time/defaults.yaml create mode 100644 states/time/init.sls create mode 100644 states/time/map.jinja create mode 100644 states/time/ntp.conf.j2 create mode 100644 states/time/service.sls create mode 100644 states/time/timeupdate.sls create mode 100644 states/time/timezone.sls create mode 100644 states/tmux/defaults.yaml create mode 100644 states/tmux/init.sls create mode 100644 states/tmux/map.jinja create mode 100644 states/tmux/tmux.conf.j2 create mode 100644 states/top.sls create mode 100644 states/tor/defaults.yaml create mode 100644 states/tor/init.sls create mode 100644 states/tor/map.jinja create mode 100644 states/tor/torrc.j2 create mode 100644 states/transmission/config.sls create mode 100644 states/transmission/defaults.yaml create mode 100644 states/transmission/init.sls create mode 100644 states/transmission/install.sls create mode 100644 states/transmission/map.jinja create mode 100644 states/transmission/service.sls create mode 100644 states/transmission/settings.json.j2 create mode 100644 states/users/defaults.yaml create mode 100644 states/users/groups.sls create mode 100644 states/users/init.sls create mode 100644 states/users/map.jinja create mode 100644 states/users/ssh-keys.sls create mode 100644 states/users/users.sls create mode 100644 states/vim/config.sls create mode 100644 states/vim/defaults.yaml create mode 100644 states/vim/init.sls create mode 100644 states/vim/install.sls create mode 100644 states/vim/map.jinja create mode 100644 states/vim/plugins.sls create mode 100644 states/vim/themes.sls create mode 100644 states/vim/vimrc.j2 create mode 100644 states/vsftpd/defaults.yaml create mode 100644 states/vsftpd/init.sls create mode 100644 states/vsftpd/map.jinja create mode 100644 states/vsftpd/vsftpd.conf.j2 create mode 100644 states/weather/defaults.yaml create mode 100644 states/weather/init.sls create mode 100644 states/weather/install.sls create mode 100644 states/weather/kernelmap.yaml create mode 100644 states/weather/map.jinja create mode 100644 states/weather/osarchmap.yaml create mode 100644 states/website/defaults.yaml create mode 100644 states/website/init.sls create mode 100644 states/website/install.sls create mode 100644 states/website/map.jinja create mode 100644 states/winpkg/bootstrap.sls create mode 100644 states/winpkg/defaults.yaml create mode 100644 states/winpkg/init.sls create mode 100644 states/winpkg/install.sls create mode 100644 states/winpkg/map.jinja create mode 100644 states/wintse/init.sls create mode 100644 states/youtubedl/defaults.yaml create mode 100644 states/youtubedl/init.sls create mode 100644 states/youtubedl/map.jinja create mode 100644 states/zsh/config.sls create mode 100644 states/zsh/defaults.yaml create mode 100644 states/zsh/init.sls create mode 100644 states/zsh/misc.txt create mode 100644 states/zsh/pkg.sls create mode 100644 states/zsh/users.sls create mode 100644 states/zsh/zprofile.j2 create mode 100644 states/zsh/zshrc.j2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f14fbbe --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# Pillars +/pillar + +# Python related +*.swp +*.pyc \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..dc40b7e --- /dev/null +++ b/README.md @@ -0,0 +1,117 @@ +# paulbsd-salt + +## Summary + +paulbsd-salt are sets of SaltStack states for PaulBSD infrastructure + +## Howto + +### States + +```text +states +├── acme +├── androidstudio +├── apparmor +├── appimagekit +├── apt +├── arduino +├── bareos +├── burp +├── cds +├── clamav +├── collectd +├── cron +├── dovecot +├── flash +├── gitea +├── grafana +├── gufw +├── influxdb +├── ipfs +├── iptables +├── java +├── maildb +├── mariadb +├── misc +├── _modules +├── molotov +├── motd +├── netbox +├── nextcloud_desktop +├── nftables +├── nginx +├── npf +├── opendkim +├── openvpn_client +├── openvpn_server +├── packer +├── pkg +├── postfix +├── postgresql +├── pycharm +├── rclone +├── reactor +├── repos +├── rsync +├── rsyslog +├── _runners +├── salt_minion +├── samba +├── sensu +├── services +├── snmp +├── ssh +├── _states +├── sublimetext +├── sudo +├── syncthing +├── telegraf +├── telegram +├── tests +├── time +├── tmux +├── tor +├── transmission +├── users +├── vim +├── vsftpd +├── winpkg +├── wintse +└── zsh +``` + +### Scripts + +TBD + +## License + +```text +Copyright (c) 2019, 2020 PaulBSD +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation are those +of the authors and should not be interpreted as representing official policies, +either expressed or implied, of the paulbsd project. +``` diff --git a/cloud/cloud b/cloud/cloud new file mode 100644 index 0000000..b02a938 --- /dev/null +++ b/cloud/cloud @@ -0,0 +1,3 @@ +# vim:set ft=yaml: +wait_for_ip_timeout: 600 +sync_after_install: all diff --git a/cloud/cloud.profiles b/cloud/cloud.profiles new file mode 100644 index 0000000..4c7c759 --- /dev/null +++ b/cloud/cloud.profiles @@ -0,0 +1,20 @@ +# vim:set ft=yaml: +scaleway-xsmall-arm-ubuntu-16: + provider: scaleway-paris + image: eeb73cbf-78a9-4481-9e38-9aaadaf8e0c9 + commercial_type: C1 + +scaleway-xsmall-x86-ubuntu-16: + provider: scaleway-paris + image: 047f1372-3923-471f-82ca-5ff69dbaf0f7 + commercial_type: VC1S + +scaleway-lamp: + provider: scaleway-paris + image: 89457135-d446-41ba-a8df-d53e5bb54710 + commercial_type: VC1S + +scaleway-external: + provider: scaleway-amsterdam + image: 89ee4018-f8c3-4dc4-a6b5-bca14f985ebe + commercial_type: VC1S diff --git a/cloud/cloud.providers b/cloud/cloud.providers new file mode 100644 index 0000000..7dc4b15 --- /dev/null +++ b/cloud/cloud.providers @@ -0,0 +1,14 @@ +# vim:set ft=yaml: +scaleway-paris: + access_key: + token: + driver: scaleway + region: par1 + api_root: https://cp-par1.scaleway.com + +scaleway-amsterdam: + access_key: + token: + driver: scaleway + region: ams1 + api_root: https://cp-ams1.scaleway.com diff --git a/cloud/scaleway/scw01-ams.paulbsd.com b/cloud/scaleway/scw01-ams.paulbsd.com new file mode 100644 index 0000000..f55c1e0 --- /dev/null +++ b/cloud/scaleway/scw01-ams.paulbsd.com @@ -0,0 +1,6 @@ +scaleway-external: + - scw01-ams.paulbsd.com: + minion: + master: salt.paulbsd.com + ssh_username: root + key_filename: /root/.ssh/id_rsa diff --git a/cloud/scaleway/scw_lamp.map b/cloud/scaleway/scw_lamp.map new file mode 100644 index 0000000..8bc2182 --- /dev/null +++ b/cloud/scaleway/scw_lamp.map @@ -0,0 +1,7 @@ +scaleway-lamp: + - lamp1: + minion: + master: salt.paulbsd.com + ssh_username: root + key_filename: /root/.ssh/id_rsa + script_args: -P diff --git a/cloud/scaleway/scw_web.map b/cloud/scaleway/scw_web.map new file mode 100644 index 0000000..d375c2c --- /dev/null +++ b/cloud/scaleway/scw_web.map @@ -0,0 +1,16 @@ +scaleway-xsmall-x86-ubuntu-16: + - web1: + minion: + master: salt.paulbsd.com + ssh_username: root + key_filename: /root/.ssh/id_rsa + - web2: + minion: + master: salt.paulbsd.com + ssh_username: root + key_filename: /root/.ssh/id_rsa + - web3: + minion: + master: salt.paulbsd.com + ssh_username: root + key_filename: /root/.ssh/id_rsa diff --git a/config/master b/config/master new file mode 100644 index 0000000..fab423b --- /dev/null +++ b/config/master @@ -0,0 +1,80 @@ +# # vi:syntax=yaml + +interface: '0.0.0.0' + +log_level: debug +log_file: /var/log/salt/master + +file_recv: True + +#cache: mysql +#master_job_cache: redis +#ext_job_cache: redis +return: smtp +event_return: mysql + +external_auth: + pam: + paul: + - .* + salt: + - .* + - '@runner' + - '@wheel' + +rest_cherrypy: + port: 8033 + disable_ssl: True + +runner_dirs: + - /srv/salt/states/_runners + +reactor: + - 'salt/minion/*/start': + - salt://reactor/auth.sls + - 'salt/job/*/ret/*': + - salt://reactor/email-on-failure.sls + +state_output: changes +cython_enable: True + +file_roots: + base: + - /srv/salt/states + +pillar_roots: + base: + - /srv/salt/pillar + +pillar_includes_override_sls: True +pillar_merge_lists: False +pillar_source_merging_strategy: recurse + +ext_pillar: + - etcd: etcd_config root=/salt/common + - etcd: etcd_config root=/salt/hosts/%(minion_id)s + +etcd_config: + etcd.host: 127.0.0.1 + etcd.port: 2379 + +mysql.host: 'scw02-ams.paulbsd.com' +mysql.user: 'salt' +mysql.pass: '' +mysql.password: '' +mysql.db: 'salt' +mysql.port: 3306 +#mysql.table_name: 'salt_cache' + +#smtp.from: 'salt@paulbsd.com' +#smtp.to: 'postmaster@paulbsd.com' +#smtp.host: 'smtp.paulbsd.com' +#smtp.port: 465 +#smtp.username: 'sys@paulbsd.com' +#smtp.password: '' +#smtp.tls: True + +#mine_functions: +# provision: +# - mine_function: state.sls +# - provision diff --git a/config/master.sample b/config/master.sample new file mode 100644 index 0000000..8b30841 --- /dev/null +++ b/config/master.sample @@ -0,0 +1,867 @@ +##### Primary configuration settings ##### +########################################## +# This configuration file is used to manage the behavior of the Salt Master. +# Values that are commented out but have an empty line after the comment are +# defaults that do not need to be set in the config. If there is no blank line +# after the comment then the value is presented as an example and is not the +# default. + +# Per default, the master will automatically include all config files +# from master.d/*.conf (master.d is a directory in the same directory +# as the main master config file). +#default_include: master.d/*.conf + +# The address of the interface to bind to: +interface: '0.0.0.0' + +# Whether the master should listen for IPv6 connections. If this is set to True, +# the interface option must be adjusted, too. (For example: "interface: '::'") +#ipv6: True + +# The tcp port used by the publisher: +#publish_port: 4505 + +# The user under which the salt master will run. Salt will update all +# permissions to allow the specified user to run the master. The exception is +# the job cache, which must be deleted if this user is changed. If the +# modified files cause conflicts, set verify_env to False. +#user: root + +# The port used by the communication interface. The ret (return) port is the +# interface used for the file server, authentication, job returns, etc. +#ret_port: 4506 + +# Specify the location of the daemon process ID file: +#pidfile: /var/run/salt-master.pid + +# The root directory prepended to these options: pki_dir, cachedir, +# sock_dir, log_file, autosign_file, autoreject_file, extension_modules, +# key_logfile, pidfile: +#root_dir: / + +# Directory used to store public key data: +#pki_dir: /usr/pkg/etc/salt/pki/master + +# Directory to store job and cache data: +# This directory may contain sensitive data and should be protected accordingly. +# +#cachedir: /var/cache/salt/master + +# Directory for custom modules. This directory can contain subdirectories for +# each of Salt's module types such as "runners", "output", "wheel", "modules", +# "states", "returners", etc. +#extension_modules: + +# Directory for custom modules. This directory can contain subdirectories for +# each of Salt's module types such as "runners", "output", "wheel", "modules", +# "states", "returners", etc. +# Like 'extension_modules' but can take an array of paths +#module_dirs: +# - /var/cache/salt/minion/extmods + +# Verify and set permissions on configuration directories at startup: +#verify_env: True + +# Set the number of hours to keep old job information in the job cache: +#keep_jobs: 24 + +# Set the default timeout for the salt command and api. The default is 5 +# seconds. +#timeout: 5 + +# The loop_interval option controls the seconds for the master's maintenance +# process check cycle. This process updates file server backends, cleans the +# job cache and executes the scheduler. +#loop_interval: 60 + +# Set the default outputter used by the salt command. The default is "nested". +#output: nested + +# Return minions that timeout when running commands like test.ping +#show_timeout: True + +# By default, output is colored. To disable colored output, set the color value +# to False. +#color: True + +# Do not strip off the colored output from nested results and state outputs +# (true by default). +# strip_colors: False + +# Set the directory used to hold unix sockets: +#sock_dir: /var/run/salt/master + +# The master can take a while to start up when lspci and/or dmidecode is used +# to populate the grains for the master. Enable if you want to see GPU hardware +# data for your master. +# enable_gpu_grains: False + +# The master maintains a job cache. While this is a great addition, it can be +# a burden on the master for larger deployments (over 5000 minions). +# Disabling the job cache will make previously executed jobs unavailable to +# the jobs system and is not generally recommended. +#job_cache: True + +# Cache minion grains and pillar data in the cachedir. +#minion_data_cache: True + +# Store all returns in the given returner. +# Setting this option requires that any returner-specific configuration also +# be set. See various returners in salt/returners for details on required +# configuration values. (See also, event_return_queue below.) +# +event_return: mysql + +# On busy systems, enabling event_returns can cause a considerable load on +# the storage system for returners. Events can be queued on the master and +# stored in a batched fashion using a single transaction for multiple events. +# By default, events are not queued. +#event_return_queue: 0 + +# Only events returns matching tags in a whitelist +# event_return_whitelist: +# - salt/master/a_tag +# - salt/master/another_tag + +# Store all event returns _except_ the tags in a blacklist +# event_return_blacklist: +# - salt/master/not_this_tag +# - salt/master/or_this_one + +# Passing very large events can cause the minion to consume large amounts of +# memory. This value tunes the maximum size of a message allowed onto the +# master event bus. The value is expressed in bytes. +#max_event_size: 1048576 + +# By default, the master AES key rotates every 24 hours. The next command +# following a key rotation will trigger a key refresh from the minion which may +# result in minions which do not respond to the first command after a key refresh. +# +# To tell the master to ping all minions immediately after an AES key refresh, set +# ping_on_rotate to True. This should mitigate the issue where a minion does not +# appear to initially respond after a key is rotated. +# +# Note that ping_on_rotate may cause high load on the master immediately after +# the key rotation event as minions reconnect. Consider this carefully if this +# salt master is managing a large number of minions. +# +# If disabled, it is recommended to handle this event by listening for the +# 'aes_key_rotate' event with the 'key' tag and acting appropriately. +# ping_on_rotate: False + +# By default, the master deletes its cache of minion data when the key for that +# minion is removed. To preserve the cache after key deletion, set +# 'preserve_minion_cache' to True. +# +# WARNING: This may have security implications if compromised minions auth with +# a previous deleted minion ID. +#preserve_minion_cache: False + +# If max_minions is used in large installations, the master might experience +# high-load situations because of having to check the number of connected +# minions for every authentication. This cache provides the minion-ids of +# all connected minions to all MWorker-processes and greatly improves the +# performance of max_minions. +# con_cache: False + +# The master can include configuration from other files. To enable this, +# pass a list of paths to this option. The paths can be either relative or +# absolute; if relative, they are considered to be relative to the directory +# the main master configuration file lives in (this file). Paths can make use +# of shell-style globbing. If no files are matched by a path passed to this +# option, then the master will log a warning message. +# +# Include a config file from some other path: +# include: /usr/pkg/etc/salt/extra_config +# +# Include config from several files and directories: +# include: +# - /usr/pkg/etc/salt/extra_config + + +##### Large-scale tuning settings ##### +########################################## +# Max open files +# +# Each minion connecting to the master uses AT LEAST one file descriptor, the +# master subscription connection. If enough minions connect you might start +# seeing on the console (and then salt-master crashes): +# Too many open files (tcp_listener.cpp:335) +# Aborted (core dumped) +# +# By default this value will be the one of `ulimit -Hn`, ie, the hard limit for +# max open files. +# +# If you wish to set a different value than the default one, uncomment and +# configure this setting. Remember that this value CANNOT be higher than the +# hard limit. Raising the hard limit depends on your OS and/or distribution, +# a good way to find the limit is to search the internet. For example: +# raise max open files hard limit debian +# +#max_open_files: 100000 + +# The number of worker threads to start. These threads are used to manage +# return calls made from minions to the master. If the master seems to be +# running slowly, increase the number of threads. This setting can not be +# set lower than 3. +#worker_threads: 5 + +# Set the ZeroMQ high water marks +# http://api.zeromq.org/3-2:zmq-setsockopt + +# The publisher interface ZeroMQPubServerChannel +#pub_hwm: 1000 + +# These two ZMQ HWM settings, salt_event_pub_hwm and event_publisher_pub_hwm +# are significant for masters with thousands of minions. When these are +# insufficiently high it will manifest in random responses missing in the CLI +# and even missing from the job cache. Masters that have fast CPUs and many +# cores with appropriate worker_threads will not need these set as high. + +# On deployment with 8,000 minions, 2.4GHz CPUs, 24 cores, 32GiB memory has +# these settings: +# +# salt_event_pub_hwm: 128000 +# event_publisher_pub_hwm: 64000 + +# ZMQ high-water-mark for SaltEvent pub socket +#salt_event_pub_hwm: 20000 + +# ZMQ high-water-mark for EventPublisher pub socket +#event_publisher_pub_hwm: 10000 + + + +##### Security settings ##### +########################################## +# Enable "open mode", this mode still maintains encryption, but turns off +# authentication, this is only intended for highly secure environments or for +# the situation where your keys end up in a bad state. If you run in open mode +# you do so at your own risk! +#open_mode: False + +# Enable auto_accept, this setting will automatically accept all incoming +# public keys from the minions. Note that this is insecure. +#auto_accept: False + +# Time in minutes that a incoming public key with a matching name found in +# pki_dir/minion_autosign/keyid is automatically accepted. Expired autosign keys +# are removed when the master checks the minion_autosign directory. +# 0 equals no timeout +# autosign_timeout: 120 + +# If the autosign_file is specified, incoming keys specified in the +# autosign_file will be automatically accepted. This is insecure. Regular +# expressions as well as globing lines are supported. +#autosign_file: /usr/pkg/etc/salt/autosign.conf + +# Works like autosign_file, but instead allows you to specify minion IDs for +# which keys will automatically be rejected. Will override both membership in +# the autosign_file and the auto_accept setting. +#autoreject_file: /usr/pkg/etc/salt/autoreject.conf + +# Enable permissive access to the salt keys. This allows you to run the +# master or minion as root, but have a non-root group be given access to +# your pki_dir. To make the access explicit, root must belong to the group +# you've given access to. This is potentially quite insecure. If an autosign_file +# is specified, enabling permissive_pki_access will allow group access to that +# specific file. +#permissive_pki_access: False + +# Allow users on the master access to execute specific commands on minions. +# This setting should be treated with care since it opens up execution +# capabilities to non root users. By default this capability is completely +# disabled. +#client_acl: +# larry: +# - test.ping +# - network.* +# +# Blacklist any of the following users or modules +# +# This example would blacklist all non sudo users, including root from +# running any commands. It would also blacklist any use of the "cmd" +# module. This is completely disabled by default. +# +#client_acl_blacklist: +# users: +# - root +# - '^(?!sudo_).*$' # all non sudo users +# modules: +# - cmd + +# Enforce client_acl & client_acl_blacklist when users have sudo +# access to the salt command. +# +#sudo_acl: False + +# The external auth system uses the Salt auth modules to authenticate and +# validate users to access areas of the Salt system. +external_auth: + pam: + paul: + - .* + salt: + - .* + - '@runner' + - '@wheel' + +rest_cherrypy: +# host: 127.0.0.1 + port: 8000 + disable_ssl: True + +# +# Time (in seconds) for a newly generated token to live. Default: 12 hours +#token_expire: 43200 + +# Allow minions to push files to the master. This is disabled by default, for +# security purposes. +#file_recv: False + +# Set a hard-limit on the size of the files that can be pushed to the master. +# It will be interpreted as megabytes. Default: 100 +#file_recv_max_size: 100 + +# Signature verification on messages published from the master. +# This causes the master to cryptographically sign all messages published to its event +# bus, and minions then verify that signature before acting on the message. +# +# This is False by default. +# +# Note that to facilitate interoperability with masters and minions that are different +# versions, if sign_pub_messages is True but a message is received by a minion with +# no signature, it will still be accepted, and a warning message will be logged. +# Conversely, if sign_pub_messages is False, but a minion receives a signed +# message it will be accepted, the signature will not be checked, and a warning message +# will be logged. This behavior went away in Salt 2014.1.0 and these two situations +# will cause minion to throw an exception and drop the message. +# sign_pub_messages: False + +##### Salt-SSH Configuration ##### +########################################## + +# Pass in an alternative location for the salt-ssh roster file +#roster_file: /usr/pkg/etc/salt/roster + +# Pass in minion option overrides that will be inserted into the SHIM for +# salt-ssh calls. The local minion config is not used for salt-ssh. Can be +# overridden on a per-minion basis in the roster (`minion_opts`) +#ssh_minion_opts: +# gpg_keydir: /root/gpg + +##### Master Module Management ##### +########################################## +# Manage how master side modules are loaded. + +# Add any additional locations to look for master runners: +#runner_dirs: [] + +runner_dirs: + - /usr/pkg/etc/salt/_runners + +reactor: + - 'salt/minion/*/start': + - salt://reactor/auth.sls + - 'salt/job/*/ret/*': + - salt://reactor/email-on-failure.sls + +state_output: changes + +# Enable Cython for master side modules: +cython_enable: True + + +##### State System settings ##### +########################################## +# The state system uses a "top" file to tell the minions what environment to +# use and what modules to use. The state_top file is defined relative to the +# root of the base environment as defined in "File Server settings" below. +#state_top: top.sls + +# The master_tops option replaces the external_nodes option by creating +# a plugable system for the generation of external top data. The external_nodes +# option is deprecated by the master_tops option. +# +# To gain the capabilities of the classic external_nodes system, use the +# following configuration: +# master_tops: +# ext_nodes: +# +#master_tops: {} + +# The external_nodes option allows Salt to gather data that would normally be +# placed in a top file. The external_nodes option is the executable that will +# return the ENC data. Remember that Salt will look for external nodes AND top +# files and combine the results if both are enabled! +#external_nodes: None + +# The renderer to use on the minions to render the state data +#renderer: yaml_jinja + +# The Jinja renderer can strip extra carriage returns and whitespace +# See http://jinja.pocoo.org/docs/api/#high-level-api +# +# If this is set to True the first newline after a Jinja block is removed +# (block, not variable tag!). Defaults to False, corresponds to the Jinja +# environment init variable "trim_blocks". +#jinja_trim_blocks: True +# +# If this is set to True leading spaces and tabs are stripped from the start +# of a line to a block. Defaults to False, corresponds to the Jinja +# environment init variable "lstrip_blocks". +#jinja_lstrip_blocks: False + +# The failhard option tells the minions to stop immediately after the first +# failure detected in the state execution, defaults to False +#failhard: False + +# The state_verbose and state_output settings can be used to change the way +# state system data is printed to the display. By default all data is printed. +# The state_verbose setting can be set to True or False, when set to False +# all data that has a result of True and no changes will be suppressed. +#state_verbose: True + +# The state_output setting changes if the output is the full multi line +# output for each changed state if set to 'full', but if set to 'terse' +# the output will be shortened to a single line. If set to 'mixed', the output +# will be terse unless a state failed, in which case that output will be full. +# If set to 'changes', the output will be full unless the state didn't change. +#state_output: full + +# Automatically aggregate all states that have support for mod_aggregate by +# setting to 'True'. Or pass a list of state module names to automatically +# aggregate just those types. +# +# state_aggregate: +# - pkg +# +#state_aggregate: False + +# Send progress events as each function in a state run completes execution +# by setting to 'True'. Progress events are in the format +# 'salt/job//prog//'. +#state_events: False + +##### File Server settings ##### +########################################## +# Salt runs a lightweight file server written in zeromq to deliver files to +# minions. This file server is built into the master daemon and does not +# require a dedicated port. + +# The file server works on environments passed to the master, each environment +# can have multiple root directories, the subdirectories in the multiple file +# roots cannot match, otherwise the downloaded files will not be able to be +# reliably ensured. A base environment is required to house the top file. +# Example: +# file_roots: +# base: +# - /usr/pkg/etc/salt/states/ +# dev: +# - /usr/pkg/etc/salt/states/dev/services +# - /usr/pkg/etc/salt/states/dev/states +# prod: +# - /usr/pkg/etc/salt/states/prod/services +# - /usr/pkg/etc/salt/states/prod/states +# +file_roots: + base: + - /usr/pkg/etc/salt/states + - /home/shares/repo +# + +# When using multiple environments, each with their own top file, the +# default behaviour is an unordered merge. To prevent top files from +# being merged together and instead to only use the top file from the +# requested environment, set this value to 'same'. +#top_file_merging_strategy: merge + +# To specify the order in which environments are merged, set the ordering +# in the env_order option. Given a conflict, the last matching value will +# win. +#env_order: ['base', 'dev', 'prod'] + +# If top_file_merging_strategy is set to 'same' and an environment does not +# contain a top file, the top file in the environment specified by default_top +# will be used instead. +#default_top: base + +# The hash_type is the hash to use when discovering the hash of a file on +# the master server. The default is md5, but sha1, sha224, sha256, sha384 +# and sha512 are also supported. +# +# Prior to changing this value, the master should be stopped and all Salt +# caches should be cleared. +#hash_type: md5 + +# The buffer size in the file server can be adjusted here: +#file_buffer_size: 1048576 + +# A regular expression (or a list of expressions) that will be matched +# against the file path before syncing the modules and states to the minions. +# This includes files affected by the file.recurse state. +# For example, if you manage your custom modules and states in subversion +# and don't want all the '.svn' folders and content synced to your minions, +# you could set this to '/\.svn($|/)'. By default nothing is ignored. +#file_ignore_regex: +# - '/\.svn($|/)' +# - '/\.git($|/)' + +# A file glob (or list of file globs) that will be matched against the file +# path before syncing the modules and states to the minions. This is similar +# to file_ignore_regex above, but works on globs instead of regex. By default +# nothing is ignored. +# file_ignore_glob: +# - '*.pyc' +# - '*/somefolder/*.bak' +# - '*.swp' + +# File Server Backend +# +# Salt supports a modular fileserver backend system, this system allows +# the salt master to link directly to third party systems to gather and +# manage the files available to minions. Multiple backends can be +# configured and will be searched for the requested file in the order in which +# they are defined here. The default setting only enables the standard backend +# "roots" which uses the "file_roots" option. +#fileserver_backend: +# - roots +# +# To use multiple backends list them in the order they are searched: +#fileserver_backend: +# - git +# - roots +# +# Uncomment the line below if you do not want the file_server to follow +# symlinks when walking the filesystem tree. This is set to True +# by default. Currently this only applies to the default roots +# fileserver_backend. +#fileserver_followsymlinks: False +# +# Uncomment the line below if you do not want symlinks to be +# treated as the files they are pointing to. By default this is set to +# False. By uncommenting the line below, any detected symlink while listing +# files on the Master will not be returned to the Minion. +#fileserver_ignoresymlinks: True +# +# By default, the Salt fileserver recurses fully into all defined environments +# to attempt to find files. To limit this behavior so that the fileserver only +# traverses directories with SLS files and special Salt directories like _modules, +# enable the option below. This might be useful for installations where a file root +# has a very large number of files and performance is impacted. Default is False. +# fileserver_limit_traversal: False +# +# The fileserver can fire events off every time the fileserver is updated, +# these are disabled by default, but can be easily turned on by setting this +# flag to True +#fileserver_events: False + +# Git File Server Backend Configuration +# +# Gitfs can be provided by one of two python modules: GitPython or pygit2. If +# using pygit2, both libgit2 and git must also be installed. +#gitfs_provider: gitpython +# +# When using the git fileserver backend at least one git remote needs to be +# defined. The user running the salt master will need read access to the repo. +# +# The repos will be searched in order to find the file requested by a client +# and the first repo to have the file will return it. +# When using the git backend branches and tags are translated into salt +# environments. +# Note: file:// repos will be treated as a remote, so refs you want used must +# exist in that repo as *local* refs. +#gitfs_remotes: +# - git://github.com/saltstack/salt-states.git +# - file:///var/git/saltmaster +# +# The gitfs_ssl_verify option specifies whether to ignore ssl certificate +# errors when contacting the gitfs backend. You might want to set this to +# false if you're using a git backend that uses a self-signed certificate but +# keep in mind that setting this flag to anything other than the default of True +# is a security concern, you may want to try using the ssh transport. +#gitfs_ssl_verify: True +# +# The gitfs_root option gives the ability to serve files from a subdirectory +# within the repository. The path is defined relative to the root of the +# repository and defaults to the repository root. +#gitfs_root: somefolder/otherfolder +# +# +##### Pillar settings ##### +########################################## +# Salt Pillars allow for the building of global data that can be made selectively +# available to different minions based on minion grain filtering. The Salt +# Pillar is laid out in the same fashion as the file server, with environments, +# a top file and sls files. However, pillar data does not need to be in the +# highstate format, and is generally just key/value pairs. +pillar_roots: + base: + - /usr/pkg/etc/salt/pillar +# +#ext_pillar: +# - hiera: /etc/hiera.yaml +# - cmd_yaml: cat /usr/pkg/etc/salt/yaml + +#ext_pillar: +# - mysql: +# fromdb: +# query: 'SELECT +# FROM pillar +# WHERE minion_pattern LIKE %s' +# depth: 5 +# as_list: True +# with_lists: [1,3] + +#_pillar_first option allows for external pillar sources to populate +# before file system pillar. This allows for targeting file system pillar from +# ext_pillar. +#ext_pillar_first: False + +# The pillar_gitfs_ssl_verify option specifies whether to ignore ssl certificate +# errors when contacting the pillar gitfs backend. You might want to set this to +# false if you're using a git backend that uses a self-signed certificate but +# keep in mind that setting this flag to anything other than the default of True +# is a security concern, you may want to try using the ssh transport. +#pillar_gitfs_ssl_verify: True + +# The pillar_opts option adds the master configuration file data to a dict in +# the pillar called "master". This is used to set simple configurations in the +# master config file that can then be used on minions. +#pillar_opts: False + +# The pillar_safe_render_error option prevents the master from passing pillar +# render errors to the minion. This is set on by default because the error could +# contain templating data which would give that minion information it shouldn't +# have, like a password! When set true the error message will only show: +# Rendering SLS 'my.sls' failed. Please see master log for details. +#pillar_safe_render_error: True + +# The pillar_source_merging_strategy option allows you to configure merging strategy +# between different sources. It accepts four values: recurse, aggregate, overwrite, +# or smart. Recurse will merge recursively mapping of data. Aggregate instructs +# aggregation of elements between sources that use the #!yamlex renderer. Overwrite +# will verwrite elements according the order in which they are processed. This is +# behavior of the 2014.1 branch and earlier. Smart guesses the best strategy based +# on the "renderer" setting and is the default value. +#pillar_source_merging_strategy: smart + +# Recursively merge lists by aggregating them instead of replacing them. +#pillar_merge_lists: False + + +##### Syndic settings ##### +########################################## +# The Salt syndic is used to pass commands through a master from a higher +# master. Using the syndic is simple. If this is a master that will have +# syndic servers(s) below it, then set the "order_masters" setting to True. +# +# If this is a master that will be running a syndic daemon for passthrough, then +# the "syndic_master" setting needs to be set to the location of the master server +# to receive commands from. + +# Set the order_masters setting to True if this master will command lower +# masters' syndic interfaces. +#order_masters: False + +# If this master will be running a salt syndic daemon, syndic_master tells +# this master where to receive commands from. +#syndic_master: masterofmaster + +# This is the 'ret_port' of the MasterOfMaster: +#syndic_master_port: 4506 + +# PID file of the syndic daemon: +#syndic_pidfile: /var/run/salt-syndic.pid + +# LOG file of the syndic daemon: +#syndic_log_file: syndic.log + + +##### Peer Publish settings ##### +########################################## +# Salt minions can send commands to other minions, but only if the minion is +# allowed to. By default "Peer Publication" is disabled, and when enabled it +# is enabled for specific minions and specific commands. This allows secure +# compartmentalization of commands based on individual minions. + +# The configuration uses regular expressions to match minions and then a list +# of regular expressions to match functions. The following will allow the +# minion authenticated as foo.example.com to execute functions from the test +# and pkg modules. +#peer: +# foo.example.com: +# - test.* +# - pkg.* +# +# This will allow all minions to execute all commands: +#peer: +# .*: +# - .* +# +# This is not recommended, since it would allow anyone who gets root on any +# single minion to instantly have root on all of the minions! + +# Minions can also be allowed to execute runners from the salt master. +# Since executing a runner from the minion could be considered a security risk, +# it needs to be enabled. This setting functions just like the peer setting +# except that it opens up runners instead of module functions. +# +# All peer runner support is turned off by default and must be enabled before +# using. This will enable all peer runners for all minions: +#peer_run: +# .*: +# - .* +# +# To enable just the manage.up runner for the minion foo.example.com: +#peer_run: +# foo.example.com: +# - manage.up +# +# +##### Mine settings ##### +##################################### +# Restrict mine.get access from minions. By default any minion has a full access +# to get all mine data from master cache. In acl definion below, only pcre matches +# are allowed. +# mine_get: +# .*: +# - .* +# +# The example below enables minion foo.example.com to get 'network.interfaces' mine +# data only, minions web* to get all network.* and disk.* mine data and all other +# minions won't get any mine data. +# mine_get: +# foo.example.com: +# - network.interfaces +# web.*: +# - network.* +# - disk.* + + +##### Logging settings ##### +########################################## +# The location of the master log file +# The master log can be sent to a regular file, local path name, or network +# location. Remote logging works best when configured to use rsyslogd(8) (e.g.: +# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI +# format is: ://:/ +log_file: /var/log/salt/master +#log_file: file:///dev/log +#log_file: udp://loghost:10514 + +#log_file: /var/log/salt/master +#key_logfile: /var/log/salt/key + +# The level of messages to send to the console. +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# +# The following log levels are considered INSECURE and may log sensitive data: +# ['garbage', 'trace', 'debug'] +# +#log_level: warning +#log_level: debug + +# The level of messages to send to the log file. +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# If using 'log_granular_levels' this must be set to the highest desired level. +#log_level_logfile: warning + +# The date and time format used in log messages. Allowed date/time formating +# can be seen here: http://docs.python.org/library/time.html#time.strftime +#log_datefmt: '%H:%M:%S' +#log_datefmt_logfile: '%Y-%m-%d %H:%M:%S' + +# The format of the console logging messages. Allowed formatting options can +# be seen here: http://docs.python.org/library/logging.html#logrecord-attributes +# +# Console log colors are specified by these additional formatters: +# +# %(colorlevel)s +# %(colorname)s +# %(colorprocess)s +# %(colormsg)s +# +# Since it is desirable to include the surrounding brackets, '[' and ']', in +# the coloring of the messages, these color formatters also include padding as +# well. Color LogRecord attributes are only available for console logging. +# +#log_fmt_console: '%(colorlevel)s %(colormsg)s' +#log_fmt_console: '[%(levelname)-8s] %(message)s' +# +#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s' + +# This can be used to control logging levels more specificically. This +# example sets the main salt library at the 'warning' level, but sets +# 'salt.modules' to log at the 'debug' level: +# log_granular_levels: +# 'salt': 'warning' +# 'salt.modules': 'debug' +# +#log_granular_levels: {} + + +##### Node Groups ###### +########################################## +# Node groups allow for logical groupings of minion nodes. A group consists of a group +# name and a compound target. +#nodegroups: +# group1: 'L@foo.domain.com,bar.domain.com,baz.domain.com and bl*.domain.com' +# group2: 'G@os:Debian and foo.domain.com' + + +##### Range Cluster settings ##### +########################################## +# The range server (and optional port) that serves your cluster information +# https://github.com/ytoolshed/range/wiki/%22yamlfile%22-module-file-spec +# +#range_server: range:80 + + +##### Windows Software Repo settings ##### +########################################### +# Location of the repo on the master: +#winrepo_dir_ng: '/usr/pkg/etc/salt/states/win/repo-ng' +# +# List of git repositories to include with the local repo: +#winrepo_remotes_ng: +# - 'https://github.com/saltstack/salt-winrepo-ng.git' + + +##### Windows Software Repo settings - Pre 2015.8 ##### +######################################################## +# Legacy repo settings for pre-2015.8 Windows minions. +# +# Location of the repo on the master: +#winrepo_dir: '/usr/pkg/etc/salt/states/win/repo' +# +# Location of the master's repo cache file: +#winrepo_mastercachefile: '/usr/pkg/etc/salt/states/win/repo/winrepo.p' +# +# List of git repositories to include with the local repo: +#winrepo_remotes: +# - 'https://github.com/saltstack/salt-winrepo.git' + + +##### Returner settings ###### +############################################ +# Which returner(s) will be used for minion's result: +#return: mysql +#return: smtp +return: mysql,smtp + +mysql.host: '127.0.0.1' +mysql.user: 'salt' +mysql.pass: 'pass' +mysql.db: 'salt' +mysql.port: 3306 + +###### Miscellaneous settings ###### +############################################ +# Default match type for filtering events tags: startswith, endswith, find, regex, fnmatch +#event_match_type: startswith + +smtp.from: salt@example.com +smtp.to: salt@example.com,bob@example.com +smtp.host: localhost +smtp.port: 25 diff --git a/config/minion b/config/minion new file mode 100755 index 0000000..9bc3f15 --- /dev/null +++ b/config/minion @@ -0,0 +1,10 @@ +## Managed by PaulBSD Salt +master: salt.paulbsd.com +hash_type: sha256 +state_verbose: True +tcp_keepalive: True +tcp_keepalive_idle: 300 +random_reauth_delay: 60 +recon_default: 1000 +recon_max: 10000 +recon_randomize: True diff --git a/config/minion.sample b/config/minion.sample new file mode 100644 index 0000000..f6e0dcb --- /dev/null +++ b/config/minion.sample @@ -0,0 +1,781 @@ +##### Primary configuration settings ##### +########################################## +# This configuration file is used to manage the behavior of the Salt Minion. +# With the exception of the location of the Salt Master Server, values that are +# commented out but have an empty line after the comment are defaults that need +# not be set in the config. If there is no blank line after the comment, the +# value is presented as an example and is not the default. + +# Per default the minion will automatically include all config files +# from minion.d/*.conf (minion.d is a directory in the same directory +# as the main minion config file). +#default_include: minion.d/*.conf + +# Set the location of the salt master server. If the master server cannot be +# resolved, then the minion will fail to start. +#master: salt + +# Set http proxy information for the minion when doing requests +#proxy_host: +#proxy_port: +#proxy_username: +#proxy_password: + +# If multiple masters are specified in the 'master' setting, the default behavior +# is to always try to connect to them in the order they are listed. If random_master is +# set to True, the order will be randomized instead. This can be helpful in distributing +# the load of many minions executing salt-call requests, for example, from a cron job. +# If only one master is listed, this setting is ignored and a warning will be logged. +# NOTE: If master_type is set to failover, use master_shuffle instead. +#random_master: False + +# Use if master_type is set to failover. +#master_shuffle: False + +# Minions can connect to multiple masters simultaneously (all masters +# are "hot"), or can be configured to failover if a master becomes +# unavailable. Multiple hot masters are configured by setting this +# value to "str". Failover masters can be requested by setting +# to "failover". MAKE SURE TO SET master_alive_interval if you are +# using failover. +# master_type: str + +# Poll interval in seconds for checking if the master is still there. Only +# respected if master_type above is "failover". To disable the interval entirely, +# set the value to -1. (This may be necessary on machines which have high numbers +# of TCP connections, such as load balancers.) +# master_alive_interval: 30 + +# If the minion is in multi-master mode and the master_type configuration option +# is set to "failover", this setting can be set to "True" to force the minion +# to fail back to the first master in the list if the first master is back online. +#master_failback: False + +# If the minion is in multi-master mode, the "master_type" configuration is set to +# "failover", and the "master_failback" option is enabled, the master failback +# interval can be set to ping the top master with this interval, in seconds. +#master_failback_interval: 0 + +# Set whether the minion should connect to the master via IPv6: +#ipv6: False + +# Set the number of seconds to wait before attempting to resolve +# the master hostname if name resolution fails. Defaults to 30 seconds. +# Set to zero if the minion should shutdown and not retry. +# retry_dns: 30 + +# Set the port used by the master reply and authentication server. +#master_port: 4506 + +# The user to run salt. +#user: root + +# The user to run salt remote execution commands as via sudo. If this option is +# enabled then sudo will be used to change the active user executing the remote +# command. If enabled the user will need to be allowed access via the sudoers +# file for the user that the salt minion is configured to run as. The most +# common option would be to use the root user. If this option is set the user +# option should also be set to a non-root user. If migrating from a root minion +# to a non root minion the minion cache should be cleared and the minion pki +# directory will need to be changed to the ownership of the new user. +#sudo_user: root + +# Specify the location of the daemon process ID file. +#pidfile: /var/run/salt-minion.pid + +# The root directory prepended to these options: pki_dir, cachedir, log_file, +# sock_dir, pidfile. +#root_dir: / + +# The path to the minion's configuration file. +#conf_file: /usr/pkg/etc/salt/minion + +# The directory to store the pki information in +#pki_dir: /usr/pkg/etc/salt/pki/minion + +# Explicitly declare the id for this minion to use, if left commented the id +# will be the hostname as returned by the python call: socket.getfqdn() +# Since salt uses detached ids it is possible to run multiple minions on the +# same machine but with different ids, this can be useful for salt compute +# clusters. +#id: + +# Cache the minion id to a file when the minion's id is not statically defined +# in the minion config. Defaults to "True". This setting prevents potential +# problems when automatic minion id resolution changes, which can cause the +# minion to lose connection with the master. To turn off minion id caching, +# set this config to ``False``. +#minion_id_caching: True + +# Append a domain to a hostname in the event that it does not exist. This is +# useful for systems where socket.getfqdn() does not actually result in a +# FQDN (for instance, Solaris). +#append_domain: + +# Custom static grains for this minion can be specified here and used in SLS +# files just like all other grains. This example sets 4 custom grains, with +# the 'roles' grain having two values that can be matched against. +#grains: +# roles: +# - webserver +# - memcache +# deployment: datacenter4 +# cabinet: 13 +# cab_u: 14-15 +# +# Where cache data goes. +# This data may contain sensitive data and should be protected accordingly. +#cachedir: /var/cache/salt/minion + +# Append minion_id to these directories. Helps with +# multiple proxies and minions running on the same machine. +# Allowed elements in the list: pki_dir, cachedir, extension_modules +# Normally not needed unless running several proxies and/or minions on the same machine +# Defaults to ['cachedir'] for proxies, [] (empty list) for regular minions +#append_minionid_config_dirs: + +# Verify and set permissions on configuration directories at startup. +#verify_env: True + +# The minion can locally cache the return data from jobs sent to it, this +# can be a good way to keep track of jobs the minion has executed +# (on the minion side). By default this feature is disabled, to enable, set +# cache_jobs to True. +#cache_jobs: False + +# Set the directory used to hold unix sockets. +#sock_dir: /var/run/salt/minion + +# Set the default outputter used by the salt-call command. The default is +# "nested". +#output: nested +# +# By default output is colored. To disable colored output, set the color value +# to False. +#color: True + +# Do not strip off the colored output from nested results and state outputs +# (true by default). +# strip_colors: False + +# Backup files that are replaced by file.managed and file.recurse under +# 'cachedir'/file_backups relative to their original location and appended +# with a timestamp. The only valid setting is "minion". Disabled by default. +# +# Alternatively this can be specified for each file in state files: +# /etc/ssh/sshd_config: +# file.managed: +# - source: salt://ssh/sshd_config +# - backup: minion +# +#backup_mode: minion + +# When waiting for a master to accept the minion's public key, salt will +# continuously attempt to reconnect until successful. This is the time, in +# seconds, between those reconnection attempts. +#acceptance_wait_time: 10 + +# If this is nonzero, the time between reconnection attempts will increase by +# acceptance_wait_time seconds per iteration, up to this maximum. If this is +# set to zero, the time between reconnection attempts will stay constant. +#acceptance_wait_time_max: 0 + +# If the master rejects the minion's public key, retry instead of exiting. +# Rejected keys will be handled the same as waiting on acceptance. +#rejected_retry: False + +# When the master key changes, the minion will try to re-auth itself to receive +# the new master key. In larger environments this can cause a SYN flood on the +# master because all minions try to re-auth immediately. To prevent this and +# have a minion wait for a random amount of time, use this optional parameter. +# The wait-time will be a random number of seconds between 0 and the defined value. +random_reauth_delay: 60 + +# When waiting for a master to accept the minion's public key, salt will +# continuously attempt to reconnect until successful. This is the timeout value, +# in seconds, for each individual attempt. After this timeout expires, the minion +# will wait for acceptance_wait_time seconds before trying again. Unless your master +# is under unusually heavy load, this should be left at the default. +#auth_timeout: 60 + +# Number of consecutive SaltReqTimeoutError that are acceptable when trying to +# authenticate. +#auth_tries: 7 + +# The number of attempts to connect to a master before giving up. +# Set this to -1 for unlimited attempts. This allows for a master to have +# downtime and the minion to reconnect to it later when it comes back up. +# In 'failover' mode, it is the number of attempts for each set of masters. +# In this mode, it will cycle through the list of masters for each attempt. +# +# This is different than auth_tries because auth_tries attempts to +# retry auth attempts with a single master. auth_tries is under the +# assumption that you can connect to the master but not gain +# authorization from it. master_tries will still cycle through all +# the masters in a given try, so it is appropriate if you expect +# occasional downtime from the master(s). +#master_tries: 1 + +# If authentication fails due to SaltReqTimeoutError during a ping_interval, +# cause sub minion process to restart. +#auth_safemode: False + +# Ping Master to ensure connection is alive (minutes). +#ping_interval: 0 + +# To auto recover minions if master changes IP address (DDNS) +# auth_tries: 10 +# auth_safemode: False +# ping_interval: 90 +# +# Minions won't know master is missing until a ping fails. After the ping fail, +# the minion will attempt authentication and likely fails out and cause a restart. +# When the minion restarts it will resolve the masters IP and attempt to reconnect. + +# If you don't have any problems with syn-floods, don't bother with the +# three recon_* settings described below, just leave the defaults! +# +# The ZeroMQ pull-socket that binds to the masters publishing interface tries +# to reconnect immediately, if the socket is disconnected (for example if +# the master processes are restarted). In large setups this will have all +# minions reconnect immediately which might flood the master (the ZeroMQ-default +# is usually a 100ms delay). To prevent this, these three recon_* settings +# can be used. +# recon_default: the interval in milliseconds that the socket should wait before +# trying to reconnect to the master (1000ms = 1 second) +# +# recon_max: the maximum time a socket should wait. each interval the time to wait +# is calculated by doubling the previous time. if recon_max is reached, +# it starts again at recon_default. Short example: +# +# reconnect 1: the socket will wait 'recon_default' milliseconds +# reconnect 2: 'recon_default' * 2 +# reconnect 3: ('recon_default' * 2) * 2 +# reconnect 4: value from previous interval * 2 +# reconnect 5: value from previous interval * 2 +# reconnect x: if value >= recon_max, it starts again with recon_default +# +# recon_randomize: generate a random wait time on minion start. The wait time will +# be a random value between recon_default and recon_default + +# recon_max. Having all minions reconnect with the same recon_default +# and recon_max value kind of defeats the purpose of being able to +# change these settings. If all minions have the same values and your +# setup is quite large (several thousand minions), they will still +# flood the master. The desired behavior is to have timeframe within +# all minions try to reconnect. +# +# Example on how to use these settings. The goal: have all minions reconnect within a +# 60 second timeframe on a disconnect. +recon_default: 1000 +recon_max: 59000 +recon_randomize: True +# +# Each minion will have a randomized reconnect value between 'recon_default' +# and 'recon_default + recon_max', which in this example means between 1000ms +# 60000ms (or between 1 and 60 seconds). The generated random-value will be +# doubled after each attempt to reconnect. Lets say the generated random +# value is 11 seconds (or 11000ms). +# reconnect 1: wait 11 seconds +# reconnect 2: wait 22 seconds +# reconnect 3: wait 33 seconds +# reconnect 4: wait 44 seconds +# reconnect 5: wait 55 seconds +# reconnect 6: wait time is bigger than 60 seconds (recon_default + recon_max) +# reconnect 7: wait 11 seconds +# reconnect 8: wait 22 seconds +# reconnect 9: wait 33 seconds +# reconnect x: etc. +# +# In a setup with ~6000 thousand hosts these settings would average the reconnects +# to about 100 per second and all hosts would be reconnected within 60 seconds. +# recon_default: 100 +# recon_max: 5000 +# recon_randomize: False +# +# +# The loop_interval sets how long in seconds the minion will wait between +# evaluating the scheduler and running cleanup tasks. This defaults to a +# sane 60 seconds, but if the minion scheduler needs to be evaluated more +# often lower this value +#loop_interval: 60 + +# The grains can be merged, instead of overridden, using this option. +# This allows custom grains to defined different subvalues of a dictionary +# grain. By default this feature is disabled, to enable set grains_deep_merge +# to ``True``. +#grains_deep_merge: False + +# The grains_refresh_every setting allows for a minion to periodically check +# its grains to see if they have changed and, if so, to inform the master +# of the new grains. This operation is moderately expensive, therefore +# care should be taken not to set this value too low. +# +# Note: This value is expressed in __minutes__! +# +# A value of 10 minutes is a reasonable default. +# +# If the value is set to zero, this check is disabled. +#grains_refresh_every: 1 + +# Cache grains on the minion. Default is False. +#grains_cache: False + +# Cache rendered pillar data on the minion. Default is False. +# This may cause 'cachedir'/pillar to contain sensitive data that should be +# protected accordingly. +#minion_pillar_cache: False + +# Grains cache expiration, in seconds. If the cache file is older than this +# number of seconds then the grains cache will be dumped and fully re-populated +# with fresh data. Defaults to 5 minutes. Will have no effect if 'grains_cache' +# is not enabled. +# grains_cache_expiration: 300 + +# Determines whether or not the salt minion should run scheduled mine updates. +# Defaults to "True". Set to "False" to disable the scheduled mine updates +# (this essentially just does not add the mine update function to the minion's +# scheduler). +#mine_enabled: True + +# Determines whether or not scheduled mine updates should be accompanied by a job +# return for the job cache. Defaults to "False". Set to "True" to include job +# returns in the job cache for mine updates. +#mine_return_job: False + +# Example functions that can be run via the mine facility +# NO mine functions are established by default. +# Note these can be defined in the minion's pillar as well. +#mine_functions: +# test.ping: [] +# network.ip_addrs: +# interface: eth0 +# cidr: '10.0.0.0/8' + +# Windows platforms lack posix IPC and must rely on slower TCP based inter- +# process communications. Set ipc_mode to 'tcp' on such systems +#ipc_mode: ipc + +# Overwrite the default tcp ports used by the minion when in tcp mode +#tcp_pub_port: 4510 +#tcp_pull_port: 4511 + +# Passing very large events can cause the minion to consume large amounts of +# memory. This value tunes the maximum size of a message allowed onto the +# minion event bus. The value is expressed in bytes. +#max_event_size: 1048576 + +# To detect failed master(s) and fire events on connect/disconnect, set +# master_alive_interval to the number of seconds to poll the masters for +# connection events. +# +#master_alive_interval: 30 + +# The minion can include configuration from other files. To enable this, +# pass a list of paths to this option. The paths can be either relative or +# absolute; if relative, they are considered to be relative to the directory +# the main minion configuration file lives in (this file). Paths can make use +# of shell-style globbing. If no files are matched by a path passed to this +# option then the minion will log a warning message. +# +# Include a config file from some other path: +# include: /usr/pkg/etc/salt/extra_config +# +# Include config from several files and directories: +#include: +# - /usr/pkg/etc/salt/extra_config +# - /etc/roles/webserver + +# The syndic minion can verify that it is talking to the correct master via the +# key fingerprint of the higher-level master with the "syndic_finger" config. +#syndic_finger: '' +# +# +# +##### Minion module management ##### +########################################## +# Disable specific modules. This allows the admin to limit the level of +# access the master has to the minion. +#disable_modules: [cmd,test] +#disable_returners: [] + +# This is the reverse of disable_modules. The default, like disable_modules, is the empty list, +# but if this option is set to *anything* then *only* those modules will load. +# Note that this is a very large hammer and it can be quite difficult to keep the minion working +# the way you think it should since Salt uses many modules internally itself. At a bare minimum +# you need the following enabled or else the minion won't start. +#whitelist_modules: +# - cmdmod +# - test +# - config + +# Modules can be loaded from arbitrary paths. This enables the easy deployment +# of third party modules. Modules for returners and minions can be loaded. +# Specify a list of extra directories to search for minion modules and +# returners. These paths must be fully qualified! +#module_dirs: [] +#returner_dirs: [] +#states_dirs: [] +#render_dirs: [] +#utils_dirs: [] +# +# A module provider can be statically overwritten or extended for the minion +# via the providers option, in this case the default module will be +# overwritten by the specified module. In this example the pkg module will +# be provided by the yumpkg5 module instead of the system default. +#providers: +# pkg: yumpkg5 +# +# Enable Cython modules searching and loading. (Default: False) +#cython_enable: False +# +# Specify a max size (in bytes) for modules on import. This feature is currently +# only supported on *nix operating systems and requires psutil. +# modules_max_memory: -1 + + +##### State Management Settings ##### +########################################### +# The state management system executes all of the state templates on the minion +# to enable more granular control of system state management. The type of +# template and serialization used for state management needs to be configured +# on the minion, the default renderer is yaml_jinja. This is a yaml file +# rendered from a jinja template, the available options are: +# yaml_jinja +# yaml_mako +# yaml_wempy +# json_jinja +# json_mako +# json_wempy +# +#renderer: yaml_jinja +# +# The failhard option tells the minions to stop immediately after the first +# failure detected in the state execution. Defaults to False. +#failhard: False +# +# Reload the modules prior to a highstate run. +#autoload_dynamic_modules: True +# +# clean_dynamic_modules keeps the dynamic modules on the minion in sync with +# the dynamic modules on the master, this means that if a dynamic module is +# not on the master it will be deleted from the minion. By default, this is +# enabled and can be disabled by changing this value to False. +#clean_dynamic_modules: True +# +# Normally, the minion is not isolated to any single environment on the master +# when running states, but the environment can be isolated on the minion side +# by statically setting it. Remember that the recommended way to manage +# environments is to isolate via the top file. +#environment: None +# +# Isolates the pillar environment on the minion side. This functions the same +# as the environment setting, but for pillar instead of states. +#pillarenv: None +# +# If using the local file directory, then the state top file name needs to be +# defined, by default this is top.sls. +#state_top: top.sls +# +# Run states when the minion daemon starts. To enable, set startup_states to: +# 'highstate' -- Execute state.highstate +# 'sls' -- Read in the sls_list option and execute the named sls files +# 'top' -- Read top_file option and execute based on that file on the Master +#startup_states: '' +# +# List of states to run when the minion starts up if startup_states is 'sls': +#sls_list: +# - edit.vim +# - hyper +# +# Top file to execute if startup_states is 'top': +#top_file: '' + +# Automatically aggregate all states that have support for mod_aggregate by +# setting to True. Or pass a list of state module names to automatically +# aggregate just those types. +# +# state_aggregate: +# - pkg +# +#state_aggregate: False + +##### File Directory Settings ##### +########################################## +# The Salt Minion can redirect all file server operations to a local directory, +# this allows for the same state tree that is on the master to be used if +# copied completely onto the minion. This is a literal copy of the settings on +# the master but used to reference a local directory on the minion. + +# Set the file client. The client defaults to looking on the master server for +# files, but can be directed to look at the local file directory setting +# defined below by setting it to "local". Setting a local file_client runs the +# minion in masterless mode. +#file_client: remote + +# The file directory works on environments passed to the minion, each environment +# can have multiple root directories, the subdirectories in the multiple file +# roots cannot match, otherwise the downloaded files will not be able to be +# reliably ensured. A base environment is required to house the top file. +# Example: +# file_roots: +# base: +# - /usr/pkg/etc/salt/states/ +# dev: +# - /usr/pkg/etc/salt/states/dev/services +# - /usr/pkg/etc/salt/states/dev/states +# prod: +# - /usr/pkg/etc/salt/states/prod/services +# - /usr/pkg/etc/salt/states/prod/states +# +#file_roots: +# base: +# - /usr/pkg/etc/salt/states + +# Uncomment the line below if you do not want the file_server to follow +# symlinks when walking the filesystem tree. This is set to True +# by default. Currently this only applies to the default roots +# fileserver_backend. +#fileserver_followsymlinks: False +# +# Uncomment the line below if you do not want symlinks to be +# treated as the files they are pointing to. By default this is set to +# False. By uncommenting the line below, any detected symlink while listing +# files on the Master will not be returned to the Minion. +#fileserver_ignoresymlinks: True +# +# By default, the Salt fileserver recurses fully into all defined environments +# to attempt to find files. To limit this behavior so that the fileserver only +# traverses directories with SLS files and special Salt directories like _modules, +# enable the option below. This might be useful for installations where a file root +# has a very large number of files and performance is negatively impacted. Default +# is False. +#fileserver_limit_traversal: False + +# The hash_type is the hash to use when discovering the hash of a file in +# the local fileserver. The default is sha256, sha224, sha384 and sha512 are also supported. +# +# WARNING: While md5 and sha1 are also supported, do not use it due to the high chance +# of possible collisions and thus security breach. +# +# Warning: Prior to changing this value, the minion should be stopped and all +# Salt caches should be cleared. +#hash_type: sha256 + +# The Salt pillar is searched for locally if file_client is set to local. If +# this is the case, and pillar data is defined, then the pillar_roots need to +# also be configured on the minion: +#pillar_roots: +# base: +# - /usr/pkg/etc/salt/pillar + +# Set a hard-limit on the size of the files that can be pushed to the master. +# It will be interpreted as megabytes. Default: 100 +#file_recv_max_size: 100 +# +# +###### Security settings ##### +########################################### +# Enable "open mode", this mode still maintains encryption, but turns off +# authentication, this is only intended for highly secure environments or for +# the situation where your keys end up in a bad state. If you run in open mode +# you do so at your own risk! +#open_mode: False + +# Enable permissive access to the salt keys. This allows you to run the +# master or minion as root, but have a non-root group be given access to +# your pki_dir. To make the access explicit, root must belong to the group +# you've given access to. This is potentially quite insecure. +#permissive_pki_access: False + +# The state_verbose and state_output settings can be used to change the way +# state system data is printed to the display. By default all data is printed. +# The state_verbose setting can be set to True or False, when set to False +# all data that has a result of True and no changes will be suppressed. +#state_verbose: True + +# The state_output setting changes if the output is the full multi line +# output for each changed state if set to 'full', but if set to 'terse' +# the output will be shortened to a single line. +#state_output: full + +# The state_output_diff setting changes whether or not the output from +# successful states is returned. Useful when even the terse output of these +# states is cluttering the logs. Set it to True to ignore them. +#state_output_diff: False + +# The state_output_profile setting changes whether profile information +# will be shown for each state run. +#state_output_profile: True + +# Fingerprint of the master public key to validate the identity of your Salt master +# before the initial key exchange. The master fingerprint can be found by running +# "salt-key -F master" on the Salt master. +#master_finger: '' + + +###### Thread settings ##### +########################################### +# Disable multiprocessing support, by default when a minion receives a +# publication a new process is spawned and the command is executed therein. +#multiprocessing: True + + +##### Logging settings ##### +########################################## +# The location of the minion log file +# The minion log can be sent to a regular file, local path name, or network +# location. Remote logging works best when configured to use rsyslogd(8) (e.g.: +# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI +# format is: ://:/ +#log_file: /var/log/salt/minion +#log_file: file:///dev/log +#log_file: udp://loghost:10514 +# +#log_file: /var/log/salt/minion +#key_logfile: /var/log/salt/key + +# The level of messages to send to the console. +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# +# The following log levels are considered INSECURE and may log sensitive data: +# ['garbage', 'trace', 'debug'] +# +# Default: 'warning' +log_level: debug + +# The level of messages to send to the log file. +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# If using 'log_granular_levels' this must be set to the highest desired level. +# Default: 'warning' +#log_level_logfile: + +# The date and time format used in log messages. Allowed date/time formatting +# can be seen here: http://docs.python.org/library/time.html#time.strftime +#log_datefmt: '%H:%M:%S' +#log_datefmt_logfile: '%Y-%m-%d %H:%M:%S' + +# The format of the console logging messages. Allowed formatting options can +# be seen here: http://docs.python.org/library/logging.html#logrecord-attributes +# +# Console log colors are specified by these additional formatters: +# +# %(colorlevel)s +# %(colorname)s +# %(colorprocess)s +# %(colormsg)s +# +# Since it is desirable to include the surrounding brackets, '[' and ']', in +# the coloring of the messages, these color formatters also include padding as +# well. Color LogRecord attributes are only available for console logging. +# +#log_fmt_console: '%(colorlevel)s %(colormsg)s' +#log_fmt_console: '[%(levelname)-8s] %(message)s' +# +#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s' + +# This can be used to control logging levels more specificically. This +# example sets the main salt library at the 'warning' level, but sets +# 'salt.modules' to log at the 'debug' level: +# log_granular_levels: +# 'salt': 'warning' +# 'salt.modules': 'debug' +# +#log_granular_levels: {} + +# To diagnose issues with minions disconnecting or missing returns, ZeroMQ +# supports the use of monitor sockets to log connection events. This +# feature requires ZeroMQ 4.0 or higher. +# +# To enable ZeroMQ monitor sockets, set 'zmq_monitor' to 'True' and log at a +# debug level or higher. +# +# A sample log event is as follows: +# +# [DEBUG ] ZeroMQ event: {'endpoint': 'tcp://127.0.0.1:4505', 'event': 512, +# 'value': 27, 'description': 'EVENT_DISCONNECTED'} +# +# All events logged will include the string 'ZeroMQ event'. A connection event +# should be logged as the minion starts up and initially connects to the +# master. If not, check for debug log level and that the necessary version of +# ZeroMQ is installed. +# +#zmq_monitor: False + +###### Module configuration ##### +########################################### +# Salt allows for modules to be passed arbitrary configuration data, any data +# passed here in valid yaml format will be passed on to the salt minion modules +# for use. It is STRONGLY recommended that a naming convention be used in which +# the module name is followed by a . and then the value. Also, all top level +# data must be applied via the yaml dict construct, some examples: +# +# You can specify that all modules should run in test mode: +#test: True +# +# A simple value for the test module: +#test.foo: foo +# +# A list for the test module: +#test.bar: [baz,quo] +# +# A dict for the test module: +#test.baz: {spam: sausage, cheese: bread} +# +# +###### Update settings ###### +########################################### +# Using the features in Esky, a salt minion can both run as a frozen app and +# be updated on the fly. These options control how the update process +# (saltutil.update()) behaves. +# +# The url for finding and downloading updates. Disabled by default. +#update_url: False +# +# The list of services to restart after a successful update. Empty by default. +#update_restart_services: [] + + +###### Keepalive settings ###### +############################################ +# ZeroMQ now includes support for configuring SO_KEEPALIVE if supported by +# the OS. If connections between the minion and the master pass through +# a state tracking device such as a firewall or VPN gateway, there is +# the risk that it could tear down the connection the master and minion +# without informing either party that their connection has been taken away. +# Enabling TCP Keepalives prevents this from happening. + +# Overall state of TCP Keepalives, enable (1 or True), disable (0 or False) +# or leave to the OS defaults (-1), on Linux, typically disabled. Default True, enabled. +#tcp_keepalive: True + +# How long before the first keepalive should be sent in seconds. Default 300 +# to send the first keepalive after 5 minutes, OS default (-1) is typically 7200 seconds +# on Linux see /proc/sys/net/ipv4/tcp_keepalive_time. +#tcp_keepalive_idle: 300 + +# How many lost probes are needed to consider the connection lost. Default -1 +# to use OS defaults, typically 9 on Linux, see /proc/sys/net/ipv4/tcp_keepalive_probes. +#tcp_keepalive_cnt: -1 + +# How often, in seconds, to send keepalives after the first one. Default -1 to +# use OS defaults, typically 75 seconds on Linux, see +# /proc/sys/net/ipv4/tcp_keepalive_intvl. +#tcp_keepalive_intvl: -1 + + +###### Windows Software settings ###### +############################################ +# Location of the repository cache file on the master: +#win_repo_cachefile: 'salt://win/repo/winrepo.p' + + +###### Returner settings ###### +############################################ +# Which returner(s) will be used for minion's result: +#return: mysql + + +###### Miscellaneous settings ###### +############################################ +# Default match type for filtering events tags: startswith, endswith, find, regex, fnmatch +#event_match_type: startswith diff --git a/config/roster b/config/roster new file mode 100644 index 0000000..b422c39 --- /dev/null +++ b/config/roster @@ -0,0 +1,17 @@ +scw01-ams: + host: scw01-ams.paulbsd.com + user: paul + sudo: True + +scw02-ams: + host: scw02-ams.paulbsd.com + user: paul + +lxc01: + host: lxc01.paulbsd.com + user: paul + sudo: True + +nuc: + host: nuc.paulbsd.com + user: paul diff --git a/config/roster.sample b/config/roster.sample new file mode 100644 index 0000000..acdf276 --- /dev/null +++ b/config/roster.sample @@ -0,0 +1,11 @@ +scw01-ams.paulbsd.com: + host: scw01-ams.paulbsd.com + user: paul + sudo: True + priv: /home/paul/.ssh/id_rsa + +aws01-par.paulbsd.com: + host: aws01-par.paulbsd.com + user: ubuntu + sudo: True + priv: /home/paul/.ssh/id_rsa diff --git a/scripts/encrypt_password b/scripts/encrypt_password new file mode 100755 index 0000000..0a8cb8b --- /dev/null +++ b/scripts/encrypt_password @@ -0,0 +1,10 @@ +#!/bin/bash + +key_id=salt + +if [[ $1 != "" ]] +then + echo -n $1 | gpg --armor --batch --homedir="/etc/salt/gpgkeys" --trust-model always --encrypt -r "${key_id}" +else + echo "Please specify a password" +fi diff --git a/scripts/salt-test.sh b/scripts/salt-test.sh new file mode 100755 index 0000000..01b525e --- /dev/null +++ b/scripts/salt-test.sh @@ -0,0 +1,2 @@ +#!/bin/bash +salt-call -l debug --local --file-root=./states state.sls $1 diff --git a/states/_modules/custom.py b/states/_modules/custom.py new file mode 100755 index 0000000..db9af76 --- /dev/null +++ b/states/_modules/custom.py @@ -0,0 +1,19 @@ +#!/usr/bin/python3 + +import salt.exceptions + +def current_state(name): + ret = dict() + + ret['name'] = name + ret['foo'] = 'foo' + + return ret + +def change_state(name, foo): + ret = dict() + + ret['name'] = name + ret['foo'] = foo + + return ret \ No newline at end of file diff --git a/states/_modules/dkron.py b/states/_modules/dkron.py new file mode 100644 index 0000000..4999a78 --- /dev/null +++ b/states/_modules/dkron.py @@ -0,0 +1,28 @@ +#!/usr/bin/python3 + +import requests +import json + +def get_jobs(url="http://localhost:8080", verify=False): + fullurl = f"{url}/v1/jobs" + ret = dict() + try: + req = requests.request("get", fullurl, verify=verify) + except (requests.exceptions.RequestException) as exc: + raise f"Exception {exc} occured" + ret = req.json() + if req.status_code == 200: + return ret + return None + +def set_jobs(url="http://localhost:8080", verify=False, job={}): + fullurl = f"{url}/v1/jobs" + ret = dict() + try: + req = requests.request("post", fullurl, verify=verify, json=job) + except (requests.exceptions.RequestException) as exc: + raise f"Exception {exc} occured" + ret = req.json() + if req.status_code == 201: + return ret + return None \ No newline at end of file diff --git a/states/_modules/ovhapi.py b/states/_modules/ovhapi.py new file mode 100644 index 0000000..a929e94 --- /dev/null +++ b/states/_modules/ovhapi.py @@ -0,0 +1,153 @@ +#!/usr/bin/python3 + +from __future__ import absolute_import, unicode_literals, print_function + +import re +import salt +import requests +import ovh + +from salt.exceptions import CommandExecutionError, ArgumentValueError +from ovh.exceptions import ResourceNotFoundError, APIError + + +def __virtual__(): + return True + +def _config(): + config = __salt__['config.get']('ovh') + if not config: + raise CommandExecutionError( + 'OVH execution module configuration could not be found' + ) + return config + + +def _auth(): + cfg = _config() + client = ovh.Client( + endpoint=cfg['endpoint'], + application_key=cfg['application_key'], + application_secret=cfg['application_secret'], + consumer_key=cfg['consumer_key'], + ) + return client + + +def domain_get_zone(zone=""): + ''' + Get DNS zone extraction + + zone + Zone name to fetch + ''' + + if zone == "": + raise ArgumentValueError("Zone is not defined") + client = _auth() + results = client.get(f'/domain/zone/{zone}/export') + return results + + +def domain_get_record(zone="", fieldType="", subDomain=""): + ''' + Records of the zone + + zone + Zone name to fetch + fieldType + Filter the value of fieldType property (like) + subDomain + Filter the value of subDomain property (like) + ''' + + if zone == "": + raise ArgumentValueError("Zone is not defined") + results = [] + client = _auth() + try: + records = client.get(f'/domain/zone/{zone}/record', + fieldType=fieldType, + subDomain=subDomain) + except APIError: + return "Query failed in OVH API" + for record in records: + try: + req = client.get(f'/domain/zone/{zone}/record/{record}') + results.append(req) + except APIError: + return "Query failed in OVH API" + return results + + +def domain_post_record(zone="", fieldType="", subDomain="", target="", ttl=0): + ''' + Create a new DNS record + + zone + The internal name of your zone + fieldType + Filter the value of fieldType property (like) + subDomain + Filter the value of subDomain property (like) + target + Resource record target + ttl + Resource record ttl + ''' + + if zone == "": + raise ArgumentValueError("Zone is not defined") + client = _auth() + req = client.post(f'/domain/zone/{zone}/record', + fieldType=fieldType, + subDomain=subDomain, + target=target, + ttl=ttl) + return req + + +def domain_delete_record(zone="", fieldType="", subDomain=""): + ''' + Delete a DNS record (Don't forget to refresh the zone) + + zone + The internal name of your zone + fieldType + Filter the value of fieldType property (like) + subDomain + Filter the value of subDomain property (like) + ''' + + if zone == "": + raise ArgumentValueError("Zone is not defined") + results = [] + client = _auth() + try: + records = client.get(f'/domain/zone/{zone}/record', + fieldType=fieldType, + subDomain=subDomain) + except APIError: + return "Query failed in OVH API" + for record in records: + try: + req = client.delete(f'/domain/zone/{zone}/record/{record}') + results.append(req) + except ResourceNotFoundError: + return "Resource not found in OVH API" + return results + + +def domain_refresh_zone(zone=""): + ''' + Apply zone modification on DNS servers + + zone + The internal name of your zone + ''' + + if zone == "": + raise ArgumentValueError("Zone is not defined") + client = _auth() + req = client.post(f'/domain/zone/{zone}/refresh') + return req \ No newline at end of file diff --git a/states/_modules/syncthing.py b/states/_modules/syncthing.py new file mode 100644 index 0000000..cae0b23 --- /dev/null +++ b/states/_modules/syncthing.py @@ -0,0 +1,59 @@ +#!/usr/bin/python3 + +import requests +import json +import salt.exceptions +import xml.etree.ElementTree as ET + +def get_apikey(configfile="/root/.config/syncthing/config.xml"): + try: + tree = ET.parse(configfile) + root = tree.getroot() + apikey = root.find("./gui/apikey").text + return apikey + except (FileNotFoundError,ET.ParseError,AttributeError) as e: + raise "Exception {0} occured".format(e) + return "" + +def get_config(url, verify, apikey): + fullurl = "{0}/rest/system/config".format(url) + ret = dict() + try: + req = requests.request("get", fullurl, verify=verify, headers={"X-API-Key": apikey}) + except (requests.exceptions.RequestException) as exc: + raise "Exception {0} occured".format(exc) + ret = req.json() + if req.status_code == 200: + return ret + return None + +def set_config(url, verify, apikey, config): + fullurl = "{0}/rest/system/config".format(url) + try: + req = requests.request("post", fullurl, verify=verify, headers={"X-API-Key": apikey}, json=config) + except (requests.exceptions.RequestException) as exc: + raise "Exception {0} occured".format(exc) + if req.status_code == 200: + return True + return None + +def insync(url, verify, apikey): + fullurl = "{0}/rest/system/config/insync".format(url) + try: + req = requests.request("get", fullurl, verify=verify, headers={"X-API-Key": apikey}) + except (requests.exceptions.RequestException) as exc: + raise "Exception {0} occured".format(exc) + ret = req.json() + if req.status_code == 200: + return ret + return None + +def restart(url, verify, apikey): + fullurl = "{0}/rest/system/restart".format(url) + try: + req = requests.post(fullurl, verify=verify, headers={"X-API-Key": apikey}) + except (requests.exceptions.RequestException) as exc: + raise "Exception {0} occured".format(exc) + if req.status_code == 200: + return {} + return None \ No newline at end of file diff --git a/states/_runners/process_minion_data.py b/states/_runners/process_minion_data.py new file mode 100755 index 0000000..8503982 --- /dev/null +++ b/states/_runners/process_minion_data.py @@ -0,0 +1,51 @@ +#!/usr/pkg/bin/python2 +#-*- coding: utf-8 -*- + +import os +import subprocess +import salt.modules.smtp +import json + +''' +For use with salt reactor +''' + +def email_errors(fromaddr, toaddrs, subject, data_str, smtp_server): + data = eval(data_str) + error = False + changes = False + + try: + if type(data['return']) is dict: + for state, result in data['return'].iteritems(): + if not result['result']: + error = True + break + if result['changes']: + changes = True + break + else: + if not data['success']: + error = True + except KeyError as e: + exit() + + #if error or changes: + if error: + js = subprocess.check_output(["salt-run", "--out=json", "jobs.lookup_jid", data['jid']]) + body = "JobId is %s\n" % (data['jid']) + outdata = json.loads(js) + nodename = outdata.keys()[0] + for i in outdata[nodename]: + if not outdata[nodename][i]["result"]: + name = outdata[nodename][i]["name"] + comment = outdata[nodename][i]["comment"].rstrip('\n') + data = "%s- %s / %s\n" % (body, name, comment) + salt.modules.smtp.send_msg(recipient=toaddrs, message=data, subject=subject, sender=fromaddr, server=smtp_server, use_ssl=False) + return True + +def email_auth(fromaddr, toaddrs, subject, data_str, smtp_server): + data = eval(data_str) + salt.modules.smtp.send_msg(recipient=toaddrs, message=data, subject=subject, sender=fromaddr, server=smtp_server, use_ssl=False) + + return True \ No newline at end of file diff --git a/states/_states/custom.py b/states/_states/custom.py new file mode 100755 index 0000000..caa0e1a --- /dev/null +++ b/states/_states/custom.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 + +import salt.exceptions + +def current_state(name): + ret = dict() + + ret['name'] = 'blabla' + + return ret + +def enforce_custom_thing(name, foo, bar=True): + ''' + Enforce the state of a custom thing + + This state module does a custom thing. It calls out to the execution module + ``y_custom_module`` in order to check the current system and perform any + needed changes. + + name + The thing to do something to + foo + A required argument + bar : True + An argument with a default value + ''' + ret = { + 'name': name, + 'changes': {}, + 'result': False, + 'comment': '', + } + + # Start with basic error-checking. Do all the passed parameters make sense + # and agree with each-other? + if bar == True and foo.startswith('Foo'): + raise salt.exceptions.SaltInvocationError( + 'Argument "foo" cannot start with "Foo" if argument "bar" is True.') + + # Check the current state of the system. Does anything need to change? + current_state = __salt__['custom.current_state'](name) + + if current_state == foo: + ret['result'] = True + ret['comment'] = 'System already in the correct state %s' % name + return ret + + # The state of the system does need to be changed. Check if we're running + # in ``test=true`` mode. + if __opts__['test'] == True: + ret['comment'] = 'The state of "{0}" will be changed.'.format(name) + ret['changes'] = { + 'old': current_state, + 'new': 'Description, diff, whatever of the new state', + } + + # Return ``None`` when running with ``test=true``. + ret['result'] = None + + return ret + + # Finally, make the actual change and return the result. + new_state = __salt__['custom.change_state'](name, foo) + + ret['comment'] = 'The state of "{0}" was changed!'.format(name) + + ret['changes'] = { + 'old': current_state, + 'new': new_state, + } + + ret['result'] = True + + return ret \ No newline at end of file diff --git a/states/_states/dkron.py b/states/_states/dkron.py new file mode 100644 index 0000000..6cdaf91 --- /dev/null +++ b/states/_states/dkron.py @@ -0,0 +1,24 @@ +#!/usr/bin/python3 + +from __future__ import absolute_import, print_function, unicode_literals +import salt.utils.dictupdate +import salt.utils.dictdiffer + +def jobs(name, url="http://localhost:8080", verify=False, jobs=[]): + ret = {'name': name, + 'changes': {}, + 'result': True, + 'comment': 'Config is up to date'} + + #dk_jobs = [] + #dk_jobs = __salt__['dkron.get_jobs'](url, verify) + + for job in jobs: + res = __salt__['dkron.set_jobs'](url, verify, job) + if res is not None: + ret['changes'][job['name']] = res + else: + ret['result'] = False + ret['comment'] = "Error occured" + + return ret diff --git a/states/_states/ovhapi.py b/states/_states/ovhapi.py new file mode 100644 index 0000000..faf2698 --- /dev/null +++ b/states/_states/ovhapi.py @@ -0,0 +1,65 @@ +#!/usr/bin/python3 + +from __future__ import absolute_import, print_function, unicode_literals + +import salt.utils.dictupdate +import salt.utils.dictdiffer + +def _error(ret, err_msg): + ret['result'] = False + ret['comment'] = err_msg + return ret + +def _str_split(string): + delim = "\n" + return [e + delim for e in string.split(delim) if e] + +def domain_record_present(name, + zone=None, + recordname=None, + recordtype=None, + target=None, + ttl=0): + ret = { + 'name': name, + 'changes': {}, + 'result': True, + 'comment': 'Config is up to date' + } + + + if name is None: + return _error(ret, 'Must provide name to ovhapi.domain_record_present') + if zone is None: + return _error(ret, 'Must provide dns zone to ovhapi.domain_record_present') + if recordname is None: + return _error(ret, 'Must provide record name to ovhapi.domain_record_present') + if recordtype is None: + return _error(ret, 'Must provide record type to ovhapi.domain_record_present') + if target is None: + return _error(ret, 'Must provide target to ovhapi.domain_record_present') + + # check if record exists + if len(__salt__['ovhapi.domain_get_record'](zone=zone, + fieldType=recordtype, + subDomain=recordname)): + ret['comment'] = f"Record on {zone} named {recordname} with type {recordtype} already exists" + return ret + + cur_zone_state = __salt__['ovhapi.domain_get_zone'](zone=zone) + + res = __salt__['ovhapi.domain_post_record']( + zone=zone, + subDomain=recordname, + fieldType=recordtype, + target=target, + ttl=ttl) + + new_zone_state = __salt__['ovhapi.domain_get_zone'](zone=zone) + + ret['changes'] = { + "diff": salt.utils.stringutils.get_diff(_str_split(cur_zone_state), _str_split(new_zone_state)) + } + ret['comment'] = f'Result is {res}' + + return ret \ No newline at end of file diff --git a/states/_states/syncthing.py b/states/_states/syncthing.py new file mode 100644 index 0000000..197510a --- /dev/null +++ b/states/_states/syncthing.py @@ -0,0 +1,27 @@ +#!/usr/bin/python3 + +from __future__ import absolute_import, print_function, unicode_literals +import salt.utils.dictupdate +import salt.utils.dictdiffer + +def config(name, verify, url, cfg): + ret = {'name': name, + 'changes': {}, + 'result': True, + 'comment': 'config is up to date'} + + cfg = dict(cfg) + + apikey = __salt__['syncthing.get_apikey']() + st_cfg = __salt__['syncthing.get_config'](url, verify, apikey) + + cfg['gui']['apiKey'] = apikey + + res_cfg = salt.utils.dictupdate.update(st_cfg, cfg, recursive_update=True, merge_lists=False) + + ## Return to managed to set result + __salt__['syncthing.set_config'](url, verify, apikey, res_cfg) + + ret['changes'] = salt.utils.dictdiffer.deep_diff(st_cfg, res_cfg) + + return ret \ No newline at end of file diff --git a/states/acme/defaults.yaml b/states/acme/defaults.yaml new file mode 100644 index 0000000..f66d5ee --- /dev/null +++ b/states/acme/defaults.yaml @@ -0,0 +1,20 @@ +--- +acme: + enabled: true + directories: + - "/etc/acme/dh/" + - "/etc/acme/keys/" + - "/etc/acme/certs/" + dh: + path: "/etc/acme/dh/dh.pem" + keysize: 1024 + keysize: 4096 + domain: "*.example.com" + dns: "dns_provider" + keyfile: "/etc/acme/keys/private.key" + fullchainfile: "/etc/acme/certs/certificate.crt" + provider: + api: + application_key: "test" + application_secret: "test" + consumer_key: "test" \ No newline at end of file diff --git a/states/acme/init.sls b/states/acme/init.sls new file mode 100644 index 0000000..72d1d77 --- /dev/null +++ b/states/acme/init.sls @@ -0,0 +1,50 @@ +# vim:syntax=yaml +--- +{%- from "acme/map.jinja" import acme with context %} +acme-install: + cmd.run: + - name: "curl https://get.acme.sh | sh" + - runas: root + - cwd: /root + - env: + - HOME: /root + - unless: /bin/bash -c "[[ -f /root/.acme.sh/acme.sh ]]" + +acme-upgrade: + cmd.run: + - name: /root/.acme.sh/acme.sh --upgrade + - runas: root + - cwd: /root + - env: + - HOME: /root + - require: + - cmd: acme-install + +{%- for dir in acme.directories %} +acme-directories-{{ dir }}: + file.directory: + - name: {{ dir }} + - makedirs: True +{%- endfor %} + +acme-dh-params: + cmd.run: + - name: openssl dhparam -out {{ acme.dh.path }} {{ acme.dh.keysize }} + - creates: {{ acme.dh.path }} + +acme-certs: + cmd.run: + - name: /root/.acme.sh/acme.sh --issue {%- for dom in acme.domains %} -d '{{ dom }}' {% endfor -%} --dns dns_ovh --cert-file '' --key-file '{{ acme.keyfile }}' --fullchain-file '{{ acme.fullchainfile }}' -k {{ acme.keysize }} + - env: + - OVH_AK: '{{ acme.provider.api.application_key }}' + - OVH_AS: '{{ acme.provider.api.application_secret }}' + - OVH_CK: '{{ acme.provider.api.consumer_key }}' + - HOME: '/root' + - success_retcodes: + - 0 + - 1 + - 2 + - runas: root + - cwd: /root + - require: + - cmd: acme-install \ No newline at end of file diff --git a/states/acme/map.jinja b/states/acme/map.jinja new file mode 100644 index 0000000..e63dc4b --- /dev/null +++ b/states/acme/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "acme/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='acme') -%} + +{%- set acme = salt['pillar.get']('acme', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/androidstudio/defaults.yaml b/states/androidstudio/defaults.yaml new file mode 100644 index 0000000..9f678cd --- /dev/null +++ b/states/androidstudio/defaults.yaml @@ -0,0 +1,5 @@ +--- +androidstudio: + enabled: true + install_dir: /usr/local/apps + config: \ No newline at end of file diff --git a/states/androidstudio/init.sls b/states/androidstudio/init.sls new file mode 100644 index 0000000..cf8e776 --- /dev/null +++ b/states/androidstudio/init.sls @@ -0,0 +1,24 @@ +--- +# https://developer.android.com/studio/archive.html +{%- from "androidstudio/map.jinja" import androidstudio with context %} + +{%- if salt['file.grep'](androidstudio.install_dir + '/android-studio/build.txt', androidstudio.version_regex)['retcode'] == 1 or not salt['file.file_exists'](androidstudio.install_dir + '/android-studio/build.txt') %} +androidstudio-archive-extract: + archive.extracted: + - name: {{ androidstudio.install_dir }} + - source: {{ androidstudio.mirror }}/{{ androidstudio.version }}/android-studio-ide-{{ androidstudio.tag }}-linux.tar.gz + - skip_verify: True + - archive_format: tar + - overwrite: True + +androidstudio-shortcut: + file.managed: + - name: /usr/share/applications/jetbrains-studio.desktop + - source: salt://androidstudio/jetbrains-studio.desktop.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - onchanges: + - androidstudio-archive-extract +{%- endif %} \ No newline at end of file diff --git a/states/androidstudio/jetbrains-studio.desktop.j2 b/states/androidstudio/jetbrains-studio.desktop.j2 new file mode 100644 index 0000000..94f0193 --- /dev/null +++ b/states/androidstudio/jetbrains-studio.desktop.j2 @@ -0,0 +1,12 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "androidstudio/map.jinja" import androidstudio with context %} +[Desktop Entry] +Version=1.0 +Type=Application +Name=Android Studio +Icon={{ androidstudio.install_dir }}/android-studio/bin/studio.png +Exec="{{ androidstudio.install_dir }}/android-studio/bin/studio.sh" %f +Comment=Develop with pleasure on Android! +Categories=Development;IDE; +Terminal=false +StartupWMClass=jetbrains-studio \ No newline at end of file diff --git a/states/androidstudio/map.jinja b/states/androidstudio/map.jinja new file mode 100644 index 0000000..eb4d55f --- /dev/null +++ b/states/androidstudio/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "androidstudio/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='androidstudio') -%} + +{%- set androidstudio = salt['pillar.get']('androidstudio', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/apparmor/defaults.yaml b/states/apparmor/defaults.yaml new file mode 100644 index 0000000..b45cafb --- /dev/null +++ b/states/apparmor/defaults.yaml @@ -0,0 +1,4 @@ +--- +apparmor: + enabled: true + config: \ No newline at end of file diff --git a/states/apparmor/init.sls b/states/apparmor/init.sls new file mode 100644 index 0000000..11d7d66 --- /dev/null +++ b/states/apparmor/init.sls @@ -0,0 +1,21 @@ +--- +{%- from "apparmor/map.jinja" import apparmor with context %} +{%- if apparmor.enabled is defined and apparmor.enabled %} +{%- for apparmor_config in ['usr.bin.skype','opt.kingsoft','usr.bin.spotify','opt.sublime_text_3.sublime_text'] %} +apparmor-{{ apparmor_config }}: + file.managed: + - name: "/etc/apparmor.d/{{ apparmor_config }}" + - source: "salt://apparmor/{{ apparmor_config }}.j2" + - user: root + - group: root + - mode: 0644 + - template: jinja + - watch_in: + - service: apparmor-reload +{%- endfor %} + +apparmor-reload: + service.running: + - name: apparmor + - enable: true +{%- endif %} \ No newline at end of file diff --git a/states/apparmor/map.jinja b/states/apparmor/map.jinja new file mode 100644 index 0000000..9776a15 --- /dev/null +++ b/states/apparmor/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "apparmor/defaults.yaml" as default_settings %} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='apparmor') %} + +{%- set apparmor = salt['pillar.get']('apparmor', default=defaults, merge=True) %} \ No newline at end of file diff --git a/states/apparmor/opt.kingsoft.j2 b/states/apparmor/opt.kingsoft.j2 new file mode 100644 index 0000000..fd28818 --- /dev/null +++ b/states/apparmor/opt.kingsoft.j2 @@ -0,0 +1,27 @@ +#include +/opt/kingsoft/** { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + /opt/kingsoft/** rwmkl, + owner @{HOME}/.kingsoft/** rw, + owner @{HOME}/.config/Kingsoft/ rwmkl, + owner @{HOME}/.config/Kingsoft/** rwmkl, + owner @{HOME}/ r, + owner @{HOME}/Documents/ rw, + owner @{HOME}/Documents/** rw, + + deny network inet, +} \ No newline at end of file diff --git a/states/apparmor/opt.sublime_text_3.sublime_text.j2 b/states/apparmor/opt.sublime_text_3.sublime_text.j2 new file mode 100644 index 0000000..2cccf00 --- /dev/null +++ b/states/apparmor/opt.sublime_text_3.sublime_text.j2 @@ -0,0 +1,37 @@ +#include + +/opt/sublime_text_3/sublime_text { + #include + #include + #include + #include + #include + #include + #include + #include + + /usr/share/mate/applications/** r, + /usr/bin/caja rwix, + /usr/share/glib-*/schemas/** r, + /dev/null r, + + /{dev,run}/{,shm/}** rwmkl, + /opt/sublime_text_3/ rwixmkl, + /opt/sublime_text_3/** rwixmkl, + + owner @{HOME}/.config/sublime-text-3/ rwmkl, + owner @{HOME}/.config/sublime-text-3/** rwmkl, + owner @{HOME}/ rwmkl, + owner @{HOME}/** rwmkl, + + deny network inet, + deny network inet6, + deny network raw, +} + +/opt/sublime_text_3/plugin_host { + #include + deny network inet, + deny network inet6, + deny network raw, +} \ No newline at end of file diff --git a/states/apparmor/usr.bin.skype.j2 b/states/apparmor/usr.bin.skype.j2 new file mode 100644 index 0000000..0fb3c34 --- /dev/null +++ b/states/apparmor/usr.bin.skype.j2 @@ -0,0 +1,77 @@ +#include +/usr/bin/skype { + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + @{PROC}/sys/kernel/{ostype,osrelease} r, + @{PROC}/@{pid}/net/arp r, + owner @{PROC}/@{pid}/auxv r, + owner @{PROC}/@{pid}/cmdline r, + owner @{PROC}/@{pid}/fd/ r, + owner @{PROC}/@{pid}/task/ r, + owner @{PROC}/@{pid}/task/[0-9]*/stat r, + + /sys/devices/**/power_supply/**/online r, + /sys/devices/system/cpu/ r, + /sys/devices/system/cpu/cpu[0-9]*/cpufreq/scaling_{cur_freq,max_freq} r, + + /dev/ r, + owner /{dev,run}/shm/pulse-shm* m, + /dev/snd/* m, + /dev/video* mrw, + + /var/cache/libx11/compose/* r, + + # should this be in a separate KDE abstraction? + owner @{HOME}/.kde{,4}/share/config/kioslaverc r, + + /usr/bin/skype mr, + /etc/xdg/sni-qt.conf rk, + /etc/xdg/Trolltech.conf rk, + /usr/share/skype/** kr, + /usr/share/skype/**/*.qm mr, + /usr/share/skype/sounds/*.wav kr, + /usr/lib{,32}/pango/** mr, + /usr/lib{,32}/libv4l/* mr, + + # For opening links in the browser (still requires explicit access to execute + # the browser) + /usr/bin/xdg-open ixr, + + owner @{HOME}/.Skype/ rw, + owner @{HOME}/.Skype/** krw, + owner @{HOME}/.config/ r, + owner @{HOME}/.config/*/ r, + owner @{HOME}/.config/Skype/Skype.conf rw, + owner @{HOME}/.config/Trolltech.conf kr, + + # Skype traverses the .mozilla directory and needs access to prefs.js + deny owner @{HOME}/.mozilla/ r, + deny owner @{HOME}/.mozilla/**/ r, + deny owner @{HOME}/.mozilla/*/*/prefs.js r, + + # Skype also looks around in these directories + /{,usr/,usr/local/}lib{,32}/ r, + + # Recent skype builds have an executable stack, so it tries to mmap certain + # files. Let's deny them for now. + deny /etc/passwd m, + deny /etc/group m, + deny /usr/share/fonts/** m, + + # Silence a few non-needed writes + deny /var/cache/fontconfig/ w, + deny owner @{HOME}/.fontconfig/ w, + deny owner @{HOME}/.fontconfig/*.cache-*.TMP* w, +} \ No newline at end of file diff --git a/states/apparmor/usr.bin.spotify.j2 b/states/apparmor/usr.bin.spotify.j2 new file mode 100644 index 0000000..0893217 --- /dev/null +++ b/states/apparmor/usr.bin.spotify.j2 @@ -0,0 +1,24 @@ +#include + +/usr/bin/spotify { + #include + #include + #include + #include + + /etc/xdg/Trolltech.conf rk, + /etc/xdg/sni-qt.conf r, + + /usr/share/icons/*.theme k, + /usr/share/spotify/theme/**.{png,ico} r, + /usr/share/spotify/theme/**.{splang,xml} r, + + owner @{PROC}/[0-9]*/task/ r, + + owner @{HOME}/.cache/spotify/ rw, + owner @{HOME}/.cache/spotify/** rw, + owner @{HOME}/.config/Trolltech.conf rw, + owner @{HOME}/.config/spotify/ w, + + owner @{HOME}/Music/** r, +} \ No newline at end of file diff --git a/states/appimagekit/init.sls b/states/appimagekit/init.sls new file mode 100644 index 0000000..6f2f912 --- /dev/null +++ b/states/appimagekit/init.sls @@ -0,0 +1,8 @@ +--- +appimage-config-dir: + file.directory: + - name: /usr/share/appimagekit/ + +appimage-config-file: + file.absent: + - name: /usr/share/appimagekit/no_desktopintegration \ No newline at end of file diff --git a/states/apt/10proxy.j2 b/states/apt/10proxy.j2 new file mode 100644 index 0000000..00f6880 --- /dev/null +++ b/states/apt/10proxy.j2 @@ -0,0 +1,3 @@ +{%- if salt['pillar.get']('apt-proxy:name') != "None" %} +Acquire::http { proxy "http://{{salt['pillar.get']('apt-proxy:name')}}:{{ salt['pillar.get']('apt-proxy:port')}}" } +{%- endif %} \ No newline at end of file diff --git a/states/apt/init.sls b/states/apt/init.sls new file mode 100644 index 0000000..0eefb0e --- /dev/null +++ b/states/apt/init.sls @@ -0,0 +1,28 @@ +--- +apt-unauth: + file.append: + - name: /etc/apt/apt.conf.d/99-unauth + - text: 'APT::Get::AllowUnauthenticated "true";' + +apt-aptitude-install: + pkg.latest: + - pkgs: + - aptitude + - apt-transport-https +# +# apt-upgrade: +# pkg.uptodate: +# - refresh: True +# +#apt-cacher-ng-proxy: +# file.managed: +# - name: /etc/apt/apt.conf.d/10proxy +# - source: salt://apt/10proxy.j2 +# - user: root +# - group: root +# - mode: 0644 +# - template: jinja +# +# apt-cacher-ng-proxy-delete: +# file.absent: +# - name: /etc/apt/apt.conf.d/10proxy \ No newline at end of file diff --git a/states/arduino/arduino-arduinoide.desktop.j2 b/states/arduino/arduino-arduinoide.desktop.j2 new file mode 100644 index 0000000..ccc64ea --- /dev/null +++ b/states/arduino/arduino-arduinoide.desktop.j2 @@ -0,0 +1,14 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "arduino/map.jinja" import arduino with context %} +[Desktop Entry] +Type=Application +Name=Arduino IDE +GenericName=Arduino IDE +Comment=Open-source electronics prototyping platform +Exec={{ arduino.install_dir }}/arduino +Icon=arduino-arduinoide +Terminal=false +Categories=Development;IDE;Electronics; +MimeType=text/x-arduino +Keywords=embedded electronics;electronics;avr;microcontroller; +StartupWMClass=processing-app-Base \ No newline at end of file diff --git a/states/arduino/defaults.yaml b/states/arduino/defaults.yaml new file mode 100644 index 0000000..de9672d --- /dev/null +++ b/states/arduino/defaults.yaml @@ -0,0 +1,4 @@ +--- +arduino: + mirror: "https://downloads.arduino.cc" + install_dir: "/usr/local/apps" \ No newline at end of file diff --git a/states/arduino/init.sls b/states/arduino/init.sls new file mode 100644 index 0000000..d55d903 --- /dev/null +++ b/states/arduino/init.sls @@ -0,0 +1,40 @@ +--- +{%- from "arduino/map.jinja" import arduino with context %} +{%- if not salt['file.directory_exists']( arduino.install_dir + '/arduino-' + arduino.version ) %} +arduino-archive-extract: + archive.extracted: + - name: {{ arduino.install_dir }} + - source: {{ arduino.mirror }}/arduino-{{ arduino.version }}-{{ arduino.arch }}.tar.xz + - skip_verify: True + - archive_format: tar + - keep: True + - if_missing: {{ arduino.install_dir }}/arduino-{{ arduino.version }} + +arduino-symlink: + file.symlink: + - name: {{ arduino.install_dir }}/arduino + - target: {{ arduino.install_dir }}/arduino-{{ arduino.version }} + - force: True + - onchanges: + - arduino-archive-extract + +arduino-bin-symlink: + file.symlink: + - name: /usr/bin/arduino + - target: {{ arduino.install_dir }}/arduino-{{ arduino.version }}/arduino + - force: True + - onchanges: + - arduino-archive-extract + +arduino-shortcut: + file.managed: + - name: /usr/share/applications/arduino-arduinoide.desktop + - source: salt://arduino/arduino-arduinoide.desktop.j2 + - template: jinja + - user: root + - group: root + - mode: 644 + - onchanges: + - arduino-archive-extract + - arduino-symlink +{%- endif %} \ No newline at end of file diff --git a/states/arduino/map.jinja b/states/arduino/map.jinja new file mode 100644 index 0000000..a3f6437 --- /dev/null +++ b/states/arduino/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "arduino/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='arduino') -%} + +{%- set arduino = salt['pillar.get']('arduino', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/bareos/bareos-fd.conf.j2 b/states/bareos/bareos-fd.conf.j2 new file mode 100644 index 0000000..8bbd8ef --- /dev/null +++ b/states/bareos/bareos-fd.conf.j2 @@ -0,0 +1,20 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +FileDaemon { + Name = {{ grains.get('host') }} + Maximum Concurrent Jobs = {{ salt['pillar.get']('bareos:director:max_concurrent_jobs', default=10) }} + Compatible = {{ salt['pillar.get']('bareos:director:compatible', default='No') }} + Working Directory = {{ salt['pillar.get']('bareos:director:working_dir', default='/var/run') }} +} + +Director { + Name = {{ salt['pillar.get']('bareos:director:name', default='bareos-dir') }} + Address = {{ salt['pillar.get']('bareos:director:addr', default='localhost') }} + Password = "{{ salt['pillar.get']('bareos:director:password', default='password') }}" + Connection From Client To Director = {{ salt['pillar.get']('bareos:director:initiated', default='No') }} +} + +Messages { + Name = standard + director = {{ salt['pillar.get']('bareos:director:name',default='bareos-dir') }} = all, !skipped, !restored +} \ No newline at end of file diff --git a/states/bareos/bareos-fd.service.j2 b/states/bareos/bareos-fd.service.j2 new file mode 100644 index 0000000..d315640 --- /dev/null +++ b/states/bareos/bareos-fd.service.j2 @@ -0,0 +1,21 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +[Unit] +Description=Bareos File Daemon +Before=multi-user.target +Before=graphical.target +Before=shutdown.target +After=network-online.target +After=remote-fs.target +After=time-sync.target +After=systemd-journald-dev-log.socket +Wants=network-online.target +Conflicts=shutdown.target + +[Service] +Type=forking +KillMode=process +ExecStart=/usr/sbin/bareos-fd -c /etc/bareos/bareos-fd.conf +SuccessExitStatus=0 15 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/bareos/bareos-fd.sls b/states/bareos/bareos-fd.sls new file mode 100644 index 0000000..5ff04f1 --- /dev/null +++ b/states/bareos/bareos-fd.sls @@ -0,0 +1,59 @@ +--- +{%- if salt['grains.get']('kernel') == 'Linux' %} +#bareos-pkg: +# pkg.purged: +# - pkgs: +# - bareos-common +# - bareos-filedaemon +# - bareos-bconsole + +bareos-fd-bin: + file.managed: + - name: /usr/sbin/bareos-fd + - source: https://paulbsd-pub.s3.fr-par.scw.cloud/bareos/static-bareos-fd-{{ salt['grains.get']('osarch')|lower }} + - skip_verify: True + - user: root + - group: root + - mode: 0755 + +bareos-fd-service-file: + file.managed: + - name: /etc/systemd/system/bareos-fd.service + - source: salt://bareos/bareos-fd.service.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - watch_in: + - service: bareos-fd-service + - require: + - file: bareos-fd-bin + +bareos-fd-config-dir: + file.directory: + - name: /etc/bareos + - user: root + - group: root + - mode: 0755 + +bareos-fd-config: + file.managed: + - name: /etc/bareos/bareos-fd.conf + - source: salt://bareos/bareos-fd.conf.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - watch_in: + - service: bareos-fd-service + - require: + - file: bareos-fd-bin + - file: bareos-fd-config-dir + +bareos-fd-service: + service.running: + - name: bareos-fd + - enable: True + - require: + - file: bareos-fd-service-file +{%- endif %} \ No newline at end of file diff --git a/states/bareos/config.sls b/states/bareos/config.sls new file mode 100644 index 0000000..16f08db --- /dev/null +++ b/states/bareos/config.sls @@ -0,0 +1,21 @@ +--- +bareos-fd-config-dir: + file.directory: + - name: {{ bareos.config_dir }} + - user: root + - group: root + - mode: 0755 + +bareos-fd-config: + file.managed: + - name: {{ bareos.config_dir }}/bareos-fd.conf + - source: salt://bareos/bareos-fd.conf.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - watch_in: + - service: bareos-fd-service + - require: + - file: bareos-fd-bin + - file: bareos-fd-config-dir \ No newline at end of file diff --git a/states/bareos/defaults.yaml b/states/bareos/defaults.yaml new file mode 100644 index 0000000..8366fae --- /dev/null +++ b/states/bareos/defaults.yaml @@ -0,0 +1,8 @@ +--- +bareos: + enabled: true + config_dir: /etc/bareos + install_dir: /usr/local/bin + mirror: https://paulbsd-pub.s3.fr-par.scw.cloud/bareos + os: linux + arch: amd64 \ No newline at end of file diff --git a/states/bareos/init.sls b/states/bareos/init.sls new file mode 100644 index 0000000..93e8737 --- /dev/null +++ b/states/bareos/init.sls @@ -0,0 +1,5 @@ +--- +{%- if salt['grains.get']('kernel') == 'Linux' %} +include: + - .install +{%- endif %} \ No newline at end of file diff --git a/states/bareos/install.sls b/states/bareos/install.sls new file mode 100644 index 0000000..4afb8a9 --- /dev/null +++ b/states/bareos/install.sls @@ -0,0 +1,17 @@ +--- +{%- from "bareos/map.jinja" import bareos with context %} +#bareos-pkg: +# pkg.purged: +# - pkgs: +# - bareos-common +# - bareos-filedaemon +# - bareos-bconsole + +bareos-fd-bin: + file.managed: + - name: /usr/sbin/bareos-fd + - source: {{ bareos.mirror }}/static-bareos-fd-{{ bareos.arch }} + - skip_verify: True + - user: root + - group: root + - mode: 0755 \ No newline at end of file diff --git a/states/bareos/kernelmap.yaml b/states/bareos/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/bareos/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/bareos/map.jinja b/states/bareos/map.jinja new file mode 100644 index 0000000..2203636 --- /dev/null +++ b/states/bareos/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "bareos/defaults.yaml" as default_settings -%} + +{%- import_yaml "bareos/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "bareos/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='bareos', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set bareos = salt['pillar.get']('bareos', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/bareos/osarchmap.yaml b/states/bareos/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/bareos/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/bareos/service.sls b/states/bareos/service.sls new file mode 100644 index 0000000..4ac5266 --- /dev/null +++ b/states/bareos/service.sls @@ -0,0 +1,20 @@ +--- +bareos-fd-service-file: + file.managed: + - name: /etc/systemd/system/bareos-fd.service + - source: salt://bareos/bareos-fd.service.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - watch_in: + - service: bareos-fd-service + - require: + - file: bareos-fd-bin + +bareos-fd-service: + service.running: + - name: bareos-fd + - enable: True + - require: + - file: bareos-fd-service-file \ No newline at end of file diff --git a/states/burp/burp.conf.j2 b/states/burp/burp.conf.j2 new file mode 100644 index 0000000..1b7c49d --- /dev/null +++ b/states/burp/burp.conf.j2 @@ -0,0 +1,4 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- for key, value in burp.config.items() %} +{{ key }} = {{ value }} +{%- endfor %} \ No newline at end of file diff --git a/states/burp/burp.conf.j2.sample b/states/burp/burp.conf.j2.sample new file mode 100644 index 0000000..76dba65 --- /dev/null +++ b/states/burp/burp.conf.j2.sample @@ -0,0 +1,131 @@ +# This is an example config file for the burp client. + +mode = client +port = 4971 +# A different port to use for restores - see the man page for more options. +#port_restore = 5971 +status_port = 4972 +server = {{ salt["pillar.get"]("burp:name") }} +password = {{ salt["pillar.get"]("burp:password") }} +cname = {{ grains.get('fqdn') }} + +# Choose the protocol to use. +# 0 to decide automatically, 1 to force protocol1 mode (file level granularity +# with a pseudo mirrored storage on the server and optional rsync). 2 forces +# protocol2 mode (inline deduplication with variable length blocks). +# protocol = 0 +pidfile = /var/run/burp.client.pid +syslog = 0 +stdout = 1 +progress_counter = 1 + +# Ratelimit throttles the send speed. Specified in Megabits per second (Mb/s). +# ratelimit = 1.5 +# Network timeout defaults to 7200 seconds (2 hours). +# network_timeout = 7200 +# The directory to which autoupgrade files will be downloaded. +# To never autoupgrade, leave it commented out. +# autoupgrade_dir=/etc/burp/autoupgrade/client +# OS path component for the autoupgrade directory on the server. +# autoupgrade_os=test_os +# Wait a random number of seconds between 0 and the given number before +# contacting the server on a timed backup. +# randomise = 1200 + +# Set server_can_restore to 0 if you do not want the server to be able to +# initiate a restore. +server_can_restore = 1 + +# Set server_can_override_includes to 0 if you do not want the server to be +# able to override the local include/exclude list. The default is 1. +# server_can_override_includes = 1 + +# Set an encryption password if you do not trust the server with your data. +# Note that this will mean that network deltas will not be possible. Each time +# a file changes, the whole file will be transferred on the next backup. +# encryption_password = My^$pAsswIrD%@ + +# More configuration files can be read, using syntax like the following +# (without the leading '# '). +# . path/to/more/conf + +# Run as different user/group. +# user=graham +# group=nogroup + +cross_filesystem=/home +cross_all_filesystems=0 + +# Uncomment the following lines to automatically generate a certificate signing +# request and send it to the server. +ca_burp_ca = /usr/sbin/burp_ca +ca_csr_dir = /etc/burp/CA-client + +# SSL certificate authority - same file on both server and client +ssl_cert_ca = /etc/burp/ssl_cert_ca.pem + +# Client SSL certificate +ssl_cert = /etc/burp/ssl_cert-client.pem + +# Client SSL key +ssl_key = /etc/burp/ssl_cert-client.key + +# Client SSL ciphers +#ssl_ciphers = + +# Client SSL compression. Default is zlib5. Set to zlib0 to turn it off. +#ssl_compression = zlib5 + +# SSL key password, for loading a certificate with encryption. +#ssl_key_password = password + +# Common name in the certificate that the server gives us +ssl_peer_cn = burpserver + +# Example syntax for pre/post scripts +#backup_script_pre=/path/to/a/script +#backup_script_post=/path/to/a/script +#restore_script_pre=/path/to/a/script +#restore_script_post=/path/to/a/script + +# The following options specify exactly what to backup. +# The server will override them if there is at least one 'include=' line on +# the server side and 'server_can_override_includes=1'. +#include = /home +#exclude = /home/graham/testdir/librsync-0.9.7/testsuite +#include = /home/graham/testdir/librsync-0.9.7/testsuite/deep +#include = /home/graham/xdir +#exclude = /home/graham/testdir/libr +# Exclude file names ending in '.vdi' or '.vmdk' (case insensitive) +#exclude_ext = vdi +#exclude_ext = vmd +# Exlude file path matching a regular expression +# (note that 'include_regex' is not yet implemented) +#exclude_regex = \.cache +# Exclude various temporary file systems. You may want to add devfs, devpts, +# proc, ramfs, etc. +exclude_fs = sysfs +exclude_fs = tmpfs +# Exclude files based on size. Defaults are 0, which means no limit. +#min_file_size = 0 Mb +#max_file_size = 0 Mb +# The content of directories containing a filesystem entry named like this +# will not be backed up. +nobackup = .nobackup +# By default, burp backups up the fifos themselves, rather than reading from +# them. These two options let you choose a particular fifo to read, or read +# from all fifos. +#read_fifo=/path/to/a/fifo +#read_all_fifos=0 +# The same for block device nodes. +#read_blockdev=/path/to/a/blockdev +#read_all_blockdevs=0 +# Exclude files from compression by extension. +exclude_comp=bz2 +exclude_comp=gz +# When backing up, whether to enable O_NOATIME when opening files and +# directories. The default is atime=0, which enables O_NOATIME. +#atime=1 +# When enabled, this causes problems in the phase1 scan (such as an 'include' +# being missing) to be treated as fatal errors. The default is 0. +#scan_problem_raises_error=1 \ No newline at end of file diff --git a/states/burp/client/burp-backup.service.j2 b/states/burp/client/burp-backup.service.j2 new file mode 100644 index 0000000..c5ae19a --- /dev/null +++ b/states/burp/client/burp-backup.service.j2 @@ -0,0 +1,12 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +[Unit] +Description=Burp backup task +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +ExecStart=/usr/sbin/burp -ab + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/burp/client/burp-backup.timer.j2 b/states/burp/client/burp-backup.timer.j2 new file mode 100644 index 0000000..653ad46 --- /dev/null +++ b/states/burp/client/burp-backup.timer.j2 @@ -0,0 +1,12 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +[Unit] +Description=Burp backup timer +After=network-online.target +Wants=network-online.target + +[Timer] +OnCalendar=Mon-Sun 23:30 +Persistent=true + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/states/burp/client/config.sls b/states/burp/client/config.sls new file mode 100644 index 0000000..d3a270e --- /dev/null +++ b/states/burp/client/config.sls @@ -0,0 +1,11 @@ +--- +{%- from "burp/map.jinja" import burp with context %} +burp-client-config: + file.managed: + - name: /etc/burp/burp.conf + - source: salt://burp/burp.conf.j2 + - user: root + - mode: 0644 + - template: jinja + - require: + - pkg: burp-pkg \ No newline at end of file diff --git a/states/burp/client/service.sls b/states/burp/client/service.sls new file mode 100644 index 0000000..d7e6869 --- /dev/null +++ b/states/burp/client/service.sls @@ -0,0 +1,19 @@ +--- +{%- from "burp/map.jinja" import burp with context %} +{%- if salt['grains.get']('init') == 'systemd' %} +burp-task: + file.managed: + - name: /etc/systemd/system/burp-backup.service + - source: salt://burp/burp-backup.service.j2 + - user: root + - mode: 0644 + - template: jinja + +burp-timer: + file.managed: + - name: /etc/systemd/system/burp-backup.timer + - source: salt://burp/burp-backup.timer.j2 + - user: root + - mode: 0644 + - template: jinja +{%- endif %} \ No newline at end of file diff --git a/states/burp/defaults.yaml b/states/burp/defaults.yaml new file mode 100644 index 0000000..5729862 --- /dev/null +++ b/states/burp/defaults.yaml @@ -0,0 +1,66 @@ +--- +burp: + enabled: true + server: + name: nuc.paulbsd.com + config: + mode: server + port: 4971 + port_restore: 5971 + status_port: 4972 + password: password + cname: hostname + pidfile: /var/run/burp.client.pid + syslog: 0 + stdout: 1 + progress_counter: 1 + server_can_restore: 1 + cross_filesystem: /home + cross_all_filesystems: 0 + ca_burp_ca: /usr/sbin/burp_ca + ca_csr_dir: /etc/burp/CA-client + ssl_cert_ca: /etc/burp/ssl_cert_ca.pem + ssl_cert: /etc/burp/ssl_cert-client.pem + ssl_key: /etc/burp/ssl_cert-client.key + ssl_peer_cn: burpserver + exclude_fs: sysfs + exclude_fs: tmpfs + exclude_comp: bz2 + exclude_comp: gz + nobackup: .nobackup + clients: + - name: thinkpad.paulbsd.com + schedule: + type: "after" + value: "600" + - name: scw01-ams.paulbsd.com + schedule: + type: "at" + value: "22h" + client: + config: + mode: client + port: 4971 + port_restore: 5971 + status_port: 4972 + server: nuc.paulbsd.com + password: password + cname: hostname + pidfile: /var/run/burp.client.pid + syslog: 0 + stdout: 1 + progress_counter: 1 + server_can_restore: 1 + cross_filesystem: /home + cross_all_filesystems: 0 + ca_burp_ca: /usr/sbin/burp_ca + ca_csr_dir: /etc/burp/CA-client + ssl_cert_ca: /etc/burp/ssl_cert_ca.pem + ssl_cert: /etc/burp/ssl_cert-client.pem + ssl_key: /etc/burp/ssl_cert-client.key + ssl_peer_cn: burpserver + exclude_fs: sysfs + exclude_fs: tmpfs + exclude_comp: bz2 + exclude_comp: gz + nobackup: .nobackup \ No newline at end of file diff --git a/states/burp/init.sls b/states/burp/init.sls new file mode 100644 index 0000000..5fa3b56 --- /dev/null +++ b/states/burp/init.sls @@ -0,0 +1,12 @@ +--- +{%- from "burp/map.jinja" import burp with context %} +include: + - .install + - .pkg +{%- if salt['grains.get']('fqdn') == burp.server.name %} + - .server.config + - .server.service +{%- elif salt['grains.get']('fqdn') in burp.hosts.name %} + - .client.config + - .client.service +{%- endif %} \ No newline at end of file diff --git a/states/burp/install.sls b/states/burp/install.sls new file mode 100644 index 0000000..0ceeea6 --- /dev/null +++ b/states/burp/install.sls @@ -0,0 +1,7 @@ +--- +{%- from "burp/map.jinja" import burp with context %} +burp-config-dir: + file.directory: + - name: /etc/burp + - user: root + - mode: 0755 \ No newline at end of file diff --git a/states/burp/map.jinja b/states/burp/map.jinja new file mode 100644 index 0000000..2b73e3a --- /dev/null +++ b/states/burp/map.jinja @@ -0,0 +1,8 @@ +{%- import_yaml "burp/defaults.yaml" as defaults -%} + +{%- set burp = salt['pillar.get']( + 'burp', + default=defaults.burp, + merge=True + ) +-%} \ No newline at end of file diff --git a/states/burp/pkg.sls b/states/burp/pkg.sls new file mode 100644 index 0000000..c69df6a --- /dev/null +++ b/states/burp/pkg.sls @@ -0,0 +1,4 @@ +--- +burp-pkg: + pkg.installed: + - name: burp \ No newline at end of file diff --git a/states/burp/server/burp.service.j2 b/states/burp/server/burp.service.j2 new file mode 100644 index 0000000..f165edb --- /dev/null +++ b/states/burp/server/burp.service.j2 @@ -0,0 +1,10 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +[Unit] +Description=Burp backup timer + +[Timer] +OnStartupSec=600 +#OnCalendar=Mon-Sun 22:00 + +[Install] +WantedBy=timers.target \ No newline at end of file diff --git a/states/burp/server/config.sls b/states/burp/server/config.sls new file mode 100644 index 0000000..0851869 --- /dev/null +++ b/states/burp/server/config.sls @@ -0,0 +1,11 @@ +--- +{% from "burp/map.jinja" import burp with context %} +burp-config: + file.managed: + - name: /etc/burp/burp-server.conf + - source: salt://burp/burp.conf.j2 + - user: root + - mode: 0644 + - template: jinja + - require: + - pkg: burp-pkg diff --git a/states/burp/server/service.sls b/states/burp/server/service.sls new file mode 100644 index 0000000..f8d3f7e --- /dev/null +++ b/states/burp/server/service.sls @@ -0,0 +1,11 @@ +--- +{% from "burp/map.jinja" import burp with context %} +{% if salt['grains.get']('init') == 'systemd' %} +burp-task: + file.managed: + - name: /etc/systemd/system/burp.service + - source: salt://burp/burp.service.j2 + - user: root + - mode: 0644 + - template: jinja +{% endif %} diff --git a/states/cds/config.sls b/states/cds/config.sls new file mode 100644 index 0000000..1b056bd --- /dev/null +++ b/states/cds/config.sls @@ -0,0 +1,17 @@ +--- +{%- from "cds/map.jinja" import cds with context %} +cds-config-dir: + file.directory: + - name: /etc/cds + - watch_in: + - service: cds-service + +cds-config-file: + file.managed: + - name: /etc/cds/cds.conf + - source: salt://cds/cds.conf.j2 + - user: root + - group: root + - template: jinja + - watch_in: + - service: cds-service \ No newline at end of file diff --git a/states/cds/defaults.yaml b/states/cds/defaults.yaml new file mode 100644 index 0000000..7742735 --- /dev/null +++ b/states/cds/defaults.yaml @@ -0,0 +1,7 @@ +--- +cds: + engine: + enabled: true + config: + mirror: https://github.com/ovh/cds/releases/download/ + version: 0.43.1 \ No newline at end of file diff --git a/states/cds/init.sls b/states/cds/init.sls new file mode 100644 index 0000000..d3c2f35 --- /dev/null +++ b/states/cds/init.sls @@ -0,0 +1,6 @@ +--- +{%- from "cds/map.jinja" import cds with context %} +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/cds/install.sls b/states/cds/install.sls new file mode 100644 index 0000000..fef6edc --- /dev/null +++ b/states/cds/install.sls @@ -0,0 +1,12 @@ +--- +{%- from "cds/map.jinja" import cds with context %} +cds-engine-install: + file.managed: + - name: /usr/bin/cds-engine-{{ salt['pillar.get']('cds:engine:version') }} + - source: {{ cds.mirror }}/{{ cds.version }}/cds-engine-{{ salt['grains.get']('kernel')|lower }}-{{ salt['grains.get']('osarch') }} + - skip_verify: True + +cds-bin-symlink: + file.symlink: + - name: /usr/bin/cds-engine + - target: /usr/bin/cds-engine-{{ salt['pillar.get']('cds:version') }} \ No newline at end of file diff --git a/states/cds/map.jinja b/states/cds/map.jinja new file mode 100644 index 0000000..7b48c42 --- /dev/null +++ b/states/cds/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "cds/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='cds') -%} + +{%- set cds = salt['pillar.get']('cds', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/cds/service.sls b/states/cds/service.sls new file mode 100644 index 0000000..5ecf308 --- /dev/null +++ b/states/cds/service.sls @@ -0,0 +1,15 @@ +--- +{%- from "cds/map.jinja" import cds with context %} +cds-service-file: + file.managed: + - name: /etc/systemd/system/cds.service + - source: salt://cds/cds.service.j2 + - user: root + - group: root + - watch_in: + - service: cds-service + +cds-service: + service.running: + - name: cds + - enable: True \ No newline at end of file diff --git a/states/cds/telegraf.service.j2 b/states/cds/telegraf.service.j2 new file mode 100644 index 0000000..4d95d40 --- /dev/null +++ b/states/cds/telegraf.service.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=The plugin-driven server agent for reporting metrics into InfluxDB +Documentation=https://github.com/influxdata/telegraf +After=network.target + +[Service] +EnvironmentFile=-/etc/default/telegraf +#User=telegraf +ExecStart=/usr/bin/telegraf --config $INFLUX_CONFIG +ExecReload=/bin/kill -HUP $MAINPID +Restart=on-failure +RestartForceExitStatus=SIGPIPE +KillMode=control-group + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/config/defaults.yaml b/states/config/defaults.yaml new file mode 100644 index 0000000..8df4e56 --- /dev/null +++ b/states/config/defaults.yaml @@ -0,0 +1,6 @@ +--- +config: + enabled: true + install_dir: "/usr/local/apps/config" + filename: "common.ini" + config: {} diff --git a/states/config/init.sls b/states/config/init.sls new file mode 100644 index 0000000..24ca5a8 --- /dev/null +++ b/states/config/init.sls @@ -0,0 +1,19 @@ +--- +{%- from "config/map.jinja" import config with context %} +config-dir: + file.directory: + - name: {{ config.install_dir }} + - user: root + - mode: "0755" + +config-file: + file.managed: + - name: {{ config.install_dir }}/{{ config.filename }} + - user: root + - mode: "0755" + +config-file-config: + ini.options_present: + - name: {{ config.install_dir }}/{{ config.filename }} + - separator: '=' + - sections: {{ config.config }} diff --git a/states/config/map.jinja b/states/config/map.jinja new file mode 100644 index 0000000..19fbdc9 --- /dev/null +++ b/states/config/map.jinja @@ -0,0 +1,3 @@ +{%- import_yaml "config/defaults.yaml" as defaults %} + +{%- set config = salt['pillar.get']('config', default=defaults.config, merge=True) -%} diff --git a/states/coronafana/defaults.yaml b/states/coronafana/defaults.yaml new file mode 100644 index 0000000..d3d12ad --- /dev/null +++ b/states/coronafana/defaults.yaml @@ -0,0 +1,9 @@ +--- +coronafana: + enabled: true + install_dir: /usr/local/apps + release_dir: /usr/local/apps/releases + mirror: https://git.paulbsd.com/paulbsd/coronafana/releases/download + version: 1.0.3 + os: linux + arch: amd64 \ No newline at end of file diff --git a/states/coronafana/init.sls b/states/coronafana/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/coronafana/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/coronafana/install.sls b/states/coronafana/install.sls new file mode 100644 index 0000000..227ae2a --- /dev/null +++ b/states/coronafana/install.sls @@ -0,0 +1,16 @@ +--- +{% from "coronafana/map.jinja" import coronafana with context %} +coronafana-archive-extract: + archive.extracted: + - name: {{ coronafana.release_dir }}/coronafana-{{ coronafana.version }} + - source: {{ coronafana.mirror }}/{{ coronafana.version }}/coronafana-{{ coronafana.version }}-{{ coronafana.os }}-{{ coronafana.arch }}.tar.gz + - skip_verify: True + - enforce_toplevel: False + - if_missing: {{ coronafana.release_dir }}/coronafana-{{ coronafana.version }} + +coronafana-binary-symlink: + file.symlink: + - name: {{ coronafana.install_dir }}/coronafana + - target: {{ coronafana.release_dir }}/coronafana-{{ coronafana.version }} + - require: + - archive: coronafana-archive-extract \ No newline at end of file diff --git a/states/coronafana/kernelmap.yaml b/states/coronafana/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/coronafana/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/coronafana/map.jinja b/states/coronafana/map.jinja new file mode 100644 index 0000000..b2ef6f1 --- /dev/null +++ b/states/coronafana/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "coronafana/defaults.yaml" as default_settings -%} + +{%- import_yaml "coronafana/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "coronafana/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='coronafana', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set coronafana = salt['pillar.get']('coronafana', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/coronafana/osarchmap.yaml b/states/coronafana/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/coronafana/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/cron/defaults.yaml b/states/cron/defaults.yaml new file mode 100644 index 0000000..e22a0a1 --- /dev/null +++ b/states/cron/defaults.yaml @@ -0,0 +1,10 @@ +--- +cron: + env: + SHELL: + name: SHELL + command: /bin/bash + MAILTO: + name: MAILTO + command: {{ salt['pillar.get']('syscontact') }} + tasks: \ No newline at end of file diff --git a/states/cron/init.sls b/states/cron/init.sls new file mode 100644 index 0000000..ec980ca --- /dev/null +++ b/states/cron/init.sls @@ -0,0 +1,8 @@ +--- +{%- from "cron/map.jinja" import cron with context %} +{%- for key, value in cron.env.items() %} +cron-env-{{ key.lower }}: + cron.env_present: + - name: {{ value.name }} + - value: {{ value.command }} +{%- endfor %} \ No newline at end of file diff --git a/states/cron/map.jinja b/states/cron/map.jinja new file mode 100644 index 0000000..e37acb7 --- /dev/null +++ b/states/cron/map.jinja @@ -0,0 +1,8 @@ +{%- import_yaml "cron/defaults.yaml" as defaults %} + +{%- set cron = salt['pillar.get']( + 'cron', + default=defaults.cron, + merge=True + ) +-%} \ No newline at end of file diff --git a/states/custom.sls b/states/custom.sls new file mode 100644 index 0000000..c7e2fce --- /dev/null +++ b/states/custom.sls @@ -0,0 +1,6 @@ +--- +human_friendly_state_id: + custom.enforce_custom_thing: + - name: Nom + - foo: Valeur + - bar: False \ No newline at end of file diff --git a/states/dkron/config.sls b/states/dkron/config.sls new file mode 100644 index 0000000..b02c973 --- /dev/null +++ b/states/dkron/config.sls @@ -0,0 +1,15 @@ +--- +{%- from "dkron/map.jinja" import dkron with context -%} +dkron-config-dir: + file.directory: + - name: /etc/dkron + - user: {{ dkron.runuser }} + +dkron-config: + file.managed: + - name: /etc/dkron/dkron.yml + - source: salt://dkron/dkron.yml.j2 + - user: {{ dkron.runuser }} + - template: jinja + - watch_in: + - service: dkron-service \ No newline at end of file diff --git a/states/dkron/defaults.yaml b/states/dkron/defaults.yaml new file mode 100644 index 0000000..2a39264 --- /dev/null +++ b/states/dkron/defaults.yaml @@ -0,0 +1,20 @@ +--- +dkron: + enabled: true + install_dir: "/usr/local/apps" + release_dir: "/usr/local/apps/releases" + mirror: "https://github.com/distribworks/dkron/releases/download" + version: "2.1.1" + os: "linux" + arch: "amd64" + runuser: "dkron" + user: "user" + password: "password" + url: "http://localhost:8898" + verify: false + config: + bootstrap-expect: 1 + server: true + http-addr: "127.0.0.1:8898" + data-dir: "/var/lib/dkron" + jobs: [] diff --git a/states/dkron/dkron.service.j2 b/states/dkron/dkron.service.j2 new file mode 100644 index 0000000..2e4b62b --- /dev/null +++ b/states/dkron/dkron.service.j2 @@ -0,0 +1,15 @@ +{%- from "dkron/map.jinja" import dkron with context -%} +[Unit] +Description=dkron - Open Source task scheduler +Documentation=https://dkron.io/ +After=network.target + +[Service] +User=%i +ExecStart={{ dkron.install_dir }}/dkron/dkron agent +Restart=on-failure +SuccessExitStatus=3 4 +RestartForceExitStatus=3 4 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/dkron/dkron.yml.j2 b/states/dkron/dkron.yml.j2 new file mode 100644 index 0000000..68b912d --- /dev/null +++ b/states/dkron/dkron.yml.j2 @@ -0,0 +1,4 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +--- +{%- from "dkron/map.jinja" import dkron with context %} +{{ dkron.config|yaml(False) }} \ No newline at end of file diff --git a/states/dkron/init.sls b/states/dkron/init.sls new file mode 100644 index 0000000..ef5fec3 --- /dev/null +++ b/states/dkron/init.sls @@ -0,0 +1,6 @@ +--- +include: + - .install + - .config + - .service + - .jobs \ No newline at end of file diff --git a/states/dkron/install.sls b/states/dkron/install.sls new file mode 100644 index 0000000..99828ba --- /dev/null +++ b/states/dkron/install.sls @@ -0,0 +1,30 @@ +--- +{%- from "dkron/map.jinja" import dkron with context %} +dkron-user: + user.present: + - name: {{ dkron.runuser }} + +dkron-archive-extract: + archive.extracted: + - name: {{ dkron.release_dir }}/dkron_{{ dkron.version }} + - source: {{ dkron.mirror }}/v{{ dkron.version }}/dkron_{{ dkron.version }}_{{ dkron.os }}_{{ dkron.arch }}.tar.gz + - user: {{ dkron.runuser }} + - enforce_toplevel: False + - skip_verify: True + - archive_format: tar + - if_missing: {{ dkron.release_dir }}/dkron_{{ dkron.version }} + +dkron-bin-symlink: + file.symlink: + - name: {{ dkron.install_dir }}/dkron + - target: {{ dkron.release_dir }}/dkron_{{ dkron.version }} + - user: {{ dkron.runuser }} + - watch_in: + - service: dkron-service + +dkron-data-dir: + file.directory: + - name: {{ dkron.config.get('data-dir') }} + - user: {{ dkron.runuser }} + - watch_in: + - service: dkron-service \ No newline at end of file diff --git a/states/dkron/jobs.sls b/states/dkron/jobs.sls new file mode 100644 index 0000000..7441ff4 --- /dev/null +++ b/states/dkron/jobs.sls @@ -0,0 +1,8 @@ +--- +{%- from "dkron/map.jinja" import dkron with context %} +dkron-jobs: + dkron.jobs: + - name: dkron-jobs + - url: {{ dkron.url }} + - verify: {{ dkron.verify }} + - jobs: {{ dkron.jobs }} \ No newline at end of file diff --git a/states/dkron/kernelmap.yaml b/states/dkron/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/dkron/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/dkron/map.jinja b/states/dkron/map.jinja new file mode 100644 index 0000000..c29f8a3 --- /dev/null +++ b/states/dkron/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "dkron/defaults.yaml" as default_settings -%} + +{%- import_yaml "dkron/kernelmap.yaml" as kernelmap %} +{%- import_yaml "dkron/osarchmap.yaml" as osarchmap %} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='dkron', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set dkron = salt['pillar.get']('dkron', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/dkron/osarchmap.yaml b/states/dkron/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/dkron/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/dkron/service.sls b/states/dkron/service.sls new file mode 100644 index 0000000..ec0657c --- /dev/null +++ b/states/dkron/service.sls @@ -0,0 +1,16 @@ +--- +{%- from "dkron/map.jinja" import dkron with context %} +dkron-service-file: + file.managed: + - name: /etc/systemd/system/dkron@.service + - source: salt://dkron/dkron.service.j2 + - user: root + - group: root + - template: jinja + - watch_in: + - service: dkron-service + +dkron-service: + service.running: + - name: dkron@{{ dkron.runuser }} + - enable: True \ No newline at end of file diff --git a/states/docker/config.sls b/states/docker/config.sls new file mode 100644 index 0000000..2ab846e --- /dev/null +++ b/states/docker/config.sls @@ -0,0 +1,16 @@ +--- +{%- from "docker/map.jinja" import docker with context %} +docker-config-dir: + file.directory: + - name: /etc/docker + +docker-config-file: + file.managed: + - name: {{ docker.config_dir }}/{{ docker.config_file }} + - source: salt://docker/daemon.json.j2 + - mode: 644 + - template: jinja + - require: + - file: docker-config-dir + - watch_in: + - service: docker-service \ No newline at end of file diff --git a/states/docker/daemon.json.j2 b/states/docker/daemon.json.j2 new file mode 100644 index 0000000..fcb3f3b --- /dev/null +++ b/states/docker/daemon.json.j2 @@ -0,0 +1,2 @@ +{%- from "docker/map.jinja" import docker with context -%} +{{ docker.daemon.config | tojson }} \ No newline at end of file diff --git a/states/docker/defaults.yaml b/states/docker/defaults.yaml new file mode 100644 index 0000000..604f112 --- /dev/null +++ b/states/docker/defaults.yaml @@ -0,0 +1,13 @@ +--- +docker: + enabled: true + config_dir: "/etc/docker" + config_file: "daemon.json" + pkgs: + - docker-ce + - docker-ce-cli + pip_pkgs: + - docker-compose + daemon: + config: + storage-driver: overlay2 \ No newline at end of file diff --git a/states/docker/init.sls b/states/docker/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/docker/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/docker/install.sls b/states/docker/install.sls new file mode 100644 index 0000000..72261d0 --- /dev/null +++ b/states/docker/install.sls @@ -0,0 +1,11 @@ +--- +{%- from "docker/map.jinja" import docker with context %} +docker-pkgs: + pkg.latest: + - pkgs: {{ docker.pkgs }} + +docker-pip-pkgs: + pip.installed: +{%- for pip in docker.pip_pkgs %} + - name: {{ pip }} +{%- endfor %} \ No newline at end of file diff --git a/states/docker/map.jinja b/states/docker/map.jinja new file mode 100644 index 0000000..5b076cc --- /dev/null +++ b/states/docker/map.jinja @@ -0,0 +1,6 @@ +{%- import_yaml "docker/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='docker') +-%} + +{%- set docker = salt['pillar.get']('docker', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/docker/service.sls b/states/docker/service.sls new file mode 100644 index 0000000..8c2ffed --- /dev/null +++ b/states/docker/service.sls @@ -0,0 +1,5 @@ +--- +docker-service: + service.running: + - name: docker + - enable: True \ No newline at end of file diff --git a/states/dokuwiki/config.sls b/states/dokuwiki/config.sls new file mode 100644 index 0000000..b6c6e04 --- /dev/null +++ b/states/dokuwiki/config.sls @@ -0,0 +1,9 @@ +--- +{%- from "dokuwiki/map.jinja" import dokuwiki with context %} +dokuwiki-config: + file.managed: + - name: {{ dokuwiki.install_dir }}/dokuwiki/conf/local.php + - source: salt://dokuwiki/templates/local.php.j2 + - user: {{ dokuwiki.user }} + - group: {{ dokuwiki.group }} + - template: jinja \ No newline at end of file diff --git a/states/dokuwiki/defaults.yaml b/states/dokuwiki/defaults.yaml new file mode 100644 index 0000000..f9fdd82 --- /dev/null +++ b/states/dokuwiki/defaults.yaml @@ -0,0 +1,15 @@ +--- +dokuwiki: + version: release_stable_2018-04-22b + mirror: https://github.com/splitbrain/dokuwiki/archive + install_dir: /usr/local/apps + release_dir: /usr/local/apps/releases + user: www-data + group: www-data + config: + title: "Dokuwiki" + license: '0' + useacl: 1 + superuser: '@admin' + disableactions: 'register' + savedir: '/home/dokuwiki' diff --git a/states/dokuwiki/init.sls b/states/dokuwiki/init.sls new file mode 100644 index 0000000..6bc76b0 --- /dev/null +++ b/states/dokuwiki/init.sls @@ -0,0 +1,5 @@ +--- +include: + - php + - .install + - .config \ No newline at end of file diff --git a/states/dokuwiki/install.sls b/states/dokuwiki/install.sls new file mode 100644 index 0000000..d027aac --- /dev/null +++ b/states/dokuwiki/install.sls @@ -0,0 +1,23 @@ +--- +{%- from "dokuwiki/map.jinja" import dokuwiki with context %} +dokuwiki-archive: + archive.extracted: + - name: {{ dokuwiki.release_dir }} + - source: {{ dokuwiki.mirror }}/{{ dokuwiki.version }}.tar.gz + - skip_verify: True + - archive_format: tar + - user: {{ dokuwiki.user }} + - group: {{ dokuwiki.group }} + - if_missing: {{ dokuwiki.release_dir }}/dokuwiki-{{ dokuwiki.version }} + +dokuwiki-install-link: + file.symlink: + - name: {{ dokuwiki.install_dir }}/dokuwiki + - target: {{ dokuwiki.release_dir }}/dokuwiki-{{ dokuwiki.version }} + - force: True + +dokuwiki-savedir: + file.directory: + - name: {{ dokuwiki.config.savedir }} + - user: {{ dokuwiki.user }} + - group: {{ dokuwiki.group }} diff --git a/states/dokuwiki/map.jinja b/states/dokuwiki/map.jinja new file mode 100644 index 0000000..0c30141 --- /dev/null +++ b/states/dokuwiki/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "dokuwiki/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='dokuwiki') -%} + +{%- set dokuwiki = salt['pillar.get']('dokuwiki', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/dokuwiki/templates/local.php.j2 b/states/dokuwiki/templates/local.php.j2 new file mode 100644 index 0000000..0a22bb2 --- /dev/null +++ b/states/dokuwiki/templates/local.php.j2 @@ -0,0 +1,9 @@ + 0 %} + +include: + - {{ sls_archive_clean if grafana.pkg.use_upstream_archive else sls_package_clean }} + +grafana-package-archive-remove-home-alternative-remove: + alternatives.remove: + - name: grafana-home + - path: {{ grafana.pkg.archive.name }} + - onlyif: update-alternatives --get-selections |grep ^grafana-home + - require: + - sls: {{ sls_archive_clean if grafana.pkg.use_upstream_archive else sls_package_clean }} + + {% for i in ['grafana-cli', 'grafana-server'] %} + +grafana-package-archive-remove-{{ i }}-alternative-remove: + alternatives.remove: + - name: link-{{ i }} + - path: {{ grafana.pkg.archive.name }}/bin/{{ i }} + - onlyif: update-alternatives --get-selections |grep ^link-{{ i }} + - require: + - sls: {{ sls_archive_clean if grafana.pkg.use_upstream_archive else sls_package_clean }} + + {% endfor %} + {%- endif %} diff --git a/states/grafana/config/alternatives/init.sls b/states/grafana/config/alternatives/init.sls new file mode 100644 index 0000000..d3e5518 --- /dev/null +++ b/states/grafana/config/alternatives/init.sls @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +include: + - .install diff --git a/states/grafana/config/alternatives/install.sls b/states/grafana/config/alternatives/install.sls new file mode 100644 index 0000000..0104710 --- /dev/null +++ b/states/grafana/config/alternatives/install.sls @@ -0,0 +1,71 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} +{%- set sls_archive_install = tplroot ~ '.archive.install' %} + + {%- if grafana.pkg.use_upstream_archive %} + {%- if grains.kernel|lower == 'linux' and grafana.linux.altpriority|int > 0 %} + +include: + - {{ sls_archive_install }} + +grafana-package-archive-install-home-alternative-install: + cmd.run: + - name: update-alternatives --install {{ grafana.dir }} grafana-home {{ grafana.pkg.archive.name }} {{grafana.linux.altpriority}} + - watch: + - archive: grafana-package-archive-install-archive-extracted + - require: + - sls: {{ sls_archive_install }} + - onlyif: {{ grains.os_family in ('Suse',) }} + alternatives.install: + - name: grafana-home + - link: {{ grafana.dir }} + - path: {{ grafana.pkg.archive.name }} + - priority: {{ grafana.linux.altpriority }} + - order: 10 + - watch: + - archive: grafana-package-archive-install-archive-extracted + - unless: {{ grains.os_family in ('Suse',) }} + - require: + - sls: {{ sls_archive_install }} + +grafana-package-archive-install-home-alternative-set: + alternatives.set: + - name: grafana-home + - path: {{ grafana.pkg.archive.name }} + - require: + - alternatives: grafana-package-archive-install-home-alternative-install + - unless: {{ grains.os_family in ('Suse',) }} + + {% for i in ['grafana-cli', 'grafana-server'] %} + +grafana-package-archive-install-{{ i }}-alternative-install: + cmd.run: + - name: update-alternatives --install /usr/bin/{{i}} link-{{i}} {{ grafana.pkg.archive.name }}/bin/{{i}} {{grafana.linux.altpriority}} + - require: + - cmd: grafana-package-archive-install-home-alternative-install + - onlyif: {{ grains.os_family in ('Suse',) }} + alternatives.install: + - name: link-{{ i }} + - link: /usr/bin/{{ i }} + - path: {{ grafana.pkg.archive.name }}/bin/{{ i }} + - priority: {{ grafana.linux.altpriority }} + - order: 10 + - require: + - alternatives: grafana-package-archive-install-home-alternative-install + - unless: {{ grains.os_family in ('Suse',) }} + +grafana-package-archive-install-{{ i }}-alternative-set: + alternatives.set: + - name: link-{{ i }} + - path: {{ grafana.pkg.archive.name }}/bin/{{ i }} + - require: + - alternatives: grafana-package-archive-install-{{ i }}-alternative-install + - unless: {{ grains.os_family in ('Suse',) }} + + {% endfor %} + {%- endif %} + {%- endif %} diff --git a/states/grafana/config/clean.sls b/states/grafana/config/clean.sls new file mode 100644 index 0000000..8e66263 --- /dev/null +++ b/states/grafana/config/clean.sls @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} +{%- set sls_archive_clean = tplroot ~ '.archive.clean' %} +{%- set sls_package_clean = tplroot ~ '.package.clean' %} +{%- set sls_service_clean = tplroot ~ '.service.clean' %} +{%- set sls_alternatives_clean = tplroot ~ '.config.alternatives.clean' %} + + {%- if grains.kernel|lower == 'linux' and grafana.linux.altpriority|int > 0 %} + +include: + - {{ sls_service_clean }} + - {{ sls_archive_clean if grafana.pkg.use_upstream_archive else sls_package_clean }} + - {{ sls_alternatives_clean }} + +grafana-config-clean-file-absent: + file.absent: + - names: + - {{ grafana.config_file }} + - {{ grafana.environ_file }} + - require: + - sls: {{ sls_archive_clean if grafana.pkg.use_upstream_archive else sls_package_clean }} + - sls: {{ sls_alternatives_clean }} + + {%- endif %} diff --git a/states/grafana/config/environ.sls b/states/grafana/config/environ.sls new file mode 100644 index 0000000..3805aee --- /dev/null +++ b/states/grafana/config/environ.sls @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- set sls_archive_install = tplroot ~ '.archive.install' %} +{%- set sls_package_install = tplroot ~ '.package.install' %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} +{%- from tplroot ~ "/libtofs.jinja" import files_switch with context %} + +include: + - {{ sls_archive_install if grafana.pkg.use_upstream_archive else sls_package_install }} + +grafana-config-file-file-managed-environ_file: + file.managed: + - name: {{ grafana.environ_file }} + - source: {{ files_switch(['grafana.sh.jinja'], + lookup='grafana-config-file-file-managed-environ_file' + ) + }} + - mode: 640 + - user: root + - group: {{ grafana.rootgroup if grafana.pkg.use_upstream_archive else grafana.group }} + - makedirs: True + - template: jinja + - context: + config: {{ grafana.environ|json }} + - require: + - sls: {{ sls_archive_install if grafana.pkg.use_upstream_archive else sls_package_install }} diff --git a/states/grafana/config/file.sls b/states/grafana/config/file.sls new file mode 100644 index 0000000..75256ad --- /dev/null +++ b/states/grafana/config/file.sls @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} +{%- from tplroot ~ "/libtofs.jinja" import files_switch with context %} + +{%- if 'config' in grafana and grafana.config %} + {%- if grafana.pkg.use_upstream_archive %} + {%- set sls_package_install = tplroot ~ '.archive.install' %} + {%- else %} + {%- set sls_package_install = tplroot ~ '.package.install' %} + {%- endif %} + +include: + - {{ sls_package_install }} + +grafana-config-file-file-managed-config_file: + file.managed: + - name: {{ grafana.config_file }} + - source: {{ files_switch(['grafana.ini.jinja'], + lookup='grafana-config-file-file-managed-config_file' + ) + }} + - mode: 640 + - user: {{ grafana.grafana_user }} + - group: {{ grafana.rootgroup if grafana.pkg.use_upstream_archive else grafana.group }} + - makedirs: True + - template: jinja + - context: + config: {{ grafana.config|json }} + - require: + - sls: {{ sls_package_install }} + +{%- endif %} diff --git a/states/grafana/config/init.sls b/states/grafana/config/init.sls new file mode 100644 index 0000000..008eaa5 --- /dev/null +++ b/states/grafana/config/init.sls @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +include: + - .file + - .environ + - .alternatives diff --git a/states/grafana/defaults.yaml b/states/grafana/defaults.yaml new file mode 100644 index 0000000..433e082 --- /dev/null +++ b/states/grafana/defaults.yaml @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +--- +grafana: + dir: /opt/grafana + version: '6.2.2' + grafana_url: 'https://grafana.exemple.com' + grafana_user: grafana + grafana_password: grafana + grafana_timeout: 3 + + pkg: + name: grafana + binary: grafana + use_upstream_archive: False + use_upstream_repo: False + repo: + humanname: grafana + name: grafana + comments: + - installed by salt + enabled: 1 + gpgcheck: 1 + archive: + name: /opt + uri: https://dl.grafana.com/oss/release + source: None + source_hash: None + trim_output: True {# works in 2018.3.2. onwards #} + archive_suffix: tar.gz + archive_format: tar + enforce_toplevel: False # needed for grafana + options: '--strip-components=1' # needed for grafana + + kernel: {{grains.kernel|lower}} + rootgroup: root + group: grafana + config_file: /etc/grafana/grafana.ini + config: {} + environ_file: /etc/default/grafana.sh + environ: [] + service: + name: grafana-server + user: grafana + group: grafana + + linux: + altpriority: 0 {# 'Alternatives system' priority: zero disables (default) #} diff --git a/states/grafana/files/default/grafana.ini.jinja b/states/grafana/files/default/grafana.ini.jinja new file mode 100644 index 0000000..ab79337 --- /dev/null +++ b/states/grafana/files/default/grafana.ini.jinja @@ -0,0 +1,17 @@ +######################################################################## +# File managed by Salt at <{{ source }}>. +# Your changes will be overwritten. +######################################################################## + +{%- macro render_key_value_pairs(cfg) %} +{%- for k,v in cfg.items() -%} +{{ k }} = {{ v }} +{% endfor %} +{%- endmacro %} + +{{ render_key_value_pairs(config.get("default", {})) }} +{% for section, cfg in config.items() %} +{%- if section == "default" %}{% continue %}{% endif %} +[{{section}}] +{{ render_key_value_pairs(cfg) }} +{% endfor %} diff --git a/states/grafana/files/default/grafana.sh.jinja b/states/grafana/files/default/grafana.sh.jinja new file mode 100644 index 0000000..c4e6352 --- /dev/null +++ b/states/grafana/files/default/grafana.sh.jinja @@ -0,0 +1,8 @@ +######################################################################## +# File managed by Salt at <{{ source }}>. +# Your changes will be overwritten. +######################################################################## + +{%- for item in config %} +{{ item }} +{%- endfor %} diff --git a/states/grafana/init.sls b/states/grafana/init.sls new file mode 100644 index 0000000..eb20f80 --- /dev/null +++ b/states/grafana/init.sls @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + +include: + - {{ '.archive' if grafana.pkg.use_upstream_archive else '.package' }} + - .config + - .service diff --git a/states/grafana/jinja/macros.jinja b/states/grafana/jinja/macros.jinja new file mode 100644 index 0000000..05ce5fc --- /dev/null +++ b/states/grafana/jinja/macros.jinja @@ -0,0 +1,10 @@ +# +# Collection of common macros + +{%- macro format_kwargs(kwarg) -%} + {%- filter indent(4) %} + {%- for k, v in kwarg|dictsort() %} +- {{ k }}: {{ v }} + {%- endfor %} + {%- endfilter %} +{%- endmacro %} diff --git a/states/grafana/libtofs.jinja b/states/grafana/libtofs.jinja new file mode 100644 index 0000000..ab0d0f6 --- /dev/null +++ b/states/grafana/libtofs.jinja @@ -0,0 +1,100 @@ +{%- macro files_switch(source_files, + lookup=None, + default_files_switch=['id', 'os_family'], + indent_width=6, + v1_path_prefix='') %} + {#- + Returns a valid value for the "source" parameter of a "file.managed" + state function. This makes easier the usage of the Template Override and + Files Switch (TOFS) pattern. + + Params: + * source_files: ordered list of files to look for + * lookup: key under ':tofs:source_files' to override + list of source files + * default_files_switch: if there's no config (e.g. pillar) + ':tofs:files_switch' this is the ordered list of grains to + use as selector switch of the directories under + "/files" + * indent_witdh: indentation of the result value to conform to YAML + * v1_path_prefix: (deprecated) only used for injecting a path prefix into + the source, to support older TOFS configs + + Example (based on a `tplroot` of `xxx`): + + If we have a state: + + Deploy configuration: + file.managed: + - name: /etc/yyy/zzz.conf + - source: {{ files_switch(['/etc/yyy/zzz.conf', '/etc/yyy/zzz.conf.jinja'], + lookup='Deploy configuration' + ) }} + - template: jinja + + In a minion with id=theminion and os_family=RedHat, it's going to be + rendered as: + + Deploy configuration: + file.managed: + - name: /etc/yyy/zzz.conf + - source: + - salt://xxx/files/theminion/etc/yyy/zzz.conf + - salt://xxx/files/theminion/etc/yyy/zzz.conf.jinja + - salt://xxx/files/RedHat/etc/yyy/zzz.conf + - salt://xxx/files/RedHat/etc/yyy/zzz.conf.jinja + - salt://xxx/files/default/etc/yyy/zzz.conf + - salt://xxx/files/default/etc/yyy/zzz.conf.jinja + - template: jinja + #} + {#- Get the `tplroot` from `tpldir` #} + {%- set tplroot = tpldir.split('/')[0] %} + {%- set path_prefix = salt['config.get'](tplroot ~ ':tofs:path_prefix', tplroot) %} + {%- set files_dir = salt['config.get'](tplroot ~ ':tofs:dirs:files', 'files') %} + {%- set files_switch_list = salt['config.get']( + tplroot ~ ':tofs:files_switch', + default_files_switch + ) %} + {#- Lookup source_files (v2), files (v1), or fallback to source_files parameter #} + {%- set src_files = salt['config.get']( + tplroot ~ ':tofs:source_files:' ~ lookup, + salt['config.get']( + tplroot ~ ':tofs:files:' ~ lookup, + source_files + ) + ) %} + {#- Only add to [''] when supporting older TOFS implementations #} + {%- set path_prefix_exts = [''] %} + {%- if v1_path_prefix != '' %} + {%- do path_prefix_exts.append(v1_path_prefix) %} + {%- endif %} + {%- for path_prefix_ext in path_prefix_exts %} + {%- set path_prefix_inc_ext = path_prefix ~ path_prefix_ext %} + {#- For older TOFS implementation, use `files_switch` from the config #} + {#- Use the default, new method otherwise #} + {%- set fsl = salt['config.get']( + tplroot ~ path_prefix_ext|replace('/', ':') ~ ':files_switch', + files_switch_list + ) %} + {#- Append an empty value to evaluate as `default` in the loop below #} + {%- if '' not in fsl %} + {%- do fsl.append('') %} + {%- endif %} + {%- for fs in fsl %} + {%- for src_file in src_files %} + {%- if fs %} + {%- set fs_dir = salt['config.get'](fs, fs) %} + {%- else %} + {%- set fs_dir = salt['config.get'](tplroot ~ ':tofs:dirs:default', 'default') %} + {%- endif %} + {%- set url = '- salt://' ~ '/'.join([ + path_prefix_inc_ext, + files_dir, + fs_dir, + src_file.lstrip('/') + ]) %} +{{ url | indent(indent_width, true) }} + {%- endfor %} + {%- endfor %} + {%- endfor %} +{%- endmacro %} diff --git a/states/grafana/map.jinja b/states/grafana/map.jinja new file mode 100644 index 0000000..a914239 --- /dev/null +++ b/states/grafana/map.jinja @@ -0,0 +1,49 @@ +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{#- Start imports as #} +{%- import_yaml tplroot ~ "/defaults.yaml" as default_settings %} +{%- import_yaml tplroot ~ "/osfamilymap.yaml" as osfamilymap %} +{%- import_yaml tplroot ~ "/osarchmap.yaml" as osarchmap %} +{%- import_yaml tplroot ~ "/osmap.yaml" as osmap %} +{%- import_yaml tplroot ~ "/osfingermap.yaml" as osfingermap %} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='grafana', + merge=salt['grains.filter_by'](osfamilymap, grain='os_family', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](osmap, grain='os', + merge=salt['grains.filter_by'](osfingermap, grain='osfinger', + merge=salt['pillar.get']('grafana:lookup', default={}) + ) + ) + ) + ) +) %} + +{#- Merge the grafana pillar #} +{%- set grafana = salt['pillar.get']('grafana', default=defaults, merge=True) %} + +{#- archive jinja #} +{%- if grafana.pkg.use_upstream_archive %} + {%- set name = 'grafana-%s.%s-%s'|format(grafana.version, grafana.kernel, grafana.arch) %} + {%- set uri = grafana.pkg.archive.uri + '/' + name %} + {%- do grafana.pkg.archive.update({ + 'name': grafana.pkg.archive.name + '/' + name, + 'source': uri + '.' + grafana.pkg.archive.archive_suffix, + 'source_hash': uri + '.' + grafana.pkg.archive.archive_suffix + '.sha256', + 'archive_format': grafana.pkg.archive.archive_format + }) %} + {%- do grafana.environ.append('export PATH=${PATH}:' + grafana.pkg.archive.name + '/bin') %} +{%- endif %} + +{#- Contactenate arguments #} +{%- macro concat_args(args) %} +{%- set args = args|dictsort %} +{%- if args|length > 0 %} +{%- for k,v in args -%} +{%- if not k or not v %}{% continue %}{% endif -%} + --{{ k }}={{ v }} +{%- if not loop.last %} {% endif -%} +{%- endfor -%} +{%- endif -%} +{%- endmacro %} diff --git a/states/grafana/osarchmap.yaml b/states/grafana/osarchmap.yaml new file mode 100644 index 0000000..aca537a --- /dev/null +++ b/states/grafana/osarchmap.yaml @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +# +# Setup variables using grains['osarch'] based logic. +# You just need to add the key:values for an `osarch` that differ +# from `defaults.yaml` + `os_family.yaml`. +# Only add an `osarch` which is/will be supported by the formula +# +# If you do not need to provide defaults via the `osarch` grain, +# you will need to provide at least an empty dict in this file, e.g. +# osarch: {} +--- +amd64: + arch: amd64 + +x86_64: + arch: amd64 + +386: + arch: 386 + +arm64: + arch: arm64 + +armv6l: + arch: armv6l + +armv7l: + arch: armv6l + +ppc64le: + arch: ppc64le + +s390x: + arch: s390x diff --git a/states/grafana/osfamilymap.yaml b/states/grafana/osfamilymap.yaml new file mode 100644 index 0000000..3c40a18 --- /dev/null +++ b/states/grafana/osfamilymap.yaml @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +# +# Setup variables using grains['os_family'] based logic. +# You just need to add the key:values for an `os_family` that differ +# from `defaults.yaml`. +# Only add an `os_family` which is/will be supported by the formula +# +# If you do not need to provide defaults via the `os_family` grain, +# you will need to provide at least an empty dict in this file, e.g. +# osfamilymap: {} +--- +{%- if grains.os == 'MacOS' %} + {% set macos_user = salt['cmd.run']("stat -f '%Su' /dev/console") %} + {% set macos_group = salt['cmd.run']("stat -f '%Sg' /dev/console") %} +{%- endif %} + +Debian: + pkg: + use_upstream_repo: True + repo: + humanname: grafana_official + name: deb https://packages.grafana.com/oss/deb stable main + file: /etc/apt/sources.list.d/grafana.list + key_url: https://packages.grafana.com/gpg.key + +RedHat: + pkg: + use_upstream_repo: True + repo: + baseurl: https://packages.grafana.com/oss/rpm-beta + repo_gpgcheck: 1 + gpgkey: https://packages.grafana.com/gpg.key + sslverify: 1 + sslcacert: /etc/pki/tls/certs/ca-bundle.crt + comments: + - instructions from https://grafana.com/docs/installation/rpm/ + +Suse: + pkg: + use_upstream_repo: True + repo: + baseurl: https://packages.grafana.com/oss/rpm-beta + repo_gpgcheck: 1 + gpgkey: https://packages.grafana.com/gpg.key + sslverify: 1 + sslcacert: /etc/pki/tls/certs/ca-bundle.crt + comments: + - instructions from https://grafana.com/docs/installation/rpm/ + +Gentoo: {} + +Arch: {} + +Alpine: {} + +FreeBSD: + pkg: + name: grafana5 + config_file: /usr/local/etc/grafana.conf + config: + paths: + data: /var/db/grafana/ + logs: /var/log/grafana/ + plugins: /var/db/grafana/plugins + provisioning: /var/db/grafana/provisioning + database: + log_queries: '' + service: + name: grafana + +OpenBSD: {} + +Solaris: {} + +Windows: + dir: C:\\Program Files + pkg: + archive: + name: C:\\Program Files + archive_suffix: zip + archive_format: zip + +MacOS: + rootuser: {{ macos_user | d('') }} + rootgroup: {{ macos_group | d('') }} + group: {{ macos_group | d('') }} + service: + name: grafana + rootgroup: {{ macos_group | d('') }} + group: {{ macos_group | d('') }} diff --git a/states/grafana/osfingermap.yaml b/states/grafana/osfingermap.yaml new file mode 100644 index 0000000..0fac2c6 --- /dev/null +++ b/states/grafana/osfingermap.yaml @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +# +# Setup variables using grains['osfinger'] based logic. +# You just need to add the key:values for an `osfinger` that differ +# from `defaults.yaml` + `os_family.yaml` + `osmap.yaml`. +# Only add an `osfinger` which is/will be supported by the formula +# +# If you do not need to provide defaults via the `os_finger` grain, +# you will need to provide at least an empty dict in this file, e.g. +# osfingermap: {} +--- +# os: Ubuntu +Ubuntu-18.04: {} diff --git a/states/grafana/osmap.yaml b/states/grafana/osmap.yaml new file mode 100644 index 0000000..20a12fd --- /dev/null +++ b/states/grafana/osmap.yaml @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# vim: ft=yaml +# +# Setup variables using grains['os'] based logic. +# You just need to add the key:values for an `os` that differ +# from `defaults.yaml` + `os_family.yaml`. +# Only add an `os` which is/will be supported by the formula +# +# If you do not need to provide defaults via the `os` grain, +# you will need to provide at least an empty dict in this file, e.g. +# osmap: {} +--- +# os_family: Debian +Ubuntu: {} + +Raspbian: {} + +# os_family: Gentoo +Funtoo: {} + +# os_family: Arch +Manjaro: {} + +# os_family: Solaris +SmartOS: {} diff --git a/states/grafana/package/clean.sls b/states/grafana/package/clean.sls new file mode 100644 index 0000000..8570ed6 --- /dev/null +++ b/states/grafana/package/clean.sls @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- set sls_config_clean = tplroot ~ '.config.clean' %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + +include: + - {{ sls_config_clean }} + + {%- if grafana.pkg.use_upstream_repo %} +include: + - .repo.clean + {%- endif %} + +grafana-package-clean-pkg-removed: + pkg.removed: + - name: {{ grafana.pkg.name }} + # require: + # sls: {{ sls_config_clean }} diff --git a/states/grafana/package/init.sls b/states/grafana/package/init.sls new file mode 100644 index 0000000..d3e5518 --- /dev/null +++ b/states/grafana/package/init.sls @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +include: + - .install diff --git a/states/grafana/package/install.sls b/states/grafana/package/install.sls new file mode 100644 index 0000000..d3496c3 --- /dev/null +++ b/states/grafana/package/install.sls @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + + {%- if grafana.pkg.use_upstream_repo %} +include: + - .repo + {%- endif %} + +grafana-package-install-pkg-installed: + pkg.installed: + - name: {{ grafana.pkg.name }} + - version: {{ grafana.version }} \ No newline at end of file diff --git a/states/grafana/package/repo/clean.sls b/states/grafana/package/repo/clean.sls new file mode 100644 index 0000000..d84ac4e --- /dev/null +++ b/states/grafana/package/repo/clean.sls @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + +grafana-package-repo-clean-pkgrepo-absent: + pkgrepo.absent: + - name: {{ grafana.pkg.repo.name }} diff --git a/states/grafana/package/repo/init.sls b/states/grafana/package/repo/init.sls new file mode 100644 index 0000000..d3e5518 --- /dev/null +++ b/states/grafana/package/repo/init.sls @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +include: + - .install diff --git a/states/grafana/package/repo/install.sls b/states/grafana/package/repo/install.sls new file mode 100644 index 0000000..d53fdbb --- /dev/null +++ b/states/grafana/package/repo/install.sls @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + + {%- if grafana.pkg.use_upstream_repo %} + {%- from tplroot ~ "/jinja/macros.jinja" import format_kwargs with context %} + +grafana-package-repo-install-pkgrepo-managed: + pkgrepo.managed: + {{- format_kwargs(grafana.pkg.repo) }} + + {%- endif %} diff --git a/states/grafana/service/clean.sls b/states/grafana/service/clean.sls new file mode 100644 index 0000000..1eed0e6 --- /dev/null +++ b/states/grafana/service/clean.sls @@ -0,0 +1,11 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + +grafana-service-clean-service-dead: + service.dead: + - name: {{ grafana.service.name }} + - enable: False diff --git a/states/grafana/service/init.sls b/states/grafana/service/init.sls new file mode 100644 index 0000000..6fe4d1a --- /dev/null +++ b/states/grafana/service/init.sls @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +include: + - .running diff --git a/states/grafana/service/running.sls b/states/grafana/service/running.sls new file mode 100644 index 0000000..06dd27b --- /dev/null +++ b/states/grafana/service/running.sls @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# vim: ft=sls + +{#- Get the `tplroot` from `tpldir` #} +{%- set tplroot = tpldir.split('/')[0] %} +{%- set sls_config_file = tplroot ~ '.config.file' %} +{%- from tplroot ~ "/map.jinja" import grafana with context %} + +include: + - {{ sls_config_file }} + +grafana-service-running-service-unmasked: + service.unmasked: + - name: {{ grafana.service.name }} + - onlyif: systemctl list-unit-files | grep {{ grafana.service.name }} >/dev/null 2>&1 + +grafana-service-running-service-running: + service.running: + - name: {{ grafana.service.name }} + - enable: True + {%- if 'config' in grafana and grafana.config %} + - watch: + - file: grafana-config-file-file-managed-config_file + - require: + - sls: {{ sls_config_file }} + {%- endif %} + - onlyif: systemctl list-unit-files | grep {{ grafana.service.name }} >/dev/null 2>&1 diff --git a/states/gufw/Desktop.profile b/states/gufw/Desktop.profile new file mode 100644 index 0000000..238eaa9 --- /dev/null +++ b/states/gufw/Desktop.profile @@ -0,0 +1,95 @@ +[fwBasic] +status = enabled +incoming = deny +outgoing = allow +routed = disabled + +[Rule0] +ufw_rule = Anywhere ALLOW IN 82.246.174.207 +description = IPv4 Granville +command = /usr/sbin/ufw allow in from 82.246.174.207 to any +policy = allow +direction = in +protocol = +from_ip = 82.246.174.207 +from_port = +to_ip = +to_port = +iface = +routed = +logging = + +[Rule1] +ufw_rule = Anywhere ALLOW IN 192.168.60.0/24 +description = IPv4 St-Pierre +command = /usr/sbin/ufw allow in from 192.168.60.0/24 to any +policy = allow +direction = in +protocol = +from_ip = 192.168.60.0/24 +from_port = +to_ip = +to_port = +iface = +routed = +logging = + +[Rule2] +ufw_rule = Anywhere ALLOW IN 192.168.50.0/24 +description = IPv4 Granville +command = /usr/sbin/ufw allow in from 192.168.50.0/24 to any +policy = allow +direction = in +protocol = +from_ip = 192.168.50.0/24 +from_port = +to_ip = +to_port = +iface = +routed = +logging = + +[Rule3] +ufw_rule = Anywhere ALLOW IN 10.0.0.0/8 +description = IPv4 Local 10.0.0.0/8 +command = /usr/sbin/ufw allow in from 10.0.0.0/8 to any +policy = allow +direction = in +protocol = +from_ip = 10.0.0.0/8 +from_port = +to_ip = +to_port = +iface = +routed = +logging = + +[Rule4] +ufw_rule = Anywhere (v6) ALLOW IN 2a01:e34:ec85:f190::/64 +description = IPv6 St-Pierre +command = /usr/sbin/ufw allow in from 2a01:e34:ec85:f190::/64 to any +policy = allow +direction = in +protocol = +from_ip = 2a01:e34:ec85:f190::/64 +from_port = +to_ip = +to_port = +iface = +routed = +logging = + +[Rule5] +ufw_rule = Anywhere (v6) ALLOW IN 2a01:e35:2f6a:ecf0::/64 +description = IPv6 Granville +command = /usr/sbin/ufw allow in from 2a01:e35:2f6a:ecf0::/64 to any +policy = allow +direction = in +protocol = +from_ip = 2a01:e35:2f6a:ecf0::/64 +from_port = +to_ip = +to_port = +iface = +routed = +logging = \ No newline at end of file diff --git a/states/gufw/gufw.cfg b/states/gufw/gufw.cfg new file mode 100644 index 0000000..f0b027d --- /dev/null +++ b/states/gufw/gufw.cfg @@ -0,0 +1,5 @@ +[GufwConfiguration] +profile = Desktop +showdonationbtn = no +windowwidth = 800 +windowheight = 530 \ No newline at end of file diff --git a/states/gufw/init.sls b/states/gufw/init.sls new file mode 100644 index 0000000..22a2b27 --- /dev/null +++ b/states/gufw/init.sls @@ -0,0 +1,16 @@ +--- +gufw-cfg: + file.managed: + - source: salt://gufw/gufw.cfg + - name: /etc/gufw/gufw.cfg + - user: root + - group: root + - mode: 600 + +gufw-desktop-cfg: + file.managed: + - source: salt://gufw/Desktop.profile + - name: /etc/gufw/Desktop.profile + - user: root + - group: root + - mode: 600 \ No newline at end of file diff --git a/states/influxdb/config.sls b/states/influxdb/config.sls new file mode 100644 index 0000000..06a1195 --- /dev/null +++ b/states/influxdb/config.sls @@ -0,0 +1,20 @@ +--- +{%- from "influxdb/map.jinja" import influxdb with context %} +influxdb-config-dir: + file.directory: + - name: {{ influxdb.config_dir }} + - user: {{ influxdb.user.name }} + - group: {{ influxdb.group.name }} + - watch_in: + - service: influxdb-service + +influxdb-config-file: + file.managed: + - name: {{ influxdb.config_dir }}/{{ influxdb.config_file }} + - source: salt://influxdb/influxdb.conf.j2 + - user: {{ influxdb.user.name }} + - group: {{ influxdb.group.name }} + - mode: 644 + - template: jinja + - watch_in: + - service: influxdb-service \ No newline at end of file diff --git a/states/influxdb/defaults.yaml b/states/influxdb/defaults.yaml new file mode 100644 index 0000000..7e99f44 --- /dev/null +++ b/states/influxdb/defaults.yaml @@ -0,0 +1,20 @@ +--- +influxdb: + enabled: true + mirror: "https://dl.influxdata.com/influxdb/releases" + dependencies: + - collectd + install_dir: "/usr/local/apps" + release_dir: "/usr/local/apps/releases" + config_dir: "/etc/influxdb" + config_file: "influxdb.conf" + data_dir: "/var/lib/influxdb" + version: "1.8.0" + os: linux + arch: amd64 + user: + name: influxdb + uid: 901 + group: + name: influxdb + gid: 901 diff --git a/states/influxdb/influxdb.conf.j2 b/states/influxdb/influxdb.conf.j2 new file mode 100644 index 0000000..7f91dc6 --- /dev/null +++ b/states/influxdb/influxdb.conf.j2 @@ -0,0 +1,39 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "influxdb/map.jinja" import influxdb with context %} +[meta] + dir = "{{ influxdb.data_dir }}/meta" + +[data] + dir = "{{ influxdb.data_dir }}/data" + wal-dir = "{{ influxdb.data_dir }}/wal" + +[coordinator] + +[retention] + +[shard-precreation] + +[monitor] + +[http] + enabled = true + bind-address = "127.0.0.1:8086" + auth-enabled = true + log-enabled = true + +[admin] + enabled = true + bind-address = ":8083" + https-enabled = false + +[ifql] + +[logging] + +[subscriber] + +[[collectd]] + enabled = true + port = 25826 + database = "collectd" + typesdb = "/usr/share/collectd/types.db" \ No newline at end of file diff --git a/states/influxdb/influxdb.service.j2 b/states/influxdb/influxdb.service.j2 new file mode 100644 index 0000000..f022a21 --- /dev/null +++ b/states/influxdb/influxdb.service.j2 @@ -0,0 +1,17 @@ +{%- from "influxdb/map.jinja" import influxdb with context -%} +[Unit] +Description=InfluxDB is an open-source, distributed, time series database +Documentation=https://docs.influxdata.com/influxdb/ +After=network-online.target + +[Service] +User={{ influxdb.user.name }} +Group={{ influxdb.group.name }} +LimitNOFILE=65536 +ExecStart={{ influxdb.install_dir }}/influxdb/influxd -config /etc/influxdb/influxdb.conf +KillMode=control-group +Restart=on-failure + +[Install] +WantedBy=multi-user.target +Alias=influxd.service \ No newline at end of file diff --git a/states/influxdb/init.sls b/states/influxdb/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/influxdb/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/influxdb/install.sls b/states/influxdb/install.sls new file mode 100644 index 0000000..e854fbe --- /dev/null +++ b/states/influxdb/install.sls @@ -0,0 +1,55 @@ +--- +{%- from "influxdb/map.jinja" import influxdb with context %} +influxdb-group: + group.present: + - name: influxdb + - gid: {{ influxdb.group.gid }} + - watch_in: + - service: influxdb-service + +influxdb-user: + user.present: + - name: influxdb + - uid: {{ influxdb.user.uid }} + - gid: {{ influxdb.group.gid }} + - allow_uid_change: True + - allow_gid_change: True + - home: /var/lib/influxdb + - watch_in: + - service: influxdb-service + +influxdb-dependencies: + pkg.installed: + - pkgs: {{ influxdb.dependencies }} + +influxdb-archive-extract: + archive.extracted: + - name: {{ influxdb.release_dir }}/influxdb-{{ influxdb.version }} + - source: {{ influxdb.mirror }}/influxdb-{{ influxdb.version }}_{{ influxdb.os }}_{{ influxdb.arch }}.tar.gz + - skip_verify: True + - archive_format: tar + - options: --strip 4 + - enforce_toplevel: False + - if_missing: {{ influxdb.release_dir }}/influxdb-{{ influxdb.version }}/influxd + - watch_in: + - service: influxdb-service + +influxdb-bin-symlink: + file.symlink: + - name: {{ influxdb.install_dir }}/influxdb + - target: {{ influxdb.release_dir }}/influxdb-{{ influxdb.version }} + +influxdb-data-dir: + file.directory: + - name: {{ influxdb.data_dir }} + - user: {{ influxdb.user.uid }} + - group: {{ influxdb.group.gid }} + - watch_in: + - service: influxdb-service + +{%- for bin in ['influx', 'influxd'] %} +influxdb-{{ bin }}-symlink: + file.symlink: + - name: /usr/local/sbin/{{ bin }} + - target: {{ influxdb.release_dir }}/{{ bin }} +{%- endfor %} diff --git a/states/influxdb/kernelmap.yaml b/states/influxdb/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/influxdb/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/influxdb/map.jinja b/states/influxdb/map.jinja new file mode 100644 index 0000000..8b8b0ca --- /dev/null +++ b/states/influxdb/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "influxdb/defaults.yaml" as default_settings -%} + +{%- import_yaml "influxdb/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "influxdb/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='influxdb', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set influxdb = salt['pillar.get']('influxdb', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/influxdb/osarchmap.yaml b/states/influxdb/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/influxdb/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/influxdb/service.sls b/states/influxdb/service.sls new file mode 100644 index 0000000..c44fa5e --- /dev/null +++ b/states/influxdb/service.sls @@ -0,0 +1,15 @@ +--- +{%- from "influxdb/map.jinja" import influxdb with context %} +influxdb-service-file: + file.managed: + - name: /etc/systemd/system/influxdb.service + - source: salt://influxdb/influxdb.service.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + +influxdb-service: + service.running: + - name: influxdb + - enable: True \ No newline at end of file diff --git a/states/influxdb/v2/influxdb.service_v2.j2 b/states/influxdb/v2/influxdb.service_v2.j2 new file mode 100644 index 0000000..b45e998 --- /dev/null +++ b/states/influxdb/v2/influxdb.service_v2.j2 @@ -0,0 +1,16 @@ +[Unit] +Description=InfluxDB is an open-source, distributed, time series database +Documentation=https://docs.influxdata.com/influxdb/ +After=network-online.target + +[Service] +User=influxdb +Group=influxdb +LimitNOFILE=65536 +ExecStart=/usr/local/influxdb/influxd +KillMode=control-group +Restart=on-failure + +[Install] +WantedBy=multi-user.target +Alias=influxd.service diff --git a/states/influxdb/v2/init_v2.sls b/states/influxdb/v2/init_v2.sls new file mode 100644 index 0000000..6b9bb64 --- /dev/null +++ b/states/influxdb/v2/init_v2.sls @@ -0,0 +1,58 @@ +--- +influxdb-group: + group.present: + - name: influxdb + - gid: {{ salt['pillar.get']('influxdb:gid') }} + +influxdb-user: + user.present: + - name: influxdb + - uid: {{ salt['pillar.get']('influxdb:uid') }} + - gid: {{ salt['pillar.get']('influxdb:gid') }} + - allow_uid_change: True + - allow_gid_change: True + - home: /var/lib/influxdb + +influxdb-archive-extract: + archive.extracted: + - source: {{ salt['pillar.get']('influxdb:mirror') }}/influxdb-{{ salt['pillar.get']('influxdb:version') }}_{{ salt['grains.get']('kernel')|lower }}_{{ salt['grains.get']('osarch') }}.tar.gz + - skip_verify: True + - archive_format: tar + - user: influxdb + - group: influxdb + - name: /usr/local + - if_missing: /usr/local/influxdb-{{ salt['pillar.get']('influxdb:version') }}_{{ salt['grains.get']('kernel')|lower }}_{{ salt['grains.get']('osarch') }} + +influxdb_directory: + file.directory: + - name: /var/lib/influxdb + - user: influxdb + - group: influxdb + - recurse: + - user + - group + +influxdb-link: + file.symlink: + - name: /usr/local/influxdb + - user: influxdb + - group: influxdb + - target: /usr/local/influxdb_{{ salt['pillar.get']('influxdb:version') }}_{{ salt['grains.get']('kernel')|lower }}_{{ salt['grains.get']('osarch') }} + - force: True + +influxdb-influx-link: + file.symlink: + - name: /usr/sbin/influx + - target: /usr/local/influxdb/influx + +influxdb-service-file: + file.managed: + - name: /lib/systemd/system/influxdb.service + - source: salt://influxdb/influxdb.service.j2 + - user: root + - group: root + +influxdb-service: + service.running: + - name: influxdb + - enable: True diff --git a/states/ipfs/init.sls b/states/ipfs/init.sls new file mode 100644 index 0000000..5344d13 --- /dev/null +++ b/states/ipfs/init.sls @@ -0,0 +1,89 @@ +--- +ipfs-group: + group.present: + - name: ipfs + - gid: 899 + +ipfs-user: + user.present: + - name: ipfs + - uid: 899 + - gid: 899 + - home: /var/lib/ipfs + - allow_uid_change: True + - allow_gid_change: True + +ipfs-dir: + file.directory: + - name: /var/lib/ipfs + - user: ipfs + - group: ipfs + - mode: 0750 + - recurse: + - user + +ipfs-init: + cmd.run: + - name: /usr/local/sbin/ipfs init + - runas: ipfs + - cwd: /var/lib/ipfs + - success_retcodes: + - 0 + - 1 + - watch-in: + - service: ipfs-service + +ipfs-set-storagemax: + cmd.run: + - name: /usr/local/sbin/ipfs config Datastore.StorageMax "20GB" + - runas: ipfs + - cwd: /var/lib/ipfs + - watch-in: + - service: ipfs-service + +ipfs-set-headers-1: + cmd.run: + - name: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Methods '["PUT", "GET", "POST"]' + - runas: ipfs + - cwd: /var/lib/ipfs + - watch-in: + - service: ipfs-service + +ipfs-set-headers-2: + cmd.run: + #- name: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["http://localhost:8888", "http://127.0.0.1:5001", "https://webui.ipfs.io"]' + - name: ipfs config --json API.HTTPHeaders.Access-Control-Allow-Origin '["*"]' + - runas: ipfs + - cwd: /var/lib/ipfs + - watch-in: + - service: ipfs-service + +ipfs-set-api: + cmd.run: + - name: /usr/local/sbin/ipfs config Addresses.API "/ip4/0.0.0.0/tcp/5001" + - runas: ipfs + - cwd: /var/lib/ipfs + - watch-in: + - service: ipfs-service + +ipfs-set-gateway: + cmd.run: + - name: /usr/local/sbin/ipfs config Addresses.Gateway "/ip4/0.0.0.0/tcp/8888" + - runas: ipfs + - cwd: /var/lib/ipfs + - watch-in: + - service: ipfs-service + +ipfs-service-file: + file.managed: + - name: /etc/systemd/system/ipfs.service + - source: salt://ipfs/ipfs.service + - watch-in: + - service: ipfs-service + +ipfs-service: + service.running: + - name: ipfs + - enable: True + - require: + - file: ipfs-dir \ No newline at end of file diff --git a/states/ipfs/ipfs.service b/states/ipfs/ipfs.service new file mode 100644 index 0000000..afa5ba3 --- /dev/null +++ b/states/ipfs/ipfs.service @@ -0,0 +1,20 @@ +[Unit] +Description=ipfs p2p daemon +After=network.target +Requires=network.target + +[Service] +Type=simple +User=ipfs +RestartSec=1 +Restart=always +PermissionsStartOnly=true +Nice=18 +LimitNOFILE=8192 +Environment=IPFS_FD_MAX=8192 +EnvironmentFile=-/etc/sysconfig/ipfs +StandardOutput=journal +ExecStart=/usr/local/sbin/ipfs daemon + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/iptables/init.sls b/states/iptables/init.sls new file mode 100644 index 0000000..f20b099 --- /dev/null +++ b/states/iptables/init.sls @@ -0,0 +1,96 @@ +--- +iptables-pkg: + pkg.latest: + - name: iptables + +iptables-service-config-1: + file.managed: + - name: /etc/systemd/system/iptables.service + - source: salt://iptables/iptables.service.j2 + - template: jinja + - watch_in: + cmd: iptables-reload-systemd + - require: + - pkg: iptables-pkg + +iptables-service-config-2: + file.managed: + - name: /lib/systemd/system/iptables.service + - source: salt://iptables/iptables.service.j2 + - template: jinja + - watch_in: + cmd: iptables-reload-systemd + - require: + - pkg: iptables-pkg + +iptables-reload-systemd: + cmd.run: + - name: systemctl daemon-reload + - require: + - pkg: iptables-pkg + +iptables-service-script: + file.managed: + - name: /sbin/iptables-service + - source: salt://iptables/iptables-service.j2 + - template: jinja + - user: root + - group: root + - mode: 0755 + - require: + - pkg: iptables-pkg + +iptables-config-dir: + file.directory: + - name: /etc/iptables + +iptables-main-config: + file.managed: + - name: /etc/iptables/iptables.conf + - source: salt://iptables/iptables.conf.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +iptables-reset-config: + file.managed: + - name: /etc/iptables/iptables.reset.conf + - source: salt://iptables/iptables.conf.reset.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +ip6tables-main-config: + file.managed: + - name: /etc/iptables/ip6tables.conf + - source: salt://iptables/ip6tables.conf.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +ip6tables-reset-config: + file.managed: + - name: /etc/iptables/ip6tables.reset.conf + - source: salt://iptables/ip6tables.conf.reset.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +iptables-service: + service.running: + - name: iptables + - enable: True + - require: + - pkg: iptables-pkg \ No newline at end of file diff --git a/states/iptables/ip6tables.conf.j2 b/states/iptables/ip6tables.conf.j2 new file mode 100644 index 0000000..b092b64 --- /dev/null +++ b/states/iptables/ip6tables.conf.j2 @@ -0,0 +1,18 @@ +*nat +:PREROUTING ACCEPT [10:1400] +:INPUT ACCEPT [10:1400] +:OUTPUT ACCEPT [30:15184] +:POSTROUTING ACCEPT [30:15184] +COMMIT +*filter +:INPUT DROP [7132:3757309] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [133292:28518143] +-A INPUT -i lo -j ACCEPT +-A INPUT -i tun+ -j ACCEPT +-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +-A INPUT -p ipv6-icmp -j ACCEPT +{% for pub_port in salt['pillar.get']('public_ports') %}-A INPUT -p {{ pub_port.proto }} -m {{ pub_port.proto }} --dport {{ pub_port.port }} -j ACCEPT +{% endfor %}{% for net in salt['pillar.get']('ipv6_networks') %}-A INPUT -s {{ net.ip }}/{{ net.mask }} -j ACCEPT +{% endfor %}-A INPUT -j LOG +COMMIT \ No newline at end of file diff --git a/states/iptables/ip6tables.conf.reset.j2 b/states/iptables/ip6tables.conf.reset.j2 new file mode 100644 index 0000000..3f4c684 --- /dev/null +++ b/states/iptables/ip6tables.conf.reset.j2 @@ -0,0 +1,18 @@ +*mangle +:PREROUTING ACCEPT [5346:7156816] +:INPUT ACCEPT [5346:7156816] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [5178:4339396] +:POSTROUTING ACCEPT [5200:4342124] +COMMIT +*nat +:PREROUTING ACCEPT [2159:266785] +:INPUT ACCEPT [2093:262909] +:OUTPUT ACCEPT [72:6182] +:POSTROUTING ACCEPT [72:6182] +COMMIT +*filter +:INPUT ACCEPT [80:9692] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [70:34623] +COMMIT \ No newline at end of file diff --git a/states/iptables/iptables-service.j2 b/states/iptables/iptables-service.j2 new file mode 100644 index 0000000..00b8c53 --- /dev/null +++ b/states/iptables/iptables-service.j2 @@ -0,0 +1,35 @@ +#!/bin/bash + +flush() +{ + for chain in INPUT OUTPUT FORWARD + do + iptables -P $chain ACCEPT + iptables -F $chain + ip6tables -P $chain ACCEPT + ip6tables -F $chain + done + #for chain in INPUT OUTPUT PREROUTING POSTROUTING + for chain in POSTROUTING + do + iptables -t nat -F $chain + ip6tables -t nat -F $chain + done +} + +load() +{ + iptables-restore -n /etc/iptables/iptables.conf + ip6tables-restore -n /etc/iptables/ip6tables.conf +} + +if [[ $1 == 'start' || $1 == 'restart' ]] +then + flush + load +elif [[ $1 == 'stop' ]] +then + flush +else + echo "Please provide start or stop" +fi \ No newline at end of file diff --git a/states/iptables/iptables.conf.j2 b/states/iptables/iptables.conf.j2 new file mode 100644 index 0000000..e3f1ff0 --- /dev/null +++ b/states/iptables/iptables.conf.j2 @@ -0,0 +1,25 @@ +*filter +:INPUT DROP [0:0] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] +-A INPUT -i lo -j ACCEPT +-A INPUT -i tun+ -j ACCEPT +-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT +-A INPUT -p icmp -j ACCEPT +{%- for pub_port in salt['pillar.get']('public_ports') %} +-A INPUT -p {{ pub_port.proto }} -m {{ pub_port.proto }} --dport {{ pub_port.port }} -j ACCEPT +{%- endfor %} +{%- for net in salt['pillar.get']('ipv4_networks') %} +-A INPUT -s {{ net.ip }}/{{ net.mask }} -j ACCEPT +{%- endfor %} +-A INPUT -j LOG +COMMIT +*nat +:PREROUTING ACCEPT [0:0] +:INPUT ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] +:POSTROUTING ACCEPT [0:0] +{%- for net in salt['pillar.get']('nats') %} +-A POSTROUTING -s {{ net.ip }}/{{ net.mask }} -j MASQUERADE +{%- endfor %} +COMMIT \ No newline at end of file diff --git a/states/iptables/iptables.conf.reset.j2 b/states/iptables/iptables.conf.reset.j2 new file mode 100644 index 0000000..3f4c684 --- /dev/null +++ b/states/iptables/iptables.conf.reset.j2 @@ -0,0 +1,18 @@ +*mangle +:PREROUTING ACCEPT [5346:7156816] +:INPUT ACCEPT [5346:7156816] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [5178:4339396] +:POSTROUTING ACCEPT [5200:4342124] +COMMIT +*nat +:PREROUTING ACCEPT [2159:266785] +:INPUT ACCEPT [2093:262909] +:OUTPUT ACCEPT [72:6182] +:POSTROUTING ACCEPT [72:6182] +COMMIT +*filter +:INPUT ACCEPT [80:9692] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [70:34623] +COMMIT \ No newline at end of file diff --git a/states/iptables/iptables.service.j2 b/states/iptables/iptables.service.j2 new file mode 100644 index 0000000..9a123cd --- /dev/null +++ b/states/iptables/iptables.service.j2 @@ -0,0 +1,14 @@ +[Unit] +Description=Iptables firewall rules +Documentation=man:iptables(8) + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStart=/bin/bash -c "/sbin/iptables-service start" +ExecReload=/bin/bash -c "/sbin/iptables-service restart" +ExecStop=/bin/bash -c "/sbin/iptables-service stop" +StandardOutput=syslog + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/iptables/iptables.sls b/states/iptables/iptables.sls new file mode 100644 index 0000000..38dd307 --- /dev/null +++ b/states/iptables/iptables.sls @@ -0,0 +1,92 @@ +--- +iptables-service-config-1: + file.managed: + - name: /etc/systemd/system/iptables.service + - source: salt://firewall/iptables.service.j2 + - template: jinja + - watch_in: + cmd: iptables-reload-systemd + - require: + - pkg: iptables-pkg + +iptables-service-config-2: + file.managed: + - name: /lib/systemd/system/iptables.service + - source: salt://firewall/iptables.service.j2 + - template: jinja + - watch_in: + cmd: iptables-reload-systemd + - require: + - pkg: iptables-pkg + +iptables-reload-systemd: + cmd.run: + - name: systemctl daemon-reload + - require: + - pkg: iptables-pkg + +iptables-service-script: + file.managed: + - name: /sbin/iptables-service + - source: salt://firewall/iptables-service.j2 + - template: jinja + - user: root + - group: root + - mode: 0755 + - require: + - pkg: iptables-pkg + +iptables-config-dir: + file.directory: + - name: /etc/iptables + +iptables-main-config: + file.managed: + - name: /etc/iptables/iptables.conf + - source: salt://firewall/iptables.conf.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +iptables-reset-config: + file.managed: + - name: /etc/iptables/iptables.reset.conf + - source: salt://firewall/iptables.conf.reset.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +ip6tables-main-config: + file.managed: + - name: /etc/iptables/ip6tables.conf + - source: salt://firewall/ip6tables.conf.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +ip6tables-reset-config: + file.managed: + - name: /etc/iptables/ip6tables.reset.conf + - source: salt://firewall/ip6tables.conf.reset.j2 + - template: jinja + - watch_in: + - service: iptables-service + - require: + - pkg: iptables-pkg + - file: iptables-config-dir + +iptables-service: + service.running: + - name: iptables + - enable: True + - require: + - pkg: iptables-pkg \ No newline at end of file diff --git a/states/java/defaults.yaml b/states/java/defaults.yaml new file mode 100644 index 0000000..239c4c0 --- /dev/null +++ b/states/java/defaults.yaml @@ -0,0 +1,8 @@ +--- +java: + enabled: true + fetch_url: https://paulbsd-pub.s3.fr-par.scw.cloud/java + version: "11.0.5" + install_dir: /usr/java + os: linux + arch: x64 \ No newline at end of file diff --git a/states/java/init.sls b/states/java/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/java/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/java/install.sls b/states/java/install.sls new file mode 100644 index 0000000..2e3310f --- /dev/null +++ b/states/java/install.sls @@ -0,0 +1,39 @@ +--- +{%- from "java/map.jinja" import java with context %} +java_directory: + file.directory: + - name: {{ java.install_dir }} + - user: root + - group: root + - dir_mode: 755 + - file_mode: 644 + +java_extract: + archive.extracted: + - name: {{ java.install_dir }} + - source: {{ java.fetch_url }}/jdk-{{ java.version }}_{{ java.os }}-{{ java.arch }}_bin.tar.gz + - user: root + - group: root + - skip_verify: True + - if_missing: {{ java.install_dir }}/jdk-{{ java.version }} + +java_link: + file.symlink: + - name: {{ java.install_dir }}/default + - target: {{ java.install_dir }}/jdk-{{ java.version }} + - force: True + - user: root + - group: root + +java_updatealternatives_script: + file.managed: + - name: {{ java.install_dir }}/default/update-alternatives-java.sh + - source: salt://java/update-alternatives-java.sh.j2 + - template: jinja + - user: root + - group: root + - mode: 755 + +java_default: + cmd.run: + - name: {{ java.install_dir }}/default/update-alternatives-java.sh \ No newline at end of file diff --git a/states/java/kernelmap.yaml b/states/java/kernelmap.yaml new file mode 100644 index 0000000..b01f41d --- /dev/null +++ b/states/java/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: linux \ No newline at end of file diff --git a/states/java/map.jinja b/states/java/map.jinja new file mode 100644 index 0000000..a215f2d --- /dev/null +++ b/states/java/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "java/defaults.yaml" as default_settings %} + +{%- import_yaml "java/kernelmap.yaml" as kernelmap %} +{%- import_yaml "java/osarchmap.yaml" as osarchmap %} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='java', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +%} + +{%- set java = salt['pillar.get']('java', default=defaults, merge=True) %} \ No newline at end of file diff --git a/states/java/osarchmap.yaml b/states/java/osarchmap.yaml new file mode 100644 index 0000000..9c56fe7 --- /dev/null +++ b/states/java/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: x64 + +x86_64: + arch: x64 + +# 386: +# arch: 386 + +# arm64: +# arch: arm64 + +# armv6l: +# arch: arm + +# armv7l: +# arch: arm + +# armhf: +# arch: arm \ No newline at end of file diff --git a/states/java/update-alternatives-java.sh.j2 b/states/java/update-alternatives-java.sh.j2 new file mode 100644 index 0000000..1f22940 --- /dev/null +++ b/states/java/update-alternatives-java.sh.j2 @@ -0,0 +1,7 @@ +#!/usr/bin/env bash +{%- from "java/map.jinja" import java with context %} +for item in $(ls {{ java.install_dir }}/default/bin) +do + update-alternatives --install /usr/bin/${item} ${item} {{ java.install_dir }}/default/bin/${item} 1 + update-alternatives --set ${item} {{ java.install_dir }}/default/bin/${item} +done \ No newline at end of file diff --git a/states/maildb/defaults.yaml b/states/maildb/defaults.yaml new file mode 100644 index 0000000..5bfa2af --- /dev/null +++ b/states/maildb/defaults.yaml @@ -0,0 +1,13 @@ +--- +maildb: + enabled: true + appdir: /usr/local/apps/maildb + datadir: /usr/local/data/maildb + configfile: /usr/local/apps/maildb/maildb.cfg + config: + file: /usr/local/apps/maildb/maildb.ini + db: + hostname: localhost + username: username + password: password + database: maildb \ No newline at end of file diff --git a/states/maildb/init.sls b/states/maildb/init.sls new file mode 100644 index 0000000..2c44435 --- /dev/null +++ b/states/maildb/init.sls @@ -0,0 +1,53 @@ +--- +{%- from "maildb/map.jinja" import maildb with context %} +maildb-pkg: + pkg.latest: + - pkgs: + - python + - python-mysqldb + - python3 + - python3-mysqldb + +maildb-user-vmail: + user.present: + - name: vmail + - home: /home/vmail + +maildb-apps-dir: + file.directory: + - name: {{ maildb.appdir }} + - user: vmail + - group: vmail + - mode: 0755 + - makedirs: true + +maildb-data-dir: + file.directory: + - name: {{ maildb.datadir }} + - user: vmail + - group: vmail + - mode: 0755 + - makedirs: true + +maildb-script: + file.managed: + - name: {{ maildb.appdir }}/maildb.py + - source: salt://maildb/maildb.py + - user: vmail + - group: vmail + - mode: 0755 + - require: + - file: maildb-apps-dir + - file: maildb-data-dir + +maildb-config: + file.managed: + - name: {{ maildb.appdir }}/maildb.ini + - source: salt://maildb/maildb.ini.j2 + - user: vmail + - group: vmail + - mode: 0755 + - template: jinja + - require: + - file: maildb-apps-dir + - file: maildb-data-dir \ No newline at end of file diff --git a/states/maildb/maildb.ini.j2 b/states/maildb/maildb.ini.j2 new file mode 100644 index 0000000..46f6eaf --- /dev/null +++ b/states/maildb/maildb.ini.j2 @@ -0,0 +1,6 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "maildb/map.jinja" import maildb with context %} +[maildb] +{%- for param, value in maildb.db.items() %} +{{ param }}={{ value }} +{%- endfor %} \ No newline at end of file diff --git a/states/maildb/maildb.py b/states/maildb/maildb.py new file mode 100644 index 0000000..da29254 --- /dev/null +++ b/states/maildb/maildb.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +#-*- coding: utf-8 -*- + +import argparse +import sys +import MySQLdb +import configparser +import email +from email.utils import parseaddr + + +def DBConfig(configfile): + config = configparser.ConfigParser() + config.read(configfile) + + d_dbconfig = { + 'host' : config.get("maildb", "hostname"), + 'user' : config.get("maildb", "username"), + 'passwd' : config.get("maildb", "password"), + 'db' : config.get("maildb", "database") + } + + return d_dbconfig + + +def ConnectMysqlDB(dbconfig): + conn = MySQLdb.connect(**dbconfig) + + return conn, conn.cursor() + + +def CloseDB(conn): + conn.close() + + +def CreateDB(conn, cursor): + try: + cursor.execute('''CREATE TABLE IF NOT EXISTS mail (id int(10) PRIMARY KEY AUTO_INCREMENT, sender varchar(50), recipient varchar(50), date datetime NOT NULL DEFAULT current_timestamp(), content mediumtext);''') + conn.commit() + except Exception as e: + print(e) + + +def InsertMail(conn, cursor, sender, recipient, content): + try: + cursor.execute("INSERT INTO mail (sender, recipient, content) values ('%s','%s','%s')" % (sender, recipient, content)) + conn.commit() + except Exception as e: + print(e) + + +def ParseMail(input_data): + content = "" + for line in input_data: + if line != "": + content += line + + msgobj = email.message_from_string(content) + sender = email.utils.parseaddr(msgobj['From'])[1] + recipient = email.utils.parseaddr(msgobj['To'])[1] + + return sender, recipient, content + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("-c", "--config", help="config file path", default="maildb.cfg") + parser.add_argument("sender", help="sender") + parser.add_argument("recipient", help="recipient") + args = parser.parse_args() + + d_dbconfig = DBConfig(args.config) + + maildata = ParseMail(sys.stdin) + sender = maildata[0] + recipient = maildata[1] + mailcontent = maildata[2] + + conn, cursor = ConnectMysqlDB(d_dbconfig) + CreateDB(conn, cursor) + + InsertMail(conn, cursor, sender, recipient, mailcontent) + + CloseDB(conn) + + +if __name__ == '__main__': + main() diff --git a/states/maildb/map.jinja b/states/maildb/map.jinja new file mode 100644 index 0000000..d8393b7 --- /dev/null +++ b/states/maildb/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "maildb/defaults.yaml" as default_settings %} + +{%- set defaults = salt['grains.filter_by'](default_settings,default='maildb') %} + +{%- set maildb = salt['pillar.get']('maildb', default=defaults, merge=True) %} \ No newline at end of file diff --git a/states/mariadb/config.sls b/states/mariadb/config.sls new file mode 100644 index 0000000..97b2dc6 --- /dev/null +++ b/states/mariadb/config.sls @@ -0,0 +1,15 @@ +--- +{%- from "mariadb/map.jinja" import mariadb with context %} +mariadb-config: + file.managed: + - name: /etc/mysql/mariadb.conf.d/server.cnf + - source: salt://mariadb/server.cnf.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - require: + - pkg: mariadb-install-pkgs + - pip: mariadb-install-pip-pkgs + - watch_in: + - service: mariadb-service \ No newline at end of file diff --git a/states/mariadb/databases.sls b/states/mariadb/databases.sls new file mode 100644 index 0000000..8e0fd81 --- /dev/null +++ b/states/mariadb/databases.sls @@ -0,0 +1,9 @@ +--- +{%- from "mariadb/map.jinja" import mariadb with context %} +{%- for key, value in mariadb.databases.items() %} +mariadb-database-{{ value.name }}: + mysql_database.present: + - name: {{ value.name }} + - connection_user: {{ mariadb.connection_user }} + - connection_pass: {{ mariadb.connection_pass }} +{%- endfor %} \ No newline at end of file diff --git a/states/mariadb/defaults.yaml b/states/mariadb/defaults.yaml new file mode 100644 index 0000000..a8c0a3f --- /dev/null +++ b/states/mariadb/defaults.yaml @@ -0,0 +1,42 @@ +--- +mariadb: + enabled: true + connection_user: root + connection_pass: password + connection_conv: None + databases: {} + users: {} + config: + mysqld: + name: mysqld + options: + binlog_format: ROW + bulk_insert_buffer_size: 16M + expire_logs_days: 10 + event_scheduler: 'ON' + innodb_buffer_pool_size: 256M + innodb_file_per_table: 1 + innodb_flush_method: O_DIRECT + innodb_io_capacity: 400 + innodb_log_buffer_size: 8M + innodb_open_files: 400 + key_buffer_size: 128M + log_slow_verbosity: query_plan + log_warnings: 2 + log-bin: localhost + long_query_time: 10 + max_allowed_packet: 16M + max_binlog_size: 100M + max_connections: 20 + max_heap_table_size: 32M + query_cache_limit: 128K + query_cache_size: 64M + read_buffer_size: 2M + read_rnd_buffer_size: 1M + server-id: 9999 + slow_query_log_file: /var/log/mysql/mariadb-slow.log + sort_buffer_size: 8M + table_open_cache: 400 + thread_cache_size: 128 + tmp_table_size: 32M + wait_timeout: 600 \ No newline at end of file diff --git a/states/mariadb/init.sls b/states/mariadb/init.sls new file mode 100644 index 0000000..43f5f7c --- /dev/null +++ b/states/mariadb/init.sls @@ -0,0 +1,7 @@ +--- +include: + - .install + - .config + - .service + - .databases + - .users \ No newline at end of file diff --git a/states/mariadb/install.sls b/states/mariadb/install.sls new file mode 100644 index 0000000..0419de3 --- /dev/null +++ b/states/mariadb/install.sls @@ -0,0 +1,15 @@ +--- +mariadb-install-pkgs: + pkg.installed: + - pkgs: + - libmariadb-dev + - libmariadbclient18 + - mariadb-client + - mariadb-backup + - mariadb-server + +{%- for pkg in ['mysqlclient'] %} +mariadb-install-pip-pkgs: + pip.installed: + - name: {{ pkg }} +{%- endfor %} \ No newline at end of file diff --git a/states/mariadb/map.jinja b/states/mariadb/map.jinja new file mode 100644 index 0000000..731ff93 --- /dev/null +++ b/states/mariadb/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "mariadb/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='mariadb') -%} + +{%- set mariadb = salt['pillar.get']('mariadb', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/mariadb/server.cnf.j2 b/states/mariadb/server.cnf.j2 new file mode 100644 index 0000000..c36b5ee --- /dev/null +++ b/states/mariadb/server.cnf.j2 @@ -0,0 +1,9 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "mariadb/map.jinja" import mariadb with context -%} +{%- for section, elems in mariadb.config.items() %} +[{{ elems.name }}] +{%- for key, value in elems.options.items() %} +{{ key }}={{ value }} +{%- endfor %} +{%- endfor %} \ No newline at end of file diff --git a/states/mariadb/service.sls b/states/mariadb/service.sls new file mode 100644 index 0000000..1c7f3d6 --- /dev/null +++ b/states/mariadb/service.sls @@ -0,0 +1,5 @@ +--- +mariadb-service: + service.running: + - name: mariadb + - enable: True \ No newline at end of file diff --git a/states/mariadb/users.sls b/states/mariadb/users.sls new file mode 100644 index 0000000..db335a8 --- /dev/null +++ b/states/mariadb/users.sls @@ -0,0 +1,23 @@ +--- +{%- from "mariadb/map.jinja" import mariadb with context %} +{%- for key, user in mariadb.users.items() %} +mariadb-user-{{ user.name }}: + mysql_user.present: + - name: {{ user.name }} + - password: {{ user.password }} + - host: '{{ user.host }}' + - connection_user: {{ mariadb.connection_user }} + - connection_pass: {{ mariadb.connection_pass }} + +{%- for grant in user.grants %} +mariadb-grants-{{ grant.name }}: + mysql_grants.present: + - name: {{ grant.name }} + - grant: '{{ grant.grant }}' + - database: '{{ grant.database }}' + - user: '{{ user.name }}' + - host: '{{ user.host }}' + - connection_user: {{ mariadb.connection_user }} + - connection_pass: {{ mariadb.connection_pass }} +{%- endfor %} +{%- endfor %} diff --git a/states/misc/init.sls b/states/misc/init.sls new file mode 100644 index 0000000..7b87592 --- /dev/null +++ b/states/misc/init.sls @@ -0,0 +1,35 @@ +--- +us_locale: + locale.present: + - name: en_US.UTF-8 + +default_locale: + locale.system: + - name: en_US.UTF-8 + - require: + - locale: us_locale + +misc-public-dir: + file.directory: + - name: /home/public + - user: root + - group: root + - mode: 0755 + +{%- for file in salt['pillar.get']('misc_files') %} +{{ file.name }}: + file.managed: + - name: {{ file.dest }} + - source: {{ file.src }} + - source_hash: sha256={{ file.checksum }} + - user: root + - group: root + - mode: 644 +{%- endfor %} + +misc-files-delete: + file.absent: + - names: +{%- for file in salt['pillar.get']('misc_files_delete') %} + - {{ file }} +{%- endfor %} diff --git a/states/molotov/init.sls b/states/molotov/init.sls new file mode 100644 index 0000000..c6d1b93 --- /dev/null +++ b/states/molotov/init.sls @@ -0,0 +1,28 @@ +--- +{%- if not salt['file.file_exists']( salt['pillar.get']('molotov:dest_path') ) or not salt['file.check_hash']( salt['pillar.get']('molotov:dest_path'), salt['pillar.get']('molotov:file_sha256sum') ) %} +molotov-install: + file.managed: + - name: {{ salt['pillar.get']('molotov:dest_path') }} + - source: {{ salt['pillar.get']('molotov:url') }}/{{ salt['pillar.get']('molotov:file') }} + - source_hash: sha256={{ salt['pillar.get']('molotov:file_sha256sum') }} + - user: root + - group: root + - mode: 755 +{%- endif %} + +molotov-icon: + file.managed: + - name: /usr/share/icons/molotov.png + - source: salt://molotov/molotov.png + - user: root + - group: root + - mode: 644 + +molotov-desktop-entry: + file.managed: + - name: /usr/share/applications/molotov.desktop + - source: salt://molotov/molotov.desktop.j2 + - template: jinja + - user: root + - group: root + - mode: 644 diff --git a/states/molotov/molotov.desktop.j2 b/states/molotov/molotov.desktop.j2 new file mode 100644 index 0000000..fdbc7a4 --- /dev/null +++ b/states/molotov/molotov.desktop.j2 @@ -0,0 +1,12 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Name=Molotov +Comment=L'app pour regarder la TV, gratuitement +Exec={{ salt['pillar.get']('molotov:dest_path') }} +Icon=/usr/share/icons/molotov.png +Terminal=false +StartupWMClass=Molotov +Type=Application +Categories=AudioVideo;Player; +X-Desktop-File-Install-Version=0.22 \ No newline at end of file diff --git a/states/molotov/molotov.png b/states/molotov/molotov.png new file mode 100644 index 0000000000000000000000000000000000000000..99ac8195a6c47f064694a332b9cb15bcde49980b GIT binary patch literal 62400 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�#Dw}!k2EPB5VoeyJ z**`{{YLwrzdf&y6(R}(cSXyrLQ-H4n(eP^Isn`*m4?3TqsBTmrY|s0;LdqMUN@LN6 zbI^w{Eor?R`BhxxG)Xg2shue0o`xcqBKLVC?SnpNsQ&9*dVcVOq?ahZUuCk=N}Kt8 zW)P*gh>#llRAQd`CrC9p(e}fRxgAHy1%-FeGJJ?un?bCMfpDc|`wgFlxvrSZ1qkDP>1dsm$sem_yUI(%Qwu=tQ#l8zyXF{eo zIdwMQz~PIPUVcKI8g4;S{e;c#W}j(*jbNsu>&H?^@FSp=QfW5y*%+9)h$j!-w4jc8 z?aDCQrA7WUseeafg2<|MIKM9%$bN_}9Zr&7M81?m@#C+jk5GaYu|p;_vH zJ{E?3G5bay+JscVAW8l&Na||N((8%Eo^8X@b+4NAJ^s;d#@dFBHhNvMNil;??h1J- z_d))?gLM-J2Kgeins)YzJ9eDDtyEP8PQd=fu;=knE^S54)H>5HeOhC!in{Y(ai7Qs zoeQw$C5in_Ebw62bMAgLZSS`ea>>Zx!t?wYWoBdgoPpJW;aAboG}%5yq~#3XNO?cn zp=oz-4gM4FCVg^~l*LBO1J-#cZBD?yT=3v8kN#utPB}a2&-eNn3S|ZgXK4n_n9<5C z!-CEZA&`AQI9jEUfHQYhbvb5*3W25d*Qq%gUd%^oR-LFk<4VF<{rV9O`L>^|6(Fu6 zJGzH&s^+NH>sH8(bFUMN(4isou;ipknGcG2uSa)xjD~>qOcskp+D?NFLiP-%;oIlN zyd3*=p|dQpqi7b^Qwzae&~xU8GikZ~);z*U$MO1&Bvt$*z$h->*7y0O)v{}=X0&y|Q;^4c!GPoOY1QZ5u>+22e2gR5#H50l zMfSt8nt+*$o$}#v^g7+Xw$|{G?@FjTSJN@;#E3^3L#bWP`sI!KTI|NmUj-WEb)^!M zUqR>zaY)v3EOl4((v(}vo6Z;-)j8H2x1gH>(u|puMsMGxLUw3+vv~WTcwRf&gNk5D52Xz8S?EMzzH=hCQlhic)Iy)Rw&Owl zW@Hf~4w60H5h>s8>!XV*74vrWVy2^HzKPE^h&uJpRXogo-3R&VLDn(c94l=*Z_8*bG3c5CV}sSI(@9|Bxf-Yr|G(!~v& z|Ekf4c{J(wP0CY=N3f%t{brd(UCN)M>U{Lfd5ZBgR!DW5hBrV|2imq$f|w#23A)>V zh0yDKwLbJs#Fzr_inl?U44oe)dhRaXZC9v1l^mW^fSzVakHyeLoeF>A)&yyGCfKMT z*e5B`n^5o>?WMq(Zs;%KPD9>wNuF0JwN4~Sr~0Cmi><7q`5L|MYA@Tdr8<>Zi4yj? zxr&u)IBS+oeJx^oo{ki|PQHy)9CP6gn%vZlc)nt%2LDBX`#i(<0yaO%i6CAR=NkFU zb5~1b0JY3;(e6|qX#g9cZ!GRnxJ@L8-gnAl32%_}L8bX=g|5ejN*-a6C2g#Ji)xo5 z+G8k}^>L+~e|gN>-+fw(HOgO6Y%Vg{Rxv4ymSEe9<~l zn?mC{_0fc?t3JHl5n8Ds^>1Ily?p4=U!i==(vICn$A-fha8^dl*+o2@;Xtm6&IOe{ zoZgdy=L}^PJap(HzlHA>wM61n5HeD(4aqyf&X+Qk;=~AlL0U}j<>|D(-F}a3AlGmA znL}o1>|b}=!-r!JqJv$4x2N;b%|bBz#xU|at)d?+VXy8&6%$F{;p^*~>$lGinall` z-xux?EKw{Sy7<`(n-*oR<4Ku5ltC;~>3g_R%#L24r@z#*H;{^Hex&@qOw!)P?{N3T z3??A%s(q`g<_OLKACI%L0@x6;--fa}CO@6~KLD~oO~0N*siPcq!|sY(RF6b$M-*`2 zMY-byE2ps|ru$9G5du?joLt3bY1fp5Q8#*_a&#t;nMnAU5>?c^Vuu&S#MOIF$1o1D?RenY9**$o!eoDsJKhcVp(va_udbH z-+LDuldQg!E)q9K0us8nA3*J3d)LsR4(lbj;{+?%aivIUYqZwoNHsGy@m8*8u9jo0 zo2HWY_P4Hm`5!;~rY|~ME4b(~HDxG4geXMmrd~_0S&llvh)@sK4 z5H;c@ulH{v~;B<8%E{Mbi6 zdNI>g-CLjcvMxhuhjku@ed?>qUs?TyzS+RVyL0Fi5IRMB{VR#=mD&{2_24*q*gPbi zSz7mwB@A^~FUg%JSnkmUOJ~nwkeItf6SA65SvtU4u~xwO<9x@rK6q+dlb)(ahbhllMLgeJ_akb^V48 z{)Pt_$zBqkw8w0q?I$40$idi$-U342p z*3C%CkXKG3M?^?u)d2jQSHf4dp#6h)OGQcdoul-LQwpQeF|yJ1=QkjvG~J~7yw`1f!5j7b#`dJ7QV%)fp$H{1 zqIH*UkJ`kK&Vn){t1xeP{NlU;98s)oC6LAYjdSZ3Fb9i_gDlITom*!Tr4qaPG1V>? z=D5H1LD9U3I&xw;R?)g`TarYfO%QGAVS9i2_FsSciAyYr1nT)N0hDEvkR+r`4XG?j zF=8OberFQ!ph}=#j+MXot$g8Ug*Cr&vYb>@VYL1SPj0oV+RVIcs~b;Zi8$nJ4N%B5 zxCZBSue`eo(gle@GHQ%yEblgp-NkFa@Betb1ufT-?GqEyN`J0at%&^}1(Zb>t`aOpe$g9jftzc+3um7oX#k9Y|My;`lN*m0Ogcg?8<9bejN zy3Dz{bqfa*ymPJezzZ1n9bTyi|LNX0^=M}tBW$cQ9{H#s*qU3Jm=ShGva&(mATVq5 z+{?Y@!q2{CK1+mgg)~SDhiew2?dlu5+oNr18xTgLF<8pUnUC20$}i-bzw+3z;?f#v zEpB4dM&gs(EFCOL>ZDf)5KqTBbFGi#+Sr*He^#naEy zK=Nhh{w@mCMnjjo>I0k>)R|6+LM<9mp=DB)(HDQ|PhEK3{nzd~HJet1h{ln|Lam@4 zJ@)h>Ijzy=)SVw$t?~~Sd~)4Ho!)HEk>2+2NDH@H7xfilLAd|!vXEtz`mm&3R=KLa z=#*8dupT_}z{lZ^6D$^`IH!&yJpJWg`angkB3y$7Y;8_Cx6a;Q`b+1JAOF3|w=PUZ zRiu!vNi9M^rE4|rvRC4LT>_BFncTK$m_G94UME<=QC8b->(=UiFkwoIPumg_Ep z2&P!qk$wKm`eaH`6v~=gTp7!Fu#I{-3ZwDNXyo|Y-+B2DK1_n8&w6b-KtQt5I;m+I zqmyGW+EhbR#Ij=lmFM}+Z@Kr(c>C3drm*SUUY@;5F^1{LT;-Me)w8fdh6XXP3WC%V6{T*UZy@(-f7B# zL52{;SG%RsRp548fNg}=z4}a{If!-3{yve=UpMJs2!-5?^XwhUohMilx{!QY?0wx= zp1-iZU$(o)s%dQ2PgKX0kG}DB^%s18zWYM6R+0A$36T(y(7iXVY(QE_;x4QHRVSKD z?y48U=+P&xwt#Eb;O{Ek(m_GHfJu(5{#dORJ8A1=jex6&hR%vb2*TZWpNIt^h){`y zF6;^pxfCYz+ICxZ>Wv@%@h37>QLXn2y`#I1-Wfe*BV`<@%25|F=Cn|4CC$&Q@qho0 z^$W+II#FQ@FURZI=B>8dbGvjEm|!Y2Mi-OZINDA8izU+$=@s`dDYq-ND3hEaN;VoB zHC%Z6d!G3HkJb&U1tN%{CT^r|VJ(gq{kAF?meH0}R^ZU2(we(B_>OOV?TKR_n~tm$ zB1GA6m&_tzO^zRY_^Net*rab%^K5dR84#=2>7c{VtsnE#8PrDnx^M0le1-6;2R38y zuLw zU;VDv+;eVO7@Aq%*fcFngCQBunvvGEh8j*8ax=ubdjqB4Bbu(wwHrT~Wa}p}+ zUF}7NB{y8^o~_qzJWj#{c-?C#|4=nB=uFtf)>_*HTc|zq=v5OE z;m9lUye8*#* zcS`f~0j|49acsSKc6Wc|kN=skf5>MK|IE+WGV!Wc^B4Z&J$IeHROxJ_Y)mHJZdnDm z^n=S2ol|EY7%5&rMAflOHW!8YqPe^SGV)wsH-JKsj9!0f+G=*YAH5-5M1nga(xt#U z&$;{WJ!RcA>aZia6h||pkl0_grsZav?t1f&{Xq*Y()k#KZo%36^GP?E-J#7X#L`kH z9%)=^U8lzQ@;|~ifAs_7^pU&Hvb~eeo(fB=Ypl)H!w+9oLvvEHLJc0}MmORe>D9Q_ zl=Bo`_bSGtg*u{}mX^3@FY5Nhd*8qJJ0F0UaLI|nDf-bBW0Qo}XjaRiv-E|gkn*L>qwwR`g`)p|a8Vj-#CEJu?v@9oWxpIEb1ZL>%gOuF&nc617=;sF

M(Kd3F6-tFq8f^veWamn_7TpW;XD4^sk0k< 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 "{{ v }}"{% if last %},{% else %}{% endif %} +{%- endfor %} +{%- elif value is sequence and value is not string -%} +array( + {%- for value2 in value %} + {{ mac(value2,loop.last) }} + {%- endfor -%} + ), +{%- elif value is true or value is false -%} +{{ value|lower }}, +{%- elif value is number -%} +{{ value }}, +{%- else -%} +"{{ value }}", +{%- endif %} +{%- endmacro %} +$CONFIG = array( +{%- for key, value in nextcloud.config.items() %} + "{{ key }}" => {{ mac(value,loop.last) }} +{%- endfor %} +); \ No newline at end of file diff --git a/states/nextcloud_desktop/init.sls b/states/nextcloud_desktop/init.sls new file mode 100644 index 0000000..b2040a2 --- /dev/null +++ b/states/nextcloud_desktop/init.sls @@ -0,0 +1,27 @@ +--- +{%- if not salt['file.file_exists']( salt['pillar.get']('nextcloud_desktop:dest_path') ) or not salt['file.check_hash']( salt['pillar.get']('nextcloud_desktop:dest_path'), salt['pillar.get']('nextcloud_desktop:file_sha256sum') ) %} +nextcloud-install: + file.managed: + - name: {{ salt['pillar.get']('nextcloud_desktop:dest_path') }} + - source: {{ salt['pillar.get']('nextcloud_desktop:url') }}/Nextcloud-{{ salt['pillar.get']('nextcloud_desktop:version') }}-x86_64.AppImage + - source_hash: sha256={{ salt['pillar.get']('nextcloud_desktop:file_sha256sum') }} + - user: root + - group: root + - mode: 755 +{%- endif %} + +nextcloud-icon: + file.managed: + - name: /usr/share/icons/nextcloud.png + - source: salt://nextcloud_desktop/nextcloud.png + - user: root + - group: root + - mode: 644 + +nextcloud-desktop-entry: + file.managed: + - name: /usr/share/applications/nextcloud.desktop + - source: salt://nextcloud_desktop/nextcloud.desktop + - user: root + - group: root + - mode: 644 \ No newline at end of file diff --git a/states/nextcloud_desktop/nextcloud.desktop b/states/nextcloud_desktop/nextcloud.desktop new file mode 100644 index 0000000..3911044 --- /dev/null +++ b/states/nextcloud_desktop/nextcloud.desktop @@ -0,0 +1,188 @@ +[Desktop Entry] +Categories=Utility; +Type=Application +Exec=nextcloud +Name=Nextcloud +Comment=PaulBSD Cloud desktop synchronization client +GenericName=Folder Sync +Icon=Nextcloud +Keywords=Nextcloud;syncing;file;sharing; +X-GNOME-Autostart-Delay=3 + +Comment[oc]=Nextcloud sincronizacion del client +GenericName[oc]=Dorsièr de Sincronizacion +Name[oc]=Nextcloud sincronizacion del client +Icon[oc]=Nextcloud +Comment[ar]=Nextcloud زبون مزامنة مكتبي +GenericName[ar]=مزامنة المجلد +Name[ar]=Nextcloud زبون مزامنة مكتبي +Icon[ar]=Nextcloud +Comment[bg_BG]=Nextcloud клиент за десктоп синхронизация +GenericName[bg_BG]=Синхронизиране на папката +Name[bg_BG]=Nextcloud клиент десктоп синхронизация +Icon[bg_BG]=Nextcloud +Comment[ca]=Client de sincronització d'escriptori Nextcloud +GenericName[ca]=Sincronització de carpetes +Name[ca]=Client de sincronització d'escriptori Nextcloud +Icon[ca]=Nextcloud +Comment[da]=Nextcloud skrivebordsklient til synkronisering +GenericName[da]=Mappesynkronisering +Name[da]=Nextcloud skrivebordsklient til synk +Icon[da]=Nextcloud +Comment[de]=Nextcloud Desktop-Synchronisationsclient +GenericName[de]=Ordner-Synchronisation +Name[de]=Nextcloud Desktop-Synchronisationsclient +Icon[de]=Nextcloud +Comment[ja_JP]=Nextcloud デスクトップ同期クライアント +GenericName[ja_JP]=フォルダー同期 +Name[ja_JP]=Nextcloud デスクトップ同期クライアント +Icon[ja_JP]=Nextcloud +Comment[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη +GenericName[el]=Συγχρονισμός φακέλου +Name[el]=@ΟΝΟΜΑ_ΕΦΑΡΜΟΓΗΣ@ συγχρονισμός επιφάνειας εργασίας πελάτη +Icon[el]=Nextcloud +Comment[en_GB]=Nextcloud desktop synchronisation client +GenericName[en_GB]=Folder Sync +Name[en_GB]=Nextcloud desktop sync client +Icon[en_GB]=Nextcloud +Comment[es]=Nextcloud cliente de sincronización de escritorio +GenericName[es]=Sincronización de carpeta +Name[es]=Nextcloud cliente de sincronización de escritorio +Icon[es]=Nextcloud +Comment[de_DE]=Nextcloud Desktop-Synchronisationsclient +GenericName[de_DE]=Ordner-Synchronisation +Name[de_DE]=Nextcloud Desktop-Synchronisationsclient +Icon[de_DE]=Nextcloud +Comment[eu]=Nextcloud mahaigaineko sinkronizazio bezeroa +GenericName[eu]=Karpetaren sinkronizazioa +Name[eu]=Nextcloud mahaigaineko sinkronizazio bezeroa +Icon[eu]=Nextcloud +GenericName[fa]=همسان سازی پوشه‌ها +Name[fa]=nextcloud نسخه‌ی همسان سازی مشتری +Icon[fa]=Nextcloud +Comment[fr]=Synchronisez vos dossiers avec un serveur Nextcloud +GenericName[fr]=Synchronisation de dossier +Name[fr]=Client de synchronisation Nextcloud +Icon[fr]=Nextcloud +Comment[gl]=Nextcloud cliente de sincronización para escritorio +GenericName[gl]=Sincronizar Cartafol +Name[gl]=Nextcloud cliente de sincronización para escritorio +Icon[gl]=Nextcloud +Comment[he]=Nextcloud לקוח סנכון שולחן עבודה +GenericName[he]=סנכון תיקייה +Name[he]=Nextcloud לקוח סנכרון שולחן עבודה +Icon[he]=Nextcloud +Comment[ia]=Nextcloud cliente de synchronisation pro scriptorio +GenericName[ia]=Synchronisar Dossier +Name[ia]=Nextcloud cliente de synchronisation pro scriptorio +Icon[ia]=Nextcloud +Comment[id]=Klien sinkronisasi desktop Nextcloud +GenericName[id]=Folder Sync +Name[id]=Klien sync desktop Nextcloud +Icon[id]=Nextcloud +Comment[is]=Nextcloud skjáborðsforrit samstillingar +GenericName[is]=Samstilling möppu +Name[is]=Nextcloud skjáborðsforrit samstillingar +Icon[is]=Nextcloud +Comment[it]=Client di sincronizzazione del desktop di Nextcloud +GenericName[it]=Sincronizzazione cartella +Name[it]=Client di sincronizzazione del desktop di Nextcloud +Icon[it]=Nextcloud +Comment[ko]=Nextcloud 데스크톱 동기화 클라이언트 +GenericName[ko]=폴더 동기화 +Name[ko]=Nextcloud 데스크톱 동기화 클라이언트 +Comment[hu_HU]=Nextcloud asztali szinkronizációs kliens +GenericName[hu_HU]=Könyvtár szinkronizálás +Name[hu_HU]=Nextcloud asztali szinkr. kliens +Icon[hu_HU]=Nextcloud +Comment[nl]=Nextcloud desktop synchronisatie client +GenericName[nl]=Mappen sync +Name[nl]=Nextcloud desktop sync client +Icon[nl]=Nextcloud +Comment[et_EE]=Nextcloud sünkroonimise klient töölauale +GenericName[et_EE]=Kaustade sünkroonimine +Name[et_EE]=Nextcloud sünkroonimise klient töölauale +Icon[et_EE]=Nextcloud +Comment[pl]=Nextcloud klient synchronizacji dla komputerów stacjonarnych +GenericName[pl]=Folder Synchronizacji +Name[pl]=Nextcloud klient synchronizacji dla komputerów stacjonarnych +Icon[pl]=Nextcloud +Comment[pt_BR]=Nextcloud cliente de sincronização do computador +GenericName[pt_BR]=Sincronização de Pasta +Name[pt_BR]=Nextcloud cliente de sincronização de desktop +Icon[pt_BR]=Nextcloud +Comment[cs_CZ]=Nextcloud počítačový synchronizační klient +GenericName[cs_CZ]=Synchronizace adresáře +Name[cs_CZ]=Nextcloud počítačový synchronizační klient +Icon[cs_CZ]=Nextcloud +Comment[ru]=Настольный клиент синхронизации Nextcloud +GenericName[ru]=Синхронизация каталогов +Name[ru]=Настольный клиент синхронизации Nextcloud +Icon[ru]=Nextcloud +Comment[sl]=Nextcloud ‒ Program za usklajevanje datotek z namizjem +GenericName[sl]=Usklajevanje map +Name[sl]=Nextcloud ‒ Program za usklajevanje datotek z namizjem +Icon[sl]=Nextcloud +Comment[sq]=Klient njëkohësimesh Nextcloud për desktop +GenericName[sq]=Njëkohësim Dosjesh +Name[sq]=Klient njëkohësimesh Nextcloud për desktop +Icon[sq]=Nextcloud +Comment[fi_FI]=Nextcloud työpöytäsynkronointisovellus +GenericName[fi_FI]=Kansion synkronointi +Name[fi_FI]=Nextcloud työpöytäsynkronointisovellus +Icon[fi_FI]=Nextcloud +Comment[sv]=Nextcloud desktop synkroniseringsklient +GenericName[sv]=Mappsynk +Name[sv]=Nextcloud desktop synk-klient +Icon[sv]=Nextcloud +Comment[tr]=Nextcloud masaüstü eşitleme istemcisi +GenericName[tr]=Dosya Eşitleme +Name[tr]=Nextcloud masaüstü eşitleme istemcisi +Icon[tr]=Nextcloud +Comment[uk]=Настільний клієнт синхронізації Nextcloud +GenericName[uk]=Синхронізація теки +Name[uk]=Настільний клієнт синхронізації Nextcloud +Icon[uk]=Nextcloud +Comment[ro]=Nextcloud client de sincronizare pe desktop +GenericName[ro]=Sincronizare director +Name[ro]=Nextcloud client de sincronizare pe desktop +Icon[ro]=Nextcloud +Comment[zh_CN]=Nextcloud 桌面同步客户端 +GenericName[zh_CN]=文件夹同步 +Name[zh_CN]=Nextcloud 桌面同步客户端 +Icon[zh_CN]=Nextcloud +Comment[zh_HK]=桌面版同步客户端 +GenericName[zh_TW]=資料夾同步 +Comment[es_AR]=Cliente de sincronización para escritorio Nextcloud +GenericName[es_AR]=Sincronización de directorio +Name[es_AR]=Cliente de sincronización para escritorio Nextcloud +Icon[es_AR]=Nextcloud +Comment[lt_LT]=Nextcloud darbalaukio sinchronizavimo programa +GenericName[lt_LT]=Katalogo sinchnorizacija +Name[lt_LT]=Nextcloud darbalaukio programa +Icon[lt_LT]=Nextcloud +Comment[th_TH]=Nextcloud ไคลเอนต์ประสานข้อมูลเดสก์ท็อป +GenericName[th_TH]=ประสานข้อมูลโฟลเดอร์ +Name[th_TH]= Nextcloud ไคลเอนต์ประสานข้อมูลเดสก์ท็อป +Icon[th_TH]=Nextcloud +Comment[es_MX]=Cliente de escritorio para sincronziación de Nextcloud +GenericName[es_MX]=Sincronización de Carpetas +Name[es_MX]=Cliente de escritorio para sincronziación de Nextcloud +Icon[es_MX]=Nextcloud +Comment[nb_NO]=Nextcloud skrivebordssynkroniseringsklient +GenericName[nb_NO]=Mappesynkronisering +Name[nb_NO]=Nextcloud skrivebordssynkroniseringsklient +Icon[nb_NO]=Nextcloud +Comment[nn_NO]=Nextcloud klient for å synkronisera frå skrivebord +GenericName[nn_NO]=Mappe synkronisering +Name[nn_NO]=Nextcloud klient for å synkronisera frå skrivebord +Icon[nn_NO]=Nextcloud +Comment[pt_PT]=Nextcloud - Cliente de Sincronização para PC +GenericName[pt_PT]=Sincronizar Pasta +Name[pt_PT]=Nextcloud - Cliente de Sincronização para PC +Icon[pt_PT]=Nextcloud +Icon[km]=Nextcloud +Comment[lb]=Nextcloud Desktop Synchronisatioun Client +GenericName[lb]=Dossier Dync +Name[lb]=Nextcloud Desktop Sync Client +Icon[lb]=Nextcloud \ No newline at end of file diff --git a/states/nextcloud_desktop/nextcloud.png b/states/nextcloud_desktop/nextcloud.png new file mode 100644 index 0000000000000000000000000000000000000000..c97f3680759394f7a0996030f31b4896abcf7188 GIT binary patch literal 17488 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 type ipset file "{{ value.filename }}" +#table <{{ key }}> type cdb file "{{ value.filename }}" +{%- endfor %} +{%- endif %} + + +## Translations ## +{%- for key, value in net.nats.items() %} +#map $ext dynamic {{ value.ip }}/{{ value.mask }} -> inet4({{ net.interfaces.ext.id }}) +{%- endfor %} +map $ext dynamic 10.99.99.0/24 -> inet4({{ net.interfaces.ext.id }}) +map $ext dynamic 192.168.50.0/26 -> inet4({{ net.interfaces.ext.id }}) + +alg "icmp" + +{%- if net.log is defined and net.log.enabled %} +## Procedures ## +procedure "log" { + log: {{ net.log.interface }} + normalize: {{ net.log.normalize }} +} +{%- endif %} + + +## Rules ## +{%- for key, value in net.interfaces.items() %} +{%- if value.type == 'lan' %} +group "{{ key }}" on ${{ key }} { +{%- for family, net in [('inet4','mynet4'), ('inet6','mynet6')] %} + pass stateful out final family {{ family }} all +{%- endfor %} +{%- for family, net in [('inet4','mynet4'), ('inet6','mynet6')] %} + pass stateful in final family {{ family }} from ${{ net }} to any +{%- endfor %} + + block in all apply "log" + block in final from apply "log" + + pass stateful in final family inet4 proto icmp all + pass stateful in final family inet6 proto ipv6-icmp all + + pass stateful in family inet4 proto tcp from any to any port $public_ports_tcp + pass stateful in family inet6 proto tcp from any to any port $public_ports_tcp + + pass stateful in family inet4 proto udp from any to any port $public_ports_udp + pass stateful in family inet6 proto udp from any to any port $public_ports_udp +} +{%- endif %} +{%- endfor %} + +group default { + block all {%- if net.log is defined and net.log.enabled %}{{ set_log() }}{%- endif %} +{%- for key, value in net.interfaces.items() %} +{%- if value.skip is defined and value.skip %} + pass on ${{ key }} all +{%- endif %} +{%- endfor %} +} \ No newline at end of file diff --git a/states/oldstates/clamav/clamd.conf.j2 b/states/oldstates/clamav/clamd.conf.j2 new file mode 100644 index 0000000..6c91da2 --- /dev/null +++ b/states/oldstates/clamav/clamd.conf.j2 @@ -0,0 +1,4 @@ +{%- from "maildb/map.jinja" import maildb with context -%} +{%- for option, param in clamav.config.clamd.options.items() %} +{{ option }}={{ param }} +{%- endfor %} \ No newline at end of file diff --git a/states/oldstates/clamav/config.sls b/states/oldstates/clamav/config.sls new file mode 100644 index 0000000..3362c77 --- /dev/null +++ b/states/oldstates/clamav/config.sls @@ -0,0 +1,11 @@ +--- +clamav-clamd-config: + file.managed: + - name: {{ clamav.config.clamd.file }} + - source: salt://clamav/clamd.conf.j2 + - user: root + - group: root + - mode: 0755 + - template: jinja + - watch_in: + - service: clamav-clamd-service \ No newline at end of file diff --git a/states/oldstates/clamav/defaults.yaml b/states/oldstates/clamav/defaults.yaml new file mode 100644 index 0000000..229fed2 --- /dev/null +++ b/states/oldstates/clamav/defaults.yaml @@ -0,0 +1,91 @@ +--- +clamav: + enabled: true + config: + clamd: + file: /etc/clamav/clamd.conf + options: + AlgorithmicDetection: true + AllowAllMatchScan: true + ArchiveBlockEncrypted: false + Bytecode: true + BytecodeSecurity: TrustSigned + BytecodeTimeout: 60000 + CommandReadTimeout: 5 + CrossFilesystems: true + DatabaseDirectory: /var/lib/clamav + Debug: false + DetectBrokenExecutables: false + DetectPUA: false + DisableCache: false + DisableCertCheck: false + ExitOnOOM: false + ExtendedDetectionInfo: true + FixStaleSocket: true + FollowDirectorySymlinks: false + FollowFileSymlinks: false + ForceToDisk: false + Foreground: false + HeuristicScanPrecedence: false + IdleTimeout: 30 + LeaveTemporaryFiles: false + LocalSocket: /var/run/clamav/clamd.ctl + LocalSocketGroup: root + LocalSocketMode: 666 + LogClean: false + LogFacility: LOG_LOCAL6 + LogFile: /var/log/clamav/clamav.log + LogFileMaxSize: 0 + LogFileUnlock: false + LogRotate: true + LogSyslog: true + LogTime: true + LogVerbose: false + MaxConnectionQueueLength: 15 + MaxDirectoryRecursion: 15 + MaxEmbeddedPE: 10M + MaxFileSize: 25M + MaxFiles: 10000 + MaxHTMLNoTags: 2M + MaxHTMLNormalize: 10M + MaxIconsPE: 100 + MaxPartitions: 50 + MaxQueue: 100 + MaxRecHWP3: 16 + MaxRecursion: 16 + MaxScanSize: 100M + MaxScriptNormalize: 5M + MaxThreads: 12 + MaxZipTypeRcg: 1M + OLE2BlockMacros: false + OfficialDatabaseOnly: false + OnAccessIncludePath: /home + OnAccessPrevention: yes + PCREMatchLimit: 10000 + PCREMaxFileSize: 25M + PCRERecMatchLimit: 5000 + PartitionIntersection: false + PhishingAlwaysBlockCloak: false + PhishingAlwaysBlockSSLMismatch: false + PhishingScanURLs: true + PhishingSignatures: true + PreludeAnalyzerName: ClamAV + PreludeEnable: no + ReadTimeout: 180 + ScanArchive: true + ScanELF: true + ScanHTML: true + ScanHWP3: true + ScanMail: true + ScanOLE2: true + ScanOnAccess: true + ScanPDF: true + ScanPE: true + ScanPartialMessages: false + ScanSWF: true + ScanXMLDOCS: true + SelfCheck: 3600 + SendBufTimeout: 200 + StreamMaxLength: 25M + StructuredDataDetection: false + User: root \ No newline at end of file diff --git a/states/oldstates/clamav/init.sls b/states/oldstates/clamav/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/oldstates/clamav/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/oldstates/clamav/install.sls b/states/oldstates/clamav/install.sls new file mode 100644 index 0000000..189b062 --- /dev/null +++ b/states/oldstates/clamav/install.sls @@ -0,0 +1,6 @@ +--- +clamav-install: + pkg.latest: + - pkgs: + - clamav + - clamav-daemon \ No newline at end of file diff --git a/states/oldstates/clamav/map.jinja b/states/oldstates/clamav/map.jinja new file mode 100644 index 0000000..8135e2f --- /dev/null +++ b/states/oldstates/clamav/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "clamav/defaults.yaml" as default_settings %} + +{%- set defaults = salt['grains.filter_by'](default_settings,default='clamav') %} + +{%- set clamav = salt['pillar.get']('clamav', default=defaults, merge=True) %} \ No newline at end of file diff --git a/states/oldstates/clamav/service.sls b/states/oldstates/clamav/service.sls new file mode 100644 index 0000000..0a06f3a --- /dev/null +++ b/states/oldstates/clamav/service.sls @@ -0,0 +1,7 @@ +--- +clamav-clamd-service: + service.dead: + - name: clamav-daemon + - enable: False + - require: + - file: clamav-clamd-config \ No newline at end of file diff --git a/states/oldstates/collectd/collectd.conf.j2 b/states/oldstates/collectd/collectd.conf.j2 new file mode 100644 index 0000000..24069c6 --- /dev/null +++ b/states/oldstates/collectd/collectd.conf.j2 @@ -0,0 +1,83 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +Hostname "{{ grains.get('fqdn') }}" +FQDNLookup true +Interval 60.0 + +TypesDB "/usr/share/collectd/types.db" + +LoadPlugin battery +LoadPlugin contextswitch +LoadPlugin cpu +LoadPlugin df +LoadPlugin disk +LoadPlugin entropy +LoadPlugin interface +LoadPlugin irq +LoadPlugin load +LoadPlugin memory +LoadPlugin network +#LoadPlugin ping +LoadPlugin processes +#LoadPlugin rrdtool +LoadPlugin sensors +LoadPlugin swap +LoadPlugin syslog +LoadPlugin tcpconns +LoadPlugin uptime +LoadPlugin users + + + ReportByCpu true + ReportByState true + ValuesPercentage false + ReportNumCpu false + ReportGuestState false + SubtractGuestState true + + + + FSType rootfs + FSType sysfs + FSType proc + FSType devtmpfs + FSType devpts + FSType tmpfs + FSType fusectl + FSType cgroup + IgnoreSelected true + + + + Server "{{ salt['pillar.get']('collectd:hostname') }}" "{{ salt['pillar.get']('collectd:port')}}" + Username "{{ salt['pillar.get']('collectd:username') }}" + Password "{{ salt['pillar.get']('collectd:password') }}" + + +# +# DataDir "/var/lib/collectd/rrd" +# + + + LogLevel info + + + + + Instance "auth" + + Regex "\\" + DSType "CounterInc" + Type "counter" + Instance "sshd-invalid_user" + + + + + + ListeningPorts true + + + + Filter "*.conf" + \ No newline at end of file diff --git a/states/oldstates/collectd/config.sls b/states/oldstates/collectd/config.sls new file mode 100644 index 0000000..bcdbaa8 --- /dev/null +++ b/states/oldstates/collectd/config.sls @@ -0,0 +1,13 @@ +--- +collectd-config: + file.managed: + - name: /etc/collectd/collectd.conf + - source: salt://collectd/collectd.conf.j2 + - user: root + - group: root + - mode: 0664 + - template: jinja + - watch_in: + - service: collectd-service + - require: + - pkg: collectd-pkg \ No newline at end of file diff --git a/states/oldstates/collectd/init.sls b/states/oldstates/collectd/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/oldstates/collectd/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/oldstates/collectd/install.sls b/states/oldstates/collectd/install.sls new file mode 100644 index 0000000..4b8051e --- /dev/null +++ b/states/oldstates/collectd/install.sls @@ -0,0 +1,6 @@ +--- +collectd-pkg: + pkg.latest: + - pkgs: + - collectd + - collectd-utils \ No newline at end of file diff --git a/states/oldstates/collectd/service.sls b/states/oldstates/collectd/service.sls new file mode 100644 index 0000000..8e204d0 --- /dev/null +++ b/states/oldstates/collectd/service.sls @@ -0,0 +1,5 @@ +--- +collectd-service: + service.dead: + - name: collectd + - enable: False \ No newline at end of file diff --git a/states/oldstates/snmp/init.sls b/states/oldstates/snmp/init.sls new file mode 100644 index 0000000..91e40b2 --- /dev/null +++ b/states/oldstates/snmp/init.sls @@ -0,0 +1,16 @@ +--- +snmp-snmpd-config: + file.managed: + - name: /etc/snmp/snmpd.conf + - source: salt://snmp/snmpd.conf.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: snmp-snmpd-service + +snmp-snmpd-service: + service.running: + - name: snmpd + - enable: True \ No newline at end of file diff --git a/states/oldstates/snmp/snmpd.conf.j2 b/states/oldstates/snmp/snmpd.conf.j2 new file mode 100644 index 0000000..8b6a70b --- /dev/null +++ b/states/oldstates/snmp/snmpd.conf.j2 @@ -0,0 +1,29 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +agentAddress udp:161,udp6:161 +dontLogTCPWrappersConnects yes + +syscontact {{ salt['pillar.get']('snmp_syscontact') }} +{% if salt['grains.get']('location') is defined %}syslocation {{ salt['grains.get']('location') }}{% endif %} + +rocommunity {{ salt['pillar.get']('snmp_community') }} default +rocommunity6 {{ salt['pillar.get']('snmp_community') }} default + +# IPv4 settings +{% for net in salt['pillar.get']('ipv4_networks') %}com2sec {{ salt['pillar.get']('snmp_community') }}-ro {{ net.ip }}/{{ net.mask }} {{ salt['pillar.get']('snmp_community') }} +{% endfor %} +# IPv6 settings +{% for net in salt['pillar.get']('ipv6_networks') %}com2sec {{ salt['pillar.get']('snmp_community') }}-ro {{ net.ip }}/{{ net.mask }} {{ salt['pillar.get']('snmp_community') }} +{% endfor %} +group {{ salt['pillar.get']('snmp_community')}}-gro v2c {{ salt['pillar.get']('snmp_community') }}-ro + +view all included .1 + +access paulbsd-gro "" any noauth exact all none none + +# Disks to monitor +{%- for partition in salt['mount.fstab']() %} +{%- if partition != "none" -%} +disk {{ partition }} +{%- endif -%} +{%- endfor %} \ No newline at end of file diff --git a/states/opendkim/TrustedHosts.j2 b/states/opendkim/TrustedHosts.j2 new file mode 100644 index 0000000..3c2f449 --- /dev/null +++ b/states/opendkim/TrustedHosts.j2 @@ -0,0 +1,9 @@ +{%- for key, value in salt['pillar.get']('net:ipv4_networks').items() %} +{{ value.ip }}/{{ value.mask }} +{%- endfor %} +{%- for key, value in salt['pillar.get']('net:ipv6_networks').items() %} +{{ value.ip }}/{{ value.mask }} +{%- endfor %} +{%- for hostname in salt['pillar.get']('mail:hostnames') %} +{{ hostname }} +{%- endfor %} \ No newline at end of file diff --git a/states/opendkim/config.sls b/states/opendkim/config.sls new file mode 100644 index 0000000..8175da5 --- /dev/null +++ b/states/opendkim/config.sls @@ -0,0 +1,52 @@ +--- +{%- from "opendkim/map.jinja" import opendkim with context %} +opendkim-config-dir: + file.directory: + - name: {{ opendkim.config.dir }} + - user: {{ opendkim.config.user }} + - group: {{ opendkim.config.group }} + - mode: 755 + +opendkim-configuration-key: + file.managed: + - name: {{ opendkim.config.dir }}/{{ opendkim.config.key.name }} + - source: salt://opendkim/key.j2 + - user: {{ opendkim.config.user }} + - group: {{ opendkim.config.group }} + - mode: 600 + - template: jinja + - watch_in: + - service: opendkim-service + +opendkim-trusted-hosts: + file.managed: + - name: {{ opendkim.config.dir }}/TrustedHosts + - source: salt://opendkim/TrustedHosts.j2 + - user: {{ opendkim.config.user }} + - group: {{ opendkim.config.group }} + - mode: 644 + - template: jinja + - watch_in: + - service: opendkim-service + +opendkim-service-defaults: + file.managed: + - name: {{ opendkim.config.default_file }} + - source: salt://opendkim/service_defaults.j2 + - user: {{ opendkim.config.user }} + - group: {{ opendkim.config.group }} + - mode: 644 + - template: jinja + - watch_in: + - service: opendkim-service + +opendkim-config-file: + file.managed: + - name: {{ opendkim.config.file }} + - source: salt://opendkim/opendkim.conf.j2 + - user: {{ opendkim.config.user }} + - group: {{ opendkim.config.group }} + - mode: 644 + - template: jinja + - watch_in: + - service: opendkim-service \ No newline at end of file diff --git a/states/opendkim/defaults.yaml b/states/opendkim/defaults.yaml new file mode 100644 index 0000000..add16ef --- /dev/null +++ b/states/opendkim/defaults.yaml @@ -0,0 +1,49 @@ +--- +opendkim: + enabled: true + config: + dir: '/etc/opendkim' + file: '/etc/opendkim.conf' + pid_file: '/var/run/opendkim/opendkim.pid' + default_file: '/etc/default/opendkim' + user: opendkim + group: opendkim + notify_addr: postmaster@example.com + notify_report: yes + host: 'localhost' + port: 8891 + log_why: yes + canonicalization: 'relaxed/simple' + require_safe_keys: false + syslog: yes + syslog_success: yes + key: + name: mx + content: > + '-----BEGIN RSA PRIVATE KEY----- + MIIEpAIBAAKCAQEA2vdXKLA+uWs87E82sHNYCe/LH5uf8nMfuuaCAv3sZSM1VR/N + YeifdBRPujMPgCi/vXjXFLjl4mSZb/xhX9pmqSvpDTnRr6HF/0JhtSIakBjr+BE/ + ESGx3DVuJCxEnm9q2DjZwwCBEOrVwXnSRXrzJvn5CGz8Gb4buty5t79KaXd0qxmG + bV5aSEaRU9fqB5pMfRnRdBX2W5NGGfamv6lXo9MqTJglwm4z+cU/gQTL4BDT6itH + TH0WDjaXdal8w0q99Ov20XWQGzGTeveZXS24AIQMZuSgmMrBoQbcT2XCgIoF9xOQ + eMQFv4uxAdnBihnnkpKVTcmtJ9QBtKY+/wYsXQIDAQABAoIBAHFGCOm8mwYyqraY + l/MaCE5DpXG5gTncQmreeh1wHI2JdDqQFCB1RZ+4LjJl3bvv2Jj83U8UA6BlaFbd + WcZLZlZXp902rvnLl3Dgd0lL1i/7JH8U9Oo44xjG7y55VBoeC0jFyCkvppUI1wGa + sqNM4mKkaCbqslPi29eD/fNHWwhJVYc3EsmC+HpW0EfwU0vNYetmE0tpWpvkjvVs + 8sLqAf4PmEUZwx6YPAmBgb0h2cNDqzv2e57aK05mwZ7CkCk9Nj3Ex5s/2J7HqvtR + lpSwgEREDYCnYem3Ow6LL3jFTf/ENDcaS4Y9lsVQFOZ9A/XyHjIvrAfH+LFg5ZNn + vFRJv0ECgYEA9e4W9Mh/QNlOxywFC0TdseCuT45Wql0iXUAfoEBjsdiQFpodesSq + 3mulxnZAL83PommEse2mubksBhU5LrQ04tKA6xpksQeQsEwZ+/aQTeE3yXpbD1DZ + k/LUG/PQjPJxbqZlHMCaFQbZMWQKK+ygVHbrqlXnSQ5ahnFn7EkQqRECgYEA4+6b + kups1qC5/OwotxZGfnhjB658BnZ+DpFtT4QTgo+LbXzluUR1vk3QncmowzUAOo8V + LYrUruPYLiFwj+9YjMkwOJM5xLJLY8iRj/kgHUU5U0vxzdyRAt5F+dvMu6yw5NqF + MFpwxpaABryJTedtqH1yB4Hqb87zILG6Blr4Lo0CgYAJWuY6p4nXsKyDfRQwCo2Y + X6Ch3BtF3ccZ3v+U+/4O3NsWUQSAEA0j6tpig0sLxnt4hSUME6s882NOU+5Z7Xb5 + jeJjjxx3c384hZyTbhytKb0Sh/oAyiWMsDcLXSn4wpw4BlJgVp9F333RmCme5CBx + Nhje4OesasRcyuGWJm1g0QKBgQC+CIHMy4ZSq05lgL+xZfEAIH7wOJlPChuvKbBp + S0S45dTpQ+iDvoAsWdREuYzqs5WtPP37KnSIG45xZ4/hcTvypQp/IMBZLg2M8/vo + fVw5Cy3wNLE1YV/FdGwDFS5sil4xb777wWulJnU3KJmRShIzF+OqPram4XPzHjpq + cg3ZQQKBgQCYN5mgdzjjQ2DQ6lT9V8C60/Etp1L2+UuQvFs29Wq6rSwuoTEiMOzZ + HbOcr9HGn7W/ocEZwSaMNB5jbtXuSJ4IInT3+PE1Qu1mbMavyv9yl/Y3saIJ8i1T + 3+EflCWWLXEVPTazoBscnjrbDfdJly3DVOn2g0IaerdZFb0w4o1+YA== + -----END RSA PRIVATE KEY-----' \ No newline at end of file diff --git a/states/opendkim/init.sls b/states/opendkim/init.sls new file mode 100644 index 0000000..1c23bd4 --- /dev/null +++ b/states/opendkim/init.sls @@ -0,0 +1,6 @@ +--- +{%- from "opendkim/map.jinja" import opendkim with context %} +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/opendkim/install.sls b/states/opendkim/install.sls new file mode 100644 index 0000000..f1754da --- /dev/null +++ b/states/opendkim/install.sls @@ -0,0 +1,7 @@ +--- +{%- from "opendkim/map.jinja" import opendkim with context %} +opendkim-pkg: + pkg.latest: + - pkgs: + - opendkim + - opendkim-tools \ No newline at end of file diff --git a/states/opendkim/key.j2 b/states/opendkim/key.j2 new file mode 100644 index 0000000..4aabaa5 --- /dev/null +++ b/states/opendkim/key.j2 @@ -0,0 +1,2 @@ +{%- from "opendkim/map.jinja" import opendkim with context -%} +{{ opendkim.config.key.content }} \ No newline at end of file diff --git a/states/opendkim/map.jinja b/states/opendkim/map.jinja new file mode 100644 index 0000000..24009c3 --- /dev/null +++ b/states/opendkim/map.jinja @@ -0,0 +1,8 @@ +{%- import_yaml "opendkim/defaults.yaml" as defaults %} + +{%- set opendkim = salt['pillar.get']( + 'opendkim', + default=defaults.opendkim, + merge=True + ) +-%} \ No newline at end of file diff --git a/states/opendkim/opendkim.conf.j2 b/states/opendkim/opendkim.conf.j2 new file mode 100644 index 0000000..f713606 --- /dev/null +++ b/states/opendkim/opendkim.conf.j2 @@ -0,0 +1,23 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "opendkim/map.jinja" import opendkim with context %} +LogWhy {{ opendkim.config.log_why }} +Syslog {{ opendkim.config.syslog }} +SyslogSuccess {{ opendkim.config.syslog_success }} +RequireSafeKeys {{ opendkim.config.require_safe_keys }} + +Canonicalization {{ opendkim.config.canonicalization }} + +InternalHosts refile:{{ opendkim.config.dir }}/TrustedHosts + +Domain paulbsd.com +Selector mx +KeyFile {{ opendkim.config.dir }}/mx + +UserID {{ opendkim.config.user }} +PidFile {{ opendkim.config.pid_file }} + +Socket inet:{{ opendkim.config.port }}@{{ opendkim.config.host }} + +ReportAddress {{ opendkim.config.notify_addr }} +SendReports {{ opendkim.config.notify_report }} \ No newline at end of file diff --git a/states/opendkim/service.sls b/states/opendkim/service.sls new file mode 100644 index 0000000..aafe7e5 --- /dev/null +++ b/states/opendkim/service.sls @@ -0,0 +1,6 @@ +--- +{%- from "opendkim/map.jinja" import opendkim with context %} +opendkim-service: + service.running: + - name: opendkim + - enable: True \ No newline at end of file diff --git a/states/opendkim/service_defaults.j2 b/states/opendkim/service_defaults.j2 new file mode 100644 index 0000000..3af85d3 --- /dev/null +++ b/states/opendkim/service_defaults.j2 @@ -0,0 +1,2 @@ +{%- from "opendkim/map.jinja" import opendkim with context %} +SOCKET="inet:{{ opendkim.config.port }}@{{ opendkim.config.host }}" \ No newline at end of file diff --git a/states/openvpn_client/config.sls b/states/openvpn_client/config.sls new file mode 100644 index 0000000..bcdd608 --- /dev/null +++ b/states/openvpn_client/config.sls @@ -0,0 +1,32 @@ +--- +{%- from "openvpn_client/map.jinja" import openvpn with context %} +openvpn-client-main-config: + file.managed: + - name: {{ openvpn.config_dir }}/vpn.conf + - source: salt://openvpn_client/vpn.conf.j2 + - user: root + - group: root + - mode: 0600 + - template: jinja + - watch_in: + - service: openvpn-client-service + +openvpn-client-private-dir: + file.directory: + - name: {{ openvpn.config_dir }}/vpn + - user: root + - group: root + - mode: 0700 + - watch_in: + - service: openvpn-client-service + +openvpn-client-private-access: + file.managed: + - name: {{ openvpn.config_dir }}/vpn/pass + - source: salt://openvpn_client/pass.j2 + - template: jinja + - user: root + - group: root + - mode: 0600 + - watch_in: + - service: openvpn-client-service \ No newline at end of file diff --git a/states/openvpn_client/defaults.yaml b/states/openvpn_client/defaults.yaml new file mode 100644 index 0000000..ea016a4 --- /dev/null +++ b/states/openvpn_client/defaults.yaml @@ -0,0 +1,3 @@ +--- +openvpn: + config_dir: /etc/openvpn diff --git a/states/openvpn_client/init.sls b/states/openvpn_client/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/openvpn_client/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/openvpn_client/install.sls b/states/openvpn_client/install.sls new file mode 100644 index 0000000..18e459c --- /dev/null +++ b/states/openvpn_client/install.sls @@ -0,0 +1,6 @@ +--- +openvpn-client-install: + pkg.installed: + - name: openvpn + - watch_in: + - service: openvpn-client-service \ No newline at end of file diff --git a/states/openvpn_client/map.jinja b/states/openvpn_client/map.jinja new file mode 100644 index 0000000..d9f068b --- /dev/null +++ b/states/openvpn_client/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "openvpn_client/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='openvpn') -%} + +{%- set openvpn = salt['pillar.get']('openvpn', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/openvpn_client/pass.j2 b/states/openvpn_client/pass.j2 new file mode 100644 index 0000000..a586a3e --- /dev/null +++ b/states/openvpn_client/pass.j2 @@ -0,0 +1,7 @@ +{%- from "openvpn_client/map.jinja" import openvpn with context -%} +{%- for client in openvpn.config.vpn.remote_hosts -%} +{%- if client.name == salt['grains.get']('host') -%} +{{ client.name }} +{{ client.password }} +{%- endif -%} +{%- endfor -%} \ No newline at end of file diff --git a/states/openvpn_client/service.sls b/states/openvpn_client/service.sls new file mode 100644 index 0000000..af7fdaf --- /dev/null +++ b/states/openvpn_client/service.sls @@ -0,0 +1,5 @@ +--- +openvpn-client-service: + service.running: + - name: openvpn@vpn + - enable: True \ No newline at end of file diff --git a/states/openvpn_client/vpn.conf.j2 b/states/openvpn_client/vpn.conf.j2 new file mode 100644 index 0000000..aea637b --- /dev/null +++ b/states/openvpn_client/vpn.conf.j2 @@ -0,0 +1,29 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "openvpn_client/map.jinja" import openvpn with context %} + +{{ openvpn.config.vpn.ca }} + + +{{ openvpn.config.vpn.cert }} + + +{{ openvpn.config.vpn.key }} + +client +remote {{ openvpn.config.vpn.global_endpoint_host }} {{ openvpn.config.vpn.global_endpoint_port }} +proto {{ openvpn.config.vpn.global_endpoint_proto }} +dev tun + +topology subnet + +keepalive 10 120 + +nobind + +persist-key +persist-tun + +compress lzo +fast-io + +auth-user-pass vpn/pass \ No newline at end of file diff --git a/states/ovh/defaults.yaml b/states/ovh/defaults.yaml new file mode 100644 index 0000000..342be5f --- /dev/null +++ b/states/ovh/defaults.yaml @@ -0,0 +1,6 @@ +--- +ovh: + enabled: true + version: '0.5.0' + domain: + zones: {} \ No newline at end of file diff --git a/states/ovh/domain.sls b/states/ovh/domain.sls new file mode 100644 index 0000000..61fa5f1 --- /dev/null +++ b/states/ovh/domain.sls @@ -0,0 +1,14 @@ +--- +{%- from "ovh/map.jinja" import ovh with context %} +{%- for domain, values in ovh.domain.zones.items() %} +{%- for record in values.records %} +ovh-domain-{{ domain }}-{{ "{}-{}-{}".format(record.name, record.type, record.target)|md5 }}: + ovhapi.domain_record_present: + - name: ovh-domain-{{ domain }}-{{ "{}-{}-{}".format(record.name, record.type, record.target)|md5 }} + - zone: {{ domain }} + - recordname: {{ record.name }} + - recordtype: {{ record.type }} + - target: {{ record.target }} + - ttl: {{ record.ttl|default(0) }} +{%- endfor %} +{%- endfor %} \ No newline at end of file diff --git a/states/ovh/init.sls b/states/ovh/init.sls new file mode 100644 index 0000000..6e46cda --- /dev/null +++ b/states/ovh/init.sls @@ -0,0 +1,5 @@ +--- +{%- from "ovh/map.jinja" import ovh with context %} +include: + - .pkg + - .domain \ No newline at end of file diff --git a/states/ovh/map.jinja b/states/ovh/map.jinja new file mode 100644 index 0000000..9e9cf8d --- /dev/null +++ b/states/ovh/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "ovh/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='ovh') -%} + +{%- set ovh = salt['pillar.get']('ovh', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/ovh/pkg.sls b/states/ovh/pkg.sls new file mode 100644 index 0000000..07e9c73 --- /dev/null +++ b/states/ovh/pkg.sls @@ -0,0 +1,5 @@ +--- +{%- from "ovh/map.jinja" import ovh with context %} +ovh-install-pkg: + pip.installed: + - name: ovh=={{ ovh.version|default('0.5.0') }} \ No newline at end of file diff --git a/states/packer/defaults.yaml b/states/packer/defaults.yaml new file mode 100644 index 0000000..3dfb22b --- /dev/null +++ b/states/packer/defaults.yaml @@ -0,0 +1,8 @@ +--- +packer: + enabled: true + install_dir: /usr/local/sbin + mirror: https://releases.hashicorp.com/packer + version: "1.5.5" + os: linux + arch: amd64 \ No newline at end of file diff --git a/states/packer/init.sls b/states/packer/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/packer/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/packer/install.sls b/states/packer/install.sls new file mode 100644 index 0000000..0507b78 --- /dev/null +++ b/states/packer/install.sls @@ -0,0 +1,8 @@ +--- +{%- from "packer/map.jinja" import packer with context %} +packer-archive-extract: + archive.extracted: + - name: {{ packer.install_dir }} + - source: {{ packer.mirror }}/{{ packer.version }}/packer_{{ packer.version }}_{{ packer.kernel }}_{{ packer.osarch }}.zip + - skip_verify: True + - enforce_toplevel: False \ No newline at end of file diff --git a/states/packer/kernelmap.yaml b/states/packer/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/packer/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/packer/map.jinja b/states/packer/map.jinja new file mode 100644 index 0000000..bbf256a --- /dev/null +++ b/states/packer/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "packer/defaults.yaml" as default_settings -%} + +{%- import_yaml "packer/kernelmap.yaml" as kernelmap %} +{%- import_yaml "packer/osarchmap.yaml" as osarchmap %} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='packer', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set packer = salt['pillar.get']('packer', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/packer/osarchmap.yaml b/states/packer/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/packer/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/php/config.sls b/states/php/config.sls new file mode 100644 index 0000000..55643cb --- /dev/null +++ b/states/php/config.sls @@ -0,0 +1,29 @@ +--- +{%- from "php/map.jinja" import php with context %} +{%- if php.fpm.config != None %} +php-config: + ini.options_present: + - name: {{ php.config_file }} + - separator: '=' + - sections: {{ php.config }} + - watch_in: + - service: php-fpm-service +{%- endif %} + +{%- if php.fpm.config != None %} +php-fpm-config: + ini.options_present: + - name: {{ php.fpm.config_file }} + - separator: '=' + - sections: {{ php.fpm.config }} + - watch_in: + - service: php-fpm-service +{%- endif %} + +{%- for extension in php.extensions %} +php-extension-enable-{{ extension }}: + cmd.run: + - name: phpenmod {{ extension }} + - watch_in: + - service: php-fpm-service +{%- endfor %} \ No newline at end of file diff --git a/states/php/defaults.yaml b/states/php/defaults.yaml new file mode 100644 index 0000000..5952e94 --- /dev/null +++ b/states/php/defaults.yaml @@ -0,0 +1,148 @@ +--- +php: + version: '7.3' + pkgs: + - php7.3 + - php7.3-curl + - php7.3-fpm + - php7.3-gd + - php7.3-mbstring + - php7.3-pgsql + - php7.3-xml + - php7.3-zip + extensions: + - readline + config_file: /etc/php/7.3/fpm/php.ini + config: + PHP: + engine: 'On' + short_open_tag: 'Off' + precision: 14 + output_buffering: 4096 + zlib.output_compression: 'Off' + implicit_flush: 'Off' + serialize_precision: -1 + disable_functions: 'pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,' + zend.enable_gc: 'On' + expose_php: 'Off' + max_execution_time: 30 + max_input_time: 60 + memory_limit: 256M + error_reporting: E_ALL & ~E_DEPRECATED & ~E_STRICT + display_errors: 'Off' + display_startup_errors: 'Off' + log_errors: 'On' + log_errors_max_len: 1024 + ignore_repeated_errors: 'Off' + ignore_repeated_source: 'Off' + report_memleaks: 'On' + html_errors: 'On' + variables_order: "GPCS" + request_order: "GP" + register_argc_argv: 'Off' + auto_globals_jit: 'On' + post_max_size: 8M + default_mimetype: "text/html" + default_charset: "UTF-8" + enable_dl: 'Off' + file_uploads: 'On' + upload_max_filesize: 2M + max_file_uploads: 20 + allow_url_fopen: 'On' + allow_url_include: 'Off' + default_socket_timeout: 60 + CLI Server: + cli_server.color: 'On' + Date: + filter: + iconv: + imap: + intl: + sqlite3: + Pcre: + Pdo: + Pdo_mysql: + Phar: + mail function: + SMTP: localhost + smtp_port: 25 + mail.add_x_header: 'Off' + ODBC: + odbc.allow_persistent: 'On' + odbc.check_persistent: 'On' + odbc.max_persistent: -1 + odbc.max_links: -1 + odbc.defaultlrl: 4096 + odbc.defaultbinmode: 1 + Interbase: + ibase.allow_persistent: 1 + ibase.max_persistent: -1 + ibase.max_links: -1 + ibase.timestampformat: "%Y-%m-%d %H:%M:%S" + ibase.dateformat: "%Y-%m-%d" + ibase.timeformat: "%H:%M:%S" + MySQLi: + mysqli.max_persistent: -1 + mysqli.allow_persistent: 'On' + mysqli.max_links: -1 + mysqli.default_port: 3306 + mysqli.reconnect: 'Off' + mysqlnd: + mysqlnd.collect_statistics: 'On' + mysqlnd.collect_memory_statistics: 'Off' + OCI8: + PostgreSQL: + pgsql.allow_persistent: 'On' + pgsql.auto_reset_persistent: 'Off' + pgsql.max_persistent: -1 + pgsql.max_links: -1 + pgsql.ignore_notice: 0 + pgsql.log_notice: 0 + bcmath: + bcmath.scale: 0 + browscap: + Session: + session.save_handler: files + session.use_strict_mode: 0 + session.use_cookies: 1 + session.use_only_cookies: 1 + session.name: PHPSESSID + session.auto_start: 0 + session.cookie_lifetime: 0 + session.cookie_path: / + session.serialize_handler: php + session.gc_probability: 0 + session.gc_divisor: 1000 + session.gc_maxlifetime: 1440 + session.cache_limiter: nocache + session.cache_expire: 180 + session.use_trans_sid: 0 + session.sid_length: 26 + session.trans_sid_tags: "'a=href,area=href,frame=src,form='" + session.sid_bits_per_character: 5 + Assertion: + zend.assertions: -1 + COM: + mbstring: + gd: + exif: + Tidy: + tidy.clean_output: 'Off' + + soap: + soap.wsdl_cache_enabled: 1 + soap.wsdl_cache_dir: "/tmp" + soap.wsdl_cache_ttl: 86400 + soap.wsdl_cache_limit: 5 + sysvshm: + ldap: + ldap.max_links: -1 + dba: + opcache: + curl: + openssl: + + fpm: + config_file: /etc/php/7.3/fpm/php-fpm.conf + config: + socket: /var/run/php/php7.3-fpm.sock \ No newline at end of file diff --git a/states/php/init.sls b/states/php/init.sls new file mode 100644 index 0000000..83c8ed9 --- /dev/null +++ b/states/php/init.sls @@ -0,0 +1,6 @@ +--- +{%- from "php/map.jinja" import php with context %} +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/php/install.sls b/states/php/install.sls new file mode 100644 index 0000000..2c64b5a --- /dev/null +++ b/states/php/install.sls @@ -0,0 +1,7 @@ +--- +{%- from "php/map.jinja" import php with context %} +php-pkgs: + pkg.latest: + - pkgs: {{ php.pkgs }} + - watch_in: + - service: php-fpm-service \ No newline at end of file diff --git a/states/php/map.jinja b/states/php/map.jinja new file mode 100644 index 0000000..6dce858 --- /dev/null +++ b/states/php/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "php/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='php') -%} + +{%- set php = salt['pillar.get']('php', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/php/service.sls b/states/php/service.sls new file mode 100644 index 0000000..6073d34 --- /dev/null +++ b/states/php/service.sls @@ -0,0 +1,6 @@ +--- +php-fpm-service: + service.running: + - name: php7.3-fpm + - enable: True + - reload: True \ No newline at end of file diff --git a/states/pkg/bootstrap.sls b/states/pkg/bootstrap.sls new file mode 100644 index 0000000..21d4140 --- /dev/null +++ b/states/pkg/bootstrap.sls @@ -0,0 +1,9 @@ +--- +pkg-full-upgrade: + pkg.uptodate: + - name: pkg-full-upgrade + - refresh: True + +pkg-install-apt-transport-https: + pkg.latest: + - name: apt-transport-https \ No newline at end of file diff --git a/states/pkg/defaults.yaml b/states/pkg/defaults.yaml new file mode 100644 index 0000000..7287ffd --- /dev/null +++ b/states/pkg/defaults.yaml @@ -0,0 +1,5 @@ +--- +pkg: + aptpkgs: [] + manual: [] + remove: [] diff --git a/states/pkg/init.sls b/states/pkg/init.sls new file mode 100644 index 0000000..7c0e2ac --- /dev/null +++ b/states/pkg/init.sls @@ -0,0 +1,8 @@ +--- +include: + - repos + - .tasks + - .bootstrap + - .install + - .manual + - .remove diff --git a/states/pkg/install.sls b/states/pkg/install.sls new file mode 100644 index 0000000..9da8980 --- /dev/null +++ b/states/pkg/install.sls @@ -0,0 +1,13 @@ +--- +{%- from "pkg/map.jinja" import pkg with context %} +{%- if pkg.aptpkgs|length > 0 %} +pkg-install: + pkg.latest: + - pkgs: + {%- for pkg in pkg.aptpkgs %} + - {{ pkg }} + {%- endfor %} + - refresh: False + - require: + - sls: repos +{%- endif %} diff --git a/states/pkg/manual.sls b/states/pkg/manual.sls new file mode 100644 index 0000000..a0f3493 --- /dev/null +++ b/states/pkg/manual.sls @@ -0,0 +1,12 @@ +--- +{%- from "pkg/map.jinja" import pkg with context %} +{%- if pkg.manual|length > 0 %} +{%- for pkg in pkg.manual %} +{%- if salt['pkg.version'](pkg.name) != pkg.version or salt['pkg.version'](pkg.name) == None %} +pkg-install-manual-{{ pkg.name }}: + pkg.installed: + - sources: + - {{ pkg.name }}: {{ pkg.src }} +{%- endif %} +{%- endfor %} +{%- endif %} diff --git a/states/pkg/map.jinja b/states/pkg/map.jinja new file mode 100644 index 0000000..7861d59 --- /dev/null +++ b/states/pkg/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "pkg/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='pkg') -%} + +{%- set pkg = salt['pillar.get']('pkg', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/pkg/remove.sls b/states/pkg/remove.sls new file mode 100644 index 0000000..8db55b3 --- /dev/null +++ b/states/pkg/remove.sls @@ -0,0 +1,10 @@ +--- +{%- from "pkg/map.jinja" import pkg with context %} +{%- if pkg.remove|length > 0 %} +pkg-remove: + pkg.purged: + - pkgs: +{%- for pkg in pkg.remove %} + - {{ pkg }} +{%- endfor %} +{%- endif %} \ No newline at end of file diff --git a/states/pkg/tasks.sls b/states/pkg/tasks.sls new file mode 100644 index 0000000..1108529 --- /dev/null +++ b/states/pkg/tasks.sls @@ -0,0 +1,8 @@ +--- +pkg-clean-task: + cron.present: + - name: apt-get clean + - user: root + - minute: {{ range(1, 59) | random }} + - hour: {{ range(1, 23) | random }} + - identifier: pkg-clean-task \ No newline at end of file diff --git a/states/postfix/defaults.yaml b/states/postfix/defaults.yaml new file mode 100644 index 0000000..d7f8ea7 --- /dev/null +++ b/states/postfix/defaults.yaml @@ -0,0 +1,46 @@ +--- +postfix: + enabled: true + base_dir: '/etc/postfix' + config: + main: + alias_database: 'hash:/etc/aliases' + alias_maps: 'hash:/etc/aliases' + append_dot_mydomain: 'no' + biff: 'no' + enable_original_recipient: 'yes' + inet_interfaces: 'all' + inet_protocols: 'all' + mailbox_size_limit: '0' + message_size_limit: '30480000' + mydestination: + - '$myhostname' + - 'localhost.$mydomain' + - 'localhost' + mydomain: 'example.com' + myhostname: 'mx.example.com' + mynetworks: '127.0.0.1' + myorigin: '$mydomain' + non_smtpd_milters: '$smtpd_milters' + readme_directory: 'no' + recipient_delimiter: '+' + relay_domains: 'example.com' + smtp_connect_timeout: '2m' + smtpd_banner: 'Example SMTP Server' + smtpd_helo_required: 'yes' + smtpd_relay_restrictions: + - 'permit_mynetworks' + - 'defer_unauth_destination' + - 'permit' + smtpd_sender_restrictions: + - 'permit_mynetworks' + - 'check_sender_access hash:/etc/postfix/sender_access' + - 'permit' + smtputf8_enable: 'yes' + soft_bounce: 'yes' + transport_maps: 'hash:/etc/postfix/transport' + unknown_local_recipient_reject_code: 550 + compatibility_level: 2 + master: {} + sender_access: {} + transport: {} \ No newline at end of file diff --git a/states/postfix/init.sls b/states/postfix/init.sls new file mode 100644 index 0000000..c0c4af4 --- /dev/null +++ b/states/postfix/init.sls @@ -0,0 +1,73 @@ +--- +{%- from "postfix/map.jinja" import postfix with context %} +postfix-pkg: + pkg.latest: + - name: postfix + +postfix-pgsql-dir: + file.directory: + - name: {{ postfix.base_dir }}/pgsql + - user: root + - group: root + +postfix-main-cf: + file.managed: + - name: {{ postfix.base_dir }}/main.cf + - source: salt://postfix/main.cf.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: postfix-service + +postfix-master-cf: + file.managed: + - name: {{ postfix.base_dir }}/master.cf + - source: salt://postfix/master.cf.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: postfix-service + +postfix-transport-maps: + file.managed: + - name: {{ postfix.base_dir }}/transport + - source: salt://postfix/transport.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: postfix-service + - cmd: postfix-transport + +postfix-sender-access-maps: + file.managed: + - name: {{ postfix.base_dir }}/sender_access + - source: salt://postfix/sender_access.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: postfix-service + - cmd: postfix-sender-access + +postfix-transport: + cmd.run: + - name: postmap transport + - cwd: {{ postfix.base_dir }} + +postfix-sender-access: + cmd.run: + - name: postmap sender_access + - cwd: {{ postfix.base_dir }} + +postfix-service: + service.running: + - name: postfix + - enable: True + - reload: True \ No newline at end of file diff --git a/states/postfix/main.cf.j2 b/states/postfix/main.cf.j2 new file mode 100644 index 0000000..f207dd2 --- /dev/null +++ b/states/postfix/main.cf.j2 @@ -0,0 +1,6 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "postfix/map.jinja" import postfix with context %} +{%- for key, value in postfix.config.main.items() %} +{{ key }} = {% if value is iterable and value is not string -%}{{ value|join(',') }}{%- else -%}{{ value }}{%- endif -%} +{%- endfor %} \ No newline at end of file diff --git a/states/postfix/map.jinja b/states/postfix/map.jinja new file mode 100644 index 0000000..3c6175a --- /dev/null +++ b/states/postfix/map.jinja @@ -0,0 +1,8 @@ +{%- import_yaml "postfix/defaults.yaml" as defaults %} + +{%- set postfix = salt['pillar.get']( + 'postfix', + default=defaults.postfix, + merge=True + ) +-%} diff --git a/states/postfix/master.cf.j2 b/states/postfix/master.cf.j2 new file mode 100644 index 0000000..eb8a9d5 --- /dev/null +++ b/states/postfix/master.cf.j2 @@ -0,0 +1,40 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +smtp inet n - - - - smtpd +pickup unix n - - 60 1 pickup +cleanup unix n - - - 0 cleanup +qmgr unix n - n 300 1 qmgr +tlsmgr unix - - - 1000? 1 tlsmgr +rewrite unix - - - - - trivial-rewrite +bounce unix - - - - 0 bounce +defer unix - - - - 0 bounce +trace unix - - - - 0 bounce +verify unix - - - - 1 verify +flush unix n - - 1000? 0 flush +proxymap unix - - n - - proxymap +proxywrite unix - - n - 1 proxymap +smtp unix - - - - - smtp +relay unix - - - - - smtp +showq unix n - - - - showq +error unix - - - - - error +retry unix - - - - - error +discard unix - - - - - discard +local unix - n n - - local +virtual unix - n n - - virtual +lmtp unix - - - - - lmtp +anvil unix - - - - 1 anvil +scache unix - - - - 1 scache + +maildrop unix - n n - - pipe + flags=DRhu user=vmail argv=/usr/bin/maildrop -d ${recipient} + +uucp unix - n n - - pipe + flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient) + +maildb unix - n n - - pipe + flags=DROhu user=vmail:vmail argv=/usr/local/apps/maildb/maildb.py --config /usr/local/apps/maildb/maildb.ini ${sender} ${original_recipient} + +127.0.0.1:10025 inet n - n - - smtpd + -o content_filter= + -o mynetworks=127.0.0.0/8 + -o smtpd_recipient_restrictions=permit_mynetworks,reject \ No newline at end of file diff --git a/states/postfix/sender_access.j2 b/states/postfix/sender_access.j2 new file mode 100644 index 0000000..b44b756 --- /dev/null +++ b/states/postfix/sender_access.j2 @@ -0,0 +1,6 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "postfix/map.jinja" import postfix with context %} +{%- for key, value in postfix.config.sender_access.items() %} +{{ value.domain }} {{ value.action }} +{%- endfor %} \ No newline at end of file diff --git a/states/postfix/transport.j2 b/states/postfix/transport.j2 new file mode 100644 index 0000000..fadc567 --- /dev/null +++ b/states/postfix/transport.j2 @@ -0,0 +1,6 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "postfix/map.jinja" import postfix with context %} +{%- for key, value in postfix.config.transport.items() %} +{{ value.destination }} {{ value.endpoint }} +{%- endfor %} \ No newline at end of file diff --git a/states/postgresql/databases.sls b/states/postgresql/databases.sls new file mode 100644 index 0000000..e5c8ec7 --- /dev/null +++ b/states/postgresql/databases.sls @@ -0,0 +1,15 @@ +--- +{%- from "postgresql/map.jinja" import postgresql with context %} +{%- for database in postgresql.databases %} +postgresql_database_{{ database.name }}: + postgres_database.present: + - name: {{ database.name }} + - owner: {{ database.user }} + - db_user: postgres + - template: template0 + - encoding: {{ database.encoding }} + - lc_collate: {{ database.collate|default("en_US.UTF-8") }} + - lc_ctype: {{ database.ctype|default("en_US.UTF-8") }} + - require: + - postgres_user: postgresql_user_{{ database.user }} +{% endfor %} diff --git a/states/postgresql/defaults.yaml b/states/postgresql/defaults.yaml new file mode 100644 index 0000000..8e04d3d --- /dev/null +++ b/states/postgresql/defaults.yaml @@ -0,0 +1,6 @@ +--- +postgresql: + databases: + - name: postgres + user: postgres + users: [] \ No newline at end of file diff --git a/states/postgresql/init.sls b/states/postgresql/init.sls new file mode 100644 index 0000000..05606af --- /dev/null +++ b/states/postgresql/init.sls @@ -0,0 +1,6 @@ +--- +include: + - .install + - .service + - .users + - .databases \ No newline at end of file diff --git a/states/postgresql/install.sls b/states/postgresql/install.sls new file mode 100644 index 0000000..9632ac1 --- /dev/null +++ b/states/postgresql/install.sls @@ -0,0 +1,4 @@ +--- +postgresql-install: + pkg.latest: + - name: postgresql \ No newline at end of file diff --git a/states/postgresql/map.jinja b/states/postgresql/map.jinja new file mode 100644 index 0000000..cc6d1c5 --- /dev/null +++ b/states/postgresql/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "postgresql/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='postgresql') -%} + +{%- set postgresql = salt['pillar.get']('postgresql', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/postgresql/service.sls b/states/postgresql/service.sls new file mode 100644 index 0000000..664c435 --- /dev/null +++ b/states/postgresql/service.sls @@ -0,0 +1,5 @@ +--- +postgresql-service: + service.running: + - name: postgresql + - enable: True \ No newline at end of file diff --git a/states/postgresql/users.sls b/states/postgresql/users.sls new file mode 100644 index 0000000..489e39a --- /dev/null +++ b/states/postgresql/users.sls @@ -0,0 +1,13 @@ +--- +{%- from "postgresql/map.jinja" import postgresql with context %} +{%- for user in postgresql.users %} +postgresql_user_{{ user.name }}: + postgres_user.present: + - name: {{ user.name }} + {%- if user.password is defined %} + - password: {{ user.password }} + {%- endif %} + - login: {{ user.login|default(true) }} + - superuser: {{ user.superuser|default(false) }} + - db_user: postgres +{% endfor %} diff --git a/states/provision/defaults.yaml b/states/provision/defaults.yaml new file mode 100644 index 0000000..e78460b --- /dev/null +++ b/states/provision/defaults.yaml @@ -0,0 +1,12 @@ +--- +specs: + default: + enabled: true + type: none + country: none + location: none + cloud: false + container: false + sensu: false + sudo: true + websdr: false \ No newline at end of file diff --git a/states/provision/init.sls b/states/provision/init.sls new file mode 100644 index 0000000..8bc90a2 --- /dev/null +++ b/states/provision/init.sls @@ -0,0 +1,31 @@ +--- +{%- from "provision/map.jinja" import specs with context %} +schedule_highstate: + schedule.present: + - name: schedule_highstate + - function: state.highstate + - minutes: 30 + - run_on_start: False + +schedule_saltutil_clear_cache: + schedule.present: + - name: schedule_saltutil_clear_cache + - function: saltutil.clear_cache + - hours: 2 + - run_on_start: False + +schedule_pkg_refresh: + schedule.absent: + - name: schedule_pkg_refresh + +schedule_pkg_upgrade: + schedule.absent: + - name: schedule_pkg_upgrade + +{%- for key, value in specs.default.items() %} +{{ key }}: + grains.present: + - name: {{ key }} + - value: {% if specs[salt['grains.get']('id')][key] is defined %}{{ specs[salt['grains.get']('id')][key] }}{% else %}{{ value }}{% endif %} + - force: True +{% endfor %} diff --git a/states/provision/map.jinja b/states/provision/map.jinja new file mode 100644 index 0000000..e1b5fc6 --- /dev/null +++ b/states/provision/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "provision/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='specs') -%} + +{%- set specs = salt['pillar.get']('specs', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/pycharm/defaults.yaml b/states/pycharm/defaults.yaml new file mode 100644 index 0000000..8518c27 --- /dev/null +++ b/states/pycharm/defaults.yaml @@ -0,0 +1,6 @@ +--- +pycharm: + enabled: true + install_dir: /usr/local/apps + mirror: https://download.jetbrains.com/python + config: \ No newline at end of file diff --git a/states/pycharm/init.sls b/states/pycharm/init.sls new file mode 100644 index 0000000..0762b2a --- /dev/null +++ b/states/pycharm/init.sls @@ -0,0 +1,29 @@ +--- +{%- from "pycharm/map.jinja" import pycharm with context %} +{%- if not salt['file.directory_exists'](pycharm.install_dir + '/pycharm-community-' + pycharm.version) %} +pycharm-archive-extract: + archive.extracted: + - source: {{ pycharm.mirror }}/pycharm-community-{{ pycharm.version }}.tar.gz + - source_hash: {{ pycharm.mirror }}/pycharm-community-{{ pycharm.version }}.tar.gz.sha256 + - archive_format: tar + - name: {{ pycharm.install_dir }} + - if_missing: {{ pycharm.install_dir }}/pycharm-community-{{ pycharm.version }} + - watch: + - file: pycharm-link + - file: pycharm-shortcut +{%- endif %} + +pycharm-link: + file.symlink: + - name: {{ pycharm.install_dir }}/pycharm + - target: {{ pycharm.install_dir }}/pycharm-community-{{ pycharm.version }} + - force: True + +pycharm-shortcut: + file.managed: + - name: /usr/share/applications/jetbrains-pycharm-ce.desktop + - source: salt://pycharm/jetbrains-pycharm-ce.desktop.j2 + - template: jinja + - user: root + - group: root + - mode: 644 \ No newline at end of file diff --git a/states/pycharm/jetbrains-pycharm-ce.desktop.j2 b/states/pycharm/jetbrains-pycharm-ce.desktop.j2 new file mode 100644 index 0000000..9b84771 --- /dev/null +++ b/states/pycharm/jetbrains-pycharm-ce.desktop.j2 @@ -0,0 +1,12 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "pycharm/map.jinja" import pycharm with context %} +[Desktop Entry] +Version=1.0 +Type=Application +Name=PyCharm +Icon={{ pycharm.install_dir }}/pycharm/bin/pycharm.png +Exec="{{ pycharm.install_dir }}/pycharm/bin/pycharm.sh" %f +Comment=Develop with pleasure! +Categories=Development;IDE; +Terminal=false +StartupWMClass=jetbrains-pycharm-ce \ No newline at end of file diff --git a/states/pycharm/map.jinja b/states/pycharm/map.jinja new file mode 100644 index 0000000..4c5652d --- /dev/null +++ b/states/pycharm/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "pycharm/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='pycharm') -%} + +{%- set pycharm = salt['pillar.get']('pycharm', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/qrz/defaults.yaml b/states/qrz/defaults.yaml new file mode 100644 index 0000000..614c931 --- /dev/null +++ b/states/qrz/defaults.yaml @@ -0,0 +1,10 @@ +--- +qrz: + enabled: true + install_dir: /usr/local/apps + release_dir: /usr/local/apps/releases + port: 8087 + mirror: https://git.paulbsd.com/paulbsd/qrz/releases/download + version: 1.0.2 + os: linux + arch: amd64 \ No newline at end of file diff --git a/states/qrz/init.sls b/states/qrz/init.sls new file mode 100644 index 0000000..b07358d --- /dev/null +++ b/states/qrz/init.sls @@ -0,0 +1,4 @@ +--- +include: + - .install + - .service \ No newline at end of file diff --git a/states/qrz/install.sls b/states/qrz/install.sls new file mode 100644 index 0000000..18ed5a6 --- /dev/null +++ b/states/qrz/install.sls @@ -0,0 +1,20 @@ +--- +{%- from "qrz/map.jinja" import qrz with context %} +qrz-archive-extract: + archive.extracted: + - name: {{ qrz.release_dir }}/qrz-{{ qrz.version }} + - source: {{ qrz.mirror }}/{{ qrz.version }}/qrz-{{ qrz.version }}-{{ qrz.os }}-{{ qrz.arch }}.tar.gz + - skip_verify: True + - enforce_toplevel: False + - if_missing: {{ qrz.release_dir }}/qrz-{{ qrz.version }} + - watch_in: + - service: qrz-service + +qrz-binary-symlink: + file.symlink: + - name: {{ qrz.install_dir }}/qrz + - target: {{ qrz.release_dir }}/qrz-{{ qrz.version }} + - require: + - archive: qrz-archive-extract + - watch_in: + - service: qrz-service \ No newline at end of file diff --git a/states/qrz/kernelmap.yaml b/states/qrz/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/qrz/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/qrz/map.jinja b/states/qrz/map.jinja new file mode 100644 index 0000000..f10ff44 --- /dev/null +++ b/states/qrz/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "qrz/defaults.yaml" as default_settings -%} + +{%- import_yaml "qrz/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "qrz/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='qrz', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set qrz = salt['pillar.get']('qrz', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/qrz/osarchmap.yaml b/states/qrz/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/qrz/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/qrz/qrz.service.j2 b/states/qrz/qrz.service.j2 new file mode 100644 index 0000000..a6b3128 --- /dev/null +++ b/states/qrz/qrz.service.j2 @@ -0,0 +1,11 @@ +{%- from "qrz/map.jinja" import qrz with context %} +[Unit] +Description=QRZ +After=network.target + +[Service] +Type=simple +ExecStart={{ qrz.install_dir }}/qrz/qrz -configfile {{ qrz.install_dir }}/config/common.ini -port {{ qrz.port }} + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/qrz/service.sls b/states/qrz/service.sls new file mode 100644 index 0000000..8c93740 --- /dev/null +++ b/states/qrz/service.sls @@ -0,0 +1,14 @@ +--- +{%- from "qrz/map.jinja" import qrz with context %} +qrz-service-file: + file.managed: + - name: /etc/systemd/system/qrz.service + - source: salt://qrz/qrz.service.j2 + - user: root + - group: root + - template: jinja + +qrz-service: + service.running: + - name: qrz + - enable: True \ No newline at end of file diff --git a/states/rclone/defaults.yaml b/states/rclone/defaults.yaml new file mode 100644 index 0000000..5058dae --- /dev/null +++ b/states/rclone/defaults.yaml @@ -0,0 +1,9 @@ +--- +rclone: + enabled: true + install_dir: /usr/local/apps + release_dir: /usr/local/apps/releases + mirror: https://downloads.rclone.org + version: v1.50.2 + os: linux + arch: amd64 \ No newline at end of file diff --git a/states/rclone/init.sls b/states/rclone/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/rclone/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/rclone/install.sls b/states/rclone/install.sls new file mode 100644 index 0000000..c5750ed --- /dev/null +++ b/states/rclone/install.sls @@ -0,0 +1,19 @@ +--- +{%- from "rclone/map.jinja" import rclone with context %} +{%- if not salt['file.file_exists']( rclone.install_dir +"/rclone-" + rclone.version) %} +rclone-archive-extract: + archive.extracted: + - name: {{ rclone.release_dir }} + - source: {{ rclone.mirror }}/{{ rclone.version }}/rclone-{{ rclone.version }}-{{ rclone.os }}-{{ rclone.arch }}.zip + - skip_verify: True + - enforce_toplevel: False + - if_missing: {{ rclone.release_dir }}/rclone + +rclone-binary-symlink: + file.symlink: + - name: /usr/local/bin/rclone + - target: {{ rclone.release_dir }}/rclone-{{ rclone.version }}-{{ rclone.os }}-{{ rclone.arch }}/rclone + - force: True + - require: + - archive: rclone-archive-extract +{%- endif %} \ No newline at end of file diff --git a/states/rclone/kernelmap.yaml b/states/rclone/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/rclone/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/rclone/map.jinja b/states/rclone/map.jinja new file mode 100644 index 0000000..5bd605b --- /dev/null +++ b/states/rclone/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "rclone/defaults.yaml" as default_settings -%} + +{%- import_yaml "rclone/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "rclone/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='rclone', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set rclone = salt['pillar.get']('rclone', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/rclone/osarchmap.yaml b/states/rclone/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/rclone/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/reactor/auth.sls b/states/reactor/auth.sls new file mode 100644 index 0000000..1305600 --- /dev/null +++ b/states/reactor/auth.sls @@ -0,0 +1,15 @@ +--- +email-auth: + runner.process_minion_data.email_auth: + - smtp_server: 127.0.0.1 + - fromaddr: salt@paulbsd.com + - toaddrs: paul@paulbsd.com + - subject: "Salt master: minion auth: id: {{ data['id'] }}" + - data_str: {{ data|yaml_dquote }} + +#provision-run: +# local.state.sls: +# - tgt: {{ data['id'] }} +# - args: +# - mods: provision +# - ret: smtp \ No newline at end of file diff --git a/states/reactor/email-on-failure.sls b/states/reactor/email-on-failure.sls new file mode 100644 index 0000000..5e04317 --- /dev/null +++ b/states/reactor/email-on-failure.sls @@ -0,0 +1,8 @@ +--- +email-on-failure: + runner.process_minion_data.email_errors: + - smtp_server: 127.0.0.1 + - fromaddr: salt@paulbsd.com + - toaddrs: paul@paulbsd.com + - subject: "Salt failure or changes on {{ data['id'] }}" + - data_str: {{ data|yaml_dquote }} \ No newline at end of file diff --git a/states/redis/defaults.yaml b/states/redis/defaults.yaml new file mode 100644 index 0000000..18e5e68 --- /dev/null +++ b/states/redis/defaults.yaml @@ -0,0 +1,2 @@ +--- +redis: \ No newline at end of file diff --git a/states/redis/init.sls b/states/redis/init.sls new file mode 100644 index 0000000..b07358d --- /dev/null +++ b/states/redis/init.sls @@ -0,0 +1,4 @@ +--- +include: + - .install + - .service \ No newline at end of file diff --git a/states/redis/install.sls b/states/redis/install.sls new file mode 100644 index 0000000..f724236 --- /dev/null +++ b/states/redis/install.sls @@ -0,0 +1,4 @@ +--- +redis-install: + pkg.latest: + - name: redis-server \ No newline at end of file diff --git a/states/redis/map.jinja b/states/redis/map.jinja new file mode 100644 index 0000000..350f773 --- /dev/null +++ b/states/redis/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "redis/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='redis') -%} + +{%- set redis = salt['pillar.get']('redis', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/redis/service.sls b/states/redis/service.sls new file mode 100644 index 0000000..2f7c534 --- /dev/null +++ b/states/redis/service.sls @@ -0,0 +1,5 @@ +--- +redis-service: + service.running: + - name: redis + - enable: True \ No newline at end of file diff --git a/states/repos/init.sls b/states/repos/init.sls new file mode 100644 index 0000000..05ee7da --- /dev/null +++ b/states/repos/init.sls @@ -0,0 +1,33 @@ +--- +{%- for repo in pillar.get('repos') %} + +{%- if repo['enabled'] %} +pkg-repo-{{ repo['name'] }}: + pkgrepo.managed: + - humanname: {{ repo['humanname'] }} + {%- if repo['ppa'] %} + - ppa: {{ repo['ppasrc'] }} + - dist: {{ repo['dist'] }} + {%- elif not repo['ppa'] %} + - name: {{ repo['src'] }} + - gpgcheck: {{ repo['gpgcheck'] }} + - file: {{ repo['file'] }} + - clean_file: True + {%- if repo['key_method'] == 'file' %} + - key_url: {{ repo['key_url'] }} + {%- elif repo['key_method'] == 'server' %} + - keyid: {{ repo['keyid'] }} + - keyserver: {{ repo['keyserver'] }} + {%- endif %} + {%- endif %} + - refresh: True +{%- else %} + +pkg-repo-{{ repo['name'] }}: + file.absent: +{%- if not repo['ppa'] %} + - name: {{ repo['file'] }} +{%- endif %} +{%- endif %} + +{%- endfor %} \ No newline at end of file diff --git a/states/rspamd/config.sls b/states/rspamd/config.sls new file mode 100644 index 0000000..9454ff3 --- /dev/null +++ b/states/rspamd/config.sls @@ -0,0 +1,14 @@ +--- +{%- from "rspamd/map.jinja" import rspamd with context %} + +{%- for configfile in rspamd.config.files.items() %} +rspamd-config-{{ configfile }}: + file.managed: + - name: {{ rspamd.config.dir }}/{{ configfile }} + - source: salt://rspamd/templates/config.j2 + - template: jinja + - user: root + - group: root + - watch_in: + - service: rspamd-service +{%- endfor %} \ No newline at end of file diff --git a/states/rspamd/defaults.yaml b/states/rspamd/defaults.yaml new file mode 100644 index 0000000..5c38334 --- /dev/null +++ b/states/rspamd/defaults.yaml @@ -0,0 +1,5 @@ +--- +rspamd: + config: + dir: /etc/rspamd + files: {} \ No newline at end of file diff --git a/states/rspamd/init.sls b/states/rspamd/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/rspamd/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/rspamd/install.sls b/states/rspamd/install.sls new file mode 100644 index 0000000..c044be9 --- /dev/null +++ b/states/rspamd/install.sls @@ -0,0 +1,6 @@ +--- +{%- from "rspamd/map.jinja" import rspamd with context %} + +rspamd-pkgs: + pkg.installed: + - name: rspamd \ No newline at end of file diff --git a/states/rspamd/map.jinja b/states/rspamd/map.jinja new file mode 100644 index 0000000..c8d6ce8 --- /dev/null +++ b/states/rspamd/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "rspamd/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='rspamd') -%} + +{%- set rspamd = salt['pillar.get']('rspamd', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/rspamd/service.sls b/states/rspamd/service.sls new file mode 100644 index 0000000..765da76 --- /dev/null +++ b/states/rspamd/service.sls @@ -0,0 +1,5 @@ +--- +rspamd-service: + service.running: + - name: rspamd + - enable: True \ No newline at end of file diff --git a/states/rsync/config.sls b/states/rsync/config.sls new file mode 100644 index 0000000..a7c7253 --- /dev/null +++ b/states/rsync/config.sls @@ -0,0 +1,13 @@ +--- +rsync-config: + file.managed: + - name: /etc/rsyncd.conf + - source: salt://rsync/rsyncd.conf.j2 + - user: root + - group: root + - mode: 0755 + - template: jinja + - watch_in: + - service: rsync-service + - require: + - pkg: rsync-install \ No newline at end of file diff --git a/states/rsync/defaults.yaml b/states/rsync/defaults.yaml new file mode 100644 index 0000000..78fdfdb --- /dev/null +++ b/states/rsync/defaults.yaml @@ -0,0 +1,16 @@ +--- +rsync: + enabled: true + config: + global: + uid: 'root' + gid: 'root' + use chroot: 'no' + max connections: 4 + syslog facility: 4 + pid file: '/var/run/rsyncd.pid' + sections: + - name: 'tmp' + settings: + path: '/tmp' + read only: 'yes' \ No newline at end of file diff --git a/states/rsync/init.sls b/states/rsync/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/rsync/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/rsync/install.sls b/states/rsync/install.sls new file mode 100644 index 0000000..752ef61 --- /dev/null +++ b/states/rsync/install.sls @@ -0,0 +1,4 @@ +--- +rsync-install: + pkg.latest: + - name: rsync \ No newline at end of file diff --git a/states/rsync/map.jinja b/states/rsync/map.jinja new file mode 100644 index 0000000..cfc7425 --- /dev/null +++ b/states/rsync/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "rsync/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='rsync') -%} + +{%- set rsync = salt['pillar.get']('rsync', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/rsync/rsyncd.conf.j2 b/states/rsync/rsyncd.conf.j2 new file mode 100644 index 0000000..3edd11f --- /dev/null +++ b/states/rsync/rsyncd.conf.j2 @@ -0,0 +1,12 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "rsync/map.jinja" import rsync with context %} +{% for key, value in rsync.config.global.items() %} +{{ key }} = {{ value }} +{%- endfor %} + +{% for section in rsync.config.sections -%} +[{{ section.name }}] +{%- for key, value in section.settings.items() %} + {{ key }} = {{ value }} +{%- endfor %} +{%- endfor %} \ No newline at end of file diff --git a/states/rsync/service.sls b/states/rsync/service.sls new file mode 100644 index 0000000..65722f6 --- /dev/null +++ b/states/rsync/service.sls @@ -0,0 +1,7 @@ +--- +rsync-service: + service.running: + - name: rsync + - enable: True + - require: + - file: rsync-config \ No newline at end of file diff --git a/states/rsyslog/config.sls b/states/rsyslog/config.sls new file mode 100644 index 0000000..8c42880 --- /dev/null +++ b/states/rsyslog/config.sls @@ -0,0 +1,10 @@ +--- +rsyslog-main-config: + file.managed: + - name: /etc/rsyslog.conf + - source: salt://rsyslog/rsyslog.conf.j2 + - user: root + - group: root + - template: jinja + - watch_in: + - service: rsyslog-service \ No newline at end of file diff --git a/states/rsyslog/init.sls b/states/rsyslog/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/rsyslog/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/rsyslog/install.sls b/states/rsyslog/install.sls new file mode 100644 index 0000000..a7fb5e9 --- /dev/null +++ b/states/rsyslog/install.sls @@ -0,0 +1,4 @@ +--- +rsyslog-pkg: + pkg.installed: + - name: rsyslog \ No newline at end of file diff --git a/states/rsyslog/rsyslog.conf.j2 b/states/rsyslog/rsyslog.conf.j2 new file mode 100644 index 0000000..fa686a6 --- /dev/null +++ b/states/rsyslog/rsyslog.conf.j2 @@ -0,0 +1,46 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +module(load="imuxsock") +module(load="imklog" permitnonkernelfacility="on") + +$ModLoad imudp.so + +$ActionFileDefaultTemplate RSYSLOG_SyslogProtocol23Format +$RepeatedMsgReduction on +$FileOwner root +$FileGroup adm +$FileCreateMode 0640 +$DirCreateMode 0755 +$PrivDropToUser syslog +$PrivDropToGroup syslog +$UDPServerRun 514 +$Umask 0022 + +$WorkDirectory /var/spool/rsyslog + +$IncludeConfig /etc/rsyslog.d/*.conf + +auth,authpriv.* /var/log/auth.log +*.*;auth,authpriv.none -/var/log/syslog +cron.* /var/log/cron.log +daemon.* -/var/log/daemon.log +kern.* -/var/log/kern.log +lpr.* -/var/log/lpr.log +mail.* -/var/log/mail.log +user.* -/var/log/user.log + +mail.info -/var/log/mail.info +mail.warn -/var/log/mail.warn +mail.err /var/log/mail.err + +*.=debug;\ + auth,authpriv.none;\ + news.none;mail.none -/var/log/debug +*.=info;*.=notice;*.=warn;\ + auth,authpriv.none;\ + cron,daemon.none;\ + mail,news.none -/var/log/messages + +*.emerg :omusrmsg:* + +*.* @localhost:6514;RSYSLOG_SyslogProtocol23Format diff --git a/states/rsyslog/rsyslog.conf.j2.orig b/states/rsyslog/rsyslog.conf.j2.orig new file mode 100644 index 0000000..7bd37a2 --- /dev/null +++ b/states/rsyslog/rsyslog.conf.j2.orig @@ -0,0 +1,60 @@ +# /etc/rsyslog.conf Configuration file for rsyslog. +# +# For more information see +# /usr/share/doc/rsyslog-doc/html/rsyslog_conf.html +# +# Default logging rules can be found in /etc/rsyslog.d/50-default.conf + + +################# +#### MODULES #### +################# + +module(load="imuxsock") # provides support for local system logging +#module(load="immark") # provides --MARK-- message capability + +# provides UDP syslog reception +#module(load="imudp") +#input(type="imudp" port="514") + +# provides TCP syslog reception +#module(load="imtcp") +#input(type="imtcp" port="514") + +# provides kernel logging support and enable non-kernel klog messages +module(load="imklog" permitnonkernelfacility="on") + +########################### +#### GLOBAL DIRECTIVES #### +########################### + +# +# Use traditional timestamp format. +# To enable high precision timestamps, comment out the following line. +# +#$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat +$ActionFileDefaultTemplate RSYSLOG_SyslogProtocol23Format + +# Filter duplicated messages +$RepeatedMsgReduction on + +# +# Set the default permissions for all log files. +# +$FileOwner syslog +$FileGroup adm +$FileCreateMode 0640 +$DirCreateMode 0755 +$Umask 0022 +$PrivDropToUser syslog +$PrivDropToGroup syslog + +# +# Where to place spool and state files +# +$WorkDirectory /var/spool/rsyslog + +# +# Include all config files in /etc/rsyslog.d/ +# +$IncludeConfig /etc/rsyslog.d/*.conf \ No newline at end of file diff --git a/states/rsyslog/service.sls b/states/rsyslog/service.sls new file mode 100644 index 0000000..88be17b --- /dev/null +++ b/states/rsyslog/service.sls @@ -0,0 +1,5 @@ +--- +rsyslog-service: + service.running: + - name: rsyslog + - enable: True \ No newline at end of file diff --git a/states/salt_minion/defaults.yaml b/states/salt_minion/defaults.yaml new file mode 100644 index 0000000..e1d9802 --- /dev/null +++ b/states/salt_minion/defaults.yaml @@ -0,0 +1,14 @@ +--- +salt_minion: + enabled: true + version: 3000 + config: + master: salt.paulbsd.com + #hash_type: sha256 + #state_verbose: True + #tcp_keepalive: True + #tcp_keepalive_idle: 300 + #random_reauth_delay: 60 + #recon_default: 1000 + #recon_max: 10000 + #recon_randomize: True diff --git a/states/salt_minion/init.sls b/states/salt_minion/init.sls new file mode 100644 index 0000000..e41737d --- /dev/null +++ b/states/salt_minion/init.sls @@ -0,0 +1,19 @@ +--- +{%- from "salt_minion/map.jinja" import salt_minion with context -%} +salt-minion-config: + file.managed: + - name: /etc/salt/minion + - source: salt://salt_minion/minion.j2 + - template: jinja + - user: root + - group: root + - mode: 755 + +salt-minion-service: + file.managed: + - name: /etc/systemd/system/salt-minion.service + - source: salt://salt_minion/salt-minion.service.j2 + - template: jinja + - user: root + - group: root + - mode: 755 \ No newline at end of file diff --git a/states/salt_minion/map.jinja b/states/salt_minion/map.jinja new file mode 100644 index 0000000..fe97e56 --- /dev/null +++ b/states/salt_minion/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "salt_minion/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='salt_minion') -%} + +{%- set salt_minion = salt['pillar.get']('salt_minion', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/salt_minion/minion.j2 b/states/salt_minion/minion.j2 new file mode 100644 index 0000000..27a5d5c --- /dev/null +++ b/states/salt_minion/minion.j2 @@ -0,0 +1,5 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "salt_minion/map.jinja" import salt_minion with context %} +{%- for key, value in salt_minion.config.items() %} +{{ key }}: {{ value }} +{%- endfor -%} \ No newline at end of file diff --git a/states/salt_minion/minion.sample b/states/salt_minion/minion.sample new file mode 100644 index 0000000..3885e7c --- /dev/null +++ b/states/salt_minion/minion.sample @@ -0,0 +1,781 @@ +##### Primary configuration settings ##### +########################################## +# This configuration file is used to manage the behavior of the Salt Minion. +# With the exception of the location of the Salt Master Server, values that are +# commented out but have an empty line after the comment are defaults that need +# not be set in the config. If there is no blank line after the comment, the +# value is presented as an example and is not the default. + +# Per default the minion will automatically include all config files +# from minion.d/*.conf (minion.d is a directory in the same directory +# as the main minion config file). +#default_include: minion.d/*.conf + +# Set the location of the salt master server. If the master server cannot be +# resolved, then the minion will fail to start. +#master: salt + +# Set http proxy information for the minion when doing requests +#proxy_host: +#proxy_port: +#proxy_username: +#proxy_password: + +# If multiple masters are specified in the 'master' setting, the default behavior +# is to always try to connect to them in the order they are listed. If random_master is +# set to True, the order will be randomized instead. This can be helpful in distributing +# the load of many minions executing salt-call requests, for example, from a cron job. +# If only one master is listed, this setting is ignored and a warning will be logged. +# NOTE: If master_type is set to failover, use master_shuffle instead. +#random_master: False + +# Use if master_type is set to failover. +#master_shuffle: False + +# Minions can connect to multiple masters simultaneously (all masters +# are "hot"), or can be configured to failover if a master becomes +# unavailable. Multiple hot masters are configured by setting this +# value to "str". Failover masters can be requested by setting +# to "failover". MAKE SURE TO SET master_alive_interval if you are +# using failover. +# master_type: str + +# Poll interval in seconds for checking if the master is still there. Only +# respected if master_type above is "failover". To disable the interval entirely, +# set the value to -1. (This may be necessary on machines which have high numbers +# of TCP connections, such as load balancers.) +# master_alive_interval: 30 + +# If the minion is in multi-master mode and the master_type configuration option +# is set to "failover", this setting can be set to "True" to force the minion +# to fail back to the first master in the list if the first master is back online. +#master_failback: False + +# If the minion is in multi-master mode, the "master_type" configuration is set to +# "failover", and the "master_failback" option is enabled, the master failback +# interval can be set to ping the top master with this interval, in seconds. +#master_failback_interval: 0 + +# Set whether the minion should connect to the master via IPv6: +#ipv6: False + +# Set the number of seconds to wait before attempting to resolve +# the master hostname if name resolution fails. Defaults to 30 seconds. +# Set to zero if the minion should shutdown and not retry. +# retry_dns: 30 + +# Set the port used by the master reply and authentication server. +#master_port: 4506 + +# The user to run salt. +#user: root + +# The user to run salt remote execution commands as via sudo. If this option is +# enabled then sudo will be used to change the active user executing the remote +# command. If enabled the user will need to be allowed access via the sudoers +# file for the user that the salt minion is configured to run as. The most +# common option would be to use the root user. If this option is set the user +# option should also be set to a non-root user. If migrating from a root minion +# to a non root minion the minion cache should be cleared and the minion pki +# directory will need to be changed to the ownership of the new user. +#sudo_user: root + +# Specify the location of the daemon process ID file. +#pidfile: /var/run/salt-minion.pid + +# The root directory prepended to these options: pki_dir, cachedir, log_file, +# sock_dir, pidfile. +#root_dir: / + +# The path to the minion's configuration file. +#conf_file: /usr/pkg/etc/salt/minion + +# The directory to store the pki information in +#pki_dir: /usr/pkg/etc/salt/pki/minion + +# Explicitly declare the id for this minion to use, if left commented the id +# will be the hostname as returned by the python call: socket.getfqdn() +# Since salt uses detached ids it is possible to run multiple minions on the +# same machine but with different ids, this can be useful for salt compute +# clusters. +#id: + +# Cache the minion id to a file when the minion's id is not statically defined +# in the minion config. Defaults to "True". This setting prevents potential +# problems when automatic minion id resolution changes, which can cause the +# minion to lose connection with the master. To turn off minion id caching, +# set this config to ``False``. +#minion_id_caching: True + +# Append a domain to a hostname in the event that it does not exist. This is +# useful for systems where socket.getfqdn() does not actually result in a +# FQDN (for instance, Solaris). +#append_domain: + +# Custom static grains for this minion can be specified here and used in SLS +# files just like all other grains. This example sets 4 custom grains, with +# the 'roles' grain having two values that can be matched against. +#grains: +# roles: +# - webserver +# - memcache +# deployment: datacenter4 +# cabinet: 13 +# cab_u: 14-15 +# +# Where cache data goes. +# This data may contain sensitive data and should be protected accordingly. +#cachedir: /var/cache/salt/minion + +# Append minion_id to these directories. Helps with +# multiple proxies and minions running on the same machine. +# Allowed elements in the list: pki_dir, cachedir, extension_modules +# Normally not needed unless running several proxies and/or minions on the same machine +# Defaults to ['cachedir'] for proxies, [] (empty list) for regular minions +#append_minionid_config_dirs: + +# Verify and set permissions on configuration directories at startup. +#verify_env: True + +# The minion can locally cache the return data from jobs sent to it, this +# can be a good way to keep track of jobs the minion has executed +# (on the minion side). By default this feature is disabled, to enable, set +# cache_jobs to True. +#cache_jobs: False + +# Set the directory used to hold unix sockets. +#sock_dir: /var/run/salt/minion + +# Set the default outputter used by the salt-call command. The default is +# "nested". +#output: nested +# +# By default output is colored. To disable colored output, set the color value +# to False. +#color: True + +# Do not strip off the colored output from nested results and state outputs +# (true by default). +# strip_colors: False + +# Backup files that are replaced by file.managed and file.recurse under +# 'cachedir'/file_backups relative to their original location and appended +# with a timestamp. The only valid setting is "minion". Disabled by default. +# +# Alternatively this can be specified for each file in state files: +# /etc/ssh/sshd_config: +# file.managed: +# - source: salt://ssh/sshd_config +# - backup: minion +# +#backup_mode: minion + +# When waiting for a master to accept the minion's public key, salt will +# continuously attempt to reconnect until successful. This is the time, in +# seconds, between those reconnection attempts. +#acceptance_wait_time: 10 + +# If this is nonzero, the time between reconnection attempts will increase by +# acceptance_wait_time seconds per iteration, up to this maximum. If this is +# set to zero, the time between reconnection attempts will stay constant. +#acceptance_wait_time_max: 0 + +# If the master rejects the minion's public key, retry instead of exiting. +# Rejected keys will be handled the same as waiting on acceptance. +#rejected_retry: False + +# When the master key changes, the minion will try to re-auth itself to receive +# the new master key. In larger environments this can cause a SYN flood on the +# master because all minions try to re-auth immediately. To prevent this and +# have a minion wait for a random amount of time, use this optional parameter. +# The wait-time will be a random number of seconds between 0 and the defined value. +#random_reauth_delay: 60 + +# When waiting for a master to accept the minion's public key, salt will +# continuously attempt to reconnect until successful. This is the timeout value, +# in seconds, for each individual attempt. After this timeout expires, the minion +# will wait for acceptance_wait_time seconds before trying again. Unless your master +# is under unusually heavy load, this should be left at the default. +#auth_timeout: 60 + +# Number of consecutive SaltReqTimeoutError that are acceptable when trying to +# authenticate. +#auth_tries: 7 + +# The number of attempts to connect to a master before giving up. +# Set this to -1 for unlimited attempts. This allows for a master to have +# downtime and the minion to reconnect to it later when it comes back up. +# In 'failover' mode, it is the number of attempts for each set of masters. +# In this mode, it will cycle through the list of masters for each attempt. +# +# This is different than auth_tries because auth_tries attempts to +# retry auth attempts with a single master. auth_tries is under the +# assumption that you can connect to the master but not gain +# authorization from it. master_tries will still cycle through all +# the masters in a given try, so it is appropriate if you expect +# occasional downtime from the master(s). +#master_tries: 1 + +# If authentication fails due to SaltReqTimeoutError during a ping_interval, +# cause sub minion process to restart. +#auth_safemode: False + +# Ping Master to ensure connection is alive (minutes). +#ping_interval: 0 + +# To auto recover minions if master changes IP address (DDNS) +# auth_tries: 10 +# auth_safemode: False +# ping_interval: 90 +# +# Minions won't know master is missing until a ping fails. After the ping fail, +# the minion will attempt authentication and likely fails out and cause a restart. +# When the minion restarts it will resolve the masters IP and attempt to reconnect. + +# If you don't have any problems with syn-floods, don't bother with the +# three recon_* settings described below, just leave the defaults! +# +# The ZeroMQ pull-socket that binds to the masters publishing interface tries +# to reconnect immediately, if the socket is disconnected (for example if +# the master processes are restarted). In large setups this will have all +# minions reconnect immediately which might flood the master (the ZeroMQ-default +# is usually a 100ms delay). To prevent this, these three recon_* settings +# can be used. +# recon_default: the interval in milliseconds that the socket should wait before +# trying to reconnect to the master (1000ms = 1 second) +# +# recon_max: the maximum time a socket should wait. each interval the time to wait +# is calculated by doubling the previous time. if recon_max is reached, +# it starts again at recon_default. Short example: +# +# reconnect 1: the socket will wait 'recon_default' milliseconds +# reconnect 2: 'recon_default' * 2 +# reconnect 3: ('recon_default' * 2) * 2 +# reconnect 4: value from previous interval * 2 +# reconnect 5: value from previous interval * 2 +# reconnect x: if value >= recon_max, it starts again with recon_default +# +# recon_randomize: generate a random wait time on minion start. The wait time will +# be a random value between recon_default and recon_default + +# recon_max. Having all minions reconnect with the same recon_default +# and recon_max value kind of defeats the purpose of being able to +# change these settings. If all minions have the same values and your +# setup is quite large (several thousand minions), they will still +# flood the master. The desired behavior is to have timeframe within +# all minions try to reconnect. +# +# Example on how to use these settings. The goal: have all minions reconnect within a +# 60 second timeframe on a disconnect. +# recon_default: 1000 +# recon_max: 59000 +# recon_randomize: True +# +# Each minion will have a randomized reconnect value between 'recon_default' +# and 'recon_default + recon_max', which in this example means between 1000ms +# 60000ms (or between 1 and 60 seconds). The generated random-value will be +# doubled after each attempt to reconnect. Lets say the generated random +# value is 11 seconds (or 11000ms). +# reconnect 1: wait 11 seconds +# reconnect 2: wait 22 seconds +# reconnect 3: wait 33 seconds +# reconnect 4: wait 44 seconds +# reconnect 5: wait 55 seconds +# reconnect 6: wait time is bigger than 60 seconds (recon_default + recon_max) +# reconnect 7: wait 11 seconds +# reconnect 8: wait 22 seconds +# reconnect 9: wait 33 seconds +# reconnect x: etc. +# +# In a setup with ~6000 thousand hosts these settings would average the reconnects +# to about 100 per second and all hosts would be reconnected within 60 seconds. +# recon_default: 100 +# recon_max: 5000 +# recon_randomize: False +# +# +# The loop_interval sets how long in seconds the minion will wait between +# evaluating the scheduler and running cleanup tasks. This defaults to a +# sane 60 seconds, but if the minion scheduler needs to be evaluated more +# often lower this value +#loop_interval: 60 + +# The grains can be merged, instead of overridden, using this option. +# This allows custom grains to defined different subvalues of a dictionary +# grain. By default this feature is disabled, to enable set grains_deep_merge +# to ``True``. +#grains_deep_merge: False + +# The grains_refresh_every setting allows for a minion to periodically check +# its grains to see if they have changed and, if so, to inform the master +# of the new grains. This operation is moderately expensive, therefore +# care should be taken not to set this value too low. +# +# Note: This value is expressed in __minutes__! +# +# A value of 10 minutes is a reasonable default. +# +# If the value is set to zero, this check is disabled. +#grains_refresh_every: 1 + +# Cache grains on the minion. Default is False. +#grains_cache: False + +# Cache rendered pillar data on the minion. Default is False. +# This may cause 'cachedir'/pillar to contain sensitive data that should be +# protected accordingly. +#minion_pillar_cache: False + +# Grains cache expiration, in seconds. If the cache file is older than this +# number of seconds then the grains cache will be dumped and fully re-populated +# with fresh data. Defaults to 5 minutes. Will have no effect if 'grains_cache' +# is not enabled. +# grains_cache_expiration: 300 + +# Determines whether or not the salt minion should run scheduled mine updates. +# Defaults to "True". Set to "False" to disable the scheduled mine updates +# (this essentially just does not add the mine update function to the minion's +# scheduler). +#mine_enabled: True + +# Determines whether or not scheduled mine updates should be accompanied by a job +# return for the job cache. Defaults to "False". Set to "True" to include job +# returns in the job cache for mine updates. +#mine_return_job: False + +# Example functions that can be run via the mine facility +# NO mine functions are established by default. +# Note these can be defined in the minion's pillar as well. +#mine_functions: +# test.ping: [] +# network.ip_addrs: +# interface: eth0 +# cidr: '10.0.0.0/8' + +# Windows platforms lack posix IPC and must rely on slower TCP based inter- +# process communications. Set ipc_mode to 'tcp' on such systems +#ipc_mode: ipc + +# Overwrite the default tcp ports used by the minion when in tcp mode +#tcp_pub_port: 4510 +#tcp_pull_port: 4511 + +# Passing very large events can cause the minion to consume large amounts of +# memory. This value tunes the maximum size of a message allowed onto the +# minion event bus. The value is expressed in bytes. +#max_event_size: 1048576 + +# To detect failed master(s) and fire events on connect/disconnect, set +# master_alive_interval to the number of seconds to poll the masters for +# connection events. +# +#master_alive_interval: 30 + +# The minion can include configuration from other files. To enable this, +# pass a list of paths to this option. The paths can be either relative or +# absolute; if relative, they are considered to be relative to the directory +# the main minion configuration file lives in (this file). Paths can make use +# of shell-style globbing. If no files are matched by a path passed to this +# option then the minion will log a warning message. +# +# Include a config file from some other path: +# include: /usr/pkg/etc/salt/extra_config +# +# Include config from several files and directories: +#include: +# - /usr/pkg/etc/salt/extra_config +# - /etc/roles/webserver + +# The syndic minion can verify that it is talking to the correct master via the +# key fingerprint of the higher-level master with the "syndic_finger" config. +#syndic_finger: '' +# +# +# +##### Minion module management ##### +########################################## +# Disable specific modules. This allows the admin to limit the level of +# access the master has to the minion. +#disable_modules: [cmd,test] +#disable_returners: [] + +# This is the reverse of disable_modules. The default, like disable_modules, is the empty list, +# but if this option is set to *anything* then *only* those modules will load. +# Note that this is a very large hammer and it can be quite difficult to keep the minion working +# the way you think it should since Salt uses many modules internally itself. At a bare minimum +# you need the following enabled or else the minion won't start. +#whitelist_modules: +# - cmdmod +# - test +# - config + +# Modules can be loaded from arbitrary paths. This enables the easy deployment +# of third party modules. Modules for returners and minions can be loaded. +# Specify a list of extra directories to search for minion modules and +# returners. These paths must be fully qualified! +#module_dirs: [] +#returner_dirs: [] +#states_dirs: [] +#render_dirs: [] +#utils_dirs: [] +# +# A module provider can be statically overwritten or extended for the minion +# via the providers option, in this case the default module will be +# overwritten by the specified module. In this example the pkg module will +# be provided by the yumpkg5 module instead of the system default. +#providers: +# pkg: yumpkg5 +# +# Enable Cython modules searching and loading. (Default: False) +#cython_enable: False +# +# Specify a max size (in bytes) for modules on import. This feature is currently +# only supported on *nix operating systems and requires psutil. +# modules_max_memory: -1 + + +##### State Management Settings ##### +########################################### +# The state management system executes all of the state templates on the minion +# to enable more granular control of system state management. The type of +# template and serialization used for state management needs to be configured +# on the minion, the default renderer is yaml_jinja. This is a yaml file +# rendered from a jinja template, the available options are: +# yaml_jinja +# yaml_mako +# yaml_wempy +# json_jinja +# json_mako +# json_wempy +# +#renderer: yaml_jinja +# +# The failhard option tells the minions to stop immediately after the first +# failure detected in the state execution. Defaults to False. +#failhard: False +# +# Reload the modules prior to a highstate run. +#autoload_dynamic_modules: True +# +# clean_dynamic_modules keeps the dynamic modules on the minion in sync with +# the dynamic modules on the master, this means that if a dynamic module is +# not on the master it will be deleted from the minion. By default, this is +# enabled and can be disabled by changing this value to False. +#clean_dynamic_modules: True +# +# Normally, the minion is not isolated to any single environment on the master +# when running states, but the environment can be isolated on the minion side +# by statically setting it. Remember that the recommended way to manage +# environments is to isolate via the top file. +#environment: None +# +# Isolates the pillar environment on the minion side. This functions the same +# as the environment setting, but for pillar instead of states. +#pillarenv: None +# +# If using the local file directory, then the state top file name needs to be +# defined, by default this is top.sls. +#state_top: top.sls +# +# Run states when the minion daemon starts. To enable, set startup_states to: +# 'highstate' -- Execute state.highstate +# 'sls' -- Read in the sls_list option and execute the named sls files +# 'top' -- Read top_file option and execute based on that file on the Master +#startup_states: '' +# +# List of states to run when the minion starts up if startup_states is 'sls': +#sls_list: +# - edit.vim +# - hyper +# +# Top file to execute if startup_states is 'top': +#top_file: '' + +# Automatically aggregate all states that have support for mod_aggregate by +# setting to True. Or pass a list of state module names to automatically +# aggregate just those types. +# +# state_aggregate: +# - pkg +# +#state_aggregate: False + +##### File Directory Settings ##### +########################################## +# The Salt Minion can redirect all file server operations to a local directory, +# this allows for the same state tree that is on the master to be used if +# copied completely onto the minion. This is a literal copy of the settings on +# the master but used to reference a local directory on the minion. + +# Set the file client. The client defaults to looking on the master server for +# files, but can be directed to look at the local file directory setting +# defined below by setting it to "local". Setting a local file_client runs the +# minion in masterless mode. +#file_client: remote + +# The file directory works on environments passed to the minion, each environment +# can have multiple root directories, the subdirectories in the multiple file +# roots cannot match, otherwise the downloaded files will not be able to be +# reliably ensured. A base environment is required to house the top file. +# Example: +# file_roots: +# base: +# - /usr/pkg/etc/salt/states/ +# dev: +# - /usr/pkg/etc/salt/states/dev/services +# - /usr/pkg/etc/salt/states/dev/states +# prod: +# - /usr/pkg/etc/salt/states/prod/services +# - /usr/pkg/etc/salt/states/prod/states +# +#file_roots: +# base: +# - /usr/pkg/etc/salt/states + +# Uncomment the line below if you do not want the file_server to follow +# symlinks when walking the filesystem tree. This is set to True +# by default. Currently this only applies to the default roots +# fileserver_backend. +#fileserver_followsymlinks: False +# +# Uncomment the line below if you do not want symlinks to be +# treated as the files they are pointing to. By default this is set to +# False. By uncommenting the line below, any detected symlink while listing +# files on the Master will not be returned to the Minion. +#fileserver_ignoresymlinks: True +# +# By default, the Salt fileserver recurses fully into all defined environments +# to attempt to find files. To limit this behavior so that the fileserver only +# traverses directories with SLS files and special Salt directories like _modules, +# enable the option below. This might be useful for installations where a file root +# has a very large number of files and performance is negatively impacted. Default +# is False. +#fileserver_limit_traversal: False + +# The hash_type is the hash to use when discovering the hash of a file in +# the local fileserver. The default is sha256, sha224, sha384 and sha512 are also supported. +# +# WARNING: While md5 and sha1 are also supported, do not use it due to the high chance +# of possible collisions and thus security breach. +# +# Warning: Prior to changing this value, the minion should be stopped and all +# Salt caches should be cleared. +#hash_type: sha256 + +# The Salt pillar is searched for locally if file_client is set to local. If +# this is the case, and pillar data is defined, then the pillar_roots need to +# also be configured on the minion: +#pillar_roots: +# base: +# - /usr/pkg/etc/salt/pillar + +# Set a hard-limit on the size of the files that can be pushed to the master. +# It will be interpreted as megabytes. Default: 100 +#file_recv_max_size: 100 +# +# +###### Security settings ##### +########################################### +# Enable "open mode", this mode still maintains encryption, but turns off +# authentication, this is only intended for highly secure environments or for +# the situation where your keys end up in a bad state. If you run in open mode +# you do so at your own risk! +#open_mode: False + +# Enable permissive access to the salt keys. This allows you to run the +# master or minion as root, but have a non-root group be given access to +# your pki_dir. To make the access explicit, root must belong to the group +# you've given access to. This is potentially quite insecure. +#permissive_pki_access: False + +# The state_verbose and state_output settings can be used to change the way +# state system data is printed to the display. By default all data is printed. +# The state_verbose setting can be set to True or False, when set to False +# all data that has a result of True and no changes will be suppressed. +#state_verbose: True + +# The state_output setting changes if the output is the full multi line +# output for each changed state if set to 'full', but if set to 'terse' +# the output will be shortened to a single line. +#state_output: full + +# The state_output_diff setting changes whether or not the output from +# successful states is returned. Useful when even the terse output of these +# states is cluttering the logs. Set it to True to ignore them. +#state_output_diff: False + +# The state_output_profile setting changes whether profile information +# will be shown for each state run. +#state_output_profile: True + +# Fingerprint of the master public key to validate the identity of your Salt master +# before the initial key exchange. The master fingerprint can be found by running +# "salt-key -F master" on the Salt master. +#master_finger: '' + + +###### Thread settings ##### +########################################### +# Disable multiprocessing support, by default when a minion receives a +# publication a new process is spawned and the command is executed therein. +#multiprocessing: True + + +##### Logging settings ##### +########################################## +# The location of the minion log file +# The minion log can be sent to a regular file, local path name, or network +# location. Remote logging works best when configured to use rsyslogd(8) (e.g.: +# ``file:///dev/log``), with rsyslogd(8) configured for network logging. The URI +# format is: ://:/ +#log_file: /var/log/salt/minion +#log_file: file:///dev/log +#log_file: udp://loghost:10514 +# +#log_file: /var/log/salt/minion +#key_logfile: /var/log/salt/key + +# The level of messages to send to the console. +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# +# The following log levels are considered INSECURE and may log sensitive data: +# ['garbage', 'trace', 'debug'] +# +# Default: 'warning' +log_level: debug + +# The level of messages to send to the log file. +# One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. +# If using 'log_granular_levels' this must be set to the highest desired level. +# Default: 'warning' +#log_level_logfile: + +# The date and time format used in log messages. Allowed date/time formatting +# can be seen here: http://docs.python.org/library/time.html#time.strftime +#log_datefmt: '%H:%M:%S' +#log_datefmt_logfile: '%Y-%m-%d %H:%M:%S' + +# The format of the console logging messages. Allowed formatting options can +# be seen here: http://docs.python.org/library/logging.html#logrecord-attributes +# +# Console log colors are specified by these additional formatters: +# +# %(colorlevel)s +# %(colorname)s +# %(colorprocess)s +# %(colormsg)s +# +# Since it is desirable to include the surrounding brackets, '[' and ']', in +# the coloring of the messages, these color formatters also include padding as +# well. Color LogRecord attributes are only available for console logging. +# +#log_fmt_console: '%(colorlevel)s %(colormsg)s' +#log_fmt_console: '[%(levelname)-8s] %(message)s' +# +#log_fmt_logfile: '%(asctime)s,%(msecs)03.0f [%(name)-17s][%(levelname)-8s] %(message)s' + +# This can be used to control logging levels more specificically. This +# example sets the main salt library at the 'warning' level, but sets +# 'salt.modules' to log at the 'debug' level: +# log_granular_levels: +# 'salt': 'warning' +# 'salt.modules': 'debug' +# +#log_granular_levels: {} + +# To diagnose issues with minions disconnecting or missing returns, ZeroMQ +# supports the use of monitor sockets to log connection events. This +# feature requires ZeroMQ 4.0 or higher. +# +# To enable ZeroMQ monitor sockets, set 'zmq_monitor' to 'True' and log at a +# debug level or higher. +# +# A sample log event is as follows: +# +# [DEBUG ] ZeroMQ event: {'endpoint': 'tcp://127.0.0.1:4505', 'event': 512, +# 'value': 27, 'description': 'EVENT_DISCONNECTED'} +# +# All events logged will include the string 'ZeroMQ event'. A connection event +# should be logged as the minion starts up and initially connects to the +# master. If not, check for debug log level and that the necessary version of +# ZeroMQ is installed. +# +#zmq_monitor: False + +###### Module configuration ##### +########################################### +# Salt allows for modules to be passed arbitrary configuration data, any data +# passed here in valid yaml format will be passed on to the salt minion modules +# for use. It is STRONGLY recommended that a naming convention be used in which +# the module name is followed by a . and then the value. Also, all top level +# data must be applied via the yaml dict construct, some examples: +# +# You can specify that all modules should run in test mode: +#test: True +# +# A simple value for the test module: +#test.foo: foo +# +# A list for the test module: +#test.bar: [baz,quo] +# +# A dict for the test module: +#test.baz: {spam: sausage, cheese: bread} +# +# +###### Update settings ###### +########################################### +# Using the features in Esky, a salt minion can both run as a frozen app and +# be updated on the fly. These options control how the update process +# (saltutil.update()) behaves. +# +# The url for finding and downloading updates. Disabled by default. +#update_url: False +# +# The list of services to restart after a successful update. Empty by default. +#update_restart_services: [] + + +###### Keepalive settings ###### +############################################ +# ZeroMQ now includes support for configuring SO_KEEPALIVE if supported by +# the OS. If connections between the minion and the master pass through +# a state tracking device such as a firewall or VPN gateway, there is +# the risk that it could tear down the connection the master and minion +# without informing either party that their connection has been taken away. +# Enabling TCP Keepalives prevents this from happening. + +# Overall state of TCP Keepalives, enable (1 or True), disable (0 or False) +# or leave to the OS defaults (-1), on Linux, typically disabled. Default True, enabled. +#tcp_keepalive: True + +# How long before the first keepalive should be sent in seconds. Default 300 +# to send the first keepalive after 5 minutes, OS default (-1) is typically 7200 seconds +# on Linux see /proc/sys/net/ipv4/tcp_keepalive_time. +#tcp_keepalive_idle: 300 + +# How many lost probes are needed to consider the connection lost. Default -1 +# to use OS defaults, typically 9 on Linux, see /proc/sys/net/ipv4/tcp_keepalive_probes. +#tcp_keepalive_cnt: -1 + +# How often, in seconds, to send keepalives after the first one. Default -1 to +# use OS defaults, typically 75 seconds on Linux, see +# /proc/sys/net/ipv4/tcp_keepalive_intvl. +#tcp_keepalive_intvl: -1 + + +###### Windows Software settings ###### +############################################ +# Location of the repository cache file on the master: +#win_repo_cachefile: 'salt://win/repo/winrepo.p' + + +###### Returner settings ###### +############################################ +# Which returner(s) will be used for minion's result: +#return: mysql + + +###### Miscellaneous settings ###### +############################################ +# Default match type for filtering events tags: startswith, endswith, find, regex, fnmatch +#event_match_type: startswith \ No newline at end of file diff --git a/states/salt_minion/salt-minion.service.j2 b/states/salt_minion/salt-minion.service.j2 new file mode 100644 index 0000000..8226908 --- /dev/null +++ b/states/salt_minion/salt-minion.service.j2 @@ -0,0 +1,15 @@ +{%- from "salt_minion/map.jinja" import salt_minion with context -%} +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +[Unit] +Description=The Salt Minion daemon +After=network.target + +[Service] +Type=notify +KillMode=process +NotifyAccess=all +LimitNOFILE=8192 +ExecStart=/usr/local/bin/salt-minion + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/samba/config.sls b/states/samba/config.sls new file mode 100644 index 0000000..4ae7e3d --- /dev/null +++ b/states/samba/config.sls @@ -0,0 +1,6 @@ +--- +{%- from "samba/map.jinja" import samba with context %} +samba-config: + ini_manage.options_present: + - name: /etc/samba/smb.conf + - sections: {{ samba.config }} \ No newline at end of file diff --git a/states/samba/defaults.yaml b/states/samba/defaults.yaml new file mode 100644 index 0000000..98df44b --- /dev/null +++ b/states/samba/defaults.yaml @@ -0,0 +1,4 @@ +--- +samba: + enabled: true + config: \ No newline at end of file diff --git a/states/samba/init.sls b/states/samba/init.sls new file mode 100644 index 0000000..6c7ba86 --- /dev/null +++ b/states/samba/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .users diff --git a/states/samba/install.sls b/states/samba/install.sls new file mode 100644 index 0000000..9019917 --- /dev/null +++ b/states/samba/install.sls @@ -0,0 +1,8 @@ +--- +{%- from "samba/map.jinja" import samba with context %} +samba-pkg: + pkg.latest: + - pkgs: + - samba + - samba-common + - smbclient \ No newline at end of file diff --git a/states/samba/map.jinja b/states/samba/map.jinja new file mode 100644 index 0000000..9fcb514 --- /dev/null +++ b/states/samba/map.jinja @@ -0,0 +1,6 @@ +{%- import_yaml "samba/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='samba') -%} + +{%- set samba = salt['pillar.get']('samba', default=defaults, merge=True) -%} +{%- set users = salt['pillar.get']('users') -%} \ No newline at end of file diff --git a/states/samba/users.sls b/states/samba/users.sls new file mode 100644 index 0000000..af182ac --- /dev/null +++ b/states/samba/users.sls @@ -0,0 +1,18 @@ +--- +{%- from "samba/map.jinja" import samba with context %} +{%- from "samba/map.jinja" import users with context %} + +{%- for user in users %} +samba-user-{{ user.name }}: +{%- if user.enabled %} + pdbedit.managed: +{%- else %} + pdbedit.absent: +{%- endif %} + - name: {{ user.name }} + - login: {{ user.name }} + - password: "{{ user.password }}" + - password_hashed: False + - require: + - pkg: samba-pkg +{%- endfor %} \ No newline at end of file diff --git a/states/sensu/agent.sls b/states/sensu/agent.sls new file mode 100644 index 0000000..68e131b --- /dev/null +++ b/states/sensu/agent.sls @@ -0,0 +1,76 @@ +--- +{%- from "sensu/map.jinja" import sensu with context %} +sensu-group: + group.present: + - name: sensu + - gid: 900 + +sensu-user: + user.present: + - name: sensu + - uid: 900 + - gid: 900 + - allow_uid_change: True + +sensu-agent-bin: + file.managed: + - name: /usr/sbin/sensu-agent + - source: {{ sensu.fetch_url }}/sensu-agent-{{ salt['grains.get']('kernel')|lower }}-{{ salt['grains.get']('osarch')|lower }} + - skip_verify: True + - user: root + - group: root + - mode: 0755 + - watch_in: + - service: sensu-agent-service + +sensu-agent-service-file: + file.managed: + - name: /etc/systemd/system/sensu-agent.service + - source: salt://sensu/sensu-agent.service + - user: root + - group: root + - mode: 0644 + +sensu-agent-cache-dir: + file.directory: + - name: /var/cache/sensu + - user: sensu + - group: sensu + - mode: 0750 + - recurse: + - user + - group + - require: + - user: sensu-user + - group: sensu-group + +sensu-agent-config-dir: + file.directory: + - name: /etc/sensu + - user: sensu + - group: sensu + - mode: 0750 + - require: + - user: sensu-user + - group: sensu-group + +sensu-agent-config-file: + file.managed: + - name: /etc/sensu/agent.yml + - source: salt://sensu/agent.yml.j2 + - user: sensu + - group: sensu + - mode: 0640 + - template: jinja + - watch_in: + - service: sensu-agent-service + - require: + - file: sensu-agent-config-dir + - file: sensu-agent-cache-dir + +sensu-agent-service: + service.running: + - name: sensu-agent + - enable: True + - require: + - file: sensu-agent-config-file \ No newline at end of file diff --git a/states/sensu/agent.yml.j2 b/states/sensu/agent.yml.j2 new file mode 100644 index 0000000..f5c05b0 --- /dev/null +++ b/states/sensu/agent.yml.j2 @@ -0,0 +1,4 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +--- +{%- from "sensu/map.jinja" import sensu with context %} +{{ sensu.agent.config|yaml(False) }} \ No newline at end of file diff --git a/states/sensu/defaults.yaml b/states/sensu/defaults.yaml new file mode 100644 index 0000000..26b28de --- /dev/null +++ b/states/sensu/defaults.yaml @@ -0,0 +1,28 @@ +--- +sensu: + fetch_url: https://paulbsd-pub.s3.fr-par.scw.cloud/sensu-agent + backend: + enabled: false + config: + url: https://sensu.paulbsd.com + user: "admin" + password: "P@ssw0rd!" + agent: + enabled: false + config: + backend-url: [] + name: {{ salt['grains.get']('fqdn') }} + subscriptions: + - {{ salt['grains.get']('type') }} + - {{ salt['grains.get']('country') }} + - {{ salt['grains.get']('location') }} + - {% filter lower %}{{ salt['grains.get']('os') }}{% endfilter %} + user: "admin" + password: "P@ssw0rd!" + labels: + arch: {% filter lower %}{{ salt['grains.get']('osarch') }}{% endfilter %} + check_load_warning: "3,2,1" + check_load_critical: "5,3,2" + goarch: {% filter lower %}{{ salt['grains.get']('osarch') }}{% endfilter %} + goos: {% filter lower %}{{ salt['grains.get']('kernel') }}{% endfilter %} + kernel: {% filter lower %}{{ salt['grains.get']('kernel') }}{% endfilter %} \ No newline at end of file diff --git a/states/sensu/init.sls b/states/sensu/init.sls new file mode 100644 index 0000000..b4585cc --- /dev/null +++ b/states/sensu/init.sls @@ -0,0 +1,6 @@ +--- +{%- from "sensu/map.jinja" import sensu with context %} +include: +{%- if sensu.agent.enabled %} + - .agent +{%- endif %} \ No newline at end of file diff --git a/states/sensu/map.jinja b/states/sensu/map.jinja new file mode 100644 index 0000000..8809f67 --- /dev/null +++ b/states/sensu/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "sensu/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='sensu') -%} + +{%- set sensu = salt['pillar.get']('sensu', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/sensu/sensu-agent.service b/states/sensu/sensu-agent.service new file mode 100644 index 0000000..bbbce0f --- /dev/null +++ b/states/sensu/sensu-agent.service @@ -0,0 +1,17 @@ +[Unit] +Description=The Sensu Agent process. +After=network-online.target +Wants=network-online.target + +[Service] +Type=simple +User=sensu +Group=sensu +EnvironmentFile=-/etc/default/sensu-agent +EnvironmentFile=-/etc/sysconfig/sensu-agent +LimitNOFILE=65535 +ExecStart=/usr/sbin/sensu-agent start +WorkingDirectory=/ + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/services/init.sls b/states/services/init.sls new file mode 100644 index 0000000..d9cfe5b --- /dev/null +++ b/states/services/init.sls @@ -0,0 +1,23 @@ +--- +include: + - pkg + +{%- if salt['pillar.get']('services:enable') != None -%} +{%- for service in salt['pillar.get']('services:enable') %} +service-{{ service }}: + service.running: + - name: {{ service }} + - enable: True + - require: + - pkg: pkg-install +{%- endfor %} +{%- endif %} + +{%- if salt['pillar.get']('services:disable') != None -%} +{%- for service in salt['pillar.get']('services:disable') %} +service-{{ service }}: + service.dead: + - name: {{ service }} + - enable: False +{%- endfor %} +{%- endif %} \ No newline at end of file diff --git a/states/ssh/config.sls b/states/ssh/config.sls new file mode 100644 index 0000000..4308de0 --- /dev/null +++ b/states/ssh/config.sls @@ -0,0 +1,10 @@ +--- +ssh-sshd-config: + file.managed: + - name: /etc/ssh/sshd_config + - source: salt://ssh/sshd_config.j2 + - template: jinja + - user: root + - mode: 0644 + - watch_in: + - service: ssh-sshd-service \ No newline at end of file diff --git a/states/ssh/defaults.yaml b/states/ssh/defaults.yaml new file mode 100644 index 0000000..2f19c6a --- /dev/null +++ b/states/ssh/defaults.yaml @@ -0,0 +1,15 @@ +--- +ssh: + enabled: true + pkgs: + - 'openssh-server' + config: + LoginGraceTime: 60 + PermitRootLogin: 'no' + MaxAuthTries: 3 + AddressFamily: any + PasswordAuthentication: 'no' + VersionAddendum: none + Subsystem: sftp /usr/lib/openssh/sftp-server + AllowGroups: root + X11Forwarding: 'yes' diff --git a/states/ssh/init.sls b/states/ssh/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/ssh/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/ssh/install.sls b/states/ssh/install.sls new file mode 100644 index 0000000..dca78d7 --- /dev/null +++ b/states/ssh/install.sls @@ -0,0 +1,7 @@ +{%- from "ssh/map.jinja" import ssh with context %} + +{% for pkg in ssh.pkgs %} +ssh-pkg-{{ pkg }}: + pkg.installed: + - name: {{ pkg }} +{% endfor %} diff --git a/states/ssh/map.jinja b/states/ssh/map.jinja new file mode 100644 index 0000000..bc2a2c5 --- /dev/null +++ b/states/ssh/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "ssh/defaults.yaml" as default_settings %} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='ssh') -%} + +{%- set ssh = salt['pillar.get']('ssh', default=defaults, merge=True) %} diff --git a/states/ssh/old.txt b/states/ssh/old.txt new file mode 100644 index 0000000..6dd5ca5 --- /dev/null +++ b/states/ssh/old.txt @@ -0,0 +1,15 @@ +#sshd-config-permitrootlogin: +# file.replace: +# - name: /etc/ssh/sshd_config +# - pattern: "^PermitRootLogin.*$" +# - repl: "PermitRootLogin no" +# - watch_in: +# - service-sshd +# +#sshd-config-passwordauthentifcation: +# file.replace: +# - name: /etc/ssh/sshd_config +# - pattern: "^PasswordAuthentication.*$" +# - repl: "PasswordAuthentication yes" +# - watch_in: +# - service-sshd \ No newline at end of file diff --git a/states/ssh/service.sls b/states/ssh/service.sls new file mode 100644 index 0000000..ca17696 --- /dev/null +++ b/states/ssh/service.sls @@ -0,0 +1,6 @@ +--- +ssh-sshd-service: + service.running: + - name: sshd + - enable: True + - reload: True \ No newline at end of file diff --git a/states/ssh/sshd_config.j2 b/states/ssh/sshd_config.j2 new file mode 100644 index 0000000..47542d0 --- /dev/null +++ b/states/ssh/sshd_config.j2 @@ -0,0 +1,20 @@ +{%- from "ssh/map.jinja" import ssh with context -%} + +{% set net4=[] -%} +{%- for key, value in salt['pillar.get']('net:ipv4_networks').items() -%} +{%- do net4.append( value.ip + "/" + value.mask ) -%} +{%- endfor -%} + +{%- set net6=[] -%} +{%- for key, value in salt['pillar.get']('net:ipv6_networks').items() -%} +{%- do net6.append( value.ip + "/" + value.mask ) -%} +{%- endfor -%} + +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- for key, value in ssh.config.items() %} +{{ key }} {{ value }} +{%- endfor %} +Match Address {{ net4|join(',') }} + PasswordAuthentication yes +Match Address {{ net6|join(',') }} + PasswordAuthentication yes \ No newline at end of file diff --git a/states/states/defaults.yaml b/states/states/defaults.yaml new file mode 100644 index 0000000..a4e0ea2 --- /dev/null +++ b/states/states/defaults.yaml @@ -0,0 +1,2 @@ +--- +states: {} \ No newline at end of file diff --git a/states/states/init.sls b/states/states/init.sls new file mode 100644 index 0000000..d779f10 --- /dev/null +++ b/states/states/init.sls @@ -0,0 +1,13 @@ +--- +{%- from "states/map.jinja" import states with context %} + +{%- if states is defined and states | length > 0 %} +include: +{%- for state, args in states.items() %} +{%- if args != None -%} +{%- for sls in args %} + - {{ sls }} +{%- endfor %} +{%- endif -%} +{%- endfor %} +{%- endif %} \ No newline at end of file diff --git a/states/states/map.jinja b/states/states/map.jinja new file mode 100644 index 0000000..2f5d851 --- /dev/null +++ b/states/states/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "states/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='states') -%} + +{%- set states = salt['pillar.get']('states', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/sublimetext/defaults.yaml b/states/sublimetext/defaults.yaml new file mode 100644 index 0000000..1880af9 --- /dev/null +++ b/states/sublimetext/defaults.yaml @@ -0,0 +1,5 @@ +--- +sublimetext: + fetch_url: https://repo.paulbsd.com/sublime_text + archive_name: sublime_text_3_build_3176_x64.tar.bz2 + install_dir: /opt \ No newline at end of file diff --git a/states/sublimetext/init.sls b/states/sublimetext/init.sls new file mode 100644 index 0000000..0240648 --- /dev/null +++ b/states/sublimetext/init.sls @@ -0,0 +1,37 @@ +--- +{%- from "sublimetext/map.jinja" import sublimetext with context %} +sublime-text-extract-archive: + archive.extracted: + - name: {{ sublimetext.install_dir }} + - source: {{ sublimetext.fetch_url }}/{{ sublimetext.archive_name }} + - skip_verify: True + - user: root + - group: root + - mode: 0755 + - if_missing: {{ sublimetext.install_dir }}/sublime_text_3 + +sublime-text-licence-key: + file.managed: + - name: {{ sublimetext.install_dir }}/sublime_text_3/sublime_text_keys.txt + - source: salt://sublimetext/sublime_text_keys.txt.j2 + - template: jinja + - user: root + - group: root + - mode: 0644 + +sublime-text-desktop-entry: + file.managed: + - name: /usr/share/applications/sublime_text.desktop + - source: salt://sublimetext/sublime_text.desktop.j2 + - template: jinja + - user: root + - group: root + - mode: 0644 + +sublime-text-symlink: + file.symlink: + - name: /usr/bin/sublime_text + - target: {{ sublimetext.install_dir }}/sublime_text_3/sublime_text + - user: root + - group: root + - mode: 0755 \ No newline at end of file diff --git a/states/sublimetext/kernelmap.yaml b/states/sublimetext/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/sublimetext/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/sublimetext/map.jinja b/states/sublimetext/map.jinja new file mode 100644 index 0000000..d5b6e76 --- /dev/null +++ b/states/sublimetext/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "sublimetext/defaults.yaml" as default_settings %} + +{%- import_yaml "sublimetext/kernelmap.yaml" as kernelmap %} +{%- import_yaml "sublimetext/osarchmap.yaml" as osarchmap %} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='sublimetext', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +%} + +{%- set sublimetext = salt['pillar.get']('sublimetext', default=defaults, merge=True) %} \ No newline at end of file diff --git a/states/sublimetext/osarchmap.yaml b/states/sublimetext/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/sublimetext/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/sublimetext/sublime_text.desktop.j2 b/states/sublimetext/sublime_text.desktop.j2 new file mode 100644 index 0000000..e8129f0 --- /dev/null +++ b/states/sublimetext/sublime_text.desktop.j2 @@ -0,0 +1,24 @@ +{%- from "sublimetext/map.jinja" import sublimetext with context -%} +[Desktop Entry] +Version=1.0 +Type=Application +Name=Sublime Text +GenericName=Text Editor +Comment=Sophisticated text editor for code, markup and prose +Exec={{ sublimetext.install_dir }}/sublime_text_3/sublime_text %F +Terminal=false +MimeType=text/plain; +Icon={{ sublimetext.install_dir }}/sublime_text_3/Icon/48x48/sublime-text.png +Categories=TextEditor;Development; +StartupNotify=true +Actions=Window;Document; + +[Desktop Action Window] +Name=New Window +Exec={{ sublimetext.install_dir }}/sublime_text_3/sublime_text -n +OnlyShowIn=Unity; + +[Desktop Action Document] +Name=New File +Exec={{ sublimetext.install_dir }}/sublime_text_3/sublime_text --command new_file +OnlyShowIn=Unity; \ No newline at end of file diff --git a/states/sublimetext/sublime_text_keys.txt.j2 b/states/sublimetext/sublime_text_keys.txt.j2 new file mode 100644 index 0000000..f5a9b73 --- /dev/null +++ b/states/sublimetext/sublime_text_keys.txt.j2 @@ -0,0 +1,29 @@ +-- 3143 +----- BEGIN LICENSE ----- +TwitterInc +200 User License +EA7E-890007 +1D77F72E 390CDD93 4DCBA022 FAF60790 +61AA12C0 A37081C5 D0316412 4584D136 +94D7F7D4 95BC8C1C 527DA828 560BB037 +D1EDDD8C AE7B379F 50C9D69D B35179EF +2FE898C4 8E4277A8 555CE714 E1FB0E43 +D5D52613 C3D12E98 BC49967F 7652EED2 +9D2D2E61 67610860 6D338B72 5CF95C69 +E36B85CC 84991F19 7575D828 470A92AB +------ END LICENSE ------ + +-- 3176 +----- BEGIN LICENSE ----- +sgbteam +Single User License +EA7E-1153259 +8891CBB9 F1513E4F 1A3405C1 A865D53F +115F202E 7B91AB2D 0D2A40ED 352B269B +76E84F0B CD69BFC7 59F2DFEF E267328F +215652A3 E88F9D8F 4C38E3BA 5B2DAAE4 +969624E7 DC9CD4D5 717FB40C 1B9738CF +20B3C4F1 E917B5B3 87C38D9C ACCE7DD8 +5F7EF854 86B9743C FADC04AA FB0DA5C0 +F913BE58 42FEA319 F954EFDD AE881E0B +------ END LICENSE ------ \ No newline at end of file diff --git a/states/sudo/config.sls b/states/sudo/config.sls new file mode 100644 index 0000000..c56e36c --- /dev/null +++ b/states/sudo/config.sls @@ -0,0 +1,11 @@ +--- +{%- from "sudo/map.jinja" import sudo with context %} +sudo-sudoers: + file.managed: + - name: /etc/sudoers + - source: salt://sudo/sudoers.j2 + - user: root + - group: root + - mode: 0660 + - template: jinja + - check_cmd: /usr/sbin/visudo -c -f diff --git a/states/sudo/defaults.yaml b/states/sudo/defaults.yaml new file mode 100644 index 0000000..dbec4cd --- /dev/null +++ b/states/sudo/defaults.yaml @@ -0,0 +1,25 @@ +--- +sudo: + config: + defaults: + - 'env_reset' + - 'mail_badpass' + - 'secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"' + - 'env_keep += "EDITOR"' + - 'always_set_home' + acls: + root: + group: False + host: ALL + who: ALL:ALL + command: ALL + admin: + group: True + host: ALL + who: ALL + command: ALL + sudo: + group: True + host: ALL + who: ALL:ALL + command: ALL \ No newline at end of file diff --git a/states/sudo/init.sls b/states/sudo/init.sls new file mode 100644 index 0000000..b91d8ec --- /dev/null +++ b/states/sudo/init.sls @@ -0,0 +1,4 @@ +--- +include: + - .install + - .config \ No newline at end of file diff --git a/states/sudo/install.sls b/states/sudo/install.sls new file mode 100644 index 0000000..4ccb116 --- /dev/null +++ b/states/sudo/install.sls @@ -0,0 +1,5 @@ +--- +{%- from "sudo/map.jinja" import sudo with context %} +sudo-pkg: + pkg.installed: + - name: sudo \ No newline at end of file diff --git a/states/sudo/map.jinja b/states/sudo/map.jinja new file mode 100644 index 0000000..d16be86 --- /dev/null +++ b/states/sudo/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "sudo/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='sudo') -%} + +{%- set sudo = salt['pillar.get']('sudo', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/sudo/sudoers.j2 b/states/sudo/sudoers.j2 new file mode 100644 index 0000000..5743b34 --- /dev/null +++ b/states/sudo/sudoers.j2 @@ -0,0 +1,16 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "sudo/map.jinja" import sudo with context %} + +{%- if sudo.config.defaults is defined -%} +{%- for default in sudo.config.defaults %} +Defaults {{ default }} +{%- endfor %} +{%- endif %} + +{%- if sudo.config.acls is defined -%} +{%- for key, acl in sudo.config.acls.items() %} +{% if acl.group %}%{% endif %}{{ key }} {{ acl.host|default('ALL') }}=({{ acl.who|default('ALL') }}) {{ acl.command|default('ALL') }} +{%- endfor %} +{%- endif %} + +#includedir /etc/sudoers.d diff --git a/states/syncthing/config.sls b/states/syncthing/config.sls new file mode 100644 index 0000000..00917d9 --- /dev/null +++ b/states/syncthing/config.sls @@ -0,0 +1,8 @@ +--- +{%- from "syncthing/map.jinja" import syncthing with context %} +syncthing-config: + syncthing.config: + - name: syncthing-config + - url: {{ syncthing.url }} + - cfg: {{ syncthing.config }} + - verify: {{ syncthing.verify }} \ No newline at end of file diff --git a/states/syncthing/config_old.sls b/states/syncthing/config_old.sls new file mode 100644 index 0000000..ebb16e5 --- /dev/null +++ b/states/syncthing/config_old.sls @@ -0,0 +1,24 @@ +--- +{%- from "syncthing/map.jinja" import syncthing with context %} +{%- set configdir=salt['user.info'](syncthing.user)['home'] + '/.config/syncthing' %} +#syncthing-config-dir: +# file.directory: +# - name: {{ configdir }} +# - makedirs: True + # - watch_in: + # - service: syncthing-service + +#syncthing-config-file: +# file.managed: +# - name: {{ configdir }}/config.xml +# - source: salt://syncthing/config.xml.j2 +# - user: root +# - group: root +# - template: jinja +# - watch_in: +# - service: syncthing-service + +{%- set config = salt['http.query'](url="http://localhost:8384/rest/system/config", header_dict={"X-API-Key":syncthing.apikey},decode=True, decode_type='json') %} +#{{ config['dict']['gui'] }} + +{%- do salt['http.query'](method='POST', url="http://localhost:8384/rest/system/config", header_dict={"X-API-Key":syncthing.apikey}, decode=True, decode_type='json', data=json.dumps(syncthing.config)) %} \ No newline at end of file diff --git a/states/syncthing/defaults.yaml b/states/syncthing/defaults.yaml new file mode 100644 index 0000000..726c186 --- /dev/null +++ b/states/syncthing/defaults.yaml @@ -0,0 +1,17 @@ +--- +syncthing: + enabled: true + install_dir: "/usr/local/apps" + release_dir: "/usr/local/apps/releases" + mirror: "https://github.com/syncthing/syncthing/releases/download" + version: "1.3.4" + os: "linux" + arch: "amd64" + user: "root" + url: "http://localhost:8384" + verify: false + config: + gui: + address: "0.0.0.0:8384" + user: "paul" + password: "$2a$10$NeZ3cfyOgZcdMGy9ixB7LOAP6z8tCOVjico6ZGLoK2QIQy734qPw." \ No newline at end of file diff --git a/states/syncthing/init.sls b/states/syncthing/init.sls new file mode 100644 index 0000000..cbeee7c --- /dev/null +++ b/states/syncthing/init.sls @@ -0,0 +1,6 @@ +--- +{%- from "syncthing/map.jinja" import syncthing with context %} +include: + - .install + - .service + - .config \ No newline at end of file diff --git a/states/syncthing/install.sls b/states/syncthing/install.sls new file mode 100644 index 0000000..42a66ec --- /dev/null +++ b/states/syncthing/install.sls @@ -0,0 +1,19 @@ +--- +{%- from "syncthing/map.jinja" import syncthing with context %} +syncthing-archive-extract: + archive.extracted: + - name: {{ syncthing.release_dir }} + - source: {{ syncthing.mirror }}/v{{ syncthing.version }}/syncthing-{{ syncthing.os }}-{{ syncthing.arch }}-v{{ syncthing.version }}.tar.gz + - enforce_toplevel: False + - skip_verify: True + - archive_format: tar + - if_missing: {{ syncthing.release_dir }}/syncthing-{{ syncthing.os }}-{{ syncthing.arch }}-v{{ syncthing.version }} + - watch_in: + - service: syncthing-service + +syncthing-bin-symlink: + file.symlink: + - name: {{ syncthing.install_dir }}/syncthing + - target: {{ syncthing.release_dir }}/syncthing-{{ syncthing.os }}-{{ syncthing.arch }}-v{{ syncthing.version }} + - watch_in: + - service: syncthing-service \ No newline at end of file diff --git a/states/syncthing/kernelmap.yaml b/states/syncthing/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/syncthing/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/syncthing/map.jinja b/states/syncthing/map.jinja new file mode 100644 index 0000000..64b8631 --- /dev/null +++ b/states/syncthing/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "syncthing/defaults.yaml" as default_settings -%} + +{%- import_yaml "syncthing/kernelmap.yaml" as kernelmap %} +{%- import_yaml "syncthing/osarchmap.yaml" as osarchmap %} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='syncthing', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set syncthing = salt['pillar.get']('syncthing', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/syncthing/osarchmap.yaml b/states/syncthing/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/syncthing/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/syncthing/service.sls b/states/syncthing/service.sls new file mode 100644 index 0000000..b31c567 --- /dev/null +++ b/states/syncthing/service.sls @@ -0,0 +1,16 @@ +--- +{%- from "syncthing/map.jinja" import syncthing with context %} +syncthing-service-file: + file.managed: + - name: /etc/systemd/system/syncthing@.service + - source: salt://syncthing/syncthing.service.j2 + - user: root + - group: root + - template: jinja + - watch_in: + - service: syncthing-service + +syncthing-service: + service.running: + - name: syncthing@{{ syncthing.user }} + - enable: True \ No newline at end of file diff --git a/states/syncthing/syncthing.service.j2 b/states/syncthing/syncthing.service.j2 new file mode 100644 index 0000000..aeb8be7 --- /dev/null +++ b/states/syncthing/syncthing.service.j2 @@ -0,0 +1,15 @@ +{%- from "syncthing/map.jinja" import syncthing with context -%} +[Unit] +Description=Syncthing - Open Source Continuous File Synchronization for %I +Documentation=man:syncthing(1) +After=network.target + +[Service] +User=%i +ExecStart={{ syncthing.install_dir }}/syncthing/syncthing -no-browser -no-restart -logflags=0 +Restart=on-failure +SuccessExitStatus=3 4 +RestartForceExitStatus=3 4 + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/telegraf/config.sls b/states/telegraf/config.sls new file mode 100644 index 0000000..9155a97 --- /dev/null +++ b/states/telegraf/config.sls @@ -0,0 +1,18 @@ +--- +{%- from "telegraf/map.jinja" import telegraf with context %} +telegraf-config-dir: + file.directory: + - name: /etc/telegraf + - watch_in: + - service: telegraf-service + +telegraf-config-file: + file.managed: + - name: /etc/telegraf/telegraf.conf + - source: salt://telegraf/telegraf.conf.j2 + - user: root + - group: root + - mode: 600 + - template: jinja + - watch_in: + - service: telegraf-service \ No newline at end of file diff --git a/states/telegraf/defaults.yaml b/states/telegraf/defaults.yaml new file mode 100644 index 0000000..af28594 --- /dev/null +++ b/states/telegraf/defaults.yaml @@ -0,0 +1,75 @@ +--- +telegraf: + enabled: true + release_dir: /usr/local/apps/releases + install_dir: /usr/local/apps + mirror: https://dl.influxdata.com/telegraf/releases + version: 1.13.3 + influxdb_urls: + - '"http://localhost:8086"' + influxdb_username: username + influxdb_password: password + influxdb_database: telegraf + os: linux + arch: amd64 + config: + outputs: + inputs: + cpu: + name: "cpu" + params: + percpu: true + totalcpu: true + collect_cpu_time: false + report_active: false + disk: + name: "disk" + params: + ignore_fs: + - '"tmpfs"' + - '"devtmpfs"' + - '"devfs"' + - '"iso9660"' + - '"overlay"' + - '"aufs"' + - '"squashfs"' + diskio: + name: "diskio" + kernel: + name: "kernel" + kernel_vmstat: + name: "kernel_vmstat" + mem: + name: "mem" + net: + name: "net" + netstat: + name: "netstat" + nginx: + name: "nginx" + params: + urls: + - '"http://localhost/status"' + response_timeout: "5s" + ntpq: + name: "ntpq" + params: + dns_lookup: true + postfix: + name: "postfix" + processes: + name: "processes" + swap: + name: "swap" + sensors: + name: "sensors" + smart: + name: "smart" + system: + name: "system" + systemd_units: + name: "systemd_units" + syslog: + name: "syslog" + params: + server: "udp://:6514" \ No newline at end of file diff --git a/states/telegraf/init.sls b/states/telegraf/init.sls new file mode 100644 index 0000000..63261f2 --- /dev/null +++ b/states/telegraf/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/telegraf/install.sls b/states/telegraf/install.sls new file mode 100644 index 0000000..4621ea3 --- /dev/null +++ b/states/telegraf/install.sls @@ -0,0 +1,26 @@ +--- +{%- from "telegraf/map.jinja" import telegraf with context %} +telegraf-archive-extract: + archive.extracted: + - name: {{ telegraf.release_dir }}/telegraf-{{ telegraf.version }} + - source: {{ telegraf.mirror }}/telegraf-{{ telegraf.version }}_{{ salt['grains.get']('kernel')|lower }}_{{ salt['grains.get']('osarch') }}.tar.gz + - enforce_toplevel: False + - options: --transform 's|^\./telegraf|/|g' --exclude './telegraf/etc' --exclude './telegraf/usr/lib' --exclude './telegraf/var' --strip 2 + - skip_verify: True + - archive_format: tar + - if_missing: {{ telegraf.release_dir }}/telegraf-{{ telegraf.version }}/telegraf + +telegraf-bin-symlink: + file.symlink: + - name: /usr/local/bin/telegraf + - target: {{ telegraf.release_dir }}/telegraf-{{ telegraf.version }}/telegraf + +telegraf-defaults-file: + file.managed: + - name: /etc/default/telegraf + - source: salt://telegraf/telegraf_default.j2 + - user: root + - group: root + - template: jinja + - watch_in: + - service: telegraf-service \ No newline at end of file diff --git a/states/telegraf/kernelmap.yaml b/states/telegraf/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/telegraf/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/telegraf/map.jinja b/states/telegraf/map.jinja new file mode 100644 index 0000000..769366f --- /dev/null +++ b/states/telegraf/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "telegraf/defaults.yaml" as default_settings -%} + +{%- import_yaml "telegraf/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "telegraf/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='telegraf', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set telegraf = salt['pillar.get']('telegraf', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/telegraf/osarchmap.yaml b/states/telegraf/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/telegraf/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/telegraf/service.sls b/states/telegraf/service.sls new file mode 100644 index 0000000..48ec4bf --- /dev/null +++ b/states/telegraf/service.sls @@ -0,0 +1,16 @@ +--- +{%- from "telegraf/map.jinja" import telegraf with context %} +telegraf-service-file: + file.managed: + - name: /etc/systemd/system/telegraf.service + - source: salt://telegraf/telegraf.service.j2 + - user: root + - group: root + - template: jinja + - watch_in: + - service: telegraf-service + +telegraf-service: + service.running: + - name: telegraf + - enable: True \ No newline at end of file diff --git a/states/telegraf/telegraf.conf.j2 b/states/telegraf/telegraf.conf.j2 new file mode 100644 index 0000000..a7cdd39 --- /dev/null +++ b/states/telegraf/telegraf.conf.j2 @@ -0,0 +1,30 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "telegraf/map.jinja" import telegraf with context %} + +[global_tags] +[agent] + interval = "1m" + round_interval = true + metric_batch_size = 1000 + metric_buffer_limit = 10000 + collection_jitter = "0s" + flush_interval = "10s" + flush_jitter = "0s" + precision = "" + hostname = "" + omit_hostname = false + +[[outputs.influxdb]] + urls = [{{ telegraf.influxdb_urls|join(",") }}] + username = "{{ telegraf.influxdb_username }}" + password = "{{ telegraf.influxdb_password }}" + database = "{{ telegraf.influxdb_database }}" + +{% for name, value in telegraf.config.inputs.items() %} +[[inputs.{{ name }}]] +{%- if value['params'] is defined %} +{%- for paramname, paramvalue in value['params'].items() %} + {{ paramname }} = {% if paramvalue is sameas True or paramvalue is sameas False %}{{ paramvalue|string|lower }}{% elif paramvalue is iterable and paramvalue is not string %}[{{ paramvalue|join(",") }}]{% else %}"{{ paramvalue }}"{% endif %} +{%- endfor %} +{%- endif %} +{% endfor %} \ No newline at end of file diff --git a/states/telegraf/telegraf.service.j2 b/states/telegraf/telegraf.service.j2 new file mode 100644 index 0000000..8904723 --- /dev/null +++ b/states/telegraf/telegraf.service.j2 @@ -0,0 +1,17 @@ +{%- from "telegraf/map.jinja" import telegraf with context -%} +[Unit] +Description=The plugin-driven server agent for reporting metrics into InfluxDB +Documentation=https://github.com/influxdata/telegraf +After=network.target + +[Service] +EnvironmentFile=-/etc/default/telegraf +#User=telegraf +ExecStart=/usr/local/bin/telegraf --config /etc/telegraf/telegraf.conf +ExecReload=/bin/kill -HUP $MAINPID +Restart=on-failure +RestartForceExitStatus=SIGPIPE +KillMode=control-group + +[Install] +WantedBy=multi-user.target \ No newline at end of file diff --git a/states/telegraf/telegraf_default.j2 b/states/telegraf/telegraf_default.j2 new file mode 100644 index 0000000..cafac8e --- /dev/null +++ b/states/telegraf/telegraf_default.j2 @@ -0,0 +1,3 @@ +#INFLUX_CONFIG="http://host:9999/api/v2/telegrafs/0401c1afe319d000" +#INFLUX_TOKEN="test" +INFLUX_CONFIG=/etc/telegraf/telegraf.conf \ No newline at end of file diff --git a/states/telegram/defaults.yaml b/states/telegram/defaults.yaml new file mode 100644 index 0000000..d6b68a2 --- /dev/null +++ b/states/telegram/defaults.yaml @@ -0,0 +1,7 @@ +--- +telegram: + enabled: true + mirror: "https://github.com/telegramdesktop/tdesktop/releases/download" + install_dir: "/usr/local/apps" + dest_path: "/usr/local/apps/telegram-" + version: "2.1.0" \ No newline at end of file diff --git a/states/telegram/init.sls b/states/telegram/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/telegram/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/telegram/install.sls b/states/telegram/install.sls new file mode 100644 index 0000000..0f1fb3a --- /dev/null +++ b/states/telegram/install.sls @@ -0,0 +1,33 @@ +--- +{%- from "telegram/map.jinja" import telegram with context %} +telegram-archive-extract: + archive.extracted: + - name: {{ telegram.dest_path }}{{ telegram.version }} + - source: {{ telegram.mirror }}/v{{ telegram.version }}/tsetup.{{ telegram.version }}.tar.xz + - skip_verify: True + - archive_format: tar + - enforce_toplevel: False + - options: --transform 's/Telegram/telegram/g' --transform 's/Updater/updater/g' --strip 1 + - keep: True + - if_missing: {{ telegram.dest_path }}{{ telegram.version }}/telegram + +telegram-symlink: + file.symlink: + - name: {{ telegram.install_dir }}/telegram + - target: {{ telegram.dest_path }}{{ telegram.version }} + - force: True + +telegram-bin-symlink: + file.symlink: + - name: /usr/bin/telegram + - target: {{ telegram.dest_path }}{{ telegram.version }}/telegram + - force: True + +telegram-shortcut: + file.managed: + - name: /usr/share/applications/telegram.desktop + - source: salt://telegram/telegram.desktop.j2 + - user: root + - group: root + - mode: 644 + - template: jinja \ No newline at end of file diff --git a/states/telegram/map.jinja b/states/telegram/map.jinja new file mode 100644 index 0000000..45b7c02 --- /dev/null +++ b/states/telegram/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "telegram/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='telegram') -%} + +{%- set telegram = salt['pillar.get']('telegram', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/telegram/telegram.desktop.j2 b/states/telegram/telegram.desktop.j2 new file mode 100644 index 0000000..02b380c --- /dev/null +++ b/states/telegram/telegram.desktop.j2 @@ -0,0 +1,15 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "telegram/map.jinja" import telegram with context %} +[Desktop Entry] +Encoding=UTF-8 +Version=1.0 +Name=Telegram Desktop +Comment=Official desktop version of Telegram messaging app +Exec=/usr/bin/telegram -- %u +Icon=telegram +Terminal=false +StartupWMClass=Telegram +Type=Application +Categories=Network; +MimeType=x-scheme-handler/tg; +X-Desktop-File-Install-Version=0.22 \ No newline at end of file diff --git a/states/tests/repo.sls b/states/tests/repo.sls new file mode 100644 index 0000000..e33ffcd --- /dev/null +++ b/states/tests/repo.sls @@ -0,0 +1,22 @@ +--- +#pkg-repo-bareos: + # pkgrepo.absent: + # - humanname: Bareos + # - name: deb http://download.bareos.org/bareos/release/17.2/xUbuntu_16.04 / + # - gpgcheck: 1 + # - file: /etc/apt/sources.list.d/bareos.list + # - clean_file: True + # - key_url: http://download.bareos.org/bareos/release/17.2/xUbuntu_16.04/Release.key + # - refresh_db: True + +pkg-repo-test-absent: + pkgrepo.absent: + - name: deb http://repository.spotify.com stable non-free + +#pkg-bareos: +# pkg.purged: +# - pkgs: +# - bareos-common +# - bareos-filedaemon +# - bareos-traymonitor +# - bareos-bconsole \ No newline at end of file diff --git a/states/tests/tmp.sls b/states/tests/tmp.sls new file mode 100644 index 0000000..57510be --- /dev/null +++ b/states/tests/tmp.sls @@ -0,0 +1,10 @@ +--- +test1: + file.managed: + - name: /tmp/test + +test2: + file.replace: + - name: /tmp/test + - pattern: "^444" + - repl: "5555" \ No newline at end of file diff --git a/states/time/config.sls b/states/time/config.sls new file mode 100644 index 0000000..aaab6d4 --- /dev/null +++ b/states/time/config.sls @@ -0,0 +1,12 @@ +--- +{%- from "time/map.jinja" import time with context %} +time-ntp-config-file: + file.managed: + - name: /etc/ntp.conf + - source: salt://time/ntp.conf.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: ntp-service \ No newline at end of file diff --git a/states/time/defaults.yaml b/states/time/defaults.yaml new file mode 100644 index 0000000..8a803d5 --- /dev/null +++ b/states/time/defaults.yaml @@ -0,0 +1,4 @@ +--- +time: + zone: Europe/Paris + server: fr.pool.ntp.org \ No newline at end of file diff --git a/states/time/init.sls b/states/time/init.sls new file mode 100644 index 0000000..cddde2e --- /dev/null +++ b/states/time/init.sls @@ -0,0 +1,8 @@ +--- +include: + - .timezone + - .config + - .service +{%- if not salt['grains.get']('container') %} + - .timeupdate +{%- endif %} \ No newline at end of file diff --git a/states/time/map.jinja b/states/time/map.jinja new file mode 100644 index 0000000..fb6223d --- /dev/null +++ b/states/time/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "time/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='time') -%} + +{%- set time = salt['pillar.get']('time', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/time/ntp.conf.j2 b/states/time/ntp.conf.j2 new file mode 100644 index 0000000..cf8ae1b --- /dev/null +++ b/states/time/ntp.conf.j2 @@ -0,0 +1,9 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "time/map.jinja" import time with context %} +disable monitor +restrict -4 default kod nomodify notrap nopeer noquery +restrict -6 default kod nomodify notrap nopeer noquery +restrict 127.0.0.1 +restrict ::1 +server {{ time.server }} iburst +driftfile /var/lib/ntp/drift \ No newline at end of file diff --git a/states/time/service.sls b/states/time/service.sls new file mode 100644 index 0000000..5731159 --- /dev/null +++ b/states/time/service.sls @@ -0,0 +1,6 @@ +--- +{%- from "time/map.jinja" import time with context %} +ntp-service: + service.running: + - name: ntp + - enable: True \ No newline at end of file diff --git a/states/time/timeupdate.sls b/states/time/timeupdate.sls new file mode 100644 index 0000000..abfff35 --- /dev/null +++ b/states/time/timeupdate.sls @@ -0,0 +1,5 @@ +--- +{%- from "time/map.jinja" import time with context %} +time-ntp-update: + cmd.run: + - name: ntpdate -u {{ time.server }} \ No newline at end of file diff --git a/states/time/timezone.sls b/states/time/timezone.sls new file mode 100644 index 0000000..96b9dc5 --- /dev/null +++ b/states/time/timezone.sls @@ -0,0 +1,6 @@ +--- +{%- from "time/map.jinja" import time with context %} +time-timezone: + timezone.system: + - name: {{ time.zone }} + - utc: False \ No newline at end of file diff --git a/states/tmux/defaults.yaml b/states/tmux/defaults.yaml new file mode 100644 index 0000000..4ab9ef6 --- /dev/null +++ b/states/tmux/defaults.yaml @@ -0,0 +1,13 @@ +--- +tmux: + enabled: true + config: + binds: + - "v": "split-window" + - "h": "split-window -h" + sets: + - "status-bg": "blue" + - "pane-active-border-style": "fg=blue" + - "pane-border-style": "fg=blue" + - "default-terminal": "screen-256color" + - "history-limit": "1000000" \ No newline at end of file diff --git a/states/tmux/init.sls b/states/tmux/init.sls new file mode 100644 index 0000000..e2e3c83 --- /dev/null +++ b/states/tmux/init.sls @@ -0,0 +1,15 @@ +--- +{%- from "tmux/map.jinja" import tmux with context %} +tmux-pkg: + pkg.latest: + - name: tmux + +tmux-config: + file.managed: + - name: /etc/tmux.conf + - source: salt://tmux/tmux.conf.j2 + - user: root + - group: root + - template: jinja + - require: + - pkg: tmux-pkg \ No newline at end of file diff --git a/states/tmux/map.jinja b/states/tmux/map.jinja new file mode 100644 index 0000000..7b8e4ca --- /dev/null +++ b/states/tmux/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "tmux/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='tmux') -%} + +{%- set tmux = salt['pillar.get']('tmux', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/tmux/tmux.conf.j2 b/states/tmux/tmux.conf.j2 new file mode 100644 index 0000000..affe96d --- /dev/null +++ b/states/tmux/tmux.conf.j2 @@ -0,0 +1,15 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "tmux/map.jinja" import tmux with context %} + +{%- for ent in tmux.config.binds %} +{%- for key,value in ent.items() %} +bind {{ key }} {{ value }} +{%- endfor %} +{%- endfor %} + +{%- for ent in tmux.config.sets %} +{%- for key,value in ent.items() %} +set -g {{ key }} {{ value }} +{%- endfor %} +{%- endfor %} \ No newline at end of file diff --git a/states/top.sls b/states/top.sls new file mode 100644 index 0000000..670c0f4 --- /dev/null +++ b/states/top.sls @@ -0,0 +1,7 @@ +--- +base: + '*': + - provision +{%- if salt['pillar.get']('specs:' + salt['grains.get']('fqdn')) is defined %} + - states +{%- endif %} diff --git a/states/tor/defaults.yaml b/states/tor/defaults.yaml new file mode 100644 index 0000000..63b94df --- /dev/null +++ b/states/tor/defaults.yaml @@ -0,0 +1,10 @@ +--- +tor: + enabled: false + config: + client_host: '0.0.0.0' + client_port: '8081' + exit_port: '4443' + exit_policy: 'reject *:*' + nickname: 'anonymous' + syscontact: 'anonymous@example.com' \ No newline at end of file diff --git a/states/tor/init.sls b/states/tor/init.sls new file mode 100644 index 0000000..9f204f7 --- /dev/null +++ b/states/tor/init.sls @@ -0,0 +1,29 @@ +--- +{%- from "tor/map.jinja" import tor with context %} +tor-pkg: + pkg.latest: + - name: tor + +tor-config: + file.managed: + - name: /etc/tor/torrc + - source: salt://tor/torrc.j2 + - user: root + - group: root + - mode: 0660 + - template: jinja + - watch_in: + - service: tor-service + - require: + - pkg: tor-pkg + +tor-service: +{%- if tor.enabled %} + service.running: +{%- else %} + service.dead: +{%- endif %} + - name: tor + - enable: {{ tor.enabled }} + - require: + - pkg: tor-pkg \ No newline at end of file diff --git a/states/tor/map.jinja b/states/tor/map.jinja new file mode 100644 index 0000000..6bdf3ae --- /dev/null +++ b/states/tor/map.jinja @@ -0,0 +1,11 @@ +{%- import_yaml "tor/defaults.yaml" as defaults -%} + +{%- set tor = salt['pillar.get']( + 'tor', + default=defaults.tor, + merge=True + ) +-%} + +{%- set nickname = salt['pillar.get']('nickname',default='anonymous') -%} +{%- set syscontact = salt['pillar.get']('syscontact',default='anonymous@example.com') -%} \ No newline at end of file diff --git a/states/tor/torrc.j2 b/states/tor/torrc.j2 new file mode 100644 index 0000000..2ab5a00 --- /dev/null +++ b/states/tor/torrc.j2 @@ -0,0 +1,8 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{% from "tor/map.jinja" import tor with context %} + +ORPort {{ tor.config.exit_port }} +SocksPort {{ tor.config.client_host }}:{{ tor.config.client_port }} +ExitPolicy {{ tor.config.exit_policy }} +Nickname {{ tor.config.nickname }} +ContactInfo {{ tor.config.syscontact }} \ No newline at end of file diff --git a/states/transmission/config.sls b/states/transmission/config.sls new file mode 100644 index 0000000..d5a2953 --- /dev/null +++ b/states/transmission/config.sls @@ -0,0 +1,34 @@ +--- +{%- from "transmission/map.jinja" import transmission with context %} + +transmission-dl-dir: + file.directory: + - name: {{ transmission['config']['download-dir'] }} + - makedirs: True + +transmission-settings-directory: + file.directory: + - name: /etc/transmission-daemon + - user: root + - group: debian-transmission + - mode: 770 + +transmission-settings-master: + file.managed: + - name: /etc/transmission-daemon/settings.json.master + - source: salt://transmission/settings.json.j2 + - user: root + - group: debian-transmission + - mode: 0660 + - template: jinja + - watch_in: + - service: transmission-service + +transmission-settings: + file.managed: + - name: /etc/transmission-daemon/settings.json + - source: salt://transmission/settings.json.j2 + - user: root + - group: debian-transmission + - mode: 0660 + - template: jinja \ No newline at end of file diff --git a/states/transmission/defaults.yaml b/states/transmission/defaults.yaml new file mode 100644 index 0000000..0555f59 --- /dev/null +++ b/states/transmission/defaults.yaml @@ -0,0 +1,73 @@ +--- +transmission: + config: + alt-speed-down: 50 + alt-speed-enabled: false + alt-speed-time-begin: 540 + alt-speed-time-day: 127 + alt-speed-time-enabled: false + alt-speed-time-end: 1020 + alt-speed-up: 50 + bind-address-ipv4: "0.0.0.0" + bind-address-ipv6: "::" + blocklist-enabled: true + blocklist-url: "http://john.bitsurge.net/public/biglist.p2p.gz" + cache-size-mb: 4 + dht-enabled: true + download-dir: "/mnt/PAULBSDPOOL/downloads" + download-limit: 100 + download-limit-enabled: 0 + download-queue-enabled: true + download-queue-size: 5 + downloads-dir: "/tmp" + encryption: 2 + idle-seeding-limit: 30 + idle-seeding-limit-enabled: false + incomplete-dir: "/mnt/PAULBSDPOOL/downloads/incomplete" + incomplete-dir-enabled: false + lpd-enabled: true + max-peers-global: 200 + message-level: 1 + peer-congestion-algorithm: "" + peer-id-ttl-hours: 6 + peer-limit-global: 200 + peer-limit-per-torrent: 80 + peer-port: 51413 + peer-port-random-high: 65535 + peer-port-random-on-start: false + peer-socket-tos: "default" + peerport-random-low: 49152 + pex-enabled: true + port-forwarding-enabled: false + preallocation: 1 + prefetch-enabled: 1 + queue-stalled-enabled: true + queue-stalled-minutes: 30 + ratio-limit: 2 + ratio-limit-enabled: false + rename-partial-files: true + rpc-authentication-required: true + rpc-bind-address: "127.0.0.1" + rpc-enabled: true + rpc-password: "{b21d09fef2131b59bb2179668106b06757a3f9679R1Am2k7" + rpc-port: 9091 + rpc-url: "/transmission/" + rpc-username: "unknown" + rpc-whitelist: "*" + rpc-whitelist-enabled: true + scrape-paused-torrents-enabled: true + script-torrent-done-enabled: false + script-torrent-done-filename: "" + seed-queue-enabled: false + seed-queue-size: 10 + speed-limit-down: 100 + speed-limit-down-enabled: false + speed-limit-up: 100 + speed-limit-up-enabled: false + start-added-torrents: true + trash-original-torrent-files: false + umask: 2 + upload-limit: 100 + upload-limit-enabled: 0 + upload-slots-per-torrent: 14 + utp-enabled: true \ No newline at end of file diff --git a/states/transmission/init.sls b/states/transmission/init.sls new file mode 100644 index 0000000..b2edc09 --- /dev/null +++ b/states/transmission/init.sls @@ -0,0 +1,7 @@ +--- +{%- from "transmission/map.jinja" import transmission with context %} + +include: + - .install + - .config + - .service \ No newline at end of file diff --git a/states/transmission/install.sls b/states/transmission/install.sls new file mode 100644 index 0000000..d28cbe9 --- /dev/null +++ b/states/transmission/install.sls @@ -0,0 +1,9 @@ +--- +{%- from "transmission/map.jinja" import transmission with context %} + +transmission-install-server: + pkg.latest: + - pkgs: + - transmission-cli + - transmission-common + - transmission-daemon \ No newline at end of file diff --git a/states/transmission/map.jinja b/states/transmission/map.jinja new file mode 100644 index 0000000..f4955bc --- /dev/null +++ b/states/transmission/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "transmission/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='transmission') -%} + +{%- set transmission = salt['pillar.get']('transmission', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/transmission/service.sls b/states/transmission/service.sls new file mode 100644 index 0000000..41bdef4 --- /dev/null +++ b/states/transmission/service.sls @@ -0,0 +1,5 @@ +--- +transmission-service: + service.running: + - name: transmission-daemon + - enable: True \ No newline at end of file diff --git a/states/transmission/settings.json.j2 b/states/transmission/settings.json.j2 new file mode 100644 index 0000000..0c9b629 --- /dev/null +++ b/states/transmission/settings.json.j2 @@ -0,0 +1,2 @@ +{%- from "transmission/map.jinja" import transmission with context -%} +{{ transmission.config|json }} \ No newline at end of file diff --git a/states/users/defaults.yaml b/states/users/defaults.yaml new file mode 100644 index 0000000..9067c8e --- /dev/null +++ b/states/users/defaults.yaml @@ -0,0 +1,3 @@ +--- +users: [] +groups: [] \ No newline at end of file diff --git a/states/users/groups.sls b/states/users/groups.sls new file mode 100644 index 0000000..38e9e74 --- /dev/null +++ b/states/users/groups.sls @@ -0,0 +1,12 @@ +--- +{%- from "users/map.jinja" import groups with context -%} + +{%- for group in groups %} +group-{{ group.name }}: +{%- if group.enabled %} + group.present: +{%- else %} + group.absent: +{%- endif %} + - name: {{ group.name }} +{% endfor %} diff --git a/states/users/init.sls b/states/users/init.sls new file mode 100644 index 0000000..dbdf2af --- /dev/null +++ b/states/users/init.sls @@ -0,0 +1,5 @@ +--- +include: + - .groups + - .users + - .ssh-keys diff --git a/states/users/map.jinja b/states/users/map.jinja new file mode 100644 index 0000000..4022504 --- /dev/null +++ b/states/users/map.jinja @@ -0,0 +1,6 @@ +{%- import_yaml "users/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='users') -%} + +{%- set users = salt['pillar.get']('users', default=defaults, merge=True) -%} +{%- set groups = salt['pillar.get']('groups', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/users/ssh-keys.sls b/states/users/ssh-keys.sls new file mode 100644 index 0000000..d50c520 --- /dev/null +++ b/states/users/ssh-keys.sls @@ -0,0 +1,16 @@ +--- +{%- from "users/map.jinja" import users with context -%} + +{%- for user in users %} +{%- if user.sshkeys is defined %} +{%- for key in user.sshkeys %} +ssh-key-{{ user.name }}-{{ key.name }}: + ssh_auth.{{ key.state }}: + - user: {{ user.name }} + - names: + - {{ key.value }} + - require: + - user: user-{{ user.name }} +{% endfor %} +{%- endif -%} +{% endfor %} diff --git a/states/users/users.sls b/states/users/users.sls new file mode 100644 index 0000000..ae9ad2f --- /dev/null +++ b/states/users/users.sls @@ -0,0 +1,31 @@ +--- +{%- from "users/map.jinja" import users with context -%} + +{%- for user in users %} +{%- if not user.enabled %} +user-{{ user.name }}: + user.absent: + - name: {{ user.name }} +{%- endif %} +{%- endfor %} + +{%- for user in users %} +{%- if user.enabled %} +user-{{ user.name }}: + user.present: + - name: {{ user.name }} + - fullname: {{ user.fullname }} + - shell: {{ user.shell }} + - home: {{ user.home }} + - password: {{ user.password }} + - hash_password: True + - gid: {{ user.gid }} + - allow_gid_change: True + {%- if user.optional_groups is defined %} + - optional_groups: + {%- for opt_group in user.optional_groups %} + - {{ opt_group }} + {%- endfor %} + {%- endif %} + {%- endif %} +{% endfor %} diff --git a/states/vim/config.sls b/states/vim/config.sls new file mode 100644 index 0000000..75fc6cf --- /dev/null +++ b/states/vim/config.sls @@ -0,0 +1,12 @@ +--- +{%- from "vim/map.jinja" import vim with context %} +vim-vimrc-local: + file.managed: + - name: /etc/vim/vimrc.local + - source: salt://vim/vimrc.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - require: + - pkg: vim-pkg \ No newline at end of file diff --git a/states/vim/defaults.yaml b/states/vim/defaults.yaml new file mode 100644 index 0000000..1aa72c3 --- /dev/null +++ b/states/vim/defaults.yaml @@ -0,0 +1,61 @@ +--- +vim: + enabled: true + vimfiles_dir: /usr/share/vim/vimfiles + colors_dir: /usr/share/vim/vimfiles/colors + plugins_dir: /usr/share/vim/vimfiles/pack/plugins/start + config: + options: {} + lets: + defaults_vim: + name: "g:skip_defaults_vim" + value: 1 + sets: + nocompatible: + name: nocompatible + mouse: + name: mouse= + ttymouse: + name: ttymouse= + number: + name: number + viminfo: + name: viminfo + value: "'50,<1000,s100,h" + backspace: + name: backspace + value: indent,eol,start + autoindent: + name: autoindent + nocindent: + name: nocindent + ic: + name: ic + incsearch: + name: incsearch + tabstop: + name: tabstop + value: 2 + shiftwidth: + name: shiftwidth + value: 2 + softtabstop: + name: softtabstop + value: 2 + expandtab: + name: expandtab + pastetoggle: + name: pastetoggle + value: "" + t_Co: + name: t_Co + value: 256 + term: + name: term + value: xterm-256color + t_ut: + name: t_ut + value: "" + use_syntax: true + themes: {} + plugins: {} diff --git a/states/vim/init.sls b/states/vim/init.sls new file mode 100644 index 0000000..26b777b --- /dev/null +++ b/states/vim/init.sls @@ -0,0 +1,6 @@ +--- +include: + - .install + - .config + - .plugins + - .themes \ No newline at end of file diff --git a/states/vim/install.sls b/states/vim/install.sls new file mode 100644 index 0000000..fe10a54 --- /dev/null +++ b/states/vim/install.sls @@ -0,0 +1,15 @@ +--- +{%- from "vim/map.jinja" import vim with context %} +vim-pkg: + pkg.latest: + - name: vim + +vim-vimfiles-directory: + file.directory: + - name: {{ vim.plugins_dir }} + - makedirs: True + +vim-colors-directory: + file.directory: + - name: {{ vim.colors_dir }} + - makedirs: True \ No newline at end of file diff --git a/states/vim/map.jinja b/states/vim/map.jinja new file mode 100644 index 0000000..0a252a0 --- /dev/null +++ b/states/vim/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "vim/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='vim') -%} + +{%- set vim = salt['pillar.get']('vim', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/vim/plugins.sls b/states/vim/plugins.sls new file mode 100644 index 0000000..f930646 --- /dev/null +++ b/states/vim/plugins.sls @@ -0,0 +1,13 @@ +--- +{%- from "vim/map.jinja" import vim with context %} +vim-plugin-dirs: + file.directory: + - name: {{ vim.plugins_dir }} + - makedirs: True + +{%- for key, value in vim.plugins.items() %} +{{ value.name }}: + git.latest: + - name: {{ value.repo }} + - target: {{ vim.plugins_dir}}/{{ value.name }} +{%- endfor %} \ No newline at end of file diff --git a/states/vim/themes.sls b/states/vim/themes.sls new file mode 100644 index 0000000..9369774 --- /dev/null +++ b/states/vim/themes.sls @@ -0,0 +1,15 @@ +--- +{%- from "vim/map.jinja" import vim with context %} +{%- for key, value in vim.themes.items() %} +vim-{{ value.name }}-theme: + file.managed: + - name: {{ vim.colors_dir }}/{{ value.name }}.vim + - source: {{ value.url }} + - skip_verify: True + - user: root + - group: root + - mode: 0644 + - require: + - pkg: vim-pkg + - file: vim-colors-directory +{%- endfor %} \ No newline at end of file diff --git a/states/vim/vimrc.j2 b/states/vim/vimrc.j2 new file mode 100644 index 0000000..61678ea --- /dev/null +++ b/states/vim/vimrc.j2 @@ -0,0 +1,24 @@ +"{{ salt['pillar.get']('salt_managed', default='Salt Managed') }} +{%- from "vim/map.jinja" import vim with context %} +colorscheme {{ vim.config.use_theme }} +filetype plugin on +{%- if vim.config.use_syntax %} +syntax on +{%- endif %} +{% for key, value in vim.config.lets.items() %} +let {{ value.name }}{% if value.value is defined %}={{ value.value }}{% endif %} +{%- endfor %} +{% for key, value in vim.config.sets.items() %} +set {{ value.name }}{% if value.value is defined %}={{ value.value }}{% endif %} +{%- endfor %} + +if has("autocmd") + au VimEnter,InsertLeave * silent execute '!echo -ne "\e[2 q"' | redraw! + au InsertEnter,InsertChange * + \ if v:insertmode == 'i' | + \ silent execute '!echo -ne "\e[6 q"' | redraw! | + \ elseif v:insertmode == 'r' | + \ silent execute '!echo -ne "\e[4 q"' | redraw! | + \ endif + au VimLeave * silent execute '!echo -ne "\e[ q"' | redraw! +endif \ No newline at end of file diff --git a/states/vsftpd/defaults.yaml b/states/vsftpd/defaults.yaml new file mode 100644 index 0000000..86ad1ab --- /dev/null +++ b/states/vsftpd/defaults.yaml @@ -0,0 +1,18 @@ +--- +vsftpd: + enabled: true + config: + listen: "YES" + listen_ipv6: "NO" + anonymous_enable: "YES" + local_enable: "YES" + dirmessage_enable: "YES" + use_localtime: "YES" + xferlog_enable: "YES" + secure_chroot_dir: /var/run/vsftpd/empty + pam_service_name: vsftpd + ssl_enable: "NO" + pasv_enable: "YES" + pasv_min_port: 50000 + pasv_max_port: 50001 + port_enable: "YES" \ No newline at end of file diff --git a/states/vsftpd/init.sls b/states/vsftpd/init.sls new file mode 100644 index 0000000..feb2485 --- /dev/null +++ b/states/vsftpd/init.sls @@ -0,0 +1,16 @@ +--- +vsftpd-config: + file.managed: + - name: /etc/vsftpd.conf + - source: salt://vsftpd/vsftpd.conf.j2 + - user: root + - group: root + - mode: 644 + - template: jinja + - watch_in: + - service: vsftpd-service + +vsftpd-service: + service.running: + - name: vsftpd + - enable: True \ No newline at end of file diff --git a/states/vsftpd/map.jinja b/states/vsftpd/map.jinja new file mode 100644 index 0000000..524d668 --- /dev/null +++ b/states/vsftpd/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "vsftpd/defaults.yaml" as default_settings %} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='vsftpd') %} + +{%- set vsftpd = salt['pillar.get']('vsftpd', default=defaults, merge=True) %} \ No newline at end of file diff --git a/states/vsftpd/vsftpd.conf.j2 b/states/vsftpd/vsftpd.conf.j2 new file mode 100644 index 0000000..3c804ae --- /dev/null +++ b/states/vsftpd/vsftpd.conf.j2 @@ -0,0 +1,6 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +{%- from "vsftpd/map.jinja" import vsftpd with context %} +{%- for key, value in vsftpd.config.items() %} +{{ key }}={{ value }} +{%- endfor %} \ No newline at end of file diff --git a/states/weather/defaults.yaml b/states/weather/defaults.yaml new file mode 100644 index 0000000..ea0a28e --- /dev/null +++ b/states/weather/defaults.yaml @@ -0,0 +1,9 @@ +--- +weather: + enabled: true + install_dir: /usr/local/apps + release_dir: /usr/local/apps/releases + mirror: https://git.paulbsd.com/paulbsd/weather/releases/download + version: 1.0.0 + os: linux + arch: amd64 \ No newline at end of file diff --git a/states/weather/init.sls b/states/weather/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/weather/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/weather/install.sls b/states/weather/install.sls new file mode 100644 index 0000000..8956ebd --- /dev/null +++ b/states/weather/install.sls @@ -0,0 +1,16 @@ +--- +{%- from "weather/map.jinja" import weather with context %} +weather-archive-extract: + archive.extracted: + - name: {{ weather.release_dir }}/weather-{{ weather.version }} + - source: {{ weather.mirror }}/{{ weather.version }}/weather-{{ weather.version }}-{{ weather.os }}-{{ weather.arch }}.tar.gz + - skip_verify: True + - enforce_toplevel: False + - if_missing: {{ weather.release_dir }}/weather-{{ weather.version }} + +weather-binary-symlink: + file.symlink: + - name: {{ weather.install_dir }}/weather + - target: {{ weather.release_dir }}/weather-{{ weather.version }} + - require: + - archive: weather-archive-extract \ No newline at end of file diff --git a/states/weather/kernelmap.yaml b/states/weather/kernelmap.yaml new file mode 100644 index 0000000..e368b24 --- /dev/null +++ b/states/weather/kernelmap.yaml @@ -0,0 +1,3 @@ +--- +Linux: + os: "linux" \ No newline at end of file diff --git a/states/weather/map.jinja b/states/weather/map.jinja new file mode 100644 index 0000000..da1af88 --- /dev/null +++ b/states/weather/map.jinja @@ -0,0 +1,14 @@ +{%- import_yaml "weather/defaults.yaml" as default_settings -%} + +{%- import_yaml "weather/kernelmap.yaml" as kernelmap -%} +{%- import_yaml "weather/osarchmap.yaml" as osarchmap -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, + default='weather', + merge=salt['grains.filter_by'](osarchmap, grain='osarch', + merge=salt['grains.filter_by'](kernelmap, grain='kernel') + ) + ) +-%} + +{%- set weather = salt['pillar.get']('weather', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/weather/osarchmap.yaml b/states/weather/osarchmap.yaml new file mode 100644 index 0000000..c12e349 --- /dev/null +++ b/states/weather/osarchmap.yaml @@ -0,0 +1,21 @@ +--- +amd64: + arch: "amd64" + +x86_64: + arch: "amd64" + +386: + arch: "386" + +arm64: + arch: "arm64" + +armv6l: + arch: "arm" + +armv7l: + arch: "arm" + +armhf: + arch: "arm" \ No newline at end of file diff --git a/states/website/defaults.yaml b/states/website/defaults.yaml new file mode 100644 index 0000000..7a153c7 --- /dev/null +++ b/states/website/defaults.yaml @@ -0,0 +1,7 @@ +--- +website: + enabled: true + install_dir: /usr/local/apps + release_dir: /usr/local/apps/releases + mirror: https://git.paulbsd.com/paulbsd/website/releases/download + version: 1.0.0 \ No newline at end of file diff --git a/states/website/init.sls b/states/website/init.sls new file mode 100644 index 0000000..10ad9a6 --- /dev/null +++ b/states/website/init.sls @@ -0,0 +1,3 @@ +--- +include: + - .install \ No newline at end of file diff --git a/states/website/install.sls b/states/website/install.sls new file mode 100644 index 0000000..36aa11a --- /dev/null +++ b/states/website/install.sls @@ -0,0 +1,16 @@ +--- +{%- from "website/map.jinja" import website with context %} +website-archive-extract: + archive.extracted: + - name: {{ website.release_dir }}/website-{{ website.version }} + - source: {{ website.mirror }}/{{ website.version }}/website-{{ website.version }}.tar.gz + - skip_verify: True + - enforce_toplevel: False + - if_missing: {{ website.release_dir }}/website-{{ website.version }} + +website-binary-symlink: + file.symlink: + - name: {{ website.install_dir }}/website + - target: {{ website.release_dir }}/website-{{ website.version }} + - require: + - archive: website-archive-extract \ No newline at end of file diff --git a/states/website/map.jinja b/states/website/map.jinja new file mode 100644 index 0000000..f0bf327 --- /dev/null +++ b/states/website/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "website/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='website')-%} + +{%- set website = salt['pillar.get']('website', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/winpkg/bootstrap.sls b/states/winpkg/bootstrap.sls new file mode 100644 index 0000000..ea97ec6 --- /dev/null +++ b/states/winpkg/bootstrap.sls @@ -0,0 +1,4 @@ +--- +chocolatey_bootstrap: + module.run: + - name: chocolatey.bootstrap \ No newline at end of file diff --git a/states/winpkg/defaults.yaml b/states/winpkg/defaults.yaml new file mode 100644 index 0000000..58faa87 --- /dev/null +++ b/states/winpkg/defaults.yaml @@ -0,0 +1,3 @@ +--- +winpkg: + pkgs: [] \ No newline at end of file diff --git a/states/winpkg/init.sls b/states/winpkg/init.sls new file mode 100644 index 0000000..aaba48e --- /dev/null +++ b/states/winpkg/init.sls @@ -0,0 +1,4 @@ +--- +include: + - .bootstrap + - .install \ No newline at end of file diff --git a/states/winpkg/install.sls b/states/winpkg/install.sls new file mode 100644 index 0000000..8b49aaf --- /dev/null +++ b/states/winpkg/install.sls @@ -0,0 +1,9 @@ +--- +{%- from "winpkg/map.jinja" import winpkg with context %} +{%- for pkg in winpkgs.pkgs %} +winpkg-{{ pkg }}: + chocolatey.upgraded: + - name: {{ pkg }} + - require: + - module: chocolatey_bootstrap +{%- endfor %} \ No newline at end of file diff --git a/states/winpkg/map.jinja b/states/winpkg/map.jinja new file mode 100644 index 0000000..f0bf327 --- /dev/null +++ b/states/winpkg/map.jinja @@ -0,0 +1,5 @@ +{%- import_yaml "website/defaults.yaml" as default_settings -%} + +{%- set defaults = salt['grains.filter_by'](default_settings, default='website')-%} + +{%- set website = salt['pillar.get']('website', default=defaults, merge=True) -%} \ No newline at end of file diff --git a/states/wintse/init.sls b/states/wintse/init.sls new file mode 100644 index 0000000..661a037 --- /dev/null +++ b/states/wintse/init.sls @@ -0,0 +1,12 @@ +--- +wintse-firewall-rule: + win_firewall.add_rule: + - localport: 3389 + - protocol: tcp + - action: allow + +wintse-enable: + cmd.run: + - name: | + REG ADD "HKEY_LOCAL_MACHINE\SYSTEM\CurentControlSet\Control\Terminal Server" /v + fDenyTSConnections /t REG_DWORD /d 0 /f diff --git a/states/youtubedl/defaults.yaml b/states/youtubedl/defaults.yaml new file mode 100644 index 0000000..b3062bf --- /dev/null +++ b/states/youtubedl/defaults.yaml @@ -0,0 +1,4 @@ +--- +youtubedl: + install_dir: "/usr/bin" + version: "2020.03.24" \ No newline at end of file diff --git a/states/youtubedl/init.sls b/states/youtubedl/init.sls new file mode 100644 index 0000000..817d083 --- /dev/null +++ b/states/youtubedl/init.sls @@ -0,0 +1,11 @@ +--- +{%- from "youtubedl/map.jinja" import youtubedl with context %} +youtubedl-bin: + file.managed: + - name: {{ youtubedl.install_dir}}/youtube-dl + - source: https://github.com/ytdl-org/youtube-dl/releases/download/{{ youtubedl.version }}/youtube-dl + - user: root + - group: root + - mode: 0775 + - template: jinja + - skip_verify: True \ No newline at end of file diff --git a/states/youtubedl/map.jinja b/states/youtubedl/map.jinja new file mode 100644 index 0000000..e348736 --- /dev/null +++ b/states/youtubedl/map.jinja @@ -0,0 +1,6 @@ +{%- import_yaml "youtubedl/defaults.yaml" as defaults %} + +{%- set youtubedl = salt['pillar.get']('youtubedl', default=defaults.youtubedl, merge=True) %} + +{%- set nickname = salt['pillar.get']('nickname',default='anonymous') %} +{%- set syscontact = salt['pillar.get']('syscontact',default='anonymous@example.com') %} \ No newline at end of file diff --git a/states/zsh/config.sls b/states/zsh/config.sls new file mode 100644 index 0000000..be9bef0 --- /dev/null +++ b/states/zsh/config.sls @@ -0,0 +1,18 @@ +--- +zsh-omz-repo: + git.latest: + - name: https://github.com/robbyrussell/oh-my-zsh.git + - target: /usr/share/oh-my-zsh + - require: + - pkg: zsh-pkg + +zsh-omz-profile: + file.managed: + - name: /usr/share/zsh/zprofile + - source: salt://zsh/zprofile.j2 + - user: root + - group: root + - mode: 0644 + - template: jinja + - require: + - pkg: zsh-pkg \ No newline at end of file diff --git a/states/zsh/defaults.yaml b/states/zsh/defaults.yaml new file mode 100644 index 0000000..81cf240 --- /dev/null +++ b/states/zsh/defaults.yaml @@ -0,0 +1,4 @@ +--- +zsh: + enabled: true + config: \ No newline at end of file diff --git a/states/zsh/init.sls b/states/zsh/init.sls new file mode 100644 index 0000000..533e4d1 --- /dev/null +++ b/states/zsh/init.sls @@ -0,0 +1,6 @@ +--- +include: + - users + - .pkg + - .config + - .users \ No newline at end of file diff --git a/states/zsh/misc.txt b/states/zsh/misc.txt new file mode 100644 index 0000000..4f66c11 --- /dev/null +++ b/states/zsh/misc.txt @@ -0,0 +1,4 @@ +#autoload -Uz promptinit +#set autorehash +#promptinit +#prompt_redhat_setup \ No newline at end of file diff --git a/states/zsh/pkg.sls b/states/zsh/pkg.sls new file mode 100644 index 0000000..2b3fa81 --- /dev/null +++ b/states/zsh/pkg.sls @@ -0,0 +1,6 @@ +--- +zsh-pkg: + pkg.latest: + - pkgs: + - zsh + - zsh-syntax-highlighting \ No newline at end of file diff --git a/states/zsh/users.sls b/states/zsh/users.sls new file mode 100644 index 0000000..a527463 --- /dev/null +++ b/states/zsh/users.sls @@ -0,0 +1,17 @@ +--- +{%- from "users/map.jinja" import users with context -%} +{%- for user in users %} +{%- if user.enabled %} +zshrc-user-{{ user.name }}: + file.managed: + - name: {{ user.home }}/.zshrc + - source: salt://zsh/zshrc.j2 + - user: {{ user.name }} + - group: {{ user.gid }} + - mode: 0644 + - template: jinja + - require: + - pkg: zsh-pkg + - user: user-{{ user.name }} +{%- endif %} +{%- endfor %} \ No newline at end of file diff --git a/states/zsh/zprofile.j2 b/states/zsh/zprofile.j2 new file mode 100644 index 0000000..f492c57 --- /dev/null +++ b/states/zsh/zprofile.j2 @@ -0,0 +1,35 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +# Main environments variables +export EDITOR=vim +export JAVA_OPTS='-XX:+IgnoreUnrecognizedVMOptions --add-modules java.se.ee' + + +# Golang settings +export GOPATH=~/go +export GOROOT=/usr/local/go +export PATH=$PATH:$GOROOT/bin:$GOPATH/bin + + +# Oh my ZSH settings +export ZSH=/usr/share/oh-my-zsh +ZSH_THEME="jreese" +DISABLE_AUTO_UPDATE="true" +plugins=(common-aliases sudo docker salt git) + +if [[ -f $ZSH/oh-my-zsh.sh ]] +then + source $ZSH/oh-my-zsh.sh +fi + + +# Acme settings +if [[ -f /root/.acme.sh/acme.sh.env ]] +then + . /root/.acme.sh/acme.sh.env +fi + + +# Misc settings +bindkey -e +unsetopt share_history \ No newline at end of file diff --git a/states/zsh/zshrc.j2 b/states/zsh/zshrc.j2 new file mode 100644 index 0000000..603f823 --- /dev/null +++ b/states/zsh/zshrc.j2 @@ -0,0 +1,3 @@ +## {{ salt['pillar.get']('salt_managed', default='Salt Managed') }} + +source /usr/share/zsh/zprofile \ No newline at end of file