ConfigLMM 0.3.0 → 0.4.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 +36 -0
- data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +14 -0
- data/Plugins/Apps/Authentik/Authentik-Server.container +1 -0
- data/Plugins/Apps/Authentik/Authentik-Worker.container +1 -0
- data/Plugins/Apps/Authentik/Authentik.conf.erb +7 -0
- data/Plugins/Apps/Authentik/Authentik.lmm.rb +24 -2
- data/Plugins/Apps/BookStack/BookStack.conf.erb +41 -0
- data/Plugins/Apps/BookStack/BookStack.container +15 -0
- data/Plugins/Apps/BookStack/BookStack.lmm.rb +80 -0
- data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +17 -0
- data/Plugins/Apps/Discourse/Discourse.conf.erb +41 -0
- data/Plugins/Apps/Discourse/Discourse.container +17 -0
- data/Plugins/Apps/Discourse/Discourse.lmm.rb +95 -0
- data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +25 -2
- data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +19 -0
- data/Plugins/Apps/ERPNext/ERPNext-Queue.container +17 -0
- data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +17 -0
- data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +19 -0
- data/Plugins/Apps/ERPNext/ERPNext.container +18 -0
- data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +193 -0
- data/Plugins/Apps/ERPNext/ERPNext.network +12 -0
- data/Plugins/Apps/ERPNext/sites/apps.json +10 -0
- data/Plugins/Apps/ERPNext/sites/apps.txt +3 -0
- data/Plugins/Apps/ERPNext/sites/common_site_config.json +11 -0
- data/Plugins/Apps/GitLab/GitLab.container +3 -2
- data/Plugins/Apps/GitLab/GitLab.lmm.rb +37 -12
- data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +57 -0
- data/Plugins/Apps/LetsEncrypt/hooks/dovecot.sh +2 -0
- data/Plugins/Apps/LetsEncrypt/hooks/nginx.sh +2 -0
- data/Plugins/Apps/LetsEncrypt/hooks/postfix.sh +2 -0
- data/Plugins/Apps/LetsEncrypt/renew-certificates.service +7 -0
- data/Plugins/Apps/LetsEncrypt/renew-certificates.timer +12 -0
- data/Plugins/Apps/LetsEncrypt/rfc2136.ini +11 -0
- data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +115 -0
- data/Plugins/Apps/Matrix/Element.container +14 -0
- data/Plugins/Apps/Matrix/Matrix.conf.erb +49 -5
- data/Plugins/Apps/Matrix/Matrix.lmm.rb +86 -1
- data/Plugins/Apps/Matrix/Synapse.container +17 -0
- data/Plugins/Apps/Matrix/config.json +50 -0
- data/Plugins/Apps/Matrix/homeserver.yaml +70 -0
- data/Plugins/Apps/Matrix/log.config +30 -0
- data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +70 -45
- data/Plugins/Apps/Nginx/conf.d/configlmm.conf +9 -0
- data/Plugins/Apps/Nginx/config-lmm/errors.conf +10 -4
- data/Plugins/Apps/Nginx/config-lmm/proxy.conf +5 -1
- data/Plugins/Apps/Nginx/nginx.lmm.rb +15 -11
- data/Plugins/Apps/Nginx/proxy.conf.erb +13 -3
- data/Plugins/Apps/Odoo/Odoo.container +2 -1
- data/Plugins/Apps/Odoo/Odoo.lmm.rb +1 -1
- data/Plugins/Apps/OpenVidu/Ingress.container +18 -0
- data/Plugins/Apps/OpenVidu/OpenVidu.conf.erb +34 -0
- data/Plugins/Apps/OpenVidu/OpenVidu.container +16 -0
- data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +90 -0
- data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +35 -0
- data/Plugins/Apps/OpenVidu/OpenViduCall.container +15 -0
- data/Plugins/Apps/OpenVidu/ingress.yaml +10 -0
- data/Plugins/Apps/OpenVidu/livekit.yaml +13 -0
- data/Plugins/Apps/Peppermint/Peppermint.conf.erb +0 -4
- data/Plugins/Apps/Peppermint/Peppermint.container +2 -1
- data/Plugins/Apps/Postfix/Postfix.lmm.rb +32 -6
- data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +80 -11
- data/Plugins/Apps/Roundcube/Roundcube.conf.erb +75 -0
- data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +145 -0
- data/Plugins/Apps/Tunnel/tunnel.lmm.rb +63 -0
- data/Plugins/Apps/Tunnel/tunnelTCP.service +9 -0
- data/Plugins/Apps/Tunnel/tunnelTCP.socket +9 -0
- data/Plugins/Apps/Tunnel/tunnelUDP.service +9 -0
- data/Plugins/Apps/Tunnel/tunnelUDP.socket +9 -0
- data/Plugins/Apps/Valkey/Valkey.lmm.rb +32 -0
- data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +4 -0
- data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +42 -0
- data/Plugins/Apps/Wiki.js/Wiki.js.container +15 -0
- data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +61 -0
- data/Plugins/Apps/gollum/gollum.conf.erb +39 -1
- data/Plugins/Apps/gollum/gollum.container +4 -1
- data/Plugins/Apps/gollum/gollum.lmm.rb +11 -3
- data/Plugins/OS/Linux/Debian/preseed.cfg.erb +62 -0
- data/Plugins/OS/Linux/Distributions.yaml +32 -0
- data/Plugins/OS/Linux/Flavours.yaml +11 -0
- data/Plugins/OS/Linux/Linux.lmm.rb +255 -67
- data/Plugins/OS/Linux/Packages.yaml +47 -2
- data/Plugins/OS/Linux/Proxmox/answer.toml.erb +30 -0
- data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +33 -4
- data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +1 -1
- data/Plugins/Platforms/libvirt/libvirt.lmm.rb +3 -2
- data/Plugins/Services/DNS/PowerDNS.lmm.rb +95 -8
- data/bootstrap.sh +41 -3
- data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +146 -64
- data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +34 -3
- data/lib/ConfigLMM/Framework/plugins/plugin.rb +62 -6
- data/lib/ConfigLMM/cli.rb +3 -1
- data/lib/ConfigLMM/commands/cleanup.rb +1 -0
- data/lib/ConfigLMM/commands/configsCommand.rb +3 -1
- data/lib/ConfigLMM/io/configList.rb +3 -1
- data/lib/ConfigLMM/state.rb +10 -2
- data/lib/ConfigLMM/version.rb +1 -1
- metadata +54 -3
- data/Plugins/Apps/GitLab/GitLab.conf.erb +0 -26
|
@@ -21,7 +21,7 @@ module ConfigLMM
|
|
|
21
21
|
|
|
22
22
|
template = ERB.new(File.read(__dir__ + '/zone.txt.erb'))
|
|
23
23
|
|
|
24
|
-
target['DNS'].each do |domain, data|
|
|
24
|
+
target['DNS'].to_h.each do |domain, data|
|
|
25
25
|
config = { 'Domain' => domain, 'Records' => '' }
|
|
26
26
|
data.each do |name, data|
|
|
27
27
|
self.processDNS(domain, data).each do |type, records|
|
|
@@ -37,7 +37,7 @@ module ConfigLMM
|
|
|
37
37
|
server = compute.servers.all.find { |server| server.name == serverName }
|
|
38
38
|
if server
|
|
39
39
|
server.start
|
|
40
|
-
return
|
|
40
|
+
return false
|
|
41
41
|
end
|
|
42
42
|
settings = {
|
|
43
43
|
name: serverName,
|
|
@@ -77,9 +77,10 @@ module ConfigLMM
|
|
|
77
77
|
server.iso_file = File.basename(iso)
|
|
78
78
|
end
|
|
79
79
|
server.save
|
|
80
|
-
activeState['Status'] =
|
|
80
|
+
activeState['Status'] = State::STATUS_CREATED
|
|
81
81
|
state.save
|
|
82
82
|
server.start
|
|
83
|
+
true
|
|
83
84
|
end
|
|
84
85
|
|
|
85
86
|
def dirPoolXML(name, path)
|
|
@@ -4,6 +4,7 @@ require 'uri'
|
|
|
4
4
|
require 'addressable/uri'
|
|
5
5
|
require 'addressable/idna'
|
|
6
6
|
require 'fog/powerdns'
|
|
7
|
+
require 'http'
|
|
7
8
|
|
|
8
9
|
module ConfigLMM
|
|
9
10
|
module LMM
|
|
@@ -48,11 +49,38 @@ module ConfigLMM
|
|
|
48
49
|
def actionPowerDNSDeploy(id, target, activeState, context, options)
|
|
49
50
|
#actionPowerDNSDiff(id, target, activeState, context, options)
|
|
50
51
|
|
|
51
|
-
deploySettings(target, options)
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
deploySettings(target, activeState, options)
|
|
53
|
+
connect(id, target, activeState, context, options) do |host, port, key|
|
|
54
|
+
if target['TSIG']
|
|
55
|
+
updateTSIG(host, port, key, target['TSIG'])
|
|
56
|
+
end
|
|
57
|
+
if target['DNS']
|
|
54
58
|
updateDNS(host, port, key, target['DNS'])
|
|
55
59
|
end
|
|
60
|
+
if target['Metadata']
|
|
61
|
+
updateMetadata(host, port, key, target['Metadata'])
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def cleanup(configs, state, context, options)
|
|
67
|
+
cleanupType(:PowerDNS, configs, state, context, options) do |item, id, state, context, options, ssh|
|
|
68
|
+
if item['Deploy']
|
|
69
|
+
Framework::LinuxApp.stopService(SERVICE_NAME, ssh, options[:dry])
|
|
70
|
+
Framework::LinuxApp.firewallRemoveService('dns', ssh, options[:dry])
|
|
71
|
+
Framework::LinuxApp.removePackage(PACKAGE_NAME, ssh, options[:dry])
|
|
72
|
+
|
|
73
|
+
state.item(id)['Status'] = State::STATUS_DELETED unless options[:dry]
|
|
74
|
+
|
|
75
|
+
if options[:destroy]
|
|
76
|
+
item['Database'] ||= {}
|
|
77
|
+
PostgreSQL.dropUserAndDB(item['Database'], USER, ssh, options[:dry])
|
|
78
|
+
rm('/etc/pdns', options[:dry], ssh)
|
|
79
|
+
state.item(id)['Status'] = State::STATUS_DESTROYED unless options[:dry]
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
# TODO
|
|
83
|
+
end
|
|
56
84
|
end
|
|
57
85
|
end
|
|
58
86
|
|
|
@@ -99,7 +127,10 @@ module ConfigLMM
|
|
|
99
127
|
canonicalDomain = domain + '.'
|
|
100
128
|
|
|
101
129
|
if !dns.list_zones(server).map { |zone| zone['name'].downcase }.include?(canonicalDomain.downcase)
|
|
102
|
-
dns.create_zone(server, canonicalDomain, [], { kind: 'Native' })
|
|
130
|
+
dns.create_zone(server, canonicalDomain, [], { kind: 'Native' }.update(info['!'].to_h))
|
|
131
|
+
elsif !info['!'].to_h.empty?
|
|
132
|
+
puts ({ kind: 'Native' }.update(info['!'].to_h).inspect)
|
|
133
|
+
dns.update_zone(server, canonicalDomain, { kind: 'Native' }.update(info['!'].to_h))
|
|
103
134
|
end
|
|
104
135
|
|
|
105
136
|
zone = dns.get_zone(server, canonicalDomain)
|
|
@@ -107,6 +138,7 @@ module ConfigLMM
|
|
|
107
138
|
rrsets = []
|
|
108
139
|
remove = []
|
|
109
140
|
info.each do |name, data|
|
|
141
|
+
next if name == '!'
|
|
110
142
|
fullName = Addressable::IDNA.to_ascii(name) + '.' + Addressable::IDNA.to_ascii(domain) + '.'
|
|
111
143
|
fullName = Addressable::IDNA.to_ascii(domain) + '.' if name == '@'
|
|
112
144
|
self.processDNS(domain, data).each do |type, records|
|
|
@@ -124,6 +156,15 @@ module ConfigLMM
|
|
|
124
156
|
priority, name = record[:content].split(' ')
|
|
125
157
|
name = Addressable::IDNA.to_ascii(name) + '.'
|
|
126
158
|
record[:content] = [priority, name].join(' ')
|
|
159
|
+
elsif type == 'SOA'
|
|
160
|
+
ns, email, serial, refresh, again, expire, ttl = record[:content].split(' ')
|
|
161
|
+
record[:content] = [Addressable::IDNA.to_ascii(ns) + '.',
|
|
162
|
+
Addressable::IDNA.to_ascii(email) + '.',
|
|
163
|
+
serial.to_s,
|
|
164
|
+
refresh.to_s,
|
|
165
|
+
again.to_s,
|
|
166
|
+
expire.to_s,
|
|
167
|
+
ttl.to_s].join(' ')
|
|
127
168
|
end
|
|
128
169
|
rrset[:records] << { content: record[:content], disabled: false }
|
|
129
170
|
end
|
|
@@ -139,6 +180,40 @@ module ConfigLMM
|
|
|
139
180
|
|
|
140
181
|
end
|
|
141
182
|
|
|
183
|
+
def updateTSIG(host, port, key, targetTSIG)
|
|
184
|
+
server = 'localhost'
|
|
185
|
+
url = "http://#{host}:#{port}/api/v1/servers/#{server}/tsigkeys"
|
|
186
|
+
headers = { 'X-Api-Key' => key }
|
|
187
|
+
targetTSIG.each do |name, info|
|
|
188
|
+
data = { name: name, algorithm: info['Algorithm'] }
|
|
189
|
+
response = HTTP.headers(headers).post(url, json: data)
|
|
190
|
+
if response.status == 201
|
|
191
|
+
result = response.parse(:json)
|
|
192
|
+
prompt.say("TSIG #{result['name']} key: #{result['key']}", :color => :magenta)
|
|
193
|
+
elsif response.status != 409
|
|
194
|
+
prompt.say(response.body.to_s, :color => :red)
|
|
195
|
+
raise 'Failed to create TSIG key!'
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
def updateMetadata(host, port, key, targetMetadata)
|
|
201
|
+
server = 'localhost'
|
|
202
|
+
headers = { 'X-Api-Key' => key }
|
|
203
|
+
targetMetadata.each do |zone, info|
|
|
204
|
+
info.each do |kind, metadata|
|
|
205
|
+
url = "http://#{host}:#{port}/api/v1/servers/#{server}/zones/#{Addressable::IDNA.to_ascii(zone)}/metadata/#{kind}"
|
|
206
|
+
metadata = [metadata] unless metadata.is_a?(Array)
|
|
207
|
+
data = { kind: kind, metadata: metadata }
|
|
208
|
+
response = HTTP.headers(headers).put(url, json: data)
|
|
209
|
+
if response.status != 200
|
|
210
|
+
prompt.say(response.body.to_s, :color => :red)
|
|
211
|
+
raise "Failed to update Metadata for #{zone}!"
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
142
217
|
def prepareSettings(target)
|
|
143
218
|
if !target['Settings'].key?('api')
|
|
144
219
|
target['Settings']['api'] = 'yes'
|
|
@@ -154,15 +229,19 @@ module ConfigLMM
|
|
|
154
229
|
end
|
|
155
230
|
end
|
|
156
231
|
|
|
157
|
-
def deploySettings(target, options)
|
|
232
|
+
def deploySettings(target, activeState, options)
|
|
158
233
|
if target['Location']
|
|
159
234
|
uri = Addressable::URI.parse(target['Location'])
|
|
160
235
|
params = {}
|
|
161
236
|
params = CGI.parse(uri.query) if uri.query
|
|
162
237
|
if uri.scheme == 'ssh' && !params.key?('host')
|
|
163
238
|
self.class.sshStart(uri) do |ssh|
|
|
164
|
-
|
|
165
|
-
|
|
239
|
+
target['Deploy'] = !!target['Settings'] unless target.key?('Deploy')
|
|
240
|
+
activeState['Deploy'] = target['Deploy']
|
|
241
|
+
if target['Deploy']
|
|
242
|
+
Framework::LinuxApp.ensurePackages([PACKAGE_NAME], ssh)
|
|
243
|
+
Framework::LinuxApp.ensureServiceAutoStartOverSSH(SERVICE_NAME, ssh)
|
|
244
|
+
end
|
|
166
245
|
if target['Settings']
|
|
167
246
|
prepareSettings(target)
|
|
168
247
|
self.class.sshExec!(ssh, "mkdir -p #{CONFIG_DIR}")
|
|
@@ -179,9 +258,15 @@ module ConfigLMM
|
|
|
179
258
|
end
|
|
180
259
|
self.configurePostgreSQL(target['Settings'], ssh)
|
|
181
260
|
end
|
|
182
|
-
|
|
261
|
+
if target['Deploy']
|
|
262
|
+
Framework::LinuxApp.firewallAddServiceOverSSH('dns', ssh)
|
|
263
|
+
Framework::LinuxApp.startServiceOverSSH(SERVICE_NAME, ssh)
|
|
264
|
+
activeState['Status'] = State::STATUS_DEPLOYED
|
|
265
|
+
end
|
|
183
266
|
end
|
|
184
267
|
end
|
|
268
|
+
else
|
|
269
|
+
# TODO
|
|
185
270
|
end
|
|
186
271
|
end
|
|
187
272
|
|
|
@@ -190,10 +275,12 @@ module ConfigLMM
|
|
|
190
275
|
if settings['gpgsql-host'] == 'localhost' || settings['gpgsql-host'].start_with?('/')
|
|
191
276
|
PostgreSQL.createUserAndDBOverSSH(USER, password, ssh)
|
|
192
277
|
PostgreSQL.importSQL(USER, USER, '/usr/share/doc/packages/pdns/schema.pgsql.sql', ssh)
|
|
278
|
+
PostgreSQL.updateOwner(USER, USER, ssh)
|
|
193
279
|
else
|
|
194
280
|
self.class.sshStart("ssh://#{settings['gpgsql-host']}/") do |ssh|
|
|
195
281
|
PostgreSQL.createUserAndDBOverSSH(USER, password, ssh)
|
|
196
282
|
PostgreSQL.importSQL(USER, USER, '/usr/share/doc/packages/pdns/schema.pgsql.sql', ssh)
|
|
283
|
+
PostgreSQL.updateOwner(USER, USER, ssh)
|
|
197
284
|
end
|
|
198
285
|
end
|
|
199
286
|
password
|
data/bootstrap.sh
CHANGED
|
@@ -10,6 +10,29 @@ function admin {
|
|
|
10
10
|
fi
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
if [ "$EUID" -ne "0" ]; then
|
|
14
|
+
if ! command -v sudo &> /dev/null; then
|
|
15
|
+
case $distro in
|
|
16
|
+
|
|
17
|
+
opensuse-leap)
|
|
18
|
+
echo "You don't have sudo! Enter root password to install it"
|
|
19
|
+
su root -c "zypper install --no-confirm sudo"
|
|
20
|
+
;;
|
|
21
|
+
|
|
22
|
+
arch)
|
|
23
|
+
echo "You don't have sudo! Enter root password to install it"
|
|
24
|
+
admin pacman -S --noconfirm --needed sudo
|
|
25
|
+
;;
|
|
26
|
+
|
|
27
|
+
*)
|
|
28
|
+
echo "Sudo not found but is needed!" >&2
|
|
29
|
+
echo "Don't know how to install it for your $distro distribution!" >&2
|
|
30
|
+
echo "Submit a PR :)" >&2
|
|
31
|
+
exit 3
|
|
32
|
+
;;
|
|
33
|
+
esac
|
|
34
|
+
fi
|
|
35
|
+
fi
|
|
13
36
|
|
|
14
37
|
case $distro in
|
|
15
38
|
|
|
@@ -43,12 +66,27 @@ if [ "$rubyTooOld" -eq "1" ]; then
|
|
|
43
66
|
echo "Ruby is too old! Will install RVM" >&2
|
|
44
67
|
gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB >/dev/null
|
|
45
68
|
curl -sSL https://get.rvm.io | bash -s stable --ruby=3.3.4
|
|
46
|
-
|
|
69
|
+
|
|
70
|
+
if [ "$EUID" -eq "0" ]; then
|
|
71
|
+
source /etc/profile.d/rvm.sh
|
|
72
|
+
else
|
|
73
|
+
source ~/.rvm/scripts/rvm
|
|
74
|
+
fi
|
|
47
75
|
|
|
48
76
|
if [ "$SHELL" = "/usr/bin/fish" ]; then
|
|
49
|
-
curl -
|
|
77
|
+
curl -sSL --create-dirs -o ~/.config/fish/functions/rvm.fish https://raw.github.com/lunks/fish-nuggets/master/functions/rvm.fish
|
|
78
|
+
sed -i "/rvm default/d" ~/.config/fish/config.fish
|
|
50
79
|
echo "rvm default" >> ~/.config/fish/config.fish
|
|
51
80
|
fi
|
|
52
81
|
fi
|
|
53
82
|
|
|
54
|
-
|
|
83
|
+
if [ "$EUID" -eq "0" ]; then
|
|
84
|
+
# This shouldn't be needed but without it doesn't work
|
|
85
|
+
export PATH=/usr/local/rvm/gems/ruby-3.3.4/bin:/usr/local/rvm/rubies/ruby-3.3.4/bin:$PATH
|
|
86
|
+
export GEM_HOME=/usr/local/rvm/gems/ruby-3.3.4
|
|
87
|
+
export GEM_PATH=/usr/local/rvm/gems/ruby-3.3.4
|
|
88
|
+
fi
|
|
89
|
+
|
|
90
|
+
bash -lc 'gem install ConfigLMM'
|
|
91
|
+
|
|
92
|
+
echo "You need to close and reopen your shell" >&2
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'tty-which'
|
|
4
|
+
|
|
3
5
|
module ConfigLMM
|
|
4
6
|
module Framework
|
|
5
7
|
|
|
@@ -8,20 +10,28 @@ module ConfigLMM
|
|
|
8
10
|
LINUX_FOLDER = __dir__ + '/../../../../Plugins/OS/Linux/'
|
|
9
11
|
SUSE_NAME = 'openSUSE Leap'
|
|
10
12
|
SUSE_ID = 'opensuse-leap'
|
|
13
|
+
DEBIAN_NAME = 'Debian'
|
|
14
|
+
PROXMOXVE_NAME = 'Proxmox VE'
|
|
11
15
|
PODMAN_PACKAGE = 'Podman'
|
|
12
16
|
SYSTEMD_CONTAINERS_PATH = '~/.config/containers/systemd/'
|
|
13
17
|
|
|
14
|
-
def ensurePackage(name, location)
|
|
15
|
-
|
|
18
|
+
def ensurePackage(name, location, binary = nil)
|
|
19
|
+
self.class.ensurePackage(name, location, binary)
|
|
16
20
|
end
|
|
17
21
|
|
|
18
22
|
def ensurePackages(names, location)
|
|
19
|
-
|
|
23
|
+
self.class.ensurePackages(names, location)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.ensurePackage(name, location, binary = nil)
|
|
27
|
+
if binary && TTY::Which.which(binary)
|
|
28
|
+
return
|
|
29
|
+
end
|
|
30
|
+
self.ensurePackages([name], location)
|
|
20
31
|
end
|
|
21
32
|
|
|
22
33
|
def self.ensurePackages(names, locationOrSSH)
|
|
23
|
-
|
|
24
|
-
closure = Proc.new do |ssh|
|
|
34
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
25
35
|
distroInfo = self.currentDistroInfo(ssh)
|
|
26
36
|
reposPackages = self.mapPackages(names, distroInfo['Name'])
|
|
27
37
|
|
|
@@ -51,20 +61,43 @@ module ConfigLMM
|
|
|
51
61
|
end
|
|
52
62
|
distroInfo
|
|
53
63
|
end
|
|
64
|
+
end
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
|
66
|
+
def self.removePackage(name, locationOrSSH, dry = false)
|
|
67
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
68
|
+
distroInfo = self.currentDistroInfo(ssh)
|
|
69
|
+
reposPackages = self.mapPackages([name], distroInfo['Name'])
|
|
60
70
|
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
pkgs = []
|
|
72
|
+
reposPackages.each do |pkg|
|
|
73
|
+
if pkg.include?('|')
|
|
74
|
+
repoName, pkg = pkg.split('|')
|
|
75
|
+
pkgs << pkg
|
|
76
|
+
else
|
|
77
|
+
pkgs << pkg
|
|
78
|
+
end
|
|
63
79
|
end
|
|
64
|
-
|
|
65
|
-
|
|
80
|
+
|
|
81
|
+
command = distroInfo['RemovePackage'] + ' ' + pkgs.map { |pkg| pkg.shellescape }.join(' ')
|
|
82
|
+
if ssh
|
|
83
|
+
self.sshExec!(ssh, command, true, dry)
|
|
84
|
+
else
|
|
85
|
+
if `echo $EUID`.strip == '0'
|
|
86
|
+
if dry
|
|
87
|
+
puts "Would execute: #{command} >/dev/null"
|
|
88
|
+
else
|
|
89
|
+
`#{command} >/dev/null`
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
if dry
|
|
93
|
+
puts "Would execute: sudo #{command} >/dev/null"
|
|
94
|
+
else
|
|
95
|
+
`sudo #{command} >/dev/null`
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
distroInfo
|
|
66
100
|
end
|
|
67
|
-
distroInfo
|
|
68
101
|
end
|
|
69
102
|
|
|
70
103
|
def ensureServiceAutoStart(name, location)
|
|
@@ -77,90 +110,138 @@ module ConfigLMM
|
|
|
77
110
|
end
|
|
78
111
|
end
|
|
79
112
|
|
|
80
|
-
def self.
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
command = distroInfo['AutoStartService'] + ' ' + name.shellescape
|
|
85
|
-
self.sshExec!(ssh, command)
|
|
86
|
-
end
|
|
113
|
+
def self.ensureServiceAutoStart(name, locationOrSSH)
|
|
114
|
+
self.execDistroCommand(name, 'AutoStartService', locationOrSSH)
|
|
115
|
+
end
|
|
87
116
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
end
|
|
92
|
-
else
|
|
93
|
-
closure.call(locationOrSSH)
|
|
94
|
-
end
|
|
117
|
+
# Deprecated
|
|
118
|
+
def self.ensureServiceAutoStartOverSSH(name, locationOrSSH)
|
|
119
|
+
self.ensureServiceAutoStart(name, locationOrSSH)
|
|
95
120
|
end
|
|
96
121
|
|
|
97
|
-
def startService(name, location)
|
|
122
|
+
def startService(name, location, dry = false)
|
|
98
123
|
if location && location != '@me'
|
|
99
124
|
uri = Addressable::URI.parse(location)
|
|
100
125
|
raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
|
101
|
-
self.class.
|
|
126
|
+
self.class.startService(name, location, dry = false)
|
|
102
127
|
else
|
|
103
128
|
# TODO
|
|
104
129
|
end
|
|
105
130
|
end
|
|
106
131
|
|
|
107
|
-
def self.
|
|
108
|
-
|
|
109
|
-
|
|
132
|
+
def self.startService(name, locationOrSSH, dry = false)
|
|
133
|
+
self.execDistroCommand(name, 'StartService', locationOrSSH, false, dry)
|
|
134
|
+
end
|
|
110
135
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
136
|
+
# Deprecated
|
|
137
|
+
def self.startServiceOverSSH(name, locationOrSSH, dry = false)
|
|
138
|
+
self.startService(name, locationOrSSH, dry)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def self.restartService(name, locationOrSSH, dry = false)
|
|
142
|
+
self.execDistroCommand(name, 'RestartService', locationOrSSH, false, dry)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def self.reloadService(name, locationOrSSH, dry = false)
|
|
146
|
+
self.execDistroCommand(name, 'ReloadService', locationOrSSH, false, dry)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def self.stopService(name, locationOrSSH, dry = false)
|
|
150
|
+
self.execDistroCommand(name, 'StopService', locationOrSSH, true, dry)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def self.disableService(name, locationOrSSH, dry = false)
|
|
154
|
+
self.execDistroCommand(name, 'DisableService', locationOrSSH, true, dry)
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def self.reloadServiceManager(locationOrSSH, dry = false)
|
|
158
|
+
self.execDistroCommand(nil, 'ReloadServiceManager', locationOrSSH, false, dry)
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def self.deleteUserAndGroup(name, locationOrSSH, dry = false)
|
|
162
|
+
self.execDistroCommand(name, 'DeleteUser', locationOrSSH, true, dry)
|
|
163
|
+
self.execDistroCommand(name, 'DeleteGroup', locationOrSSH, true, dry)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def self.execDistroCommand(param, commandName, locationOrSSH, allowFailure = false, dry = false)
|
|
167
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
168
|
+
distroInfo = self.currentDistroInfo(ssh)
|
|
169
|
+
|
|
170
|
+
command = distroInfo[commandName]
|
|
171
|
+
command += ' ' + param.shellescape unless param.nil?
|
|
172
|
+
self.exec(command, ssh, allowFailure, dry)
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def self.doSSH(locationOrSSH, &block)
|
|
177
|
+
if locationOrSSH.nil? || locationOrSSH == '@me'
|
|
178
|
+
result = block.call(nil)
|
|
179
|
+
elsif locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
|
|
180
|
+
uri = Addressable::URI.parse(locationOrSSH)
|
|
181
|
+
raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
|
114
182
|
|
|
115
|
-
if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
|
|
116
183
|
self.sshStart(locationOrSSH) do |ssh|
|
|
117
|
-
|
|
184
|
+
result = block.call(ssh)
|
|
118
185
|
end
|
|
119
186
|
else
|
|
120
|
-
|
|
187
|
+
result = block.call(locationOrSSH)
|
|
121
188
|
end
|
|
189
|
+
result
|
|
122
190
|
end
|
|
123
191
|
|
|
124
|
-
|
|
192
|
+
# Deprecated
|
|
125
193
|
def self.firewallAddServiceOverSSH(serviceName, locationOrSSH)
|
|
126
|
-
|
|
194
|
+
self.firewallAddService(serviceName, locationOrSSH)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Deprecated
|
|
198
|
+
def self.firewallAddPortOverSSH(portName, locationOrSSH)
|
|
199
|
+
self.firewallAddPort(portName, locationOrSSH)
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
def self.firewallAddService(serviceName, locationOrSSH = nil, dry = false)
|
|
203
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
127
204
|
command = 'firewall-cmd --permanent --add-service ' + serviceName.shellescape
|
|
128
|
-
self.
|
|
205
|
+
self.exec(command, ssh, true, dry)
|
|
129
206
|
command = 'firewall-cmd --add-service ' + serviceName.shellescape
|
|
130
|
-
self.
|
|
131
|
-
|
|
207
|
+
self.exec(command, ssh, true, dry)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
132
210
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
211
|
+
def self.firewallRemoveService(serviceName, locationOrSSH = nil, dry = false)
|
|
212
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
213
|
+
command = 'firewall-cmd --permanent --remove-service ' + serviceName.shellescape
|
|
214
|
+
self.exec(command, ssh, false, dry)
|
|
215
|
+
command = 'firewall-cmd --remove-service ' + serviceName.shellescape
|
|
216
|
+
self.exec(command, ssh, false, dry)
|
|
139
217
|
end
|
|
140
218
|
end
|
|
141
219
|
|
|
142
|
-
def self.
|
|
143
|
-
|
|
220
|
+
def self.firewallAddPort(portName, locationOrSSH = nil, dry = false)
|
|
221
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
144
222
|
command = 'firewall-cmd --permanent --add-port ' + portName.shellescape
|
|
145
|
-
self.
|
|
223
|
+
self.exec(command, ssh, true, dry)
|
|
146
224
|
command = 'firewall-cmd --add-port ' + portName.shellescape
|
|
147
|
-
self.
|
|
148
|
-
|
|
225
|
+
self.exec(command, ssh, true, dry)
|
|
226
|
+
end
|
|
227
|
+
end
|
|
149
228
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
229
|
+
def self.firewallRemovePort(portName, locationOrSSH = nil, dry = false)
|
|
230
|
+
self.doSSH(locationOrSSH) do |ssh|
|
|
231
|
+
command = 'firewall-cmd --permanent --remove-port ' + portName.shellescape
|
|
232
|
+
self.exec(command, ssh, false, dry)
|
|
233
|
+
command = 'firewall-cmd --remove-port ' + portName.shellescape
|
|
234
|
+
self.exec(command, ssh, false, dry)
|
|
156
235
|
end
|
|
157
236
|
end
|
|
158
237
|
|
|
159
238
|
def self.mapPackages(packages, distroName)
|
|
160
|
-
|
|
239
|
+
allPackages = YAML.load_file(LINUX_FOLDER + 'Packages.yaml')
|
|
161
240
|
names = []
|
|
241
|
+
raise "Distro '#{distroName}' not implemented!" unless allPackages.key?(distroName)
|
|
242
|
+
distroPackages = allPackages[distroName].to_h
|
|
162
243
|
packages.to_a.each do |pkg|
|
|
163
|
-
packageName = distroPackages[
|
|
244
|
+
packageName = distroPackages[pkg]
|
|
164
245
|
if packageName
|
|
165
246
|
if packageName.is_a?(Array)
|
|
166
247
|
names += packageName
|
|
@@ -189,6 +270,7 @@ module ConfigLMM
|
|
|
189
270
|
Framework::LinuxApp.ensurePackages([PODMAN_PACKAGE], ssh)
|
|
190
271
|
addUserCmd = "#{distroInfo['CreateServiceUser']} --home-dir '#{homedir}' --create-home --comment '#{userComment}' #{user}"
|
|
191
272
|
self.sshExec!(ssh, addUserCmd, true)
|
|
273
|
+
self.sshExec!(ssh, "chmod o-rwx #{homedir}")
|
|
192
274
|
self.createSubuidsOverSSH(user, distroInfo, ssh)
|
|
193
275
|
self.sshExec!(ssh, "loginctl enable-linger #{user}")
|
|
194
276
|
self.sshExec!(ssh, "su --login #{user} --shell /bin/sh --command 'mkdir -p #{SYSTEMD_CONTAINERS_PATH}'")
|
|
@@ -25,6 +25,7 @@ module ConfigLMM
|
|
|
25
25
|
target = target.dup
|
|
26
26
|
target['NginxVersion'] = 0 unless target['NginxVersion']
|
|
27
27
|
template = ERB.new(File.read(dir + '/' + name + '.conf.erb'))
|
|
28
|
+
name = target['ConfigName'] if target['ConfigName']
|
|
28
29
|
renderTemplate(template, target, outputFolder + '/nginx/servers-lmm/' + name + '.conf', options)
|
|
29
30
|
end
|
|
30
31
|
|
|
@@ -36,15 +37,19 @@ module ConfigLMM
|
|
|
36
37
|
raise Framework::PluginProcessError.new("Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
|
37
38
|
self.class.sshStart(uri) do |ssh|
|
|
38
39
|
self.class.uploadFolder(outputFolder, CONFIG_DIR, ssh)
|
|
39
|
-
|
|
40
|
+
if target['TLS']
|
|
41
|
+
Framework::LinuxApp.firewallAddServiceOverSSH('https', ssh)
|
|
42
|
+
else
|
|
43
|
+
Framework::LinuxApp.firewallAddServiceOverSSH('http', ssh)
|
|
44
|
+
end
|
|
40
45
|
end
|
|
41
46
|
else
|
|
42
47
|
copy(outputFolder, CONFIG_DIR, options['dry'])
|
|
43
48
|
end
|
|
44
49
|
end
|
|
45
50
|
|
|
46
|
-
def cleanupNginxConfig(name, id, state, context, options)
|
|
47
|
-
rm('/etc/nginx/servers-lmm/' + name + '.conf', options['dry'])
|
|
51
|
+
def cleanupNginxConfig(name, id, state, context, options, ssh = nil)
|
|
52
|
+
rm('/etc/nginx/servers-lmm/' + name + '.conf', options['dry'], ssh)
|
|
48
53
|
end
|
|
49
54
|
|
|
50
55
|
def self.prepareNginxConfig(target, ssh = nil)
|
|
@@ -55,6 +60,32 @@ module ConfigLMM
|
|
|
55
60
|
end
|
|
56
61
|
end
|
|
57
62
|
|
|
63
|
+
def self.reload(ssh = nil, dry = false)
|
|
64
|
+
self.exec("systemctl reload nginx", ssh, false, dry)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def self.ensurePackage(ssh = nil)
|
|
68
|
+
Framework::LinuxApp.ensurePackages([NGINX_PACKAGE], ssh)
|
|
69
|
+
Framework::LinuxApp.ensureServiceAutoStartOverSSH(NGINX_PACKAGE, ssh)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def useNginxProxy(dir, configName, id, target, activeState, state, context, options, ssh)
|
|
73
|
+
self.class.ensurePackage(ssh)
|
|
74
|
+
self.class.prepareNginxConfig(target, ssh)
|
|
75
|
+
self.writeNginxConfig(dir, configName, id, target, state, context, options)
|
|
76
|
+
self.deployNginxConfig(id, target, activeState, context, options)
|
|
77
|
+
Framework::LinuxApp.startServiceOverSSH(NGINX_PACKAGE, ssh)
|
|
78
|
+
self.class.reload(ssh)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def deployNginxProxyConfig(server, name, id, target, activeState, state, context, options, ssh)
|
|
82
|
+
target = target.dup
|
|
83
|
+
target['Proxy'] = server
|
|
84
|
+
target['Name'] = name if name
|
|
85
|
+
target['ConfigName'] = target['Name']
|
|
86
|
+
useNginxProxy(__dir__ + '/../../../../Plugins/Apps/Nginx', 'proxy', id, target, activeState, state, context, options, ssh)
|
|
87
|
+
end
|
|
88
|
+
|
|
58
89
|
private
|
|
59
90
|
|
|
60
91
|
def updateTargetConfig(target)
|