ConfigLMM 0.2.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +67 -0
  3. data/Examples/Implemented.mm.yaml +75 -1
  4. data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +14 -0
  5. data/Plugins/Apps/Authentik/Authentik-Server.container +19 -0
  6. data/Plugins/Apps/Authentik/Authentik-Worker.container +18 -0
  7. data/Plugins/Apps/Authentik/Authentik.conf.erb +42 -0
  8. data/Plugins/Apps/Authentik/Authentik.lmm.rb +95 -0
  9. data/Plugins/Apps/BookStack/BookStack.conf.erb +41 -0
  10. data/Plugins/Apps/BookStack/BookStack.container +15 -0
  11. data/Plugins/Apps/BookStack/BookStack.lmm.rb +80 -0
  12. data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +41 -0
  13. data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +17 -0
  14. data/Plugins/Apps/Discourse/Discourse.conf.erb +41 -0
  15. data/Plugins/Apps/Discourse/Discourse.container +17 -0
  16. data/Plugins/Apps/Discourse/Discourse.lmm.rb +95 -0
  17. data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +171 -0
  18. data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +19 -0
  19. data/Plugins/Apps/ERPNext/ERPNext-Queue.container +17 -0
  20. data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +17 -0
  21. data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +19 -0
  22. data/Plugins/Apps/ERPNext/ERPNext.container +18 -0
  23. data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +193 -0
  24. data/Plugins/Apps/ERPNext/ERPNext.network +12 -0
  25. data/Plugins/Apps/ERPNext/sites/apps.json +10 -0
  26. data/Plugins/Apps/ERPNext/sites/apps.txt +3 -0
  27. data/Plugins/Apps/ERPNext/sites/common_site_config.json +11 -0
  28. data/Plugins/Apps/GitLab/GitLab.container +18 -0
  29. data/Plugins/Apps/GitLab/GitLab.lmm.rb +100 -0
  30. data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +57 -0
  31. data/Plugins/Apps/LetsEncrypt/hooks/dovecot.sh +2 -0
  32. data/Plugins/Apps/LetsEncrypt/hooks/nginx.sh +2 -0
  33. data/Plugins/Apps/LetsEncrypt/hooks/postfix.sh +2 -0
  34. data/Plugins/Apps/LetsEncrypt/renew-certificates.service +7 -0
  35. data/Plugins/Apps/LetsEncrypt/renew-certificates.timer +12 -0
  36. data/Plugins/Apps/LetsEncrypt/rfc2136.ini +11 -0
  37. data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +115 -0
  38. data/Plugins/Apps/Matrix/Element.container +14 -0
  39. data/Plugins/Apps/Matrix/Matrix.conf.erb +49 -5
  40. data/Plugins/Apps/Matrix/Matrix.lmm.rb +86 -1
  41. data/Plugins/Apps/Matrix/Synapse.container +17 -0
  42. data/Plugins/Apps/Matrix/config.json +50 -0
  43. data/Plugins/Apps/Matrix/homeserver.yaml +70 -0
  44. data/Plugins/Apps/Matrix/log.config +30 -0
  45. data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +48 -10
  46. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +83 -1
  47. data/Plugins/Apps/Nextcloud/config.php +18 -0
  48. data/Plugins/Apps/Nginx/conf.d/configlmm.conf +71 -0
  49. data/Plugins/Apps/Nginx/config-lmm/errors.conf +11 -5
  50. data/Plugins/Apps/Nginx/config-lmm/proxy.conf +5 -1
  51. data/Plugins/Apps/Nginx/main.conf.erb +31 -0
  52. data/Plugins/Apps/Nginx/nginx.conf +3 -68
  53. data/Plugins/Apps/Nginx/nginx.lmm.rb +83 -22
  54. data/Plugins/Apps/Nginx/proxy.conf.erb +13 -3
  55. data/Plugins/Apps/Odoo/Odoo.conf.erb +30 -13
  56. data/Plugins/Apps/Odoo/Odoo.container +18 -0
  57. data/Plugins/Apps/Odoo/Odoo.lmm.rb +62 -2
  58. data/Plugins/Apps/Odoo/odoo.conf +37 -0
  59. data/Plugins/Apps/OpenVidu/Ingress.container +18 -0
  60. data/Plugins/Apps/OpenVidu/OpenVidu.conf.erb +34 -0
  61. data/Plugins/Apps/OpenVidu/OpenVidu.container +16 -0
  62. data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +90 -0
  63. data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +35 -0
  64. data/Plugins/Apps/OpenVidu/OpenViduCall.container +15 -0
  65. data/Plugins/Apps/OpenVidu/ingress.yaml +10 -0
  66. data/Plugins/Apps/OpenVidu/livekit.yaml +13 -0
  67. data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +95 -0
  68. data/Plugins/Apps/Peppermint/Peppermint.conf.erb +60 -0
  69. data/Plugins/Apps/Peppermint/Peppermint.container +15 -0
  70. data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +58 -0
  71. data/Plugins/Apps/Postfix/Postfix.lmm.rb +165 -31
  72. data/Plugins/Apps/Postfix/smtpd.conf +3 -0
  73. data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +242 -24
  74. data/Plugins/Apps/Roundcube/Roundcube.conf.erb +75 -0
  75. data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +145 -0
  76. data/Plugins/Apps/SSH/SSH.lmm.rb +51 -0
  77. data/Plugins/Apps/Tunnel/tunnel.lmm.rb +63 -0
  78. data/Plugins/Apps/Tunnel/tunnelTCP.service +9 -0
  79. data/Plugins/Apps/Tunnel/tunnelTCP.socket +9 -0
  80. data/Plugins/Apps/Tunnel/tunnelUDP.service +9 -0
  81. data/Plugins/Apps/Tunnel/tunnelUDP.socket +9 -0
  82. data/Plugins/Apps/UVdesk/UVdesk.conf.erb +52 -0
  83. data/Plugins/Apps/UVdesk/UVdesk.lmm.rb +85 -0
  84. data/Plugins/Apps/Valkey/Valkey.lmm.rb +34 -1
  85. data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +35 -18
  86. data/Plugins/Apps/Vaultwarden/Vaultwarden.container +16 -0
  87. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +46 -3
  88. data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +42 -0
  89. data/Plugins/Apps/Wiki.js/Wiki.js.container +15 -0
  90. data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +61 -0
  91. data/Plugins/Apps/gollum/gollum.conf.erb +84 -19
  92. data/Plugins/Apps/gollum/gollum.container +15 -0
  93. data/Plugins/Apps/gollum/gollum.lmm.rb +48 -11
  94. data/Plugins/OS/Linux/Debian/preseed.cfg.erb +62 -0
  95. data/Plugins/OS/Linux/Distributions.yaml +42 -0
  96. data/Plugins/OS/Linux/Flavours.yaml +11 -0
  97. data/Plugins/OS/Linux/Linux.lmm.rb +362 -41
  98. data/Plugins/OS/Linux/Packages.yaml +88 -5
  99. data/Plugins/OS/Linux/Proxmox/answer.toml.erb +30 -0
  100. data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +137 -0
  101. data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +15 -0
  102. data/Plugins/OS/Linux/systemd/systemd.lmm.rb +28 -0
  103. data/Plugins/OS/Linux/systemd/user-0.slice +9 -0
  104. data/Plugins/OS/Linux/systemd/user@.service.d/delegate.conf +3 -0
  105. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +7 -3
  106. data/Plugins/Platforms/libvirt/libvirt.lmm.rb +3 -2
  107. data/Plugins/Services/DNS/PowerDNS.lmm.rb +158 -8
  108. data/README.md +6 -0
  109. data/bootstrap.sh +92 -0
  110. data/lib/ConfigLMM/Framework/plugins/dns.rb +1 -2
  111. data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +249 -45
  112. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +56 -7
  113. data/lib/ConfigLMM/Framework/plugins/plugin.rb +112 -16
  114. data/lib/ConfigLMM/cli.rb +3 -1
  115. data/lib/ConfigLMM/commands/cleanup.rb +1 -0
  116. data/lib/ConfigLMM/commands/configsCommand.rb +3 -1
  117. data/lib/ConfigLMM/io/configList.rb +3 -1
  118. data/lib/ConfigLMM/state.rb +10 -2
  119. data/lib/ConfigLMM/version.rb +1 -1
  120. metadata +82 -3
  121. data/Plugins/Apps/Nginx/main.conf +0 -30
