ConfigLMM 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +34 -0
- data/CNAME +1 -0
- data/Examples/.lmm.state.yaml +159 -0
- data/Examples/ConfigLMM.mm.yaml +32 -0
- data/Examples/Implemented.mm.yaml +252 -4
- data/Examples/SmallBusiness.mm.yaml +492 -0
- data/Plugins/Apps/Answer/answer.lmm.rb +165 -0
- data/Plugins/Apps/Answer/answer@.service +40 -0
- data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.conf.erb +0 -3
- data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.lmm.rb +0 -1
- data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +7 -1
- data/Plugins/Apps/Authentik/Authentik-Server.container +6 -1
- data/Plugins/Apps/Authentik/Authentik-Worker.container +6 -1
- data/Plugins/Apps/Authentik/Authentik.conf.erb +12 -7
- data/Plugins/Apps/Authentik/Authentik.lmm.rb +226 -61
- data/Plugins/Apps/BookStack/BookStack.conf.erb +0 -3
- data/Plugins/Apps/BookStack/BookStack.container +5 -0
- data/Plugins/Apps/BookStack/BookStack.lmm.rb +14 -3
- data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +9 -19
- data/Plugins/Apps/ClickHouse/ClickHouse.container +28 -0
- data/Plugins/Apps/ClickHouse/ClickHouse.lmm.rb +113 -0
- data/Plugins/Apps/ClickHouse/Config/listen.yaml +2 -0
- data/Plugins/Apps/ClickHouse/Config/logger.yaml +8 -0
- data/Plugins/Apps/ClickHouse/Config/zookeepers.yaml +5 -0
- data/Plugins/Apps/ClickHouse/Connection.rb +96 -0
- data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +5 -0
- data/Plugins/Apps/Discourse/Discourse.conf.erb +1 -4
- data/Plugins/Apps/Discourse/Discourse.container +4 -0
- data/Plugins/Apps/Discourse/Discourse.lmm.rb +116 -55
- data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +74 -62
- data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +6 -1
- data/Plugins/Apps/ERPNext/ERPNext-Queue.container +5 -0
- data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +5 -0
- data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +6 -1
- data/Plugins/Apps/ERPNext/ERPNext.container +6 -1
- data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +138 -127
- data/Plugins/Apps/GitLab/GitLab.container +6 -0
- data/Plugins/Apps/GitLab/GitLab.lmm.rb +43 -49
- data/Plugins/Apps/Homepage/Homepage.conf.erb +86 -0
- data/Plugins/Apps/Homepage/Homepage.container +19 -0
- data/Plugins/Apps/Homepage/Homepage.lmm.rb +54 -0
- data/Plugins/Apps/IPFS/IPFS.conf.erb +0 -3
- data/Plugins/Apps/IPFS/IPFS.lmm.rb +0 -1
- data/Plugins/Apps/InfluxDB/InfluxDB.conf.erb +0 -3
- data/Plugins/Apps/InfluxDB/InfluxDB.lmm.rb +0 -1
- data/Plugins/Apps/Jackett/Jackett.conf.erb +0 -3
- data/Plugins/Apps/Jackett/Jackett.lmm.rb +0 -1
- data/Plugins/Apps/Jellyfin/Jellyfin.conf.erb +0 -3
- data/Plugins/Apps/Jellyfin/Jellyfin.lmm.rb +0 -1
- data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +49 -28
- data/Plugins/Apps/LibreTranslate/LibreTranslate.container +21 -0
- data/Plugins/Apps/LibreTranslate/LibreTranslate.lmm.rb +34 -0
- data/Plugins/Apps/Lobsters/Containerfile +81 -0
- data/Plugins/Apps/Lobsters/Lobsters-Tasks.container +26 -0
- data/Plugins/Apps/Lobsters/Lobsters.conf.erb +99 -0
- data/Plugins/Apps/Lobsters/Lobsters.container +27 -0
- data/Plugins/Apps/Lobsters/Lobsters.lmm.rb +196 -0
- data/Plugins/Apps/Lobsters/crontab +3 -0
- data/Plugins/Apps/Lobsters/database.yml +26 -0
- data/Plugins/Apps/Lobsters/entrypoint.sh +30 -0
- data/Plugins/Apps/Lobsters/generateCredentials.rb +19 -0
- data/Plugins/Apps/Lobsters/lobsters-cron.sh +25 -0
- data/Plugins/Apps/Lobsters/lobsters-daily.sh +23 -0
- data/Plugins/Apps/Lobsters/puma.rb +49 -0
- data/Plugins/Apps/MariaDB/Connection.rb +55 -0
- data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +60 -53
- data/Plugins/Apps/Mastodon/Mastodon-Sidekiq.container +22 -0
- data/Plugins/Apps/Mastodon/Mastodon-Streaming.container +20 -0
- data/Plugins/Apps/Mastodon/Mastodon.conf.erb +34 -45
- data/Plugins/Apps/Mastodon/Mastodon.container +28 -0
- data/Plugins/Apps/Mastodon/Mastodon.lmm.rb +240 -5
- data/Plugins/Apps/Mastodon/configlmm.rake +30 -0
- data/Plugins/Apps/Mastodon/entrypoint.sh +16 -0
- data/Plugins/Apps/Matrix/Element.container +5 -0
- data/Plugins/Apps/Matrix/Matrix.conf.erb +2 -8
- data/Plugins/Apps/Matrix/Matrix.lmm.rb +100 -71
- data/Plugins/Apps/Matrix/Synapse.container +5 -0
- data/Plugins/Apps/Netdata/Netdata.conf.erb +0 -3
- data/Plugins/Apps/Netdata/Netdata.lmm.rb +0 -1
- data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +3 -4
- data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +150 -68
- data/Plugins/Apps/Nextcloud/autoconfig.php +13 -0
- data/Plugins/Apps/Nextcloud/config.php +10 -1
- data/Plugins/Apps/Nextcloud/nextcloudcron.service +8 -0
- data/Plugins/Apps/Nextcloud/nextcloudcron.timer +10 -0
- data/Plugins/Apps/Nginx/Connection.rb +93 -0
- data/Plugins/Apps/Nginx/conf.d/configlmm.conf +50 -9
- data/Plugins/Apps/Nginx/conf.d/languages.conf +21 -0
- data/Plugins/Apps/Nginx/config-lmm/errors.conf +25 -20
- data/Plugins/Apps/Nginx/config-lmm/gateway-errors.conf +20 -0
- data/Plugins/Apps/Nginx/config-lmm/proxy.conf +1 -1
- data/Plugins/Apps/Nginx/main.conf.erb +7 -3
- data/Plugins/Apps/Nginx/nginx.conf +2 -2
- data/Plugins/Apps/Nginx/nginx.lmm.rb +99 -81
- data/Plugins/Apps/Nginx/proxy.conf.erb +11 -3
- data/Plugins/Apps/Odoo/Odoo.conf.erb +0 -3
- data/Plugins/Apps/Odoo/Odoo.container +5 -0
- data/Plugins/Apps/Odoo/Odoo.lmm.rb +4 -5
- data/Plugins/Apps/Ollama/Ollama.container +26 -0
- data/Plugins/Apps/Ollama/Ollama.lmm.rb +73 -0
- data/Plugins/Apps/OpenTelemetry/Config/config.yaml +704 -0
- data/Plugins/Apps/OpenTelemetry/OpenTelemetry.lmm.rb +154 -0
- data/Plugins/Apps/OpenVidu/Ingress.container +5 -0
- data/Plugins/Apps/OpenVidu/OpenVidu.conf.erb +0 -3
- data/Plugins/Apps/OpenVidu/OpenVidu.container +5 -0
- data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +7 -3
- data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +0 -3
- data/Plugins/Apps/OpenVidu/OpenViduCall.container +5 -0
- data/Plugins/Apps/PHP-FPM/Connection.rb +91 -0
- data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +31 -4
- data/Plugins/Apps/Peppermint/Peppermint.conf.erb +2 -5
- data/Plugins/Apps/Peppermint/Peppermint.container +5 -0
- data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +29 -33
- data/Plugins/Apps/Perplexica/Perplexica.container +25 -0
- data/Plugins/Apps/Perplexica/Perplexica.lmm.rb +92 -0
- data/Plugins/Apps/Perplexica/config.toml +26 -0
- data/Plugins/Apps/Podman/Connection.rb +24 -0
- data/Plugins/Apps/Podman/Podman.lmm.rb +80 -0
- data/Plugins/Apps/Podman/storage.conf +6 -0
- data/Plugins/Apps/Postfix/Postfix.lmm.rb +242 -164
- data/Plugins/Apps/PostgreSQL/Connection.rb +97 -0
- data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +184 -148
- data/Plugins/Apps/Pterodactyl/Pterodactyl.conf.erb +0 -3
- data/Plugins/Apps/Pterodactyl/Pterodactyl.lmm.rb +0 -2
- data/Plugins/Apps/Pterodactyl/Wings.conf.erb +0 -3
- data/Plugins/Apps/RVM/RVM.lmm.rb +57 -0
- data/Plugins/Apps/Roundcube/Roundcube.conf.erb +0 -3
- data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +15 -19
- data/Plugins/Apps/SSH/SSH.lmm.rb +9 -15
- data/Plugins/Apps/SearXNG/SearXNG.container +22 -0
- data/Plugins/Apps/SearXNG/SearXNG.lmm.rb +79 -0
- data/Plugins/Apps/SearXNG/limiter.toml +40 -0
- data/Plugins/Apps/SearXNG/settings.yml +2 -0
- data/Plugins/Apps/SigNoz/Config/alerts.yml +11 -0
- data/Plugins/Apps/SigNoz/Config/otel-collector-config.yaml +110 -0
- data/Plugins/Apps/SigNoz/Config/otel-collector-opamp-config.yaml +1 -0
- data/Plugins/Apps/SigNoz/Config/prometheus.yml +18 -0
- data/Plugins/Apps/SigNoz/SigNoz-Collector.container +23 -0
- data/Plugins/Apps/SigNoz/SigNoz-Migrator.container +17 -0
- data/Plugins/Apps/SigNoz/SigNoz.conf.erb +61 -0
- data/Plugins/Apps/SigNoz/SigNoz.container +26 -0
- data/Plugins/Apps/SigNoz/SigNoz.lmm.rb +319 -0
- data/Plugins/Apps/Solr/log4j2.xml +89 -0
- data/Plugins/Apps/Solr/solr.lmm.rb +82 -0
- data/Plugins/Apps/Sunshine/Sunshine.conf.erb +0 -3
- data/Plugins/Apps/Sunshine/Sunshine.lmm.rb +0 -1
- data/Plugins/Apps/Tunnel/tunnel.lmm.rb +33 -37
- data/Plugins/Apps/UVdesk/UVdesk.conf.erb +0 -3
- data/Plugins/Apps/Umami/Umami.container +19 -0
- data/Plugins/Apps/Umami/Umami.lmm.rb +108 -0
- data/Plugins/Apps/Valkey/Valkey.lmm.rb +54 -42
- data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +9 -6
- data/Plugins/Apps/Vaultwarden/Vaultwarden.container +7 -1
- data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +64 -29
- data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +1 -4
- data/Plugins/Apps/Wiki.js/Wiki.js.container +5 -0
- data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +31 -37
- data/Plugins/Apps/YaCy/YaCy.conf.erb +93 -0
- data/Plugins/Apps/YaCy/YaCy.container +21 -0
- data/Plugins/Apps/YaCy/YaCy.lmm.rb +160 -0
- data/Plugins/Apps/ZooKeeper/ZooKeeper.container +24 -0
- data/Plugins/Apps/ZooKeeper/ZooKeeper.lmm.rb +68 -0
- data/Plugins/Apps/bitmagnet/bitmagnet.conf.erb +0 -3
- data/Plugins/Apps/bitmagnet/bitmagnet.lmm.rb +0 -1
- data/Plugins/Apps/gollum/gollum.conf.erb +2 -4
- data/Plugins/Apps/gollum/gollum.container +6 -0
- data/Plugins/Apps/gollum/gollum.lmm.rb +51 -50
- data/Plugins/Apps/llama.cpp/llama.cpp.container +28 -0
- data/Plugins/Apps/llama.cpp/llama.cpp.lmm.rb +90 -0
- data/Plugins/Apps/vLLM/vLLM.container +32 -0
- data/Plugins/Apps/vLLM/vLLM.lmm.rb +89 -0
- data/Plugins/OS/General/Utils.lmm.rb +26 -0
- data/Plugins/OS/Linux/Connection.rb +472 -0
- data/Plugins/OS/Linux/Debian/preseed.cfg.erb +25 -6
- data/Plugins/OS/Linux/Flavours.yaml +13 -0
- data/Plugins/OS/Linux/Grub/grub.cfg +10 -0
- data/Plugins/OS/Linux/HTTP.rb +32 -0
- data/Plugins/OS/Linux/Linux.lmm.rb +533 -187
- data/Plugins/OS/Linux/Packages.yaml +20 -1
- data/Plugins/OS/Linux/Services.yaml +8 -0
- data/Plugins/OS/Linux/Shell.rb +70 -0
- data/Plugins/OS/Linux/Syslinux/default +8 -0
- data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +83 -59
- data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +3 -0
- data/Plugins/OS/Linux/openSUSE/autoinst.xml.erb +29 -3
- data/Plugins/OS/Linux/systemd/systemd.lmm.rb +13 -11
- data/Plugins/OS/Routers/Aruba/ArubaInstant.lmm.rb +6 -5
- data/Plugins/Platforms/GitHub.lmm.rb +73 -28
- data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +9 -6
- data/Plugins/Platforms/Proxmox/Proxmox.lmm.rb +402 -0
- data/Plugins/Platforms/Proxmox/XTerm.rb +321 -0
- data/Plugins/Platforms/libvirt/libvirt.lmm.rb +38 -13
- data/Plugins/Platforms/porkbun.lmm.rb +12 -2
- data/Plugins/Platforms/porkbun_spec.rb +2 -2
- data/Plugins/Services/DNS/AmberBit.lmm.rb +1 -1
- data/Plugins/Services/DNS/ArubaItDNS.lmm.rb +1 -1
- data/Plugins/Services/DNS/NICLV.lmm.rb +1 -1
- data/Plugins/Services/DNS/PowerDNS.lmm.rb +70 -68
- data/Plugins/Services/DNS/tonic.lmm.rb +22 -12
- data/lib/ConfigLMM/Framework/plugins/dns.rb +4 -3
- data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +145 -184
- data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +34 -17
- data/lib/ConfigLMM/Framework/plugins/plugin.rb +53 -181
- data/lib/ConfigLMM/Framework/plugins/store.rb +4 -4
- data/lib/ConfigLMM/Framework/variables.rb +75 -0
- data/lib/ConfigLMM/Framework.rb +1 -0
- data/lib/ConfigLMM/cli.rb +12 -6
- data/lib/ConfigLMM/commands/configsCommand.rb +37 -6
- data/lib/ConfigLMM/commands/diff.rb +33 -9
- data/lib/ConfigLMM/context.rb +22 -3
- data/lib/ConfigLMM/io/configList.rb +82 -6
- data/lib/ConfigLMM/io/connection.rb +143 -0
- data/lib/ConfigLMM/io/dhcp.rb +330 -0
- data/lib/ConfigLMM/io/http.rb +78 -0
- data/lib/ConfigLMM/io/local.rb +207 -0
- data/lib/ConfigLMM/io/pxe.rb +92 -0
- data/lib/ConfigLMM/io/ssh.rb +156 -0
- data/lib/ConfigLMM/io/tftp.rb +105 -0
- data/lib/ConfigLMM/io.rb +2 -0
- data/lib/ConfigLMM/secrets/envStore.rb +39 -0
- data/lib/ConfigLMM/secrets/fileStore.rb +43 -0
- data/lib/ConfigLMM/state.rb +2 -1
- data/lib/ConfigLMM/version.rb +2 -1
- data/lib/ConfigLMM.rb +1 -0
- data/{Examples → scripts}/configlmmAuth.sh +7 -5
- metadata +205 -8
@@ -6,12 +6,18 @@ Arch Linux:
|
|
6
6
|
CyrusSASL: cyrus-sasl
|
7
7
|
Dovecot: dovecot
|
8
8
|
firewalld: firewalld
|
9
|
+
go: go
|
9
10
|
MariaDB: mariadb
|
10
11
|
Nextcloud: nextcloud
|
11
12
|
nginx: nginx
|
13
|
+
nodejs: nodejs
|
14
|
+
otelcol-contrib: AUR|otelcol-contrib
|
12
15
|
PHP-FPM: php-fpm
|
13
16
|
php-pecl: php-pecl
|
14
|
-
|
17
|
+
pnpm: pnpm
|
18
|
+
Podman:
|
19
|
+
- podman
|
20
|
+
- fuse-overlayfs
|
15
21
|
PostgreSQL: postgresql
|
16
22
|
Postfix: postfix
|
17
23
|
PowerDNS: powerdns
|
@@ -20,6 +26,7 @@ Arch Linux:
|
|
20
26
|
sshd: openssh
|
21
27
|
Valkey: redis
|
22
28
|
WireGuard: wireguard-tools
|
29
|
+
xorriso: libisoburn
|
23
30
|
Yarn: yarn
|
24
31
|
|
25
32
|
openSUSE Leap:
|
@@ -31,17 +38,22 @@ openSUSE Leap:
|
|
31
38
|
CyrusSASL: cyrus-sasl-plain
|
32
39
|
Dovecot: dovecot
|
33
40
|
firewalld: firewalld
|
41
|
+
go: go
|
34
42
|
MariaDB: mariadb
|
35
43
|
Nextcloud: server:php:applications|nextcloud
|
36
44
|
nginx: nginx
|
45
|
+
nodejs: nodejs-default
|
46
|
+
otelcol-contrib: GitHub|open-telemetry/opentelemetry-collector-releases:otelcol-contrib_*_linux_amd64.rpm
|
37
47
|
PHP-FPM:
|
38
48
|
- php8-devel
|
39
49
|
- php8-mbstring
|
40
50
|
- php8-fpm
|
51
|
+
- php8-imagick
|
41
52
|
- php8-redis
|
42
53
|
- php8-pgsql
|
43
54
|
- php8-mysql
|
44
55
|
php-pecl: php8-pecl
|
56
|
+
pnpm: GitHub|pnpm/pnpm:pnpm-linux-x64
|
45
57
|
Podman: podman
|
46
58
|
PostgreSQL:
|
47
59
|
- postgresql-server
|
@@ -57,6 +69,7 @@ openSUSE Leap:
|
|
57
69
|
sshd: openssh
|
58
70
|
Valkey: redis
|
59
71
|
WireGuard: wireguard-tools
|
72
|
+
xorriso: xorriso
|
60
73
|
Yarn: yarn
|
61
74
|
|
62
75
|
Debian:
|
@@ -70,16 +83,21 @@ Debian:
|
|
70
83
|
- dovecot-lmtpd
|
71
84
|
- dovecot-submissiond
|
72
85
|
firewalld: firewalld
|
86
|
+
go: golang
|
73
87
|
MariaDB: mariadb-server
|
74
88
|
Nextcloud:
|
75
89
|
nginx: nginx
|
90
|
+
nodejs: nodejs
|
91
|
+
otelcol-contrib: GitHub|open-telemetry/opentelemetry-collector-releases:otelcol-contrib_*_linux_amd64.deb
|
76
92
|
PHP-FPM:
|
77
93
|
- php-mbstring
|
78
94
|
- php-fpm
|
95
|
+
- php-imagick
|
79
96
|
- php-redis
|
80
97
|
- php-pgsql
|
81
98
|
- php-mysql
|
82
99
|
php-pecl:
|
100
|
+
pnpm: GitHub|pnpm/pnpm:pnpm-linux-x64
|
83
101
|
Podman: podman
|
84
102
|
PostgreSQL: postgresql
|
85
103
|
Postfix: postfix-lmdb
|
@@ -93,4 +111,5 @@ Debian:
|
|
93
111
|
sshd: openssh-server
|
94
112
|
Valkey: redis
|
95
113
|
WireGuard: wireguard
|
114
|
+
xorriso: xorriso
|
96
115
|
Yarn: yarnpkg
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'open3'
|
5
|
+
|
6
|
+
module ConfigLMM
|
7
|
+
module LMM
|
8
|
+
class LinuxShell
|
9
|
+
|
10
|
+
attr_reader :connection
|
11
|
+
attr_reader :user
|
12
|
+
|
13
|
+
def initialize(connection, user)
|
14
|
+
@connection = connection
|
15
|
+
@user = user
|
16
|
+
end
|
17
|
+
|
18
|
+
def distroID
|
19
|
+
@connection.distroID
|
20
|
+
end
|
21
|
+
|
22
|
+
def updateFile(*args, &block)
|
23
|
+
@connection.updateFile(*args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def exec(command, allowFailure = false, options = {})
|
27
|
+
cmd = "su --login #{@user.shellescape} --shell /usr/bin/sh --command #{command.shellescape}"
|
28
|
+
if options[:hide]
|
29
|
+
cmd = ' ' + cmd
|
30
|
+
end
|
31
|
+
@connection.exec(cmd, allowFailure, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def rm(*args)
|
35
|
+
@connection.rm(*args)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fileWrite(target, data, options = {})
|
39
|
+
hide = ''
|
40
|
+
hide = ' ' if options[:hide]
|
41
|
+
self.exec("#{hide}echo #{data.shellescape} > #{target}", false, options)
|
42
|
+
end
|
43
|
+
|
44
|
+
def fileAppend(target, data, options = {})
|
45
|
+
hide = ''
|
46
|
+
hide = ' ' if options[:hide]
|
47
|
+
self.exec("#{hide}echo #{data.shellescape} >> #{target}", false, options)
|
48
|
+
end
|
49
|
+
|
50
|
+
def fileMerge(target, file, options = {})
|
51
|
+
self.exec("cat #{file.shellescape} >> #{target}", false, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
def fileReplace(target, placeholder, result, options = {})
|
55
|
+
hide = ''
|
56
|
+
hide = ' ' if options[:hide]
|
57
|
+
pattern = "s|#{placeholder}|#{result.gsub('\\', '\\\\\\').gsub('&', '\\\\&').gsub('|', '\\\\|')}|"
|
58
|
+
self.exec("#{hide}sed -i #{pattern.shellescape} #{target}", false, options)
|
59
|
+
end
|
60
|
+
|
61
|
+
def createDirs(options, *paths)
|
62
|
+
exec("mkdir -p #{paths.join(' ')}", false, options)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.escapeSingleQuotes(command)
|
66
|
+
command.gsub("'", "'\"'\"'")
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -13,45 +13,64 @@ module ConfigLMM
|
|
13
13
|
|
14
14
|
def actionWireGuardDeploy(id, target, activeState, context, options)
|
15
15
|
self.prepareConfig(target)
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
self.class.ensurePackages([WIREGUARD_PACKAGE], ssh)
|
28
|
-
self.class.ensureServiceAutoStartOverSSH(SERVICE_NAME, ssh)
|
16
|
+
self.withConnection(target['Location'], target) do |connection|
|
17
|
+
Linux.withConnection(connection) do |linuxConnection|
|
18
|
+
linuxConnection.firewallAddPort("#{PORT}/udp", options)
|
19
|
+
linuxConnection.exec("firewall-cmd -q --permanent --zone=trusted --add-source=#{SUBNET}", options)
|
20
|
+
linuxConnection.exec("firewall-cmd -q --zone=trusted --add-source=#{SUBNET}", options)
|
21
|
+
linuxConnection.exec("firewall-cmd -q --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s #{SUBNET} ! -d #{SUBNET} -j MASQUERADE", options)
|
22
|
+
linuxConnection.exec("firewall-cmd -q --direct --add-rule ipv4 nat POSTROUTING 0 -s #{SUBNET} ! -d #{SUBNET} -j MASQUERADE", options)
|
23
|
+
|
24
|
+
linuxConnection.ensurePackage(WIREGUARD_PACKAGE, options)
|
25
|
+
linuxConnection.ensureServiceAutoStart(SERVICE_NAME, options)
|
29
26
|
|
30
27
|
dir = options['output'] + '/' + id + '/etc/wireguard/'
|
31
28
|
mkdir(dir, false)
|
32
29
|
template = ERB.new(File.read(__dir__ + '/wg0.conf.erb'))
|
33
30
|
|
34
|
-
|
31
|
+
target = target.dup
|
32
|
+
target['PrivateKey'] = context.secrets.load(target['SecretId'], 'PRIVATEKEY')
|
33
|
+
if target['PrivateKey'].nil?
|
34
|
+
target['PrivateKey'] = genkey(connection, options)
|
35
|
+
if !options['dry']
|
36
|
+
context.secrets.store(target['SecretId'], 'PRIVATEKEY', target['PrivateKey'])
|
37
|
+
context.secrets.print("Private Key", target['PrivateKey'])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
if connection.filePresent?(CONFIG_FILE, { **options, 'dry' => false })
|
35
42
|
# TODO Implement adding and removing peers
|
36
43
|
else
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
43
|
-
publicKey = pubkeyOverSSH(target['PrivateKey'], ssh)
|
44
|
-
self.class.sshExec!(ssh, "echo '#{publicKey}' > /etc/wireguard/pubkey")
|
44
|
+
publicKey = pubkey(target['PrivateKey'], connection, options)
|
45
|
+
context.secrets.store(target['SecretId'], 'PUBLICKEY', publicKey) unless options['dry']
|
46
|
+
connection.exec("echo '#{publicKey}' > /etc/wireguard/pubkey", false, options)
|
47
|
+
|
45
48
|
target['Peers'].each do |name, data|
|
46
|
-
if
|
47
|
-
data['
|
48
|
-
data['
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
49
|
+
if data['SecretId']
|
50
|
+
data['PublicKey'] = context.secrets.load(data['SecretId'], 'PUBLICKEY')
|
51
|
+
data['PrivateKey'] = context.secrets.load(data['SecretId'], 'PRIVATEKEY')
|
52
|
+
if data['PublicKey'].nil?
|
53
|
+
data['PrivateKey'] = genkey(connection, options)
|
54
|
+
data['PublicKey'] = pubkey(data['PrivateKey'], connection, options)
|
55
|
+
if !options['dry']
|
56
|
+
context.secrets.store(data['SecretId'], 'PRIVATEKEY', data['PrivateKey'])
|
57
|
+
context.secrets.store(data['SecretId'], 'PUBLICKEY', data['PublicKey'])
|
58
|
+
end
|
54
59
|
end
|
60
|
+
sharedSecretId = "#{target['SecretId'].upcase}_#{data['SecretId'].upcase}"
|
61
|
+
data['PresharedKey'] = context.secrets.load(sharedSecretId, 'PRESHAREDKEY')
|
62
|
+
if data['PresharedKey'].nil?
|
63
|
+
sharedSecretId2 = "#{data['SecretId'].upcase}_#{target['SecretId'].upcase}"
|
64
|
+
data['PresharedKey'] = context.secrets.load(sharedSecretId2, 'PRESHAREDKEY')
|
65
|
+
if data['PresharedKey'].nil?
|
66
|
+
data['PresharedKey'] = genpsk(connection, options)
|
67
|
+
context.secrets.store(sharedSecretId, 'PRESHAREDKEY', data['PresharedKey']) unless options['dry']
|
68
|
+
end
|
69
|
+
end
|
70
|
+
else
|
71
|
+
data['PrivateKey'] = genkey(connection, options)
|
72
|
+
data['PublicKey'] = pubkey(data['PrivateKey'], connection, options)
|
73
|
+
data['PresharedKey'] = genpsk(connection, options)
|
55
74
|
end
|
56
75
|
end
|
57
76
|
|
@@ -68,7 +87,7 @@ module ConfigLMM
|
|
68
87
|
psk = otherData[pskIdB]
|
69
88
|
else
|
70
89
|
pskIdA = 'PresharedKey_' + name + '_' + otherName
|
71
|
-
data[pskIdA] =
|
90
|
+
data[pskIdA] = genpsk(connection, options)
|
72
91
|
psk = data[pskIdA]
|
73
92
|
end
|
74
93
|
templateData['Peers'][otherName] = { 'PublicKey' => otherData['PublicKey'], 'PresharedKey' => psk }
|
@@ -78,59 +97,64 @@ module ConfigLMM
|
|
78
97
|
end
|
79
98
|
|
80
99
|
renderTemplate(template, target, dir + 'wg0.conf', options)
|
81
|
-
|
100
|
+
connection.upload(dir + 'wg0.conf', CONFIG_FILE, options)
|
82
101
|
end
|
83
102
|
|
103
|
+
linuxConnection.restartService(SERVICE_NAME, options)
|
84
104
|
end
|
85
|
-
else
|
86
|
-
# TODO
|
87
105
|
end
|
88
|
-
self.startService(SERVICE_NAME, target['Location'])
|
89
|
-
|
90
|
-
activeState['Status'] = State::STATUS_DEPLOYED
|
91
106
|
end
|
92
107
|
|
93
108
|
def cleanup(configs, state, context, options)
|
94
|
-
cleanupType(:WireGuard, configs, state, context, options) do |item, id, state, context, options,
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
109
|
+
cleanupType(:WireGuard, configs, state, context, options) do |item, id, state, context, options, connection|
|
110
|
+
Linux.withConnection(connection) do |linuxConnection|
|
111
|
+
linuxConnection.stopService(SERVICE_NAME, options[:dry])
|
112
|
+
linuxConnection.disableService(SERVICE_NAME, options[:dry])
|
113
|
+
linuxConnection.removePackage(WIREGUARD_PACKAGE, options[:dry])
|
114
|
+
|
115
|
+
linuxConnection.firewallRemovePort("#{PORT}/udp", options)
|
116
|
+
linuxConnection.exec("firewall-cmd -q --permanent --zone=trusted --remove-source=#{SUBNET}", false, options[:dry])
|
117
|
+
linuxConnection.exec("firewall-cmd -q --zone=trusted --remove-source=#{SUBNET}", false, options[:dry])
|
118
|
+
linuxConnection.exec("firewall-cmd -q --permanent --direct --remove-rule ipv4 nat POSTROUTING 0 -s #{SUBNET} ! -d #{SUBNET} -j MASQUERADE", false, options[:dry])
|
119
|
+
linuxConnection.exec("firewall-cmd -q --direct --remove-rule ipv4 nat POSTROUTING 0 -s #{SUBNET} ! -d #{SUBNET} -j MASQUERADE", false, options[:dry])
|
120
|
+
end
|
105
121
|
|
106
122
|
state.item(id)['Status'] = State::STATUS_DELETED unless options[:dry]
|
107
123
|
|
108
124
|
if options[:destroy]
|
109
|
-
rm('/etc/wireguard', options[:dry]
|
125
|
+
connection.rm('/etc/wireguard', options[:dry])
|
110
126
|
|
111
127
|
state.item(id)['Status'] = State::STATUS_DESTROYED unless options[:dry]
|
112
128
|
end
|
113
129
|
end
|
114
130
|
end
|
115
131
|
|
116
|
-
def
|
117
|
-
|
132
|
+
def genkey(connection, options)
|
133
|
+
key = connection.exec('wg genkey', false, options).strip
|
134
|
+
if options['dry']
|
135
|
+
key = connection.exec('wg genkey', false, { **options, 'dry' => false }).strip
|
136
|
+
end
|
137
|
+
key
|
118
138
|
end
|
119
139
|
|
120
|
-
def
|
121
|
-
|
140
|
+
def genpsk(connection, options)
|
141
|
+
key = connection.exec('wg genpsk', false, options).strip
|
142
|
+
if options['dry']
|
143
|
+
key = connection.exec('wg genpsk', false, { **options, 'dry' => false }).strip
|
144
|
+
end
|
145
|
+
key
|
122
146
|
end
|
123
147
|
|
124
|
-
def
|
125
|
-
|
148
|
+
def pubkey(privateKey, connection, options)
|
149
|
+
key = connection.exec(" echo '#{privateKey}' | wg pubkey", false, { **options, hide: true }).strip
|
150
|
+
if options['dry']
|
151
|
+
key = connection.exec(" echo '#{privateKey}' | wg pubkey", false, { **options, 'dry' => false, hide: true }).strip
|
152
|
+
end
|
153
|
+
key
|
126
154
|
end
|
127
155
|
|
128
156
|
def prepareConfig(target)
|
129
157
|
target['Address'] = '172.20.0.1' unless target['Address']
|
130
|
-
target['Peers'].each do |name, data|
|
131
|
-
target['Peers'][name] ||= {}
|
132
|
-
target['Peers'][name]['AllowedIPs'] = SUBNET unless target['Peers'][name]['AllowedIPs']
|
133
|
-
end
|
134
158
|
end
|
135
159
|
end
|
136
160
|
end
|
@@ -31,9 +31,35 @@
|
|
31
31
|
<dns t="map">
|
32
32
|
<hostname><%= config['HostName'] %></hostname>
|
33
33
|
<% if config['Domain'] %>
|
34
|
-
<domain><%= config['Domain'] %></domain>
|
34
|
+
<domain><%= Addressable::IDNA.to_ascii(config['Domain']) %></domain>
|
35
|
+
<% end %>
|
36
|
+
<% if config['DefaultNetwork']['DNS'] %>
|
37
|
+
<nameservers config:type="list">
|
38
|
+
<nameserver><%= config['DefaultNetwork']['DNS'] %></nameserver>
|
39
|
+
</nameservers>
|
35
40
|
<% end %>
|
36
41
|
</dns>
|
42
|
+
<% if config['DefaultNetwork']['IP'] %>
|
43
|
+
<interfaces config:type="list">
|
44
|
+
<interface>
|
45
|
+
<bootproto><%= config['DefaultNetwork']['IP'] == 'dhcp' ? 'dhcp' : 'static' %></bootproto>
|
46
|
+
<name>eth0</name>
|
47
|
+
<% if config['DefaultNetwork']['IP'] != 'dhcp' %><ipaddr><%= config['DefaultNetwork']['IP'] %></ipaddr><% end %>
|
48
|
+
<startmode>auto</startmode>
|
49
|
+
</interface>
|
50
|
+
</interfaces>
|
51
|
+
<% end %>
|
52
|
+
<% if config['DefaultNetwork']['Gateway'] %>
|
53
|
+
<routing>
|
54
|
+
<routes config:type="list">
|
55
|
+
<route>
|
56
|
+
<destination>default</destination>
|
57
|
+
<device>eth0</device>
|
58
|
+
<gateway><%= config['DefaultNetwork']['Gateway'] %></gateway>
|
59
|
+
</route>
|
60
|
+
</routes>
|
61
|
+
</routing>
|
62
|
+
<% end %>
|
37
63
|
</networking>
|
38
64
|
<software t="map">
|
39
65
|
<% if !config['Apps'].to_a.empty? %>
|
@@ -49,7 +75,7 @@
|
|
49
75
|
<% if !config['Services'].to_a.empty? %>
|
50
76
|
<enable t="list">
|
51
77
|
<% config['Services'].each do |service| %>
|
52
|
-
<service><%= service %></service>
|
78
|
+
<service><%= service.to_s %></service>
|
53
79
|
<% end %>
|
54
80
|
</enable>
|
55
81
|
<% end %>
|
@@ -62,7 +88,7 @@
|
|
62
88
|
<users config:type="list">
|
63
89
|
<% config['Users'].each do |user, info| %>
|
64
90
|
<user>
|
65
|
-
<username
|
91
|
+
<username><%= user %></username>
|
66
92
|
<% if info['PasswordHash'] %>
|
67
93
|
<encrypted config:type="boolean">true</encrypted>
|
68
94
|
<user_password><%= info['PasswordHash'] %></user_password>
|
@@ -7,20 +7,22 @@ module ConfigLMM
|
|
7
7
|
USER_SERVICE_DIR = '/etc/systemd/system/user@.service.d/'
|
8
8
|
|
9
9
|
def actionSystemdDeploy(id, target, activeState, context, options)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
10
|
+
self.withConnection(target['Location'], target) do |connection|
|
11
|
+
Linux.withConnection(connection) do |linuxConnection|
|
12
|
+
if target['UserCgroups']
|
13
|
+
linuxConnection.createDirs(options, USER_SERVICE_DIR)
|
14
|
+
linuxConnection.upload(__dir__ + '/user-0.slice', SYSTEMD_CONFIG_PATH, options)
|
15
|
+
linuxConnection.upload(__dir__ + '/user@.service.d/delegate.conf', USER_SERVICE_DIR, options)
|
16
|
+
end
|
17
|
+
if target['InstallServices']
|
18
|
+
target['InstallServices'].each do |file, data|
|
19
|
+
linuxConnection.upload(file, SYSTEMD_CONFIG_PATH, options)
|
20
|
+
linuxConnection.reloadServiceManager(options)
|
21
|
+
linuxConnection.ensureServiceAutoStart(File.basename(file), options)
|
22
|
+
end
|
18
23
|
end
|
19
24
|
end
|
20
|
-
else
|
21
|
-
# TODO
|
22
25
|
end
|
23
|
-
|
24
26
|
end
|
25
27
|
|
26
28
|
end
|
@@ -46,7 +46,7 @@ module ConfigLMM
|
|
46
46
|
end
|
47
47
|
|
48
48
|
creds = parseLocation(target['Location'])
|
49
|
-
password =
|
49
|
+
password = context.secrets.load(target['SecretId'], 'PASSWORD')
|
50
50
|
|
51
51
|
# Couldn't get it working with net-ssh gem so using `ssh` as workaround
|
52
52
|
# Net::SSH.start(creds[:hostname], creds[:user], password: password, port: creds[:port]) do |ssh|
|
@@ -128,14 +128,15 @@ module ConfigLMM
|
|
128
128
|
end
|
129
129
|
|
130
130
|
def authenticate(actionMethod, target, activeState, context, options)
|
131
|
-
|
132
|
-
|
133
|
-
|
131
|
+
authSecret = context.secrets.load(target['SecretId'], 'PASSWORD')
|
132
|
+
if authSecret.to_s.empty?
|
133
|
+
prompt.error("Set your Aruba Instant SSH password in #{target['SecretId']}_PASSWORD")
|
134
|
+
raise Framework::PluginPrerequisite.new('Need Aruba Instant password!')
|
134
135
|
else
|
135
136
|
if !target['Location']
|
136
137
|
raise Framework::PluginProcessError.new('Location must be provided!')
|
137
138
|
end
|
138
|
-
checkSSHAuth!(target['Location'],
|
139
|
+
checkSSHAuth!(target['Location'], authSecret)
|
139
140
|
end
|
140
141
|
true
|
141
142
|
end
|
@@ -5,53 +5,98 @@ module ConfigLMM
|
|
5
5
|
module LMM
|
6
6
|
class GitHub < Framework::Plugin
|
7
7
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
prompt.say('You need to create it manually - https://github.com/organizations/plan')
|
15
|
-
raise Framework::PluginPrerequisite.new('Organization must exist!')
|
8
|
+
def actionGitHubRefresh(id, target, activeState, context, options)
|
9
|
+
if !target['Organizations'].to_h.empty?
|
10
|
+
activeState['Organizations'] = {}
|
11
|
+
target['Organizations'].each do |name, organization|
|
12
|
+
organizationRefresh(name, target, activeState, context, options)
|
13
|
+
end
|
16
14
|
end
|
15
|
+
end
|
17
16
|
|
18
|
-
|
19
|
-
|
20
|
-
|
17
|
+
def actionGitHubDiff(id, target, activeState, context, options)
|
18
|
+
state = prepareState(target, activeState)
|
19
|
+
shouldMatch(id, state, 'Organizations', target, 'Organizations')
|
20
|
+
end
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
def actionGitHubDeploy(id, target, activeState, context, options)
|
23
|
+
actionGitHubDiff(id, target, activeState, context, options)
|
24
|
+
diff.each do |name, states|
|
25
|
+
# TODO FIXME
|
24
26
|
end
|
25
27
|
end
|
26
28
|
|
27
|
-
def
|
28
|
-
|
29
|
-
|
29
|
+
def prepareState(target, activeState)
|
30
|
+
state = activeState.dup
|
31
|
+
state['Organizations'] ||= {}
|
32
|
+
state['Organizations'].each do |name, data|
|
33
|
+
#state['Organizations'][name]['Name'] = state['Organizations'][name].delete('Login')
|
34
|
+
state['Organizations'][name]['Description'] = state['Organizations'][name].delete('description')
|
35
|
+
end
|
36
|
+
state
|
30
37
|
end
|
31
38
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
+
def organizationRefresh(name, target, activeState, context, options)
|
40
|
+
authToken = context.secrets.load(target['SecretId'], 'TOKEN')
|
41
|
+
authToken = context.secrets.load('GITHUB', 'TOKEN') if authToken.nil?
|
42
|
+
client = Octokit::Client.new(:access_token => authToken)
|
43
|
+
|
44
|
+
allOrgs = client.organizations
|
45
|
+
if allOrgs.empty?
|
46
|
+
# Fine-grained access token never returns any orgs
|
47
|
+
org = client.organization(name)
|
48
|
+
else
|
49
|
+
orgs = allOrgs.select { |org| org[:login] == name }
|
50
|
+
if orgs.empty?
|
51
|
+
prompt.say("Didn\'t find organization with name #{name}")
|
52
|
+
prompt.say('You need to create it manually - https://github.com/organizations/plan')
|
53
|
+
raise Framework::PluginPrerequisite.new('Organization must exist!')
|
39
54
|
end
|
55
|
+
org = orgs.first
|
56
|
+
end
|
57
|
+
|
58
|
+
activeState['Organizations'][org.login] ||= {}
|
59
|
+
|
60
|
+
org.each do |name, value|
|
61
|
+
data = value
|
62
|
+
data = value.to_h if value && value.respond_to?(:to_h)
|
63
|
+
data = value.to_s if value.is_a?(Time)
|
64
|
+
activeState['Organizations'][org.login][name.to_s] = data
|
40
65
|
end
|
41
|
-
# TODO FIXME
|
42
|
-
raise 'Not implemented!'
|
43
66
|
end
|
44
67
|
|
45
68
|
def authenticate(actionMethod, target, activeState, context, options)
|
46
|
-
authToken =
|
69
|
+
authToken = context.secrets.load(target['SecretId'], 'TOKEN')
|
70
|
+
authToken = context.secrets.load('GITHUB', 'TOKEN') if authToken.nil?
|
47
71
|
if authToken.to_s.empty?
|
48
72
|
prompt.say('Open https://github.com/settings/tokens and create a token!')
|
49
|
-
prompt.say(
|
50
|
-
raise Framework::PluginPrerequisite.new('Need
|
73
|
+
prompt.say("Then set it\'s value in #{target['SecretId']}_TOKEN")
|
74
|
+
raise Framework::PluginPrerequisite.new('Need GitHub token!')
|
51
75
|
end
|
52
76
|
true
|
53
77
|
end
|
54
78
|
|
79
|
+
def self.getReleases(repoId, logger, context, options)
|
80
|
+
response = HTTP.get("https://api.github.com/repos/#{repoId}/releases")
|
81
|
+
if response.status.success?
|
82
|
+
releases = response.parse
|
83
|
+
releases.reject! { |release| release['draft'] || release['prerelease'] }
|
84
|
+
return releases
|
85
|
+
end
|
86
|
+
logger.error("Failed to load GitHub release for #{repoId}")
|
87
|
+
raise response
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.getReleaseAsset(name, releases)
|
91
|
+
pattern = name.gsub('.', '\\.').gsub('*', '.*')
|
92
|
+
releases.each do |release|
|
93
|
+
release['assets'].each do |asset|
|
94
|
+
return asset if asset['name'].match?(pattern)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
raise "Couldn't find GitHub asset #{name}!"
|
98
|
+
end
|
99
|
+
|
55
100
|
end
|
56
101
|
end
|
57
102
|
end
|