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.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +14 -0
  4. data/Plugins/Apps/Authentik/Authentik-Server.container +1 -0
  5. data/Plugins/Apps/Authentik/Authentik-Worker.container +1 -0
  6. data/Plugins/Apps/Authentik/Authentik.conf.erb +7 -0
  7. data/Plugins/Apps/Authentik/Authentik.lmm.rb +24 -2
  8. data/Plugins/Apps/BookStack/BookStack.conf.erb +41 -0
  9. data/Plugins/Apps/BookStack/BookStack.container +15 -0
  10. data/Plugins/Apps/BookStack/BookStack.lmm.rb +80 -0
  11. data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +17 -0
  12. data/Plugins/Apps/Discourse/Discourse.conf.erb +41 -0
  13. data/Plugins/Apps/Discourse/Discourse.container +17 -0
  14. data/Plugins/Apps/Discourse/Discourse.lmm.rb +95 -0
  15. data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +25 -2
  16. data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +19 -0
  17. data/Plugins/Apps/ERPNext/ERPNext-Queue.container +17 -0
  18. data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +17 -0
  19. data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +19 -0
  20. data/Plugins/Apps/ERPNext/ERPNext.container +18 -0
  21. data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +193 -0
  22. data/Plugins/Apps/ERPNext/ERPNext.network +12 -0
  23. data/Plugins/Apps/ERPNext/sites/apps.json +10 -0
  24. data/Plugins/Apps/ERPNext/sites/apps.txt +3 -0
  25. data/Plugins/Apps/ERPNext/sites/common_site_config.json +11 -0
  26. data/Plugins/Apps/GitLab/GitLab.container +3 -2
  27. data/Plugins/Apps/GitLab/GitLab.lmm.rb +37 -12
  28. data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +57 -0
  29. data/Plugins/Apps/LetsEncrypt/hooks/dovecot.sh +2 -0
  30. data/Plugins/Apps/LetsEncrypt/hooks/nginx.sh +2 -0
  31. data/Plugins/Apps/LetsEncrypt/hooks/postfix.sh +2 -0
  32. data/Plugins/Apps/LetsEncrypt/renew-certificates.service +7 -0
  33. data/Plugins/Apps/LetsEncrypt/renew-certificates.timer +12 -0
  34. data/Plugins/Apps/LetsEncrypt/rfc2136.ini +11 -0
  35. data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +115 -0
  36. data/Plugins/Apps/Matrix/Element.container +14 -0
  37. data/Plugins/Apps/Matrix/Matrix.conf.erb +49 -5
  38. data/Plugins/Apps/Matrix/Matrix.lmm.rb +86 -1
  39. data/Plugins/Apps/Matrix/Synapse.container +17 -0
  40. data/Plugins/Apps/Matrix/config.json +50 -0
  41. data/Plugins/Apps/Matrix/homeserver.yaml +70 -0
  42. data/Plugins/Apps/Matrix/log.config +30 -0
  43. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +70 -45
  44. data/Plugins/Apps/Nginx/conf.d/configlmm.conf +9 -0
  45. data/Plugins/Apps/Nginx/config-lmm/errors.conf +10 -4
  46. data/Plugins/Apps/Nginx/config-lmm/proxy.conf +5 -1
  47. data/Plugins/Apps/Nginx/nginx.lmm.rb +15 -11
  48. data/Plugins/Apps/Nginx/proxy.conf.erb +13 -3
  49. data/Plugins/Apps/Odoo/Odoo.container +2 -1
  50. data/Plugins/Apps/Odoo/Odoo.lmm.rb +1 -1
  51. data/Plugins/Apps/OpenVidu/Ingress.container +18 -0
  52. data/Plugins/Apps/OpenVidu/OpenVidu.conf.erb +34 -0
  53. data/Plugins/Apps/OpenVidu/OpenVidu.container +16 -0
  54. data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +90 -0
  55. data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +35 -0
  56. data/Plugins/Apps/OpenVidu/OpenViduCall.container +15 -0
  57. data/Plugins/Apps/OpenVidu/ingress.yaml +10 -0
  58. data/Plugins/Apps/OpenVidu/livekit.yaml +13 -0
  59. data/Plugins/Apps/Peppermint/Peppermint.conf.erb +0 -4
  60. data/Plugins/Apps/Peppermint/Peppermint.container +2 -1
  61. data/Plugins/Apps/Postfix/Postfix.lmm.rb +32 -6
  62. data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +80 -11
  63. data/Plugins/Apps/Roundcube/Roundcube.conf.erb +75 -0
  64. data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +145 -0
  65. data/Plugins/Apps/Tunnel/tunnel.lmm.rb +63 -0
  66. data/Plugins/Apps/Tunnel/tunnelTCP.service +9 -0
  67. data/Plugins/Apps/Tunnel/tunnelTCP.socket +9 -0
  68. data/Plugins/Apps/Tunnel/tunnelUDP.service +9 -0
  69. data/Plugins/Apps/Tunnel/tunnelUDP.socket +9 -0
  70. data/Plugins/Apps/Valkey/Valkey.lmm.rb +32 -0
  71. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +4 -0
  72. data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +42 -0
  73. data/Plugins/Apps/Wiki.js/Wiki.js.container +15 -0
  74. data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +61 -0
  75. data/Plugins/Apps/gollum/gollum.conf.erb +39 -1
  76. data/Plugins/Apps/gollum/gollum.container +4 -1
  77. data/Plugins/Apps/gollum/gollum.lmm.rb +11 -3
  78. data/Plugins/OS/Linux/Debian/preseed.cfg.erb +62 -0
  79. data/Plugins/OS/Linux/Distributions.yaml +32 -0
  80. data/Plugins/OS/Linux/Flavours.yaml +11 -0
  81. data/Plugins/OS/Linux/Linux.lmm.rb +255 -67
  82. data/Plugins/OS/Linux/Packages.yaml +47 -2
  83. data/Plugins/OS/Linux/Proxmox/answer.toml.erb +30 -0
  84. data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +33 -4
  85. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +1 -1
  86. data/Plugins/Platforms/libvirt/libvirt.lmm.rb +3 -2
  87. data/Plugins/Services/DNS/PowerDNS.lmm.rb +95 -8
  88. data/bootstrap.sh +41 -3
  89. data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +146 -64
  90. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +34 -3
  91. data/lib/ConfigLMM/Framework/plugins/plugin.rb +62 -6
  92. data/lib/ConfigLMM/cli.rb +3 -1
  93. data/lib/ConfigLMM/commands/cleanup.rb +1 -0
  94. data/lib/ConfigLMM/commands/configsCommand.rb +3 -1
  95. data/lib/ConfigLMM/io/configList.rb +3 -1
  96. data/lib/ConfigLMM/state.rb +10 -2
  97. data/lib/ConfigLMM/version.rb +1 -1
  98. metadata +54 -3
  99. 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'] = 'CREATED'
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
- if target['DNS']
53
- connect(id, target, activeState, context, options) do |host, port, key|
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
- Framework::LinuxApp.ensurePackages([PACKAGE_NAME], ssh)
165
- Framework::LinuxApp.ensureServiceAutoStartOverSSH(SERVICE_NAME, ssh)
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
- Framework::LinuxApp.startServiceOverSSH(SERVICE_NAME, ssh)
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
- source /etc/profile.d/rvm.sh
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 -L --create-dirs -o ~/.config/fish/functions/rvm.fish https://raw.github.com/lunks/fish-nuggets/master/functions/rvm.fish
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
- gem install ConfigLMM
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
- self.class.ensurePackages([name], location)
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
- self.class.ensurePackages(names, location)
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
- distroInfo = nil
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
- if locationOrSSH.nil? || locationOrSSH == '@me'
56
- distroInfo = closure.call(nil)
57
- elsif locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
58
- uri = Addressable::URI.parse(locationOrSSH)
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
- self.sshStart(locationOrSSH) do |ssh|
62
- distroInfo = closure.call(ssh)
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
- else
65
- distroInfo = closure.call(locationOrSSH)
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.ensureServiceAutoStartOverSSH(name, locationOrSSH)
81
- closure = Proc.new do |ssh|
82
- distroInfo = self.currentDistroInfo(ssh)
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
- if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
89
- self.sshStart(locationOrSSH) do |ssh|
90
- closure.call(ssh)
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.startServiceOverSSH(name, location)
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.startServiceOverSSH(name, locationOrSSH)
108
- closure = Proc.new do |ssh|
109
- distroInfo = self.currentDistroInfo(ssh)
132
+ def self.startService(name, locationOrSSH, dry = false)
133
+ self.execDistroCommand(name, 'StartService', locationOrSSH, false, dry)
134
+ end
110
135
 