@@ -0,0 +1,100 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class GitLab < Framework::NginxApp
5
+
6
+ HOME_DIR = '/var/lib/gitlab'
7
+ IMAGE_ID = 'docker.io/gitlab/gitlab-ce:latest'
8
+
9
+ def actionGitLabDeploy(id, target, activeState, context, options)
10
+
11
+ if target['Location'] && target['Location'] != '@me'
12
+ uri = Addressable::URI.parse(target['Location'])
13
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
14
+
15
+ self.class.sshStart(uri) do |ssh|
16
+ self.prepareConfig(target, ssh)
17
+
18
+ distroInfo = Framework::LinuxApp.currentDistroInfo(ssh)
19
+ self.class.exec("mkdir -p #{HOME_DIR}/config", ssh)
20
+ self.class.exec("mkdir -p #{HOME_DIR}/logs", ssh)
21
+ self.class.exec("mkdir -p #{HOME_DIR}/data", ssh)
22
+ self.class.exec("mkdir -p #{HOME_DIR}/backups", ssh)
23
+
24
+ path = '/etc/containers/systemd'
25
+ ssh.scp.upload!(__dir__ + '/GitLab.container', path)
26
+
27
+ if !target.key?('Proxy') || target['Proxy']
28
+ deployNginxProxyConfig('http://127.0.0.1:18100', 'GitLab', id, target, activeState, state, context, options, ssh)
29
+ elsif target.key?('Proxy') && target['Proxy'] == false
30
+ self.class.exec("sed -i 's|PublishPort=127.0.0.1:18100:|PublishPort=0.0.0.0:18100:|' #{path}/GitLab.container", ssh)
31
+ Framework::LinuxApp.firewallAddPort('18100/tcp', ssh)
32
+ end
33
+
34
+ Framework::LinuxApp.reloadServiceManager(ssh)
35
+ Framework::LinuxApp.restartService('GitLab', ssh)
36
+
37
+ configFile = HOME_DIR + '/config/gitlab.rb'
38
+ while !self.class.remoteFilePresent?(configFile, ssh)
39
+ sleep(2)
40
+ end
41
+ updateRemoteFile(ssh, configFile, options, true) do |fileLines|
42
+ fileLines << "external_url 'https://#{target['Domain']}'\n"
43
+ fileLines << "letsencrypt['enable'] = false\n"
44
+ fileLines << "nginx['listen_port'] = 80\n"
45
+ fileLines << "nginx['listen_https'] = false\n"
46
+ fileLines << "registry_nginx['listen_port'] = 80\n"
47
+ fileLines << "registry_nginx['listen_https'] = false\n"
48
+ fileLines << "mattermost_nginx['listen_port'] = 80\n"
49
+ fileLines << "mattermost_nginx['listen_https'] = false\n"
50
+ if target['SMTP']
51
+ fileLines << "gitlab_rails['smtp_address'] = '#{target['SMTP']['HostName']}'\n"
52
+ fileLines << "gitlab_rails['smtp_port'] = '#{target['SMTP']['Port']}'\n"
53
+ fileLines << "gitlab_rails['smtp_user_name'] = '#{target['SMTP']['User']}'\n"
54
+ if target['SMTP']['TLS']
55
+ fileLines << "gitlab_rails['smtp_tls'] = true\n"
56
+ fileLines << "gitlab_rails['smtp_openssl_verify_mode'] = 'peer'\n"
57
+ end
58
+ end
59
+ end
60
+
61
+ Framework::LinuxApp.restartService('GitLab', ssh)
62
+ end
63
+ else
64
+ # TODO
65
+ end
66
+ activeState['Status'] = State::STATUS_DEPLOYED
67
+ end
68
+
69
+ def prepareConfig(target, ssh)
70
+ raise Framework::PluginProcessError.new('Domain field must be set!') unless target['Domain']
71
+
72
+ Framework::LinuxApp.ensurePackages([NGINX_PACKAGE], ssh)
73
+ self.class.prepareNginxConfig(target, ssh)
74
+ end
75
+
76
+ def cleanup(configs, state, context, options)
77
+ cleanupType(:GitLab, configs, state, context, options) do |item, id, state, context, options, ssh|
78
+ if item['Proxy'].nil? || item['Proxy']
79
+ self.cleanupNginxConfig('GitLab', id, state, context, options, ssh)
80
+ self.class.reload(ssh, options[:dry])
81
+ end
82
+ Framework::LinuxApp.firewallRemovePort('18100/tcp', ssh, options[:dry])
83
+ Framework::LinuxApp.stopService('GitLab', ssh, options[:dry])
84
+ rm('/etc/containers/systemd/GitLab.container', options[:dry], ssh)
85
+ self.class.exec("podman rmi #{IMAGE_ID}", ssh, true, options[:dry])
86
+ state.item(id)['Status'] = State::STATUS_DELETED unless options[:dry]
87
+ if options[:destroy]
88
+ rm('/var/lib/gitlab', options[:dry], ssh)
89
+ rm('/var/log/nginx/gitlab.access.log', options[:dry], ssh)
90
+ rm('/var/log/nginx/gitlab.error.log', options[:dry], ssh)
91
+ state.item(id)['Status'] = State::STATUS_DESTROYED unless options[:dry]
92
+ end
93
+ end
94
+ end
95
+
96
+ end
97
+
98
+ end
99
+ end
100
+
@@ -0,0 +1,57 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class LetsEncrypt < Framework::LinuxApp
5
+
6
+ PACKAGE_NAME = 'CertBotNginx'
7
+ CONFIG_DIR = '/etc/letsencrypt/'
8
+
9
+ def actionLetsEncryptDeploy(id, target, activeState, context, options)
10
+ self.ensurePackage(PACKAGE_NAME, target['Location'])
11
+
12
+ if target['Location'] && target['Location'] != '@me'
13
+ uri = Addressable::URI.parse(target['Location'])
14
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
15
+
16
+ self.class.sshStart(uri) do |ssh|
17
+ ssh.scp.upload!(__dir__ + '/rfc2136.ini', CONFIG_DIR)
18
+ ssh.scp.upload!(__dir__ + '/renew-certificates.service', '/etc/systemd/system/')
19
+ ssh.scp.upload!(__dir__ + '/renew-certificates.timer', '/etc/systemd/system/')
20
+ self.class.exec("mkdir -p #{CONFIG_DIR}renewal-hooks/deploy", ssh)
21
+ target['Hooks'].to_a.each do |hook|
22
+ ssh.scp.upload!(__dir__ + '/hooks/' + hook + '.sh', "#{CONFIG_DIR}renewal-hooks/deploy/")
23
+ end
24
+ self.class.exec("chmod +x #{CONFIG_DIR}renewal-hooks/deploy/*.sh", ssh)
25
+ self.class.exec("sed -i 's|$IP|#{target['DNS']['IP']}|' #{CONFIG_DIR}/rfc2136.ini", ssh)
26
+ self.class.exec("sed -i 's|$SECRET|#{ENV['LETSENCRYPT_DNS_SECRET']}|' #{CONFIG_DIR}/rfc2136.ini", ssh)
27
+ self.class.exec("chmod 600 #{CONFIG_DIR}/rfc2136.ini", ssh)
28
+ if target['Domain']
29
+ createCertificate('Wildcard', target['Domain'], target, ssh)
30
+ end
31
+ target['Certificates'].to_h.each do |name, domain|
32
+ createCertificate(name, domain, target, ssh)
33
+ end
34
+
35
+ self.class.exec("systemctl daemon-reload", ssh)
36
+ self.class.exec("systemctl enable renew-certificates.timer", ssh)
37
+ self.class.exec("systemctl start renew-certificates.timer", ssh)
38
+ end
39
+ else
40
+ # TODO
41
+ end
42
+ end
43
+
44
+ def createCertificate(name, domain, target, ssh)
45
+ domains = ['--domains "' + Addressable::IDNA.to_ascii(domain) + '"']
46
+ if domain.start_with?('*.')
47
+ domains << '--domains "' + Addressable::IDNA.to_ascii(domain[2..-1]) + '"'
48
+ end
49
+ extra = ''
50
+ extra = '--dns-rfc2136-propagation-seconds ' + target['DNS']['Propagation'].to_s if target['DNS']['Propagation']
51
+ self.class.exec("certbot certonly --dns-rfc2136 --dns-rfc2136-credentials=/etc/letsencrypt/rfc2136.ini #{extra} --non-interactive --agree-tos --email #{target['EMail']} --cert-name '#{name}' #{domains.join(' ')}", ssh)
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/sh
2
+ systemctl reload dovecot
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/sh
2
+ systemctl reload nginx
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/sh
2
+ systemctl reload postfix
@@ -0,0 +1,7 @@
1
+
2
+ [Unit]
3
+ Description=Renew certificates
4
+
5
+ [Service]
6
+ Type=oneshot
7
+ ExecStart=/usr/bin/certbot renew
@@ -0,0 +1,12 @@
1
+
2
+ [Unit]
3
+ Description=Renew certificates
4
+
5
+ [Timer]
6
+ OnCalendar=*-*-1/2 04:20
7
+ AccuracySec=12h
8
+ RandomizedDelaySec=24h
9
+ Persistent=true
10
+
11
+ [Install]
12
+ WantedBy=timers.target
@@ -0,0 +1,11 @@
1
+
2
+ # Target DNS server
3
+ dns_rfc2136_server = $IP
4
+ # Target DNS port
5
+ dns_rfc2136_port = 53
6
+ # TSIG key name
7
+ dns_rfc2136_name = configlmm.
8
+ # TSIG key secret
9
+ dns_rfc2136_secret = $SECRET
10
+ # TSIG key algorithm
11
+ dns_rfc2136_algorithm = HMAC-SHA512
@@ -0,0 +1,115 @@
1
+ require_relative '../../OS/Linux/Linux.lmm.rb'
2
+
3
+ module ConfigLMM
4
+ module LMM
5
+ class MariaDB < Framework::LinuxApp
6
+ PACKAGE_NAME = 'MariaDB'
7
+ SERVICE_NAME = 'mariadb'
8
+ USER_NAME = 'mariadb'
9
+
10
+ def actionMariaDBDeploy(id, target, activeState, context, options)
11
+ self.ensurePackage(PACKAGE_NAME, target['Location'])
12
+ self.ensureServiceAutoStart(SERVICE_NAME, target['Location'])
13
+ self.startService(SERVICE_NAME, target['Location'])
14
+
15
+ if target['Location'] && target['Location'] != '@me'
16
+ uri = Addressable::URI.parse(target['Location'])
17
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
18
+
19
+ self.class.sshStart(uri) do |ssh|
20
+ self.class.secureInstallation(ssh)
21
+ if target['Listen']
22
+ self.class.exec("sed -i 's|bind-address .*|bind-address = #{target['Listen']}|' /etc/my.cnf", ssh)
23
+ self.class.restartService(SERVICE_NAME, ssh)
24
+ end
25
+ end
26
+ else
27
+ # TODO
28
+ end
29
+
30
+ activeState['Status'] = State::STATUS_DEPLOYED
31
+ end
32
+
33
+ def cleanup(configs, state, context, options)
34
+ cleanupType(:MariaDB, configs, state, context, options) do |item, id, state, context, options, ssh|
35
+ Framework::LinuxApp.stopService(SERVICE_NAME, ssh, options[:dry])
36
+ Framework::LinuxApp.disableService(SERVICE_NAME, ssh, options[:dry])
37
+ Framework::LinuxApp.removePackage(PACKAGE_NAME, ssh, options[:dry])
38
+
39
+ state.item(id)['Status'] = State::STATUS_DELETED unless options[:dry]
40
+ end
41
+ end
42
+
43
+ def self.secureInstallation(ssh)
44
+ status = {}
45
+ output = ''
46
+ channel = ssh.exec("mariadb-secure-installation", status: status) do |channel, stream, data|
47
+ output += data
48
+ channel.send_data("\n") # Empty root password
49
+ channel.send_data("Y\n") # unix_socket authentication
50
+ channel.send_data("N\n") # change the root password
51
+ channel.send_data("Y\n") # remove anonymous users
52
+ channel.send_data("Y\n") # disallow root login remotely
53
+ channel.send_data("Y\n") # remove test database
54
+ channel.send_data("Y\n") # reload privileges
55
+ end
56
+ channel.wait
57
+ if !status[:exit_code].zero?
58
+ $stderr.puts(output)
59
+ raise Framework::PluginProcessError.new("mariadb-secure-installation failed!")
60
+ end
61
+ end
62
+
63
+ def self.createRemoteUserAndDB(settings, user, password, ssh = nil)
64
+ self.executeRemotely(settings, ssh) do |ssh|
65
+ host = 'localhost'
66
+ host = '%' if settings['HostName'] != 'localhost'
67
+ self.createUserAndDB(user, password, host, ssh)
68
+ end
69
+ end
70
+
71
+ def self.executeRemotely(settings, ssh = nil)
72
+ settings['HostName'] = 'localhost' unless settings['HostName']
73
+ if settings['HostName'] == 'localhost'
74
+ yield(ssh)
75
+ else
76
+ self.sshStart("ssh://#{settings['HostName']}/") do |ssh|
77
+ yield(ssh)
78
+ end
79
+ end
80
+ end
81
+
82
+ def self.createUserAndDB(user, password, host, ssh = nil)
83
+ self.executeSQL("CREATE USER '#{user}'@'#{host}'", nil, ssh, true)
84
+ self.executeSQL("ALTER USER '#{user}'@'#{host}' IDENTIFIED BY '#{password}'", nil, ssh)
85
+ self.executeSQL("CREATE DATABASE #{user}", nil, ssh, true)
86
+ self.executeSQL("GRANT ALL PRIVILEGES ON #{user}.* TO '#{user}'@'#{host}'", nil, ssh)
87
+ end
88
+
89
+ def self.createAdmin(ssh)
90
+ self.executeSQL("CREATE USER 'admin'@'%'", nil, ssh, true)
91
+ password = SecureRandom.alphanumeric(20)
92
+ self.executeSQL("ALTER USER 'admin'@'%' IDENTIFIED BY '#{password}'", nil, ssh)
93
+ self.executeSQL("GRANT ALL PRIVILEGES ON *.* TO 'admin'@'%' WITH GRANT OPTION", nil, ssh)
94
+ password
95
+ end
96
+
97
+ def self.dropAdmin(ssh)
98
+ self.executeSQL("DROP USER 'admin'@'%'", nil, ssh, true)
99
+ end
100
+
101
+ def self.tableExist?(db, table, ssh)
102
+ table = self.executeSQL("SHOW TABLES LIKE '#{table}'", db, ssh).strip
103
+ !table.empty?
104
+ end
105
+
106
+ def self.executeSQL(sql, db = nil, ssh = nil, allowFailure = false, dry = false)
107
+ db = '' unless db
108
+ cmd = " mariadb #{db} --execute=\"#{sql.gsub('"', '\\"')};\""
109
+ self.exec(cmd, ssh, allowFailure, dry)
110
+ end
111
+
112
+ end
113
+
114
+ end
115
+ end
@@ -0,0 +1,14 @@
1
+
2
+ [Unit]
3
+ Description=Matrix (Element) container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ Image=docker.io/vectorim/element-web:latest
8
+ EnvironmentFile=/var/lib/matrix/.config/containers/systemd/Matrix.env
9
+ PublishPort=127.0.0.1:18300:80
10
+ Volume=/var/lib/matrix/config.json:/app/config.json
11
+ AutoUpdate=registry
12
+
13
+ [Install]
14
+ WantedBy=multi-user.target default.target
@@ -1,12 +1,56 @@
1
1
 
