ConfigLMM 0.1.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 +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
|
+
![Yo Dawg I Heard you like config so I put a config in your Config](/Images/configINconfig.png)
|
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
|
+
![One Config to Rule Them All](/Images/singleConfig.png)
|
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 }
|