ConfigLMM 0.1.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 +63 -1
- data/Examples/Implemented.mm.yaml +120 -0
- data/Examples/Keys.ini +2 -0
- data/Examples/Linux.mm.yaml +14 -3
- data/Images/configINconfig.png +0 -0
- data/Images/singleConfig.png +0 -0
- 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 +165 -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 +2 -2
- data/Plugins/Apps/Nginx/config-lmm/security.conf +4 -0
- 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 +184 -0
- data/Plugins/Apps/Postfix/smtpd.conf +3 -0
- data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +225 -0
- 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 +56 -0
- 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 +16 -0
- data/Plugins/OS/Linux/Linux.lmm.rb +389 -0
- data/Plugins/OS/Linux/Packages.yaml +51 -0
- data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +108 -0
- data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +15 -0
- data/Plugins/OS/Linux/openSUSE/autoinst.xml.erb +87 -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 -1
- data/Plugins/Platforms/libvirt/libvirt.lmm.rb +103 -0
- data/Plugins/Services/DNS/PowerDNS.lmm.rb +69 -6
- data/README.md +10 -0
- data/bootstrap.sh +54 -0
- data/lib/ConfigLMM/Framework/plugins/dns.rb +1 -2
- data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +237 -0
- data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +24 -6
- data/lib/ConfigLMM/Framework/plugins/plugin.rb +150 -0
- data/lib/ConfigLMM/Framework/plugins.rb +1 -0
- data/lib/ConfigLMM/commands/configsCommand.rb +3 -0
- data/lib/ConfigLMM/version.rb +1 -1
- metadata +87 -5
- data/Plugins/Apps/Nginx/main.conf +0 -30
- data/Plugins/OS/Linux.lmm.rb +0 -64
@@ -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,87 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<!DOCTYPE profile>
|
3
|
+
<profile xmlns="http://www.suse.com/1.0/yast2ns" xmlns:config="http://www.suse.com/1.0/configns">
|
4
|
+
<general config:type="map">
|
5
|
+
<mode config:type="map">
|
6
|
+
<confirm config:type="boolean">false</confirm>
|
7
|
+
</mode>
|
8
|
+
</general>
|
9
|
+
<bootloader t="map">
|
10
|
+
<global t="map">
|
11
|
+
<append>splash=silent preempt=full mitigations=auto quiet security=apparmor console=ttyS0,115200</append>
|
12
|
+
</global>
|
13
|
+
</bootloader>
|
14
|
+
<host t="map">
|
15
|
+
<% if !config['Hosts'].to_a.empty? %>
|
16
|
+
<hosts t="list">
|
17
|
+
<% config['Hosts'].each do |address, names| %>
|
18
|
+
<hosts_entry t="map">
|
19
|
+
<host_address><%= address %></host_address>
|
20
|
+
<names t="list">
|
21
|
+
<% names.each do |name| %>
|
22
|
+
<name><%= name %></name>
|
23
|
+
<% end %>
|
24
|
+
</names>
|
25
|
+
</hosts_entry>
|
26
|
+
<% end %>
|
27
|
+
</hosts>
|
28
|
+
<% end %>
|
29
|
+
</host>
|
30
|
+
<networking t="map">
|
31
|
+
<dns t="map">
|
32
|
+
<hostname><%= config['HostName'] %></hostname>
|
33
|
+
<% if config['Domain'] %>
|
34
|
+
<domain><%= config['Domain'] %></domain>
|
35
|
+
<% end %>
|
36
|
+
</dns>
|
37
|
+
</networking>
|
38
|
+
<software t="map">
|
39
|
+
<% if !config['Apps'].to_a.empty? %>
|
40
|
+
<packages t="list">
|
41
|
+
<% config['Apps'].each do |app| %>
|
42
|
+
<package><%= app.downcase %></package>
|
43
|
+
<% end %>
|
44
|
+
</packages>
|
45
|
+
<% end %>
|
46
|
+
</software>
|
47
|
+
<services-manager t="map">
|
48
|
+
<services t="map">
|
49
|
+
<% if !config['Services'].to_a.empty? %>
|
50
|
+
<enable t="list">
|
51
|
+
<% config['Services'].each do |service| %>
|
52
|
+
<service><%= service %></service>
|
53
|
+
<% end %>
|
54
|
+
</enable>
|
55
|
+
<% end %>
|
56
|
+
</services>
|
57
|
+
</services-manager>
|
58
|
+
<timezone t="map">
|
59
|
+
<timezone>Etc/UTC</timezone>
|
60
|
+
</timezone>
|
61
|
+
<% if !config['Users'].to_h.empty? %>
|
62
|
+
<users config:type="list">
|
63
|
+
<% config['Users'].each do |user, info| %>
|
64
|
+
<user>
|
65
|
+
<username>root</username>
|
66
|
+
<% if info['PasswordHash'] %>
|
67
|
+
<encrypted config:type="boolean">true</encrypted>
|
68
|
+
<user_password><%= info['PasswordHash'] %></user_password>
|
69
|
+
<% elsif info['Password'] %>
|
70
|
+
<encrypted config:type="boolean">false</encrypted>
|
71
|
+
<user_password><%= info['Password'] %></user_password>
|
72
|
+
<% end %>
|
73
|
+
<% if info['Shell'] %>
|
74
|
+
<shell>/usr/bin/<%= info['Shell'] %></shell>
|
75
|
+
<% end %>
|
76
|
+
<% if !info['AuthorizedKeys'].to_a.empty? %>
|
77
|
+
<authorized_keys config:type="list">
|
78
|
+
<% info['AuthorizedKeys'].each do |entry| %>
|
79
|
+
<listentry><%= entry %></listentry>
|
80
|
+
<% end %>
|
81
|
+
</authorized_keys>
|
82
|
+
<% end %>
|
83
|
+
</user>
|
84
|
+
<% end %>
|
85
|
+
</users>
|
86
|
+
<% end %>
|
87
|
+
</profile>
|
@@ -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,7 +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 = name
|
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
|
30
35
|
config['Records'] += [shortName, record[:ttl], ' IN ', record[:type], record[:content]].join("\t") + "\n"
|
31
36
|
end
|
32
37
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
|
2
|
+
require 'fog/libvirt'
|
3
|
+
require 'filesize'
|
4
|
+
|
5
|
+
module ConfigLMM
|
6
|
+
module LMM
|
7
|
+
class Libvirt < Framework::Plugin
|
8
|
+
|
9
|
+
DEFAULT_IMAGE_PATH = '~/.local/share/libvirt/images/'
|
10
|
+
DEFAULT_VRAM = 16 * 1024 # 16 MiB
|
11
|
+
|
12
|
+
def actionLibvirtDeploy(id, target, activeState, context, options)
|
13
|
+
if !target['Location']
|
14
|
+
target['Location'] = 'qemu:///session'
|
15
|
+
end
|
16
|
+
compute = Fog::Compute.new(provider: :libvirt, libvirt_uri: target['Location'])
|
17
|
+
createPools(target, compute, self.class.isLocal?(target['Location']))
|
18
|
+
end
|
19
|
+
|
20
|
+
def createPools(target, compute, isLocal)
|
21
|
+
if target['Pools']
|
22
|
+
allPools = compute.pools.all
|
23
|
+
target['Pools'].each do |name, location|
|
24
|
+
next if allPools.find { |pool| pool.name == name }
|
25
|
+
location = DEFAULT_IMAGE_PATH unless location
|
26
|
+
location = File.expand_path(location) if isLocal
|
27
|
+
xml = dirPoolXML(name, location)
|
28
|
+
pool = compute.pools.create(persistent: true, autostart: true, xml: xml)
|
29
|
+
pool.build
|
30
|
+
pool.start
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def createVM(serverName, serverInfo, targetUri, iso, activeState)
|
36
|
+
compute = Fog::Compute.new(provider: :libvirt, libvirt_uri: targetUri)
|
37
|
+
server = compute.servers.all.find { |server| server.name == serverName }
|
38
|
+
if server
|
39
|
+
server.start
|
40
|
+
return
|
41
|
+
end
|
42
|
+
settings = {
|
43
|
+
name: serverName,
|
44
|
+
cpu: {
|
45
|
+
mode: 'host-passthrough'
|
46
|
+
},
|
47
|
+
video: { type: 'qxl', vram: DEFAULT_VRAM }
|
48
|
+
}
|
49
|
+
if serverInfo['CPU']
|
50
|
+
settings[:cpus] = serverInfo['CPU']
|
51
|
+
end
|
52
|
+
if serverInfo['RAM']
|
53
|
+
settings[:memory_size] = Filesize.from(serverInfo['RAM']).to_f('KiB').to_i
|
54
|
+
end
|
55
|
+
volumeName = serverName + '.img'
|
56
|
+
volume = compute.volumes.all.find { |volume| volume.name == volumeName }
|
57
|
+
if volume
|
58
|
+
settings[:volumes] = [volume]
|
59
|
+
elsif serverInfo['Storage']
|
60
|
+
storage = Filesize.from(serverInfo['Storage']).to_f('GiB').to_i
|
61
|
+
volume = compute.volumes.create(
|
62
|
+
name: volumeName,
|
63
|
+
pool_name: compute.pools.first.name,
|
64
|
+
capacity: storage
|
65
|
+
)
|
66
|
+
settings[:volumes] = [volume]
|
67
|
+
end
|
68
|
+
if serverInfo['NetworkBridge']
|
69
|
+
nic = {
|
70
|
+
bridge: serverInfo['NetworkBridge'],
|
71
|
+
}
|
72
|
+
settings[:nics] = [nic]
|
73
|
+
end
|
74
|
+
server = compute.servers.new(**settings)
|
75
|
+
if iso
|
76
|
+
server.iso_dir = File.dirname(iso)
|
77
|
+
server.iso_file = File.basename(iso)
|
78
|
+
end
|
79
|
+
server.save
|
80
|
+
activeState['Status'] = 'CREATED'
|
81
|
+
state.save
|
82
|
+
server.start
|
83
|
+
end
|
84
|
+
|
85
|
+
def dirPoolXML(name, path)
|
86
|
+
xml = '<pool type="dir">'
|
87
|
+
xml += " <name>#{name.encode(:xml => :text)}</name>"
|
88
|
+
xml += "<target><path>#{path.encode(:xml => :text)}</path></target>"
|
89
|
+
xml += '</pool>'
|
90
|
+
xml
|
91
|
+
end
|
92
|
+
|
93
|
+
def self.isLocal?(location)
|
94
|
+
self.getLocation(location).empty?
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.getLocation(location)
|
98
|
+
uri = Addressable::URI.parse(location)
|
99
|
+
uri.hostname
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -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
@@ -1,5 +1,7 @@
|
|
1
1
|
# ConfigLMM - Large Configuration Management Manager
|
2
2
|
|
3
|
+

|
4
|
+
|
3
5
|
## Manage The Management with ease!
|
4
6
|
|
5
7
|
You define how you want your applications/systems/containers/services/servers
|
@@ -57,6 +59,8 @@ to work without being vendor locked into any particular implementation or provid
|
|
57
59
|
|
58
60
|
The true [GitOps](https://en.wikipedia.org/wiki/DevOps#GitOps)/DevOps/DevSecOps/[TestOps](https://en.wikipedia.org/wiki/TestOps)/SysOps/[AIOps](https://en.wikipedia.org/wiki/Artificial_Intelligence_for_IT_Operations)[DataOps](https://en.wikipedia.org/wiki/DataOps) which I call AllTheOps :)
|
59
61
|
|
62
|
+

|
63
|
+
|
60
64
|
## Benefits
|
61
65
|
|
62
66
|
* Compare performance and price among different providers
|
@@ -217,6 +221,12 @@ First you need to have Ruby and RubyGems. Then you can install it with:
|
|
217
221
|
|
218
222
|
$ gem install ConfigLMM
|
219
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
|
+
|
220
230
|
## Usage
|
221
231
|
|
222
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 }
|