ConfigLMM 0.2.0 → 0.3.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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/Examples/Implemented.mm.yaml +75 -1
  4. data/Plugins/Apps/Authentik/Authentik-Server.container +18 -0
  5. data/Plugins/Apps/Authentik/Authentik-Worker.container +17 -0
  6. data/Plugins/Apps/Authentik/Authentik.conf.erb +35 -0
  7. data/Plugins/Apps/Authentik/Authentik.lmm.rb +73 -0
  8. data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +41 -0
  9. data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +148 -0
  10. data/Plugins/Apps/GitLab/GitLab.conf.erb +26 -0
  11. data/Plugins/Apps/GitLab/GitLab.container +17 -0
  12. data/Plugins/Apps/GitLab/GitLab.lmm.rb +75 -0
  13. data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +48 -10
  14. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +59 -2
  15. data/Plugins/Apps/Nextcloud/config.php +18 -0
  16. data/Plugins/Apps/Nginx/conf.d/configlmm.conf +62 -0
  17. data/Plugins/Apps/Nginx/config-lmm/errors.conf +1 -1
  18. data/Plugins/Apps/Nginx/main.conf.erb +31 -0
  19. data/Plugins/Apps/Nginx/nginx.conf +3 -68
  20. data/Plugins/Apps/Nginx/nginx.lmm.rb +71 -14
  21. data/Plugins/Apps/Odoo/Odoo.conf.erb +30 -13
  22. data/Plugins/Apps/Odoo/Odoo.container +17 -0
  23. data/Plugins/Apps/Odoo/Odoo.lmm.rb +62 -2
  24. data/Plugins/Apps/Odoo/odoo.conf +37 -0
  25. data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +95 -0
  26. data/Plugins/Apps/Peppermint/Peppermint.conf.erb +64 -0
  27. data/Plugins/Apps/Peppermint/Peppermint.container +14 -0
  28. data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +58 -0
  29. data/Plugins/Apps/Postfix/Postfix.lmm.rb +139 -31
  30. data/Plugins/Apps/Postfix/smtpd.conf +3 -0
  31. data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +172 -23
  32. data/Plugins/Apps/SSH/SSH.lmm.rb +51 -0
  33. data/Plugins/Apps/UVdesk/UVdesk.conf.erb +52 -0
  34. data/Plugins/Apps/UVdesk/UVdesk.lmm.rb +85 -0
  35. data/Plugins/Apps/Valkey/Valkey.lmm.rb +2 -1
  36. data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +35 -18
  37. data/Plugins/Apps/Vaultwarden/Vaultwarden.container +16 -0
  38. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +42 -3
  39. data/Plugins/Apps/gollum/gollum.conf.erb +45 -18
  40. data/Plugins/Apps/gollum/gollum.container +12 -0
  41. data/Plugins/Apps/gollum/gollum.lmm.rb +39 -10
  42. data/Plugins/OS/Linux/Distributions.yaml +10 -0
  43. data/Plugins/OS/Linux/Linux.lmm.rb +145 -12
  44. data/Plugins/OS/Linux/Packages.yaml +42 -4
  45. data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +108 -0
  46. data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +15 -0
  47. data/Plugins/OS/Linux/systemd/systemd.lmm.rb +28 -0
  48. data/Plugins/OS/Linux/systemd/user-0.slice +9 -0
  49. data/Plugins/OS/Linux/systemd/user@.service.d/delegate.conf +3 -0
  50. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +6 -2
  51. data/Plugins/Services/DNS/PowerDNS.lmm.rb +69 -6
  52. data/README.md +6 -0
  53. data/bootstrap.sh +54 -0
  54. data/lib/ConfigLMM/Framework/plugins/dns.rb +1 -2
  55. data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +157 -35
  56. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +24 -6
  57. data/lib/ConfigLMM/Framework/plugins/plugin.rb +52 -12
  58. data/lib/ConfigLMM/version.rb +1 -1
  59. metadata +31 -3
  60. data/Plugins/Apps/Nginx/main.conf +0 -30
@@ -1,22 +1,37 @@
1
1
 