111
- command = distroInfo['StartService'] + ' ' + name.shellescape
112
- self.sshExec!(ssh, command)
113
- end
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
- closure.call(ssh)
184
+ result = block.call(ssh)
118
185
  end
119
186
  else
120
- closure.call(locationOrSSH)
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
- closure = Proc.new do |ssh|
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.sshExec!(ssh, command, true)
205
+ self.exec(command, ssh, true, dry)
129
206
  command = 'firewall-cmd --add-service ' + serviceName.shellescape
130
- self.sshExec!(ssh, command, true)
131
- end
207
+ self.exec(command, ssh, true, dry)
208
+ end
209
+ end
132
210
 
133
- if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
134
- self.sshStart(locationOrSSH) do |ssh|
135
- closure.call(ssh)
136
- end
137
- else
138
- closure.call(locationOrSSH)
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.firewallAddPortOverSSH(portName, locationOrSSH)
143
- closure = Proc.new do |ssh|
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.sshExec!(ssh, command, true)
223
+ self.exec(command, ssh, true, dry)
146
224
  command = 'firewall-cmd --add-port ' + portName.shellescape
147
- self.sshExec!(ssh, command, true)
148
- end
225
+ self.exec(command, ssh, true, dry)
226
+ end
227
+ end
149
228
 
150
- if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
151
- self.sshStart(locationOrSSH) do |ssh|
152
- closure.call(ssh)
153
- end
154
- else
155
- closure.call(locationOrSSH)
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
- distroPackages = YAML.load_file(LINUX_FOLDER + 'Packages.yaml')
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[distroName][pkg]
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
- Framework::LinuxApp.firewallAddServiceOverSSH('https', ssh)
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)