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.
- 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
@@ -13,6 +13,9 @@ module ConfigLMM
|
|
13
13
|
DEFAULT_HOST = 'localhost'
|
14
14
|
DEFAULT_PORT = 8081
|
15
15
|
SSH_TIMEOUT = 10
|
16
|
+
PACKAGE_NAME = 'PowerDNS'
|
17
|
+
SERVICE_NAME = 'pdns'
|
18
|
+
USER = 'pdns'
|
16
19
|
|
17
20
|
# TODO
|
18
21
|
# def actionPowerDNSValidate(id, target, activeState, context, options)
|
@@ -21,6 +24,7 @@ module ConfigLMM
|
|
21
24
|
|
22
25
|
def actionPowerDNSBuild(id, target, activeState, context, options)
|
23
26
|
if target['Settings']
|
27
|
+
prepareSettings(target)
|
24
28
|
targetDir = options['output'] + CONFIG_DIR + '/'
|
25
29
|
mkdir(targetDir, options['dry'])
|
26
30
|
content = ''
|
@@ -44,7 +48,7 @@ module ConfigLMM
|
|
44
48
|
def actionPowerDNSDeploy(id, target, activeState, context, options)
|
45
49
|
#actionPowerDNSDiff(id, target, activeState, context, options)
|
46
50
|
|
47
|
-
deploySettings(target)
|
51
|
+
deploySettings(target, options)
|
48
52
|
if target['DNS']
|
49
53
|
connect(id, target, activeState, context, options) do |host, port, key|
|
50
54
|
updateDNS(host, port, key, target['DNS'])
|
@@ -103,8 +107,8 @@ module ConfigLMM
|
|
103
107
|
rrsets = []
|
104
108
|
remove = []
|
105
109
|
info.each do |name, data|
|
106
|
-
fullName = name + '.' + domain + '.'
|
107
|
-
fullName = domain + '.' if name == '@'
|
110
|
+
fullName = Addressable::IDNA.to_ascii(name) + '.' + Addressable::IDNA.to_ascii(domain) + '.'
|
111
|
+
fullName = Addressable::IDNA.to_ascii(domain) + '.' if name == '@'
|
108
112
|
self.processDNS(domain, data).each do |type, records|
|
109
113
|
#remove += removeConflicting(zone, fullName, type)
|
110
114
|
rrset = {
|
@@ -115,6 +119,12 @@ module ConfigLMM
|
|
115
119
|
records: []
|
116
120
|
}
|
117
121
|
records.each do |record|
|
122
|
+
record[:content] = Addressable::IDNA.to_ascii(record[:content]) + '.' if type == 'CNAME' || type == 'ALIAS'
|
123
|
+
if type == 'MX'
|
124
|
+
priority, name = record[:content].split(' ')
|
125
|
+
name = Addressable::IDNA.to_ascii(name) + '.'
|
126
|
+
record[:content] = [priority, name].join(' ')
|
127
|
+
end
|
118
128
|
rrset[:records] << { content: record[:content], disabled: false }
|
119
129
|
end
|
120
130
|
rrsets << rrset
|
@@ -129,12 +139,65 @@ module ConfigLMM
|
|
129
139
|
|
130
140
|
end
|
131
141
|
|
132
|
-
def
|
133
|
-
if target['Settings']
|
134
|
-
|
142
|
+
def prepareSettings(target)
|
143
|
+
if !target['Settings'].key?('api')
|
144
|
+
target['Settings']['api'] = 'yes'
|
145
|
+
end
|
146
|
+
if !target['Settings'].key?('expand-alias')
|
147
|
+
target['Settings']['expand-alias'] = 'yes'
|
148
|
+
end
|
149
|
+
if !target['Settings'].key?('launch')
|
150
|
+
target['Settings']['launch'] = 'gpgsql'
|
151
|
+
target['Settings']['gpgsql-host'] = '/run/postgresql'
|
152
|
+
target['Settings']['gpgsql-user'] = USER
|
153
|
+
target['Settings']['gpgsql-dbname'] = USER
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def deploySettings(target, options)
|
158
|
+
if target['Location']
|
159
|
+
uri = Addressable::URI.parse(target['Location'])
|
160
|
+
params = {}
|
161
|
+
params = CGI.parse(uri.query) if uri.query
|
162
|
+
if uri.scheme == 'ssh' && !params.key?('host')
|
163
|
+
self.class.sshStart(uri) do |ssh|
|
164
|
+
Framework::LinuxApp.ensurePackages([PACKAGE_NAME], ssh)
|
165
|
+
Framework::LinuxApp.ensureServiceAutoStartOverSSH(SERVICE_NAME, ssh)
|
166
|
+
if target['Settings']
|
167
|
+
prepareSettings(target)
|
168
|
+
self.class.sshExec!(ssh, "mkdir -p #{CONFIG_DIR}")
|
169
|
+
self.class.sshExec!(ssh, "sed -i 's|# include-dir=|include-dir=#{CONFIG_DIR}|' /etc/pdns/pdns.conf")
|
170
|
+
ssh.scp.upload!(options['output'] + CONFIG_DIR + '/configlmm.conf', CONFIG_DIR + '/configlmm.conf')
|
171
|
+
apiKeyFile = CONFIG_DIR + '/apiKey.conf'
|
172
|
+
if !self.class.remoteFilePresent?(apiKeyFile, ssh)
|
173
|
+
apiKey = ENV['POWERDNS_API_KEY']
|
174
|
+
apiKey = SecureRandom.urlsafe_base64(60) unless apiKey
|
175
|
+
self.class.sshExec!(ssh, " echo 'api-key=#{apiKey}' > #{apiKeyFile}")
|
176
|
+
self.class.sshExec!(ssh, " chown #{USER}:#{USER} #{apiKeyFile}")
|
177
|
+
self.class.sshExec!(ssh, " chmod 400 #{apiKeyFile}")
|
178
|
+
prompt.say("PowerDNS API Key: #{apiKey}", )
|
179
|
+
end
|
180
|
+
self.configurePostgreSQL(target['Settings'], ssh)
|
181
|
+
end
|
182
|
+
Framework::LinuxApp.startServiceOverSSH(SERVICE_NAME, ssh)
|
183
|
+
end
|
184
|
+
end
|
135
185
|
end
|
136
186
|
end
|
137
187
|
|
188
|
+
def configurePostgreSQL(settings, ssh)
|
189
|
+
password = SecureRandom.alphanumeric(20)
|
190
|
+
if settings['gpgsql-host'] == 'localhost' || settings['gpgsql-host'].start_with?('/')
|
191
|
+
PostgreSQL.createUserAndDBOverSSH(USER, password, ssh)
|
192
|
+
PostgreSQL.importSQL(USER, USER, '/usr/share/doc/packages/pdns/schema.pgsql.sql', ssh)
|
193
|
+
else
|
194
|
+
self.class.sshStart("ssh://#{settings['gpgsql-host']}/") do |ssh|
|
195
|
+
PostgreSQL.createUserAndDBOverSSH(USER, password, ssh)
|
196
|
+
PostgreSQL.importSQL(USER, USER, '/usr/share/doc/packages/pdns/schema.pgsql.sql', ssh)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
password
|
200
|
+
end
|
138
201
|
|
139
202
|
def connect(id, target, activeState, context, options)
|
140
203
|
host = DEFAULT_HOST
|
data/README.md
CHANGED
@@ -221,6 +221,12 @@ First you need to have Ruby and RubyGems. Then you can install it with:
|
|
221
221
|
|
222
222
|
$ gem install ConfigLMM
|
223
223
|
|
224
|
+
If that doesn't work (eg. your Ruby is too old) then install with (it will install [RVM](https://rvm.io/))
|
225
|
+
|
226
|
+
```
|
227
|
+
$ curl -sS https://raw.githubusercontent.com/ConfigLMM/ConfigLMM/master/bootstrap.sh | sh
|
228
|
+
```
|
229
|
+
|
224
230
|
## Usage
|
225
231
|
|
226
232
|
Create yaml file with desired config, eg.
|
data/bootstrap.sh
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#!/usr/bin/sh
|
2
|
+
|
3
|
+
distro=$(cat /etc/os-release | grep ^ID= | cut -d '=' -f 2 | cut -d '"' -f 2)
|
4
|
+
|
5
|
+
function admin {
|
6
|
+
if [ "$EUID" -eq "0" ]; then
|
7
|
+
"$@"
|
8
|
+
else
|
9
|
+
sudo "$@"
|
10
|
+
fi
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
case $distro in
|
15
|
+
|
16
|
+
opensuse-leap)
|
17
|
+
admin zypper install --no-confirm ruby libvirt-devel
|
18
|
+
;;
|
19
|
+
|
20
|
+
arch)
|
21
|
+
admin pacman -S --noconfirm --needed ruby rubygems
|
22
|
+
;;
|
23
|
+
|
24
|
+
*)
|
25
|
+
if ! command -v ruby &> /dev/null
|
26
|
+
then
|
27
|
+
echo "Ruby not found!" >&2
|
28
|
+
echo "Don't know how to install it for your $distro distribution!" >&2
|
29
|
+
echo "Submit a PR :)" >&2
|
30
|
+
exit 1
|
31
|
+
fi
|
32
|
+
;;
|
33
|
+
esac
|
34
|
+
|
35
|
+
if ! command -v gem &> /dev/null; then
|
36
|
+
echo "RubyGems not found!" >&2
|
37
|
+
exit 2
|
38
|
+
fi
|
39
|
+
|
40
|
+
rubyTooOld=$(ruby -e 'puts RUBY_VERSION.to_f < 3.3 ? 1 : 0')
|
41
|
+
|
42
|
+
if [ "$rubyTooOld" -eq "1" ]; then
|
43
|
+
echo "Ruby is too old! Will install RVM" >&2
|
44
|
+
gpg2 --keyserver hkp://keyserver.ubuntu.com --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB >/dev/null
|
45
|
+
curl -sSL https://get.rvm.io | bash -s stable --ruby=3.3.4
|
46
|
+
source /etc/profile.d/rvm.sh
|
47
|
+
|
48
|
+
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
|
50
|
+
echo "rvm default" >> ~/.config/fish/config.fish
|
51
|
+
fi
|
52
|
+
fi
|
53
|
+
|
54
|
+
gem install ConfigLMM
|
@@ -23,8 +23,7 @@ module ConfigLMM
|
|
23
23
|
|
24
24
|
items.each do |item|
|
25
25
|
type, content = item.strip.split('=')
|
26
|
-
content
|
27
|
-
content = domain + '.' if content == '@' || content == '@.'
|
26
|
+
content = domain if content == '@'
|
28
27
|
content = self.class.externalIp if content == '@me'
|
29
28
|
records[type] ||= []
|
30
29
|
records[type] << { type: type, content: content, ttl: DEFAULT_TTL }
|
@@ -8,29 +8,80 @@ module ConfigLMM
|
|
8
8
|
LINUX_FOLDER = __dir__ + '/../../../../Plugins/OS/Linux/'
|
9
9
|
SUSE_NAME = 'openSUSE Leap'
|
10
10
|
SUSE_ID = 'opensuse-leap'
|
11
|
+
PODMAN_PACKAGE = 'Podman'
|
12
|
+
SYSTEMD_CONTAINERS_PATH = '~/.config/containers/systemd/'
|
11
13
|
|
12
14
|
def ensurePackage(name, location)
|
13
|
-
self.class.
|
15
|
+
self.class.ensurePackages([name], location)
|
14
16
|
end
|
15
17
|
|
16
|
-
def
|
18
|
+
def ensurePackages(names, location)
|
19
|
+
self.class.ensurePackages(names, location)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.ensurePackages(names, locationOrSSH)
|
23
|
+
distroInfo = nil
|
24
|
+
closure = Proc.new do |ssh|
|
25
|
+
distroInfo = self.currentDistroInfo(ssh)
|
26
|
+
reposPackages = self.mapPackages(names, distroInfo['Name'])
|
27
|
+
|
28
|
+
repos = []
|
29
|
+
pkgs = []
|
30
|
+
reposPackages.each do |pkg|
|
31
|
+
if pkg.include?('|')
|
32
|
+
repoName, pkg = pkg.split('|')
|
33
|
+
repos << repoName
|
34
|
+
pkgs << pkg
|
35
|
+
else
|
36
|
+
pkgs << pkg
|
37
|
+
end
|
38
|
+
end
|
39
|
+
repos.each do |repoName|
|
40
|
+
self.addRepo(repoName, distroInfo, ssh)
|
41
|
+
end
|
42
|
+
command = distroInfo['InstallPackage'] + ' ' + pkgs.map { |pkg| pkg.shellescape }.join(' ')
|
43
|
+
if ssh
|
44
|
+
self.sshExec!(ssh, command)
|
45
|
+
else
|
46
|
+
if `echo $EUID`.strip == '0'
|
47
|
+
`#{command} >/dev/null`
|
48
|
+
else
|
49
|
+
`sudo #{command} >/dev/null`
|
50
|
+
end
|
51
|
+
end
|
52
|
+
distroInfo
|
53
|
+
end
|
54
|
+
|
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'
|
60
|
+
|
61
|
+
self.sshStart(locationOrSSH) do |ssh|
|
62
|
+
distroInfo = closure.call(ssh)
|
63
|
+
end
|
64
|
+
else
|
65
|
+
distroInfo = closure.call(locationOrSSH)
|
66
|
+
end
|
67
|
+
distroInfo
|
68
|
+
end
|
69
|
+
|
70
|
+
def ensureServiceAutoStart(name, location)
|
17
71
|
if location && location != '@me'
|
18
72
|
uri = Addressable::URI.parse(location)
|
19
73
|
raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
20
|
-
self.
|
74
|
+
self.class.ensureServiceAutoStartOverSSH(name, uri)
|
21
75
|
else
|
22
76
|
# TODO
|
23
77
|
end
|
24
78
|
end
|
25
79
|
|
26
|
-
def self.
|
27
|
-
|
80
|
+
def self.ensureServiceAutoStartOverSSH(name, locationOrSSH)
|
28
81
|
closure = Proc.new do |ssh|
|
29
|
-
distroInfo = self.
|
30
|
-
pkgs = self.mapPackages([name], distroInfo['Name']).first
|
82
|
+
distroInfo = self.currentDistroInfo(ssh)
|
31
83
|
|
32
|
-
|
33
|
-
command = distroInfo['InstallPackage'] + ' ' + pkgs.map { |pkg| pkg.shellescape }.join(' ')
|
84
|
+
command = distroInfo['AutoStartService'] + ' ' + name.shellescape
|
34
85
|
self.sshExec!(ssh, command)
|
35
86
|
end
|
36
87
|
|
@@ -39,38 +90,69 @@ module ConfigLMM
|
|
39
90
|
closure.call(ssh)
|
40
91
|
end
|
41
92
|
else
|
42
|
-
|
93
|
+
closure.call(locationOrSSH)
|
43
94
|
end
|
44
|
-
|
45
95
|
end
|
46
96
|
|
47
|
-
def
|
97
|
+
def startService(name, location)
|
48
98
|
if location && location != '@me'
|
49
99
|
uri = Addressable::URI.parse(location)
|
50
100
|
raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
51
|
-
self.class.
|
52
|
-
|
101
|
+
self.class.startServiceOverSSH(name, location)
|
102
|
+
else
|
103
|
+
# TODO
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def self.startServiceOverSSH(name, locationOrSSH)
|
108
|
+
closure = Proc.new do |ssh|
|
109
|
+
distroInfo = self.currentDistroInfo(ssh)
|
110
|
+
|
111
|
+
command = distroInfo['StartService'] + ' ' + name.shellescape
|
112
|
+
self.sshExec!(ssh, command)
|
113
|
+
end
|
53
114
|
|
54
|
-
|
55
|
-
|
115
|
+
if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
|
116
|
+
self.sshStart(locationOrSSH) do |ssh|
|
117
|
+
closure.call(ssh)
|
56
118
|
end
|
57
119
|
else
|
58
|
-
|
120
|
+
closure.call(locationOrSSH)
|
59
121
|
end
|
60
122
|
end
|
61
123
|
|
62
|
-
def startService(name, location)
|
63
|
-
if location && location != '@me'
|
64
|
-
uri = Addressable::URI.parse(location)
|
65
|
-
raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
66
|
-
self.class.sshStart(uri) do |ssh|
|
67
|
-
distroInfo = self.class.distroInfoFromSSH(ssh)
|
68
124
|
|
69
|
-
|
70
|
-
|
125
|
+
def self.firewallAddServiceOverSSH(serviceName, locationOrSSH)
|
126
|
+
closure = Proc.new do |ssh|
|
127
|
+
command = 'firewall-cmd --permanent --add-service ' + serviceName.shellescape
|
128
|
+
self.sshExec!(ssh, command, true)
|
129
|
+
command = 'firewall-cmd --add-service ' + serviceName.shellescape
|
130
|
+
self.sshExec!(ssh, command, true)
|
131
|
+
end
|
132
|
+
|
133
|
+
if locationOrSSH.is_a?(String) || locationOrSSH.is_a?(Addressable::URI)
|
134
|
+
self.sshStart(locationOrSSH) do |ssh|
|
135
|
+
closure.call(ssh)
|
71
136
|
end
|
72
137
|
else
|
73
|
-
|
138
|
+
closure.call(locationOrSSH)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.firewallAddPortOverSSH(portName, locationOrSSH)
|
143
|
+
closure = Proc.new do |ssh|
|
144
|
+
command = 'firewall-cmd --permanent --add-port ' + portName.shellescape
|
145
|
+
self.sshExec!(ssh, command, true)
|
146
|
+
command = 'firewall-cmd --add-port ' + portName.shellescape
|
147
|
+
self.sshExec!(ssh, command, true)
|
148
|
+
end
|
149
|
+
|
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)
|
74
156
|
end
|
75
157
|
end
|
76
158
|
|
@@ -80,7 +162,11 @@ module ConfigLMM
|
|
80
162
|
packages.to_a.each do |pkg|
|
81
163
|
packageName = distroPackages[distroName][pkg]
|
82
164
|
if packageName
|
83
|
-
|
165
|
+
if packageName.is_a?(Array)
|
166
|
+
names += packageName
|
167
|
+
else
|
168
|
+
names << packageName
|
169
|
+
end
|
84
170
|
else
|
85
171
|
names << pkg.downcase
|
86
172
|
end
|
@@ -88,20 +174,56 @@ module ConfigLMM
|
|
88
174
|
names
|
89
175
|
end
|
90
176
|
|
91
|
-
def self.
|
92
|
-
|
177
|
+
def self.createCertificateOverSSH(ssh)
|
178
|
+
dir = "/etc/letsencrypt/live/Wildcard/"
|
179
|
+
self.sshExec!(ssh, "mkdir -p #{dir}")
|
180
|
+
# Need this temporarily before real certs are created
|
181
|
+
if !self.remoteFilePresent?(dir + 'fullchain.pem', ssh)
|
182
|
+
self.sshExec!(ssh, "openssl req -x509 -noenc -days 90 -newkey rsa:2048 -keyout #{dir}privkey.pem -out #{dir}fullchain.pem -subj '/C=US/O=ConfigLMM/CN=Wildcard'")
|
183
|
+
self.sshExec!(ssh, "cp #{dir}fullchain.pem #{dir}chain.pem")
|
184
|
+
end
|
185
|
+
dir
|
93
186
|
end
|
94
187
|
|
95
|
-
def self.
|
96
|
-
|
188
|
+
def self.configurePodmanServiceOverSSH(user, homedir, userComment, distroInfo, ssh)
|
189
|
+
Framework::LinuxApp.ensurePackages([PODMAN_PACKAGE], ssh)
|
190
|
+
addUserCmd = "#{distroInfo['CreateServiceUser']} --home-dir '#{homedir}' --create-home --comment '#{userComment}' #{user}"
|
191
|
+
self.sshExec!(ssh, addUserCmd, true)
|
192
|
+
self.createSubuidsOverSSH(user, distroInfo, ssh)
|
193
|
+
self.sshExec!(ssh, "loginctl enable-linger #{user}")
|
194
|
+
self.sshExec!(ssh, "su --login #{user} --shell /bin/sh --command 'mkdir -p #{SYSTEMD_CONTAINERS_PATH}'")
|
97
195
|
end
|
98
196
|
|
99
|
-
def self.
|
100
|
-
|
197
|
+
def self.addRepo(name, distroInfo, ssh = nil)
|
198
|
+
if distroInfo['Name'] == 'openSUSE Leap'
|
199
|
+
if ssh
|
200
|
+
versionId = ssh.exec!('cat /etc/os-release | grep "^VERSION_ID=" | cut -d "=" -f 2').strip.gsub('"', '')
|
201
|
+
self.sshExec!(ssh, "zypper addrepo https://download.opensuse.org/repositories/#{name}/#{versionId}/#{name}.repo", true)
|
202
|
+
self.sshExec!(ssh, "zypper --gpg-auto-import-keys refresh")
|
203
|
+
else
|
204
|
+
versionId = `cat /etc/os-release | grep "^VERSION_ID=" | cut -d "=" -f 2`.strip.gsub('"', '')
|
205
|
+
`zypper addrepo https://download.opensuse.org/repositories/#{name}/#{versionId}/#{name}.repo`
|
206
|
+
`zypper --gpg-auto-import-keys refresh`
|
207
|
+
end
|
208
|
+
else
|
209
|
+
# TODO
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
def self.createSubuidsOverSSH(user, distroInfo, ssh)
|
214
|
+
self.sshExec!(ssh, "#{distroInfo['ModifyUser']} --add-subuids 100000-165535 --add-subgids 100000-165535 #{user}")
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.distroID(ssh = nil)
|
218
|
+
if ssh
|
219
|
+
ssh.exec!('cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2').strip.gsub('"', '')
|
220
|
+
else
|
221
|
+
`cat /etc/os-release | grep "^ID=" | cut -d "=" -f 2`.strip.gsub('"', '')
|
222
|
+
end
|
101
223
|
end
|
102
224
|
|
103
|
-
def self.
|
104
|
-
|
225
|
+
def self.currentDistroInfo(ssh)
|
226
|
+
self.distroInfo(self.distroID(ssh))
|
105
227
|
end
|
106
228
|
|
107
229
|
def self.distroInfo(distroID)
|
@@ -13,30 +13,48 @@ module ConfigLMM
|
|
13
13
|
|
14
14
|
class NginxApp < Framework::Plugin
|
15
15
|
|
16
|
+
NGINX_PACKAGE = 'nginx'
|
17
|
+
CONFIG_DIR = '/etc/nginx/'
|
18
|
+
WWW_DIR = '/srv/www/'
|
19
|
+
|
16
20
|
def writeNginxConfig(dir, name, id, target, activeState, context, options)
|
17
21
|
outputFolder = options['output']
|
18
22
|
|
19
23
|
updateTargetConfig(target)
|
20
24
|
|
25
|
+
target = target.dup
|
26
|
+
target['NginxVersion'] = 0 unless target['NginxVersion']
|
21
27
|
template = ERB.new(File.read(dir + '/' + name + '.conf.erb'))
|
22
28
|
renderTemplate(template, target, outputFolder + '/nginx/servers-lmm/' + name + '.conf', options)
|
23
|
-
plugins[:Nginx].actionNginxBuild(id, target, activeState, context, options)
|
24
29
|
end
|
25
30
|
|
26
31
|
def deployNginxConfig(id, target, activeState, context, options)
|
27
|
-
outputFolder = options['output']
|
32
|
+
outputFolder = options['output'] + '/nginx/servers-lmm'
|
28
33
|
|
29
|
-
if
|
30
|
-
|
34
|
+
if target['Location'] && target['Location'] != '@me'
|
35
|
+
uri = Addressable::URI.parse(target['Location'])
|
36
|
+
raise Framework::PluginProcessError.new("Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
|
37
|
+
self.class.sshStart(uri) do |ssh|
|
38
|
+
self.class.uploadFolder(outputFolder, CONFIG_DIR, ssh)
|
39
|
+
Framework::LinuxApp.firewallAddServiceOverSSH('https', ssh)
|
40
|
+
end
|
41
|
+
else
|
42
|
+
copy(outputFolder, CONFIG_DIR, options['dry'])
|
31
43
|
end
|
32
|
-
|
33
|
-
plugins[:Nginx].actionNginxDeploy(id, target, activeState, context, options)
|
34
44
|
end
|
35
45
|
|
36
46
|
def cleanupNginxConfig(name, id, state, context, options)
|
37
47
|
rm('/etc/nginx/servers-lmm/' + name + '.conf', options['dry'])
|
38
48
|
end
|
39
49
|
|
50
|
+
def self.prepareNginxConfig(target, ssh = nil)
|
51
|
+
if ssh
|
52
|
+
target['NginxVersion'] = self.sshExec!(ssh, 'nginx -v').strip.split('/')[1].to_f
|
53
|
+
else
|
54
|
+
target['NginxVersion'] = `nginx -v`.strip.split('/')[1].to_f
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
40
58
|
private
|
41
59
|
|
42
60
|
def updateTargetConfig(target)
|
@@ -7,6 +7,7 @@ require 'http'
|
|
7
7
|
require 'fileutils'
|
8
8
|
require 'net/ssh'
|
9
9
|
require 'net/scp'
|
10
|
+
require 'open3'
|
10
11
|
|
11
12
|
module ConfigLMM
|
12
13
|
module Framework
|
@@ -146,25 +147,41 @@ module ConfigLMM
|
|
146
147
|
end
|
147
148
|
end
|
148
149
|
|
150
|
+
def self.loadVariable(value, target)
|
151
|
+
variableStart = value.index('${')
|
152
|
+
return value unless variableStart
|
153
|
+
variableEnd = value.index('}', variableStart + 2)
|
154
|
+
variableName = value[variableStart + 2...variableEnd]
|
155
|
+
if variableName.start_with?('ENV:')
|
156
|
+
value = value[0...variableStart].to_s + ENV[variableName[4..variableEnd]] + value[(variableEnd + 1)..-1].to_s
|
157
|
+
else
|
158
|
+
raise 'Not implemented!'
|
159
|
+
end
|
160
|
+
value
|
161
|
+
end
|
162
|
+
|
149
163
|
CONFIGLMM_SECTION_BEGIN = "# -----BEGIN CONFIGLMM-----\n"
|
150
164
|
CONFIGLMM_SECTION_END = "# -----END CONFIGLMM-----\n"
|
151
165
|
|
152
|
-
def updateLocalFile(file, options, atTop = false)
|
166
|
+
def updateLocalFile(file, options, atTop = false, comment = '#')
|
167
|
+
File.write(file, '') unless File.exist?(file)
|
168
|
+
sectionBegin = CONFIGLMM_SECTION_BEGIN.gsub('#', comment)
|
169
|
+
sectionEnd = CONFIGLMM_SECTION_END.gsub('#', comment)
|
153
170
|
fileLines = File.read(file).lines
|
154
|
-
sectionBeginIndex = fileLines.index(
|
155
|
-
sectionEndIndex = fileLines.index(
|
171
|
+
sectionBeginIndex = fileLines.index(sectionBegin)
|
172
|
+
sectionEndIndex = fileLines.index(sectionEnd)
|
156
173
|
if sectionBeginIndex.nil?
|
157
174
|
linesBefore = []
|
158
175
|
linesBefore = fileLines unless atTop
|
159
176
|
linesBefore << "\n"
|
160
|
-
linesBefore <<
|
161
|
-
linesAfter = [
|
177
|
+
linesBefore << sectionBegin
|
178
|
+
linesAfter = [sectionEnd]
|
162
179
|
linesAfter << "\n"
|
163
180
|
linesAfter += fileLines if atTop
|
164
181
|
else
|
165
182
|
linesBefore = fileLines[0..sectionBeginIndex]
|
166
183
|
if sectionEndIndex.nil?
|
167
|
-
linesAfter = [
|
184
|
+
linesAfter = [sectionEnd]
|
168
185
|
linesAfter << "\n"
|
169
186
|
else
|
170
187
|
linesAfter = fileLines[sectionEndIndex..fileLines.length]
|
@@ -178,14 +195,14 @@ module ConfigLMM
|
|
178
195
|
fileWrite(file, fileLines.join(), options[:dry])
|
179
196
|
end
|
180
197
|
|
181
|
-
def updateRemoteFile(locationOrSSH, file, options, atTop = false, &block)
|
198
|
+
def updateRemoteFile(locationOrSSH, file, options, atTop = false, comment = '#', &block)
|
182
199
|
|
183
200
|
closure = Proc.new do |ssh|
|
184
201
|
localFile = options['output'] + '/' + SecureRandom.alphanumeric(10)
|
185
202
|
File.write(localFile, '')
|
186
203
|
self.class.sshExec!(ssh, "touch #{file}")
|
187
204
|
ssh.scp.download!(file, localFile)
|
188
|
-
updateLocalFile(localFile, options, atTop, &block)
|
205
|
+
updateLocalFile(localFile, options, atTop, comment, &block)
|
189
206
|
ssh.scp.upload!(localFile, file)
|
190
207
|
end
|
191
208
|
|
@@ -201,14 +218,23 @@ module ConfigLMM
|
|
201
218
|
end
|
202
219
|
end
|
203
220
|
|
204
|
-
def self.
|
205
|
-
result = self.
|
221
|
+
def self.filePresent?(file, ssh = nil)
|
222
|
+
result = self.exec("stat #{file}", ssh, true)
|
206
223
|
!result.start_with?('stat: cannot')
|
207
224
|
end
|
208
225
|
|
226
|
+
# DEPRECATED - use filePresent()
|
227
|
+
def self.remoteFilePresent?(file, ssh)
|
228
|
+
self.filePresent?(file, ssh)
|
229
|
+
end
|
230
|
+
|
231
|
+
def self.remoteFileContains?(file, content, ssh)
|
232
|
+
!self.sshExec!(ssh, "grep '#{content}' #{file}", true).strip.empty?
|
233
|
+
end
|
234
|
+
|
209
235
|
def self.uploadNotPresent(file, target, ssh)
|
210
236
|
target += '/' + File.basename(file)
|
211
|
-
if !self.remoteFilePresent?(target)
|
237
|
+
if !self.remoteFilePresent?(target, ssh)
|
212
238
|
ssh.scp.upload!(file, target)
|
213
239
|
end
|
214
240
|
end
|
@@ -217,7 +243,21 @@ module ConfigLMM
|
|
217
243
|
target += '/' + File.basename(folder) + '/'
|
218
244
|
self.sshExec!(ssh, "mkdir -p #{target}")
|
219
245
|
Dir[folder + '/*'].each do |file|
|
220
|
-
ssh.scp.upload!(file, target + File.basename(file))
|
246
|
+
ssh.scp.upload!(file, target + File.basename(file), recursive: true)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def self.exec(command, ssh = nil, allowFailure = false)
|
251
|
+
if ssh.nil?
|
252
|
+
stdout, stdeerr, status = Open3.capture3(command)
|
253
|
+
if !allowFailure && !status.success?
|
254
|
+
$stderr.puts(stdout)
|
255
|
+
$stderr.puts(stdeerr)
|
256
|
+
raise Framework::PluginProcessError.new("Failed '#{command}'")
|
257
|
+
end
|
258
|
+
stdout + stdeerr
|
259
|
+
else
|
260
|
+
self.sshExec!(ssh, command, allowFailure)
|
221
261
|
end
|
222
262
|
end
|
223
263
|
|
data/lib/ConfigLMM/version.rb
CHANGED