2
2
  server {
3
3
 
4
- <% if !config['TLS'] %>
5
- listen <%= config['Port'] %>;
6
- listen [::]:<%= config['Port'] %>;
7
- <% else %>
8
- listen <%= config['Port'] %> ssl;
9
- listen [::]:<%= config['Port'] %> ssl;
4
+ <% if config['NginxVersion'] >= 1.25 %>
5
+ <% if !config['TLS'] %>
6
+ listen <%= config['Port'] %>;
7
+ listen [::]:<%= config['Port'] %>;
8
+ <% else %>
9
+ listen <%= config['Port'] %> ssl;
10
+ listen [::]:<%= config['Port'] %> ssl;
11
+
12
+ include config-lmm/ssl.conf;
13
+ <% end %>
10
14
  http2 on;
11
- include config-lmm/ssl.conf;
15
+ http3 on;
16
+ quic_retry on;
17
+ add_header Alt-Svc 'h3=":443"; ma=86400';
18
+ <% else %>
19
+ <% if !config['TLS'] %>
20
+ listen <%= config['Port'] %>;
21
+ listen [::]:<%= config['Port'] %>;
22
+ <% else %>
23
+ listen <%= config['Port'] %> ssl http2;
24
+ listen [::]:<%= config['Port'] %> ssl http2;
25
+
26
+ include config-lmm/ssl.conf;
27
+ <% end %>
12
28
  <% end %>
13
29
 
14
30
  server_name <%= config['Domain'] %>;
15
31
 
16
- root <%= config['Root'] %>;
17
- passenger_app_root /srv/gollum;
18
-
19
- try_files $uri @Passenger;
32
+ <% if config['Root'] %>
33
+ root <%= config['Root'] %>;
34
+ <% end %>
20
35
 
21
36
  access_log /var/log/nginx/gollum.access.log;
22
37
  error_log /var/log/nginx/gollum.error.log;
@@ -30,12 +45,24 @@ server {
30
45
  ssl_trusted_certificate "/etc/letsencrypt/live/<%= config['CertName'] %>/chain.pem";
31
46
  <% end %>
32
47
 
33
- location @Passenger {
34
- passenger_enabled on;
35
- passenger_min_instances 1;
36
- rails_env production;
48
+ <% if config['Passenger'] %>
49
+ passenger_app_root /srv/gollum;
37
50
 
38
- #passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;
39
- #limit_req zone=one burst=5;
40
- }
51
+ try_files $uri @Passenger;
52
+
53
+ location @Passenger {
54
+ passenger_enabled on;
55
+ passenger_min_instances 1;
56
+ rails_env production;
57
+
58
+ #passenger_set_cgi_param HTTP_X_FORWARDED_PROTO https;
59
+ #limit_req zone=one burst=5;
60
+ }
61
+ <% else %>
62
+ location / {
63
+ proxy_pass <%= config['Server'] %>;
64
+
65
+ include config-lmm/proxy.conf;
66
+ }
67
+ <% end %>
41
68
  }
@@ -0,0 +1,12 @@
1
+ [Unit]
2
+ Description=gollum container
3
+ After=local-fs.target
4
+
5
+ [Container]
6
+ Image=gollumwiki/gollum:master
7
+ PublishPort=0.0.0.0:14567:4567
8
+ UserNS=keep-id:uid=1000,gid=1000
9
+ Volume=/srv/gollum/repo:/wiki
10
+
11
+ [Install]
12
+ WantedBy=multi-user.target default.target
@@ -4,12 +4,11 @@ module ConfigLMM
4
4
  class Gollum < Framework::NginxApp
5
5
 
6
6
  NAME = 'gollum'
7
+ USER = 'gollum'
7
8
  GOLLUM_PATH = '/srv/gollum'
9
+ HOME_DIR = '/var/lib/authentik'
8
10
 
9
11
  def actionGollumBuild(id, target, activeState, context, options)
10
- if !target['Root'] && (!target['Location'] || target['Location'] == '@me')
11
- target['Root'] = File.dirname(`gem which gollum`.strip) + '/gollum/public'
12
- end
13
12
  writeNginxConfig(__dir__, NAME, id, target, activeState, context, options)
14
13
  targetDir = options['output'] + GOLLUM_PATH
15
14
  mkdir(targetDir, options['dry'])
@@ -22,16 +21,46 @@ module ConfigLMM
22
21
  end
23
22
 
24
23
  def actionGollumDeploy(id, target, activeState, context, options)
25
- if !target['Location'] || target['Location'] == '@me'
24
+ if target['Location'] && target['Location'] != '@me'
25
+ uri = Addressable::URI.parse(target['Location'])
26
+ self.class.sshStart(uri) do |ssh|
27
+ if !target.key?('Proxy') || !!target['Proxy']
28
+ self.class.prepareNginxConfig(target, ssh)
29
+ if !target['Root']
30
+ gollumPath = ssh.exec!('gem which gollum').strip
31
+ target['Root'] = File.dirname(gollumPath) + '/gollum/public'
32
+ end
33
+ writeNginxConfig(__dir__, NAME, id, target, state, context, options)
34
+ deployNginxConfig(id, target, activeState, context, options)
35
+ end
36
+ if !target.key?('Proxy') || target['Proxy'] != 'only'
37
+ distroInfo = Framework::LinuxApp.currentDistroInfo(ssh)
38
+ Framework::LinuxApp.configurePodmanServiceOverSSH(USER, GOLLUM_PATH, 'gollum', distroInfo, ssh)
39
+ self.class.uploadFolder(options['output'] + GOLLUM_PATH, '/srv', ssh)
40
+ path = Framework::LinuxApp::SYSTEMD_CONTAINERS_PATH.gsub('~', GOLLUM_PATH)
41
+ ssh.scp.upload!(__dir__ + '/gollum.container', path)
42
+ self.class.sshExec!(ssh, "chown -R #{USER}:#{USER} #{GOLLUM_PATH}")
43
+ self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ daemon-reload")
44
+ self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ start gollum")
45
+ end
46
+ end
47
+ else
26
48
  targetDir = GOLLUM_PATH
27
49
  mkdir(targetDir, options['dry'])
28
- deployNginxConfig(id, target, activeState, context, options)
29
- copy(options['output'] + GOLLUM_PATH + '/config.ru', GOLLUM_PATH, options['dry'])
30
- copyNotPresent(options['output'] + GOLLUM_PATH + '/repo', GOLLUM_PATH, options['dry'])
31
- chown('http', 'http', GOLLUM_PATH, options['dry'])
50
+ if !target.key?('Proxy') || !!target['Proxy']
51
+ self.class.prepareNginxConfig(target)
52
+ if !target['Root']
53
+ target['Root'] = File.dirname(`gem which gollum`.strip) + '/gollum/public'
54
+ end
55
+ writeNginxConfig(__dir__, NAME, id, target, state, context, options)
56
+ deployNginxConfig(id, target, activeState, context, options)
57
+ end
58
+ if !target.key?('Proxy') || target['Proxy'] != 'only'
59
+ copy(options['output'] + GOLLUM_PATH + '/config.ru', GOLLUM_PATH, options['dry'])
60
+ copyNotPresent(options['output'] + GOLLUM_PATH + '/repo', GOLLUM_PATH, options['dry'])
61
+ chown('http', 'http', GOLLUM_PATH, options['dry'])
62
+ end
32
63
  activeState['Location'] = '@me'
33
- else
34
- # TODO
35
64
  end
36
65
  end
37
66
 
@@ -4,3 +4,13 @@ opensuse-leap:
4
4
  InstallPackage: zypper install --no-confirm
5
5
  AutoStartService: systemctl enable
6
6
  StartService: systemctl start
7
+ CreateServiceUser: useradd --system --shell /usr/sbin/nologin --user-group
8
+ ModifyUser: usermod
9
+
10
+ arch:
11
+ Name: Arch Linux
12
+ InstallPackage: pacman -S --noconfirm --needed
13
+ AutoStartService: systemctl enable
14
+ StartService: systemctl start
15
+ CreateServiceUser: useradd --system --shell /usr/sbin/nologin --user-group
16
+ ModifyUser: usermod
@@ -10,8 +10,11 @@ module ConfigLMM
10
10
 
11
11
  ISO_LOCATION = '~/.cache/configlmm/images/'
12
12
  HOSTS_FILE = '/etc/hosts'
13
+ FSTAB_FILE = '/etc/fstab'
13
14
  SSH_CONFIG = '~/.ssh/config'
14
- SYSCTL_FILE = '/etc/sysctl.d/10-configlmm.conf'
15
+ SYSCTL_FILE = '/etc/sysctl.d/90-configlmm.conf'
16
+ FIREWALL_PACKAGE = 'firewalld'
17
+ FIREWALL_SERVICE = 'firewalld'
15
18
 
16
19
  def actionLinuxBuild(id, target, activeState, context, options)
17
20
  prepareConfig(target)
@@ -33,8 +36,7 @@ module ConfigLMM
33
36
  raise Framework::PluginProcessError.new("#{id}: Unknown protocol: #{uri.scheme}!")
34
37
  end
35
38
  else
36
- deployLocalHostsFile(target, options)
37
- deployLocalSSHConfig(target, options)
39
+ deployLocal(target, options)
38
40
  end
39
41
  if target['AlternativeLocation']
40
42
  uri = Addressable::URI.parse(target['AlternativeLocation'])
@@ -44,31 +46,162 @@ module ConfigLMM
44
46
  end
45
47
 
46
48
  def deployOverSSH(locationUri, id, target, activeState, context, options)
47
- if target['Domain'] || target['Hosts']
48
- hostsLines = []
49
- if target['Domain']
50
- self.class.sshStart(locationUri) do |ssh|
49
+ self.class.sshStart(locationUri) do |ssh|
50
+ if target['Domain'] || target['Hosts']
51
+ hostsLines = []
52
+ if target['Domain']
51
53
  envs = self.class.sshExec!(ssh, "env").split("\n")
52
54
  envVars = Hash[envs.map { |vars| vars.split('=', 2) }]
53
55
  ipAddr = envVars['SSH_CONNECTION'].split[-2]
54
56
  hostsLines << ipAddr.ljust(16) + target['Domain'] + ' ' + target['Name'] + "\n"
55
57
  end
56
- end
57
- target['Hosts'].to_a.each do |ip, entries|
58
+ target['Hosts'].to_a.each do |ip, entries|
58
59
  hostsLines << ip.ljust(16) + entries.join(' ') + "\n"
60
+ end
61
+ updateRemoteFile(ssh, HOSTS_FILE, options, false) do |fileLines|
62
+ fileLines + hostsLines
63
+ end
59
64
  end
60
- updateRemoteFile(locationUri, HOSTS_FILE, options, false) do |fileLines|
61
- fileLines + hostsLines
65
+ distroInfo = self.class.currentDistroInfo(ssh)
66
+ if target['Network']
67
+ if distroInfo['Name'] == 'openSUSE Leap'
68
+ networkFile = '/etc/sysconfig/network/ifcfg-eth0'
69
+ if target['Network'] == 'dhcp'
70
+ self.class.sshExec!(ssh, "sed -i \"s|^BOOTPROTO=.*|BOOTPROTO='dhcp'|\" #{networkFile}")
71
+ else
72
+ self.class.sshExec!(ssh, "sed -i \"s|^BOOTPROTO=.*|BOOTPROTO='static'|\" #{networkFile}")
73
+ updateRemoteFile(ssh, networkFile, options, false) do |fileLines|
74
+ fileLines << "\n"
75
+ if target['Network']['IP']
76
+ if target['Network']['IP'].is_a?(Array)
77
+ # TODO
78
+ else
79
+ self.class.sshExec!(ssh, "sed -i 's|^IPADDR=|#IPADDR=|' #{networkFile}")
80
+ fileLines << "IPADDR=#{target['Network']['IP']}\n"
81
+ end
82
+ end
83
+ fileLines
84
+ end
85
+ if target['Network']['DNS']
86
+ configFile = '/etc/sysconfig/network/config'
87
+ dns = target['Network']['DNS']
88
+ dns = [dns] unless dns.is_a?(Array)
89
+ self.class.sshExec!(ssh, "sed -i 's|^NETCONFIG_DNS_STATIC_SERVERS=.*|NETCONFIG_DNS_STATIC_SERVERS=\"#{dns.join(' ')}\"|' #{configFile}")
90
+ end
91
+ if target['Network']['Gateway']
92
+ routesFile = '/etc/sysconfig/network/routes'
93
+ self.class.sshExec!(ssh, "sed -i 's|^default |#default |' #{routesFile}")
94
+ updateRemoteFile(ssh, routesFile, options) do |fileLines|
95
+ fileLines << "default #{target['Network']['Gateway']}\n"
96
+ end
97
+ end
98
+ end
99
+ else
100
+ # TODO
101
+ raise 'Not Unimplemented!'
102
+ end
103
+ end
104
+ if target['Tmpfs']
105
+ self.class.sshExec!(ssh, "sed -i '/ \\/tmp /d' #{FSTAB_FILE}")
106
+ updateRemoteFile(ssh, FSTAB_FILE, options, false) do |fileLines|
107
+ fileLines << "tmpfs /tmp tmpfs nodev,nosuid,size=#{target['Tmpfs']} 0 0\n"
108
+ end
62
109
  end
110
+ if target['Sysctl']
111
+ updateRemoteFile(ssh, SYSCTL_FILE, options, false) do |fileLines|
112
+ target['Sysctl'].each do |name, value|
113
+ fileLines << "#{name} = #{value}\n"
114
+ self.class.sshExec!(ssh, "sysctl #{name}=#{value}")
115
+ end
116
+ fileLines
117
+ end
118
+ end
119
+ if target['Users']
120
+ target['Users'].each do |name, info|
121
+ userId = ssh.exec!("id -u #{name} 2>/dev/null").strip
122
+ if userId.empty?
123
+ shell = ''
124
+ if info['Shell']
125
+ shell = "--shell '/usr/bin/#{info['Shell']}'"
126
+ end
127
+ badname = '--badname'
128
+ badname = '--badnames' if distroInfo['Name'] == 'openSUSE Leap'
129
+ self.class.sshExec!(ssh, "useradd #{badname} --create-home --user-group #{shell} #{name}")
130
+ end
131
+ homeDir = self.class.sshExec!(ssh, "getent passwd #{name} | cut -d ':' -f 6").strip
132
+ keyFile = homeDir + "/.ssh/id_ed25519"
133
+ if info['SSHKey'] && !self.class.remoteFilePresent?(keyFile, ssh)
134
+ self.class.sshExec!(ssh, "mkdir -p #{homeDir}/.ssh")
135
+ self.class.sshExec!(ssh, "ssh-keygen -t ed25519 -f #{keyFile} -P ''")
136
+ self.class.sshExec!(ssh, "chown -R #{name}:#{name} #{homeDir}/.ssh")
137
+ end
138
+ end
139
+ end
140
+ self.executeCommands(target['Execute'], ssh)
141
+ end
142
+ if target['Firewall'] && target['Firewall'] != 'no'
143
+ self.ensurePackage(FIREWALL_PACKAGE, locationUri)
144
+ self.ensureServiceAutoStart(FIREWALL_SERVICE, locationUri)
145
+ self.startService(FIREWALL_SERVICE, locationUri)
63
146
  end
147
+ end
148
+
149
+ def deployLocal(target, options)
150
+ deployLocalHostsFile(target, options)
151
+ deployLocalSSHConfig(target, options)
64
152
  if target['Sysctl']
65
- updateRemoteFile(locationUri, SYSCTL_FILE, options, false) do |fileLines|
153
+ updateLocalFile(SYSCTL_FILE, options) do |fileLines|
66
154
  target['Sysctl'].each do |name, value|
67
155
  fileLines << "#{name} = #{value}\n"
156
+ `sysctl #{name}=#{value}`
68
157
  end
69
158
  fileLines
70
159
  end
71
160
  end
161
+ if target['Users']
162
+ target['Users'].each do |name, info|
163
+ userId = self.class.exec("id -u #{name} 2>/dev/null", nil, true).strip
164
+ if userId.empty?
165
+ shell = ''
166
+ if info['Shell']
167
+ shell = "--shell '/usr/bin/#{info['Shell']}'"
168
+ end
169
+ distroInfo = self.class.currentDistroInfo(nil)
170
+ badname = '--badname'
171
+ badname = '--badnames' if distroInfo['Name'] == 'openSUSE Leap'
172
+ self.class.exec("useradd #{badname} --create-home --user-group #{shell} #{name}")
173
+ end
174
+ homeDir = self.class.exec("getent passwd #{name} | cut -d ':' -f 6").strip
175
+ keyFile = homeDir + "/.ssh/id_ed25519"
176
+ if info['SSHKey'] && !self.class.filePresent?(keyFile)
177
+ self.class.exec("mkdir -p #{homeDir}/.ssh")
178
+ self.class.exec("ssh-keygen -t ed25519 -f #{keyFile} -P ''")
179
+ self.class.exec("chown -R #{name}:#{name} #{homeDir}/.ssh")
180
+ end
181
+ end
182
+ end
183
+ if target['Firewall'] && target['Firewall'] != 'no'
184
+ self.ensurePackage(FIREWALL_PACKAGE, locationUri)
185
+ self.ensureServiceAutoStart(FIREWALL_SERVICE, locationUri)
186
+ self.startService(FIREWALL_SERVICE, locationUri)
187
+ end
188
+ self.executeCommands(target['Execute'])
189
+ end
190
+
191
+ def executeCommands(commands, ssh = nil)
192
+ return unless commands
193
+
194
+ commands.each do |type, data|
195
+ case type
196
+ when 'sh'
197
+ data = [data] unless data.is_a?(Array)
198
+ data.each do |cmd|
199
+ self.class.exec(cmd, ssh)
200
+ end
201
+ else
202
+ raise 'Unimplemented!'
203
+ end
204
+ end
72
205
  end
73
206
 
74
207
  def deployOverLibvirt(id, target, activeState, context, options)
@@ -1,13 +1,51 @@
1
1
  Arch Linux:
2
- sshd: openssh
3
- Postfix: postfix
2
+ Cassandra: AUR|cassandra
3
+ CertBotNginx:
4
+ - certbot-nginx
5
+ - certbot-dns-rfc2136
6
+ CyrusSASL: cyrus-sasl
4
7
  Dovecot: dovecot
8
+ firewalld: firewalld
9
+ Nextcloud: nextcloud
10
+ nginx: nginx
11
+ PHP-FPM: php-fpm
12
+ php-pecl: php-pecl
13
+ Podman: podman
5
14
  PostgreSQL: postgresql
15
+ Postfix: postfix
16
+ PowerDNS: powerdns
17
+ sshd: openssh
6
18
  Valkey: redis
19
+ WireGuard: wireguard-tools
20
+ Yarn: yarn
7
21
 
8
22
  openSUSE Leap:
9
- sshd: openssh
10
- Postfix: postfix
23
+ Cassandra: home:davispuh:branches:server:database|cassandra
24
+ CertBotNginx:
25
+ - python3-certbot-nginx
26
+ - certbot-systemd-timer
27
+ - python3-certbot-dns-rfc2136
28
+ CyrusSASL: cyrus-sasl-plain
11
29
  Dovecot: dovecot
30
+ firewalld: firewalld
31
+ Nextcloud: server:php:applications|nextcloud
32
+ nginx: nginx
33
+ PHP-FPM:
34
+ - php8-devel
35
+ - php8-mbstring
36
+ - php8-fpm
37
+ - php8-redis
38
+ - php8-pgsql
39
+ - php8-mysql
40
+ php-pecl: php8-pecl
41
+ Podman: podman
12
42
  PostgreSQL: postgresql-server
43
+ Postfix: postfix
44
+ PowerDNS:
45
+ - pdns-backend-geoip
46
+ - pdns-backend-sqlite3
47
+ - pdns-backend-postgresql
48
+ sshd: openssh
13
49
  Valkey: redis
50
+ WireGuard: wireguard-tools
51
+ Yarn: yarn
@@ -0,0 +1,108 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class WireGuard < Framework::LinuxApp
5
+
6
+ WIREGUARD_PACKAGE = 'WireGuard'
7
+ SERVICE_NAME = 'wg-quick@wg0'
8
+ CONFIG_FILE = '/etc/wireguard/wg0.conf'
9
+ PORT = '51820'
10
+ SUBNET = '172.20.0.0/20'
11
+
12
+ persistBuildDir
13
+
14
+ def actionWireGuardDeploy(id, target, activeState, context, options)
15
+ self.prepareConfig(target)
16
+ if target['Location'] && target['Location'] != '@me'
17
+ uri = Addressable::URI.parse(target['Location'])
18
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
19
+ self.class.sshStart(uri) do |ssh|
20
+ sharedKey = self.class.sshExec!(ssh, "firewall-cmd -q --permanent --add-port='#{PORT}'/udp")
21
+ sharedKey = self.class.sshExec!(ssh, "firewall-cmd -q --permanent --zone=trusted --add-source=#{SUBNET}")
22
+ sharedKey = self.class.sshExec!(ssh, "firewall-cmd -q --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -s #{SUBNET} ! -d #{SUBNET} -j MASQUERADE")
23
+
24
+ self.class.ensurePackages([WIREGUARD_PACKAGE], ssh)
25
+ self.class.ensureServiceAutoStartOverSSH(SERVICE_NAME, ssh)
26
+
27
+ dir = options['output'] + '/' + id + '/etc/wireguard/'
28
+ mkdir(dir, false)
29
+ template = ERB.new(File.read(__dir__ + '/wg0.conf.erb'))
30
+
31
+ if self.class.remoteFilePresent?(CONFIG_FILE, ssh)
32
+ # TODO Implement adding and removing peers
33
+ else
34
+ if !target['PrivateKey']
35
+ target['PrivateKey'] = ENV['WIREGUARD_PRIVATEKEY_' + id]
36
+ if !target['PrivateKey']
37
+ target['PrivateKey'] = genkeyOverSSH(ssh)
38
+ end
39
+ end
40
+ publicKey = pubkeyOverSSH(target['PrivateKey'], ssh)
41
+ self.class.sshExec!(ssh, "echo '#{publicKey}' > /etc/wireguard/pubkey")
42
+ target['Peers'].each do |name, data|
43
+ if !data['PublicKey']
44
+ data['PrivateKey'] = genkeyOverSSH(ssh)
45
+ data['PublicKey'] = pubkeyOverSSH(data['PrivateKey'], ssh)
46
+ end
47
+ if !data['PresharedKey']
48
+ data['PresharedKey'] = ENV['WIREGUARD_PRESHAREDKEY_' + id + '_' + name]
49
+ if !data['PresharedKey']
50
+ data['PresharedKey'] = genpskOverSSH(ssh)
51
+ end
52
+ end
53
+ end
54
+
55
+ target['Peers'].each do |name, data|
56
+ templateData = {}
57
+ templateData['Address'] = target['Address']
58
+ templateData['PrivateKey'] = data['PrivateKey']
59
+ templateData['Peers'] = {}
60
+ templateData['Peers'][id] = { 'PublicKey' => publicKey, 'PresharedKey' => data['PresharedKey'] }
61
+ target['Peers'].each do |otherName, otherData|
62
+ next if name == otherName
63
+ pskIdB = 'PresharedKey_' + otherName + '_' + name
64
+ if otherData.key?(pskIdB)
65
+ psk = otherData[pskIdB]
66
+ else
67
+ pskIdA = 'PresharedKey_' + name + '_' + otherName
68
+ data[pskIdA] = genpskOverSSH(ssh)
69
+ psk = data[pskIdA]
70
+ end
71
+ templateData['Peers'][otherName] = { 'PublicKey' => otherData['PublicKey'], 'PresharedKey' => psk }
72
+ end
73
+
74
+ renderTemplate(template, templateData, dir + name + '.conf', options)
75
+ end
76
+
77
+ renderTemplate(template, target, dir + 'wg0.conf', options)
78
+ ssh.scp.upload!(dir + 'wg0.conf', CONFIG_FILE)
79
+ end
80
+
81
+ end
82
+ else
83
+ # TODO
84
+ end
85
+ self.startService(SERVICE_NAME, target['Location'])
86
+ end
87
+
88
+ def genkeyOverSSH(ssh)
89
+ self.class.sshExec!(ssh, 'wg genkey')
90
+ end
91
+
92
+ def genpskOverSSH(ssh)
93
+ self.class.sshExec!(ssh, 'wg genpsk')
94
+ end
95
+
96
+ def pubkeyOverSSH(privateKey, ssh)
97
+ self.class.sshExec!(ssh, " echo '#{privateKey}' | wg pubkey")
98
+ end
99
+
100
+ def prepareConfig(target)
101
+ target['Address'] = '172.20.0.1' unless target['Address']
102
+ target['Peers'].each do |name, data|
103
+ data['AllowedIPs'] = SUBNET unless data['AllowedIPs']
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,15 @@
1
+ [Interface]
2
+ Address = <%= config['Address'] %>
3
+ ListenPort = 51820
4
+ PrivateKey = <%= config['PrivateKey'] %>
5
+
6
+ <% config['Peers'].to_h.each do |name, peer| %>
7
+ # <%= name %>
8
+ [Peer]
9
+ PublicKey = <%= peer['PublicKey'] %>
10
+ PresharedKey = <%= peer['PresharedKey'] %>
11
+ AllowedIPs = <%= peer['AllowedIPs'] %>
12
+ <% if peer['Endpoint'] %>
13
+ Endpoint = <%= peer['Endpoint'] %>:51820
14
+ <% end %>
15
+ <% end %>
@@ -0,0 +1,28 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class Systemd < Framework::LinuxApp
5
+
6
+ SYSTEMD_CONFIG_PATH = '/etc/systemd/system/'
7
+ USER_SERVICE_DIR = '/etc/systemd/system/user@.service.d/'
8
+
9
+ def actionSystemdDeploy(id, target, activeState, context, options)
10
+ if target['Location'] && target['Location'] != '@me'
11
+ if target['UserCgroups']
12
+ uri = Addressable::URI.parse(target['Location'])
13
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
14
+ self.class.sshStart(uri) do |ssh|
15
+ self.class.sshExec!(ssh, "mkdir -p #{USER_SERVICE_DIR}")
16
+ ssh.scp.upload!(__dir__ + '/user-0.slice', SYSTEMD_CONFIG_PATH)
17
+ ssh.scp.upload!(__dir__ + '/user@.service.d/delegate.conf', USER_SERVICE_DIR)
18
+ end
19
+ end
20
+ else
21
+ # TODO
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,9 @@
1
+
2
+ [Unit]
3
+ Before=systemd-logind.service
4
+
5
+ [Slice]
6
+ Slice=user.slice
7
+
8
+ [Install]
9
+ WantedBy=multi-user.target
@@ -0,0 +1,3 @@
1
+
2
+ [Service]
3
+ Delegate=memory pids
@@ -26,8 +26,12 @@ module ConfigLMM
26
26
  data.each do |name, data|
27
27
  self.processDNS(domain, data).each do |type, records|
28
28
  records.each do |record|
29
- shortName = Addressable::IDNA.to_ascii(name)
30
- record[:content] = Addressable::IDNA.to_ascii(record[:content]) if record[:type] == 'CNAME'
29
+ shortName = Addressable::IDNA.to_ascii(name) + '.' if type == 'CNAME' || type == 'ALIAS'
30
+ if record[:type] == 'MX'
31
+ priority, name = record[:content].split(' ')
32
+ name = Addressable::IDNA.to_ascii(name) + '.'
33
+ record[:content] = [priority, name].join(' ')
34
+ end
31
35
  config['Records'] += [shortName, record[:ttl], ' IN ', record[:type], record[:content]].join("\t") + "\n"
32
36
  end
33
37
  end