2
2
  server {
3
+
4
+ <% if !config['TLS'] %>
5
+ listen <%= config['Port'] %>;
6
+ listen [::]:<%= config['Port'] %>;
7
+ <% else %>
8
+ <% if config['NginxVersion'] >= 1.25 %>
9
+ listen <%= config['Port'] %> ssl;
10
+ listen [::]:<%= config['Port'] %> ssl;
11
+ http2 on;
12
+ http3 on;
13
+ quic_retry on;
14
+ add_header Alt-Svc 'h3=":<%= config['Port'] %>"; ma=86400';
15
+ <% else %>
16
+ listen <%= config['Port'] %> ssl http2;
17
+ listen [::]:<%= config['Port'] %> ssl http2;
18
+ <% end %>
19
+
20
+ include config-lmm/ssl.conf;
21
+ <% end %>
22
+
23
+ server_name <%= config['Domain'] %>;
24
+
25
+ access_log /var/log/nginx/matrix.access.log;
26
+ error_log /var/log/nginx/matrix.error.log;
27
+
28
+ include config-lmm/errors.conf;
29
+
30
+ location / {
31
+ proxy_pass http://localhost:18300;
32
+ include config-lmm/proxy.conf;
33
+ }
34
+ }
35
+
36
+ server {
37
+
3
38
  <% if !config['TLS'] %>
4
39
  listen <%= config['Port'] %>;
5
40
  listen [::]:<%= config['Port'] %>;
6
41
  <% else %>
7
- listen <%= config['Port'] %> ssl;
8
- listen [::]:<%= config['Port'] %> ssl;
9
- http2 on;
42
+ <% if config['NginxVersion'] >= 1.25 %>
43
+ listen <%= config['Port'] %> ssl;
44
+ listen [::]:<%= config['Port'] %> ssl;
45
+ http2 on;
46
+ http3 on;
47
+ quic_retry on;
48
+ add_header Alt-Svc 'h3=":<%= config['Port'] %>"; ma=86400';
49
+ <% else %>
50
+ listen <%= config['Port'] %> ssl http2;
51
+ listen [::]:<%= config['Port'] %> ssl http2;
52
+ <% end %>
53
+
10
54
  include config-lmm/ssl.conf;
11
55
  <% end %>
12
56
 
@@ -14,12 +58,11 @@ server {
14
58
  #listen 8448 ssl http2 default_server;
15
59
  #listen [::]:8448 ssl http2 default_server;
16
60
 
17
- server_name <%= config['Domain'] %>;
61
+ server_name <%= config['SynapseDomain'] %>;
18
62
 
19
63
  access_log /var/log/nginx/matrix.access.log;
20
64
  error_log /var/log/nginx/matrix.error.log;
21
65
 
22
- include config-lmm/private.conf;
23
66
  include config-lmm/errors.conf;
24
67
 
25
68
  location ~ ^(/_matrix|/_synapse/client) {
@@ -28,6 +71,7 @@ server {
28
71
  # errors.
29
72
  proxy_pass http://localhost:8008;
30
73
  include config-lmm/proxy.conf;
74
+ proxy_intercept_errors off;
31
75
 
32
76
  # Nginx by default only allows file uploads up to 1M in size
33
77
  # Increase client_max_body_size to match max_upload_size defined in homeserver.yaml
@@ -3,6 +3,9 @@ module ConfigLMM
3
3
  module LMM
4
4
  class Matrix < Framework::NginxApp
5
5
 
6
+ USER = 'matrix'
7
+ HOME_DIR = '/var/lib/matrix'
8
+
6
9
  def actionMatrixBuild(id, target, state, context, options)
7
10
  writeNginxConfig(__dir__, 'Matrix', id, target, state, context, options)
8
11
  end
@@ -12,12 +15,94 @@ module ConfigLMM
12
15
  end
13
16
 
14
17
  def actionMatrixDeploy(id, target, activeState, context, options)
15
- if !target['Location'] || target['Location'] == '@me'
18
+ raise Framework::PluginProcessError.new('Domain field must be set!') unless target['Domain']
19
+ raise Framework::PluginProcessError.new('ServerName field must be set!') unless target['ServerName']
20
+
21
+ target['Database'] ||= {}
22
+ if target['Location'] && target['Location'] != '@me'
23
+ uri = Addressable::URI.parse(target['Location'])
24
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
25
+
26
+ self.class.sshStart(uri) do |ssh|
27
+
28
+ dbPassword = self.configurePostgreSQL(target['Database'], ssh)
29
+ distroInfo = Framework::LinuxApp.currentDistroInfo(ssh)
30
+
31
+ Framework::LinuxApp.configurePodmanServiceOverSSH(USER, HOME_DIR, 'Matrix', distroInfo, ssh)
32
+ self.class.sshExec!(ssh, "su --login #{USER} --shell /bin/sh --command 'mkdir -p ~/data'")
33
+
34
+ path = Framework::LinuxApp::SYSTEMD_CONTAINERS_PATH.gsub('~', HOME_DIR)
35
+ self.class.exec("touch #{path}/Matrix.env", ssh)
36
+
37
+ self.class.exec("chown #{USER}:#{USER} #{path}/Matrix.env", ssh)
38
+ self.class.exec("chmod 600 #{path}/Matrix.env", ssh)
39
+
40
+ ssh.scp.upload!(__dir__ + '/homeserver.yaml', HOME_DIR + '/data/')
41
+ ssh.scp.upload!(__dir__ + '/log.config', HOME_DIR + '/data/')
42
+ ssh.scp.upload!(__dir__ + '/config.json', HOME_DIR + '/')
43
+ self.class.exec("chown -R #{USER}:#{USER} #{HOME_DIR}/data", ssh)
44
+
45
+ self.class.exec("sed -i 's|$SERVER_NAME|#{target['ServerName']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
46
+ self.class.exec("sed -i 's|$SYNAPSE_DOMAIN|#{target['SynapseDomain'].downcase}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
47
+ self.class.exec("sed -i 's|$DB_PASSWORD|#{dbPassword}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
48
+ self.class.exec("sed -i 's|$SECRET1|#{SecureRandom.urlsafe_base64(45)}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
49
+ self.class.exec("sed -i 's|$SECRET2|#{SecureRandom.urlsafe_base64(45)}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
50
+ self.class.exec("sed -i 's|$SECRET3|#{SecureRandom.urlsafe_base64(45)}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
51
+
52
+ self.class.exec("sed -i 's|$SYNAPSE_DOMAIN|#{target['SynapseDomain']}|' #{HOME_DIR}/config.json", ssh)
53
+ self.class.exec("sed -i 's|$SERVER_NAME|#{target['ServerName']}|' #{HOME_DIR}/config.json", ssh)
54
+
55
+ if target['SMTP']
56
+ host = target['SMTP']['Host']
57
+ host = HOST_IP if ['localhost', '127.0.0.1'].include?(host)
58
+ self.class.exec("sed -i 's|smtp_host:.*|smtp_host: #{host}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
59
+ self.class.exec("sed -i 's|smtp_port:.*|smtp_port: #{target['SMTP']['Port']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
60
+ self.class.exec("sed -i 's|smtp_user:.*|smtp_user: #{target['SMTP']['Username']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
61
+ self.class.exec("sed -i 's|smtp_pass:.*|smtp_pass: #{ENV['MATRIX_SMTP_PASSWORD']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
62
+ self.class.exec("sed -i 's|notif_from:.*|notif_from: #{target['SMTP']['From']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
63
+
64
+ if target['SMTP']['Port'] == 465
65
+ self.class.exec("sed -i 's|force_tls:.*|force_tls: true|' #{HOME_DIR}/data/homeserver.yaml", ssh)
66
+ end
67
+ else
68
+ self.class.exec("sed -i 's|email:|ignore_email:|' #{HOME_DIR}/data/homeserver.yaml", ssh)
69
+ end
70
+
71
+ if target['OIDC']
72
+ self.class.exec("sed -i 's|$OIDC_ISSUER|#{target['OIDC']['Issuer']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
73
+ self.class.exec("sed -i 's|$CLIENT_ID|#{ENV['MATRIX_OIDC_CLIENT_ID']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
74
+ self.class.exec("sed -i 's|$CLIENT_SECRET|#{ENV['MATRIX_OIDC_CLIENT_SECRET']}|' #{HOME_DIR}/data/homeserver.yaml", ssh)
75
+ self.class.exec("sed -i 's|enabled: true|enabled: false|' #{HOME_DIR}/data/homeserver.yaml", ssh)
76
+ else
77
+ self.class.exec("sed -i 's|oidc_providers:|ignore_oidc_providers:|' #{HOME_DIR}/data/homeserver.yaml", ssh)
78
+ end
79
+
80
+ ssh.scp.upload!(__dir__ + '/Synapse.container', path)
81
+ ssh.scp.upload!(__dir__ + '/Element.container', path)
82
+ self.class.exec("systemctl --user --machine=#{USER}@ daemon-reload", ssh)
83
+ self.class.exec("systemctl --user --machine=#{USER}@ restart Synapse", ssh)
84
+ self.class.exec("systemctl --user --machine=#{USER}@ restart Element", ssh)
85
+
86
+ Framework::LinuxApp.ensurePackages([NGINX_PACKAGE], ssh)
87
+ Framework::LinuxApp.ensureServiceAutoStartOverSSH(NGINX_PACKAGE, ssh)
88
+ self.class.prepareNginxConfig(target, ssh)
89
+ self.writeNginxConfig(__dir__, 'Matrix', id, target, state, context, options)
90
+ self.deployNginxConfig(id, target, activeState, context, options)
91
+ Framework::LinuxApp.startServiceOverSSH(NGINX_PACKAGE, ssh)
92
+
93
+ end
94
+ else
16
95
  deployNginxConfig(id, target, activeState, context, options)
17
96
  activeState['Location'] = '@me'
18
97
  end
19
98
  end
20
99
 
100
+ def configurePostgreSQL(settings, ssh)
101
+ password = SecureRandom.alphanumeric(20)
102
+ PostgreSQL.createRemoteUserAndDBOverSSH(settings, USER, password, ssh)
103
+ password
104
+ end
105
+
21
106
  end
22
107
  end
23
108
  end
@@ -0,0 +1,17 @@
1
+
2
+
3
+ [Unit]
4
+ Description=Matrix (Synapse) container
5
+ After=local-fs.target
6
+
7
+ [Container]
8
+ Image=docker.io/matrixdotorg/synapse:latest
9
+ EnvironmentFile=/var/lib/matrix/.config/containers/systemd/Matrix.env
10
+ Network=slirp4netns:allow_host_loopback=true
11
+ PublishPort=127.0.0.1:8008:8008
12
+ UserNS=keep-id:uid=991,gid=991
13
+ Volume=/var/lib/matrix/data:/data
14
+ AutoUpdate=registry
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,50 @@
1
+ {
2
+ "default_server_config": {
3
+ "m.homeserver": {
4
+ "base_url": "https://$SYNAPSE_DOMAIN",
5
+ "server_name": "$SERVER_NAME"
6
+ },
7
+ "m.identity_server": {
8
+ "base_url": "https://matrix.org"
9
+ }
10
+ },
11
+ "disable_custom_urls": false,
12
+ "disable_guests": false,
13
+ "disable_login_language_selector": false,
14
+ "disable_3pid_login": false,
15
+ "brand": "Element",
16
+ "integrations_ui_url": "https://scalar.vector.im/",
17
+ "integrations_rest_url": "https://scalar.vector.im/api",
18
+ "integrations_widgets_urls": [
19
+ "https://scalar.vector.im/_matrix/integrations/v1",
20
+ "https://scalar.vector.im/api",
21
+ "https://scalar-staging.vector.im/_matrix/integrations/v1",
22
+ "https://scalar-staging.vector.im/api",
23
+ "https://scalar-staging.riot.im/scalar/api"
24
+ ],
25
+ "default_widget_height": 280,
26
+ "default_country_code": "GB",
27
+ "show_labs_settings": false,
28
+ "features": {},
29
+ "default_federate": true,
30
+ "default_theme": "light",
31
+ "room_directory": {
32
+ "servers": ["matrix.org"]
33
+ },
34
+ "enable_presence_by_hs_url": {
35
+ "https://matrix.org": false,
36
+ "https://matrix-client.matrix.org": false
37
+ },
38
+ "setting_defaults": {
39
+ "breadcrumbs": true
40
+ },
41
+ "jitsi": {
42
+ "preferred_domain": "meet.element.io"
43
+ },
44
+ "element_call": {
45
+ "url": "https://call.element.io",
46
+ "participant_limit": 8,
47
+ "brand": "Element Call"
48
+ },
49
+ "map_style_url": "https://api.maptiler.com/maps/streets/style.json?key=fU3vlMsMn4Jb6dnEIFsx"
50
+ }
@@ -0,0 +1,70 @@
1
+ # Configuration file for Synapse.
2
+ #
3
+ # This is a YAML file: see [1] for a quick introduction. Note in particular
4
+ # that *indentation is important*: all the elements of a list or dictionary
5
+ # should have the same indentation.
6
+ #
7
+ # [1] https://docs.ansible.com/ansible/latest/reference_appendices/YAMLSyntax.html
8
+ #
9
+ # For more information on how to configure Synapse, including a complete accounting of
10
+ # each option, go to docs/usage/configuration/config_documentation.md or
11
+ # https://element-hq.github.io/synapse/latest/usage/configuration/config_documentation.html
12
+ server_name: $SERVER_NAME
13
+ public_baseurl: https://$SYNAPSE_DOMAIN/
14
+ pid_file: /data/homeserver.pid
15
+ listeners:
16
+ - port: 8008
17
+ tls: false
18
+ type: http
19
+ x_forwarded: true
20
+ resources:
21
+ - names: [client, federation]
22
+ compress: false
23
+
24
+ database:
25
+ name: psycopg2
26
+ allow_unsafe_locale: true
27
+ args:
28
+ user: matrix
29
+ password: $DB_PASSWORD
30
+ dbname: matrix
31
+ host: 10.0.2.2
32
+
33
+ email:
34
+ smtp_host: 10.0.2.2
35
+ smtp_port: 25
36
+ smtp_user:
37
+ smtp_pass:
38
+ force_tls: false
39
+ notif_from:
40
+
41
+ password_config:
42
+ enabled: true
43
+
44
+ oidc_providers:
45
+ - idp_id: OIDC
46
+ idp_name: OIDC
47
+ discover: true
48
+ issuer: $OIDC_ISSUER
49
+ client_id: $CLIENT_ID
50
+ client_secret: $CLIENT_SECRET
51
+ scopes:
52
+ - "openid"
53
+ - "profile"
54
+ - "email"
55
+ user_mapping_provider:
56
+ config:
57
+ localpart_template: "{{ user.preferred_username }}"
58
+ display_name_template: "{{ user.name|capitalize }}"
59
+
60
+ log_config: "/data/log.config"
61
+ media_store_path: /data/media_store
62
+ registration_shared_secret: $SECRET1
63
+ report_stats: false
64
+ macaroon_secret_key: $SECRET2
65
+ form_secret: $SECRET3
66
+ report_stats: false
67
+ signing_key_path: "/data/signing.key"
68
+ trusted_key_servers:
69
+ - server_name: "matrix.org"
70
+ suppress_key_server_warning: true