ConfigLMM 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/Examples/Implemented.mm.yaml +75 -1
- data/Plugins/Apps/Authentik/Authentik-Server.container +18 -0
- data/Plugins/Apps/Authentik/Authentik-Worker.container +17 -0
- data/Plugins/Apps/Authentik/Authentik.conf.erb +35 -0
- data/Plugins/Apps/Authentik/Authentik.lmm.rb +73 -0
- data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +41 -0
- data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +148 -0
- data/Plugins/Apps/GitLab/GitLab.conf.erb +26 -0
- data/Plugins/Apps/GitLab/GitLab.container +17 -0
- data/Plugins/Apps/GitLab/GitLab.lmm.rb +75 -0
- data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +48 -10
- data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +59 -2
- data/Plugins/Apps/Nextcloud/config.php +18 -0
- data/Plugins/Apps/Nginx/conf.d/configlmm.conf +62 -0
- data/Plugins/Apps/Nginx/config-lmm/errors.conf +1 -1
- data/Plugins/Apps/Nginx/main.conf.erb +31 -0
- data/Plugins/Apps/Nginx/nginx.conf +3 -68
- data/Plugins/Apps/Nginx/nginx.lmm.rb +71 -14
- data/Plugins/Apps/Odoo/Odoo.conf.erb +30 -13
- data/Plugins/Apps/Odoo/Odoo.container +17 -0
- data/Plugins/Apps/Odoo/Odoo.lmm.rb +62 -2
- data/Plugins/Apps/Odoo/odoo.conf +37 -0
- data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +95 -0
- data/Plugins/Apps/Peppermint/Peppermint.conf.erb +64 -0
- data/Plugins/Apps/Peppermint/Peppermint.container +14 -0
- data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +58 -0
- data/Plugins/Apps/Postfix/Postfix.lmm.rb +139 -31
- data/Plugins/Apps/Postfix/smtpd.conf +3 -0
- data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +172 -23
- data/Plugins/Apps/SSH/SSH.lmm.rb +51 -0
- data/Plugins/Apps/UVdesk/UVdesk.conf.erb +52 -0
- data/Plugins/Apps/UVdesk/UVdesk.lmm.rb +85 -0
- data/Plugins/Apps/Valkey/Valkey.lmm.rb +2 -1
- data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +35 -18
- data/Plugins/Apps/Vaultwarden/Vaultwarden.container +16 -0
- data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +42 -3
- data/Plugins/Apps/gollum/gollum.conf.erb +45 -18
- data/Plugins/Apps/gollum/gollum.container +12 -0
- data/Plugins/Apps/gollum/gollum.lmm.rb +39 -10
- data/Plugins/OS/Linux/Distributions.yaml +10 -0
- data/Plugins/OS/Linux/Linux.lmm.rb +145 -12
- data/Plugins/OS/Linux/Packages.yaml +42 -4
- data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +108 -0
- data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +15 -0
- data/Plugins/OS/Linux/systemd/systemd.lmm.rb +28 -0
- data/Plugins/OS/Linux/systemd/user-0.slice +9 -0
- data/Plugins/OS/Linux/systemd/user@.service.d/delegate.conf +3 -0
- data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +6 -2
- data/Plugins/Services/DNS/PowerDNS.lmm.rb +69 -6
- data/README.md +6 -0
- data/bootstrap.sh +54 -0
- data/lib/ConfigLMM/Framework/plugins/dns.rb +1 -2
- data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +157 -35
- data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +24 -6
- data/lib/ConfigLMM/Framework/plugins/plugin.rb +52 -12
- data/lib/ConfigLMM/version.rb +1 -1
- metadata +31 -3
- data/Plugins/Apps/Nginx/main.conf +0 -30
@@ -1,22 +1,37 @@
|
|
1
1
|
|
2
2
|
server {
|
3
3
|
|
4
|
-
<% if
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
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
|
-
|
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
|
-
|
17
|
-
|
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
|
-
|
34
|
-
|
35
|
-
passenger_min_instances 1;
|
36
|
-
rails_env production;
|
48
|
+
<% if config['Passenger'] %>
|
49
|
+
passenger_app_root /srv/gollum;
|
37
50
|
|
38
|
-
|
39
|
-
|
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
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
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/
|
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
|
-
|
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
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
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
|
-
|
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
|
-
|
61
|
-
|
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
|
-
|
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
|
-
|
3
|
-
|
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
|
-
|
10
|
-
|
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
|
@@ -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
|
-
|
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
|