ConfigLMM 0.3.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 (99) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +36 -0
  3. data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +14 -0
  4. data/Plugins/Apps/Authentik/Authentik-Server.container +1 -0
  5. data/Plugins/Apps/Authentik/Authentik-Worker.container +1 -0
  6. data/Plugins/Apps/Authentik/Authentik.conf.erb +7 -0
  7. data/Plugins/Apps/Authentik/Authentik.lmm.rb +24 -2
  8. data/Plugins/Apps/BookStack/BookStack.conf.erb +41 -0
  9. data/Plugins/Apps/BookStack/BookStack.container +15 -0
  10. data/Plugins/Apps/BookStack/BookStack.lmm.rb +80 -0
  11. data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +17 -0
  12. data/Plugins/Apps/Discourse/Discourse.conf.erb +41 -0
  13. data/Plugins/Apps/Discourse/Discourse.container +17 -0
  14. data/Plugins/Apps/Discourse/Discourse.lmm.rb +95 -0
  15. data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +25 -2
  16. data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +19 -0
  17. data/Plugins/Apps/ERPNext/ERPNext-Queue.container +17 -0
  18. data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +17 -0
  19. data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +19 -0
  20. data/Plugins/Apps/ERPNext/ERPNext.container +18 -0
  21. data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +193 -0
  22. data/Plugins/Apps/ERPNext/ERPNext.network +12 -0
  23. data/Plugins/Apps/ERPNext/sites/apps.json +10 -0
  24. data/Plugins/Apps/ERPNext/sites/apps.txt +3 -0
  25. data/Plugins/Apps/ERPNext/sites/common_site_config.json +11 -0
  26. data/Plugins/Apps/GitLab/GitLab.container +3 -2
  27. data/Plugins/Apps/GitLab/GitLab.lmm.rb +37 -12
  28. data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +57 -0
  29. data/Plugins/Apps/LetsEncrypt/hooks/dovecot.sh +2 -0
  30. data/Plugins/Apps/LetsEncrypt/hooks/nginx.sh +2 -0
  31. data/Plugins/Apps/LetsEncrypt/hooks/postfix.sh +2 -0
  32. data/Plugins/Apps/LetsEncrypt/renew-certificates.service +7 -0
  33. data/Plugins/Apps/LetsEncrypt/renew-certificates.timer +12 -0
  34. data/Plugins/Apps/LetsEncrypt/rfc2136.ini +11 -0
  35. data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +115 -0
  36. data/Plugins/Apps/Matrix/Element.container +14 -0
  37. data/Plugins/Apps/Matrix/Matrix.conf.erb +49 -5
  38. data/Plugins/Apps/Matrix/Matrix.lmm.rb +86 -1
  39. data/Plugins/Apps/Matrix/Synapse.container +17 -0
  40. data/Plugins/Apps/Matrix/config.json +50 -0
  41. data/Plugins/Apps/Matrix/homeserver.yaml +70 -0
  42. data/Plugins/Apps/Matrix/log.config +30 -0
  43. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +70 -45
  44. data/Plugins/Apps/Nginx/conf.d/configlmm.conf +9 -0
  45. data/Plugins/Apps/Nginx/config-lmm/errors.conf +10 -4
  46. data/Plugins/Apps/Nginx/config-lmm/proxy.conf +5 -1
  47. data/Plugins/Apps/Nginx/nginx.lmm.rb +15 -11
  48. data/Plugins/Apps/Nginx/proxy.conf.erb +13 -3
  49. data/Plugins/Apps/Odoo/Odoo.container +2 -1
  50. data/Plugins/Apps/Odoo/Odoo.lmm.rb +1 -1
  51. data/Plugins/Apps/OpenVidu/Ingress.container +18 -0
  52. data/Plugins/Apps/OpenVidu/OpenVidu.conf.erb +34 -0
  53. data/Plugins/Apps/OpenVidu/OpenVidu.container +16 -0
  54. data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +90 -0
  55. data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +35 -0
  56. data/Plugins/Apps/OpenVidu/OpenViduCall.container +15 -0
  57. data/Plugins/Apps/OpenVidu/ingress.yaml +10 -0
  58. data/Plugins/Apps/OpenVidu/livekit.yaml +13 -0
  59. data/Plugins/Apps/Peppermint/Peppermint.conf.erb +0 -4
  60. data/Plugins/Apps/Peppermint/Peppermint.container +2 -1
  61. data/Plugins/Apps/Postfix/Postfix.lmm.rb +32 -6
  62. data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +80 -11
  63. data/Plugins/Apps/Roundcube/Roundcube.conf.erb +75 -0
  64. data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +145 -0
  65. data/Plugins/Apps/Tunnel/tunnel.lmm.rb +63 -0
  66. data/Plugins/Apps/Tunnel/tunnelTCP.service +9 -0
  67. data/Plugins/Apps/Tunnel/tunnelTCP.socket +9 -0
  68. data/Plugins/Apps/Tunnel/tunnelUDP.service +9 -0
  69. data/Plugins/Apps/Tunnel/tunnelUDP.socket +9 -0
  70. data/Plugins/Apps/Valkey/Valkey.lmm.rb +32 -0
  71. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +4 -0
  72. data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +42 -0
  73. data/Plugins/Apps/Wiki.js/Wiki.js.container +15 -0
  74. data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +61 -0
  75. data/Plugins/Apps/gollum/gollum.conf.erb +39 -1
  76. data/Plugins/Apps/gollum/gollum.container +4 -1
  77. data/Plugins/Apps/gollum/gollum.lmm.rb +11 -3
  78. data/Plugins/OS/Linux/Debian/preseed.cfg.erb +62 -0
  79. data/Plugins/OS/Linux/Distributions.yaml +32 -0
  80. data/Plugins/OS/Linux/Flavours.yaml +11 -0
  81. data/Plugins/OS/Linux/Linux.lmm.rb +255 -67
  82. data/Plugins/OS/Linux/Packages.yaml +47 -2
  83. data/Plugins/OS/Linux/Proxmox/answer.toml.erb +30 -0
  84. data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +33 -4
  85. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +1 -1
  86. data/Plugins/Platforms/libvirt/libvirt.lmm.rb +3 -2
  87. data/Plugins/Services/DNS/PowerDNS.lmm.rb +95 -8
  88. data/bootstrap.sh +41 -3
  89. data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +146 -64
  90. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +34 -3
  91. data/lib/ConfigLMM/Framework/plugins/plugin.rb +62 -6
  92. data/lib/ConfigLMM/cli.rb +3 -1
  93. data/lib/ConfigLMM/commands/cleanup.rb +1 -0
  94. data/lib/ConfigLMM/commands/configsCommand.rb +3 -1
  95. data/lib/ConfigLMM/io/configList.rb +3 -1
  96. data/lib/ConfigLMM/state.rb +10 -2
  97. data/lib/ConfigLMM/version.rb +1 -1
  98. metadata +54 -3
  99. data/Plugins/Apps/GitLab/GitLab.conf.erb +0 -26
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0078988f4f9bf52b61e5de21839b6ab1da6c56e24e4999987b9818cab6b5c7ca'
4
- data.tar.gz: 429aed3d4f1a83271175022c479408db601b85c0f991163d0f87527a6fe55135
3
+ metadata.gz: e75cd300255c00348e10301216861bbc10e1fd1473e35a19c0b879f6c90f433c
4
+ data.tar.gz: b75628e73fa064c403e4dc51cb69997c1e5d8215536df9f961ce8861bf405757
5
5
  SHA512:
6
- metadata.gz: afc93dc18858dd5102963d65c27adf37891a69e3cd80a768d2fc8c08c64ccb8cc50478a594a18c3769de7efc6c921d75fc92357fa948ace10091b010a932e6f6
7
- data.tar.gz: 615fdfbd8cb389fefa0aacd51e6b2cf3739e7e7305d0ed389dec481d79a12cb1289e12a4b3224faf092c8c858cfd357c95e803d584024feb0225409b0d8cca69
6
+ metadata.gz: f357dba4ec543ed5b7d1ac66e678df83bb5bcf0c22793b80f70a30e6c22648837ca259d429ccff099fb77fb640c87ecafcf679b052d34ff45f1764ca650eedd5
7
+ data.tar.gz: 954e1f8b9720c7ffaa4f6173fe28ead5e1dd706d488b950172651273016422df300a2fd81a70652aaeb506f84bfa755902c6c198849821d72decf551779337e2
data/CHANGELOG.md CHANGED
@@ -1,3 +1,39 @@
1
+ ## [0.4.0] - 2024-10-04
2
+
3
+ Implement:
4
+ - Authentik: Outpost deployment
5
+ - gollum: Authentik authentication
6
+ - MariaDB - https://mariadb.org/
7
+ - BookStack - https://www.bookstackapp.com/
8
+ - Discourse - https://www.discourse.org/
9
+ - OpenVidu - https://openvidu.io/
10
+ - Matrix server - https://matrix.org/
11
+ - Wiki.js - https://js.wiki/
12
+ - ERPNext - https://erpnext.com/
13
+ - Let's Encrypt - https://letsencrypt.org/
14
+ - Roundcube - https://roundcube.net/
15
+ - Tunnel support
16
+ - Support Proxmox VE distro - https://www.proxmox.com/en/proxmox-virtual-environment/overview
17
+ - Support Debian distro - https://www.debian.org/
18
+
19
+ Other improvements:
20
+ - bootstrap.sh script
21
+ - Peppermint
22
+ - Vaultwarden
23
+ - Nginx
24
+ - Valkey
25
+ - PostgreSQL
26
+ - Dovecot
27
+ - GitLab
28
+ - Linux
29
+ - PowerDNS
30
+ - GoDaddy
31
+ - NginxProxy
32
+ - Nextcloud
33
+ - Cassandra
34
+ - Postfix: Implement cleanup/uninstall functionality
35
+ - WireGuard: Implement cleanup/uninstall functionality
36
+ - Various other improvements
1
37
 
2
38
  ## [0.3.0] - 2024-08-13
3
39
 
@@ -0,0 +1,14 @@
1
+
2
+ [Unit]
3
+ Description=Authentik Proxy Outpost container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ Image=ghcr.io/goauthentik/proxy:latest
8
+ EnvironmentFile=/var/lib/authentik/.config/containers/systemd/ProxyOutpost.env
9
+ PublishPort=127.0.0.1:19010:9000
10
+ UserNS=keep-id:uid=1000,gid=1000
11
+ AutoUpdate=registry
12
+
13
+ [Install]
14
+ WantedBy=multi-user.target default.target
@@ -13,6 +13,7 @@ PublishPort=127.0.0.1:19300:9300
13
13
  UserNS=keep-id:uid=1000,gid=1000
14
14
  Volume=/var/lib/authentik/media:/media
15
15
  Volume=/var/lib/authentik/templates:/templates
16
+ AutoUpdate=registry
16
17
 
17
18
  [Install]
18
19
  WantedBy=multi-user.target default.target
@@ -12,6 +12,7 @@ UserNS=keep-id:uid=1000,gid=1000
12
12
  Volume=/var/lib/authentik/media:/media
13
13
  Volume=/var/lib/authentik/templates:/templates
14
14
  Volume=/var/lib/authentik/certs:/certs
15
+ AutoUpdate=registry
15
16
 
16
17
  [Install]
17
18
  WantedBy=multi-user.target default.target
@@ -32,4 +32,11 @@ server {
32
32
  proxy_pass http://authentik;
33
33
  include config-lmm/proxy.conf;
34
34
  }
35
+
36
+ <% if config['Outposts'].to_a.include?('Proxy') %>
37
+ location /outpost.goauthentik.io {
38
+ proxy_pass http://localhost:19010/outpost.goauthentik.io;
39
+ include config-lmm/proxy.conf;
40
+ }
41
+ <% end %>
35
42
  }
@@ -37,13 +37,15 @@ module ConfigLMM
37
37
  ssh.scp.upload!(__dir__ + '/Authentik-Server.container', path)
38
38
  ssh.scp.upload!(__dir__ + '/Authentik-Worker.container', path)
39
39
  self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ daemon-reload")
40
- self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ start Authentik-Server")
41
- self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ start Authentik-Worker")
40
+ self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ restart Authentik-Server")
41
+ self.class.sshExec!(ssh, "systemctl --user --machine=#{USER}@ restart Authentik-Worker")
42
42
 
43
43
  Framework::LinuxApp.ensureServiceAutoStartOverSSH(NGINX_PACKAGE, ssh)
44
44
  self.writeNginxConfig(__dir__, 'Authentik', id, target, state, context, options)
45
45
  self.deployNginxConfig(id, target, activeState, context, options)
46
46
  Framework::LinuxApp.startServiceOverSSH(NGINX_PACKAGE, ssh)
47
+
48
+ self.deployProxyOutpost(target, ssh)
47
49
  end
48
50
  else
49
51
  raise Framework::PluginProcessError.new("#{id}: Unknown protocol: #{uri.scheme}!")
@@ -53,6 +55,26 @@ module ConfigLMM
53
55
  end
54
56
  end
55
57
 
58
+ def deployProxyOutpost(target, ssh = nil)
59
+ return unless target['Outposts'].to_a.include?('Proxy')
60
+
61
+ path = Framework::LinuxApp::SYSTEMD_CONTAINERS_PATH.gsub('~', HOME_DIR)
62
+ self.class.exec("echo 'AUTHENTIK_HOST=https://#{target['Domain'].downcase}' > #{path}/ProxyOutpost.env", ssh)
63
+ self.class.exec("echo 'AUTHENTIK_INSECURE=false' >> #{path}/ProxyOutpost.env", ssh)
64
+ self.class.exec(" echo 'AUTHENTIK_TOKEN=#{ENV['AUTHENTIK_TOKEN']}' >> #{path}/ProxyOutpost.env", ssh)
65
+ self.class.exec("chown #{USER}:#{USER} #{path}/ProxyOutpost.env", ssh)
66
+ self.class.exec("chmod 600 #{path}/ProxyOutpost.env", ssh)
67
+
68
+ if ssh.nil?
69
+ self.class.exec("cp #{__dir__ + '/Authentik-ProxyOutpost.container'} #{path}/")
70
+ else
71
+ ssh.scp.upload!(__dir__ + '/Authentik-ProxyOutpost.container', path)
72
+ end
73
+
74
+ self.class.exec("systemctl --user --machine=#{USER}@ daemon-reload", ssh)
75
+ self.class.exec("systemctl --user --machine=#{USER}@ restart Authentik-ProxyOutpost", ssh)
76
+ end
77
+
56
78
  def prepareConfig(target, ssh)
57
79
  target['Database'] ||= {}
58
80
 
@@ -0,0 +1,41 @@
1
+
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/bookstack.access.log;
26
+ error_log /var/log/nginx/bookstack.error.log;
27
+
28
+ include config-lmm/errors.conf;
29
+ include config-lmm/security.conf;
30
+
31
+ location / {
32
+ <% if config['Server'] %>
33
+ proxy_pass <%= config['Server'] %>;
34
+ <% else %>
35
+ proxy_pass http://127.0.0.1:18200;
36
+ <% end %>
37
+
38
+ include config-lmm/proxy.conf;
39
+ }
40
+
41
+ }
@@ -0,0 +1,15 @@
1
+
2
+ [Unit]
3
+ Description=BookStack container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ Image=ghcr.io/linuxserver/bookstack:latest
8
+ EnvironmentFile=/var/lib/bookstack/.config/containers/systemd/BookStack.env
9
+ Network=slirp4netns:allow_host_loopback=true
10
+ PublishPort=127.0.0.1:18200:80
11
+ Volume=/var/lib/bookstack/config:/config
12
+ AutoUpdate=registry
13
+
14
+ [Install]
15
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,80 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class BookStack < Framework::NginxApp
5
+
6
+ USER = 'bookstack'
7
+ HOME_DIR = '/var/lib/bookstack'
8
+ HOST_IP = '10.0.2.2'
9
+
10
+ def actionBookStackDeploy(id, target, activeState, context, options)
11
+ raise Framework::PluginProcessError.new('Domain field must be set!') unless target['Domain']
12
+
13
+ target['Database'] ||= {}
14
+ if target['Location'] && target['Location'] != '@me'
15
+ uri = Addressable::URI.parse(target['Location'])
16
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
17
+
18
+ self.class.sshStart(uri) do |ssh|
19
+
20
+ dbPassword = self.configureMariaDB(target['Database'], activeState, ssh)
21
+ distroInfo = Framework::LinuxApp.currentDistroInfo(ssh)
22
+ Framework::LinuxApp.configurePodmanServiceOverSSH(USER, HOME_DIR, 'BookStack', distroInfo, ssh)
23
+ self.class.sshExec!(ssh, "su --login #{USER} --shell /bin/sh --command 'mkdir -p ~/config'")
24
+
25
+ path = Framework::LinuxApp::SYSTEMD_CONTAINERS_PATH.gsub('~', HOME_DIR)
26
+ self.class.exec(" echo 'DB_HOST=#{HOST_IP}' > #{path}/BookStack.env", ssh)
27
+ self.class.exec(" echo 'DB_DATABASE=#{USER}' >> #{path}/BookStack.env", ssh)
28
+ self.class.exec(" echo 'DB_USERNAME=#{USER}' >> #{path}/BookStack.env", ssh)
29
+ self.class.exec(" echo 'DB_PASSWORD=#{dbPassword}' >> #{path}/BookStack.env", ssh)
30
+ self.class.exec(" echo 'APP_URL=https://#{target['Domain']}' >> #{path}/BookStack.env", ssh)
31
+
32
+ if target['OIDC'] && target['OIDC']['Issuer']
33
+ self.class.exec(" echo 'AUTH_METHOD=oidc' >> #{path}/BookStack.env", ssh)
34
+ self.class.exec(" echo 'AUTH_AUTO_INITIATE=true' >> #{path}/BookStack.env", ssh)
35
+ self.class.exec(" echo 'OIDC_CLIENT_ID=#{ENV['BOOKSTACK_OIDC_CLIENT_ID']}' >> #{path}/BookStack.env", ssh)
36
+ self.class.exec(" echo 'OIDC_CLIENT_SECRET=#{ENV['BOOKSTACK_OIDC_CLIENT_SECRET']}' >> #{path}/BookStack.env", ssh)
37
+ self.class.exec(" echo 'OIDC_ISSUER=#{target['OIDC']['Issuer']}' >> #{path}/BookStack.env", ssh)
38
+ self.class.exec(" echo 'OIDC_ISSUER_DISCOVER=true' >> #{path}/BookStack.env", ssh)
39
+ self.class.exec(" echo 'OIDC_USER_TO_GROUPS=true' >> #{path}/BookStack.env", ssh)
40
+ end
41
+
42
+ if target['SMTP']
43
+ host = target['SMTP']['Host']
44
+ host = HOST_IP if ['localhost', '127.0.0.1'].include?(host)
45
+ self.class.exec(" echo 'MAIL_HOST=#{host}' >> #{path}/BookStack.env", ssh)
46
+ self.class.exec(" echo 'MAIL_PORT=#{target['SMTP']['Port']}' >> #{path}/BookStack.env", ssh)
47
+ self.class.exec(" echo 'MAIL_USERNAME=#{target['SMTP']['Username']}' >> #{path}/BookStack.env", ssh)
48
+ self.class.exec(" echo 'MAIL_PASSWORD=#{ENV['BOOKSTACK_SMTP_PASSWORD']}' >> #{path}/BookStack.env", ssh)
49
+ self.class.exec(" echo 'MAIL_FROM=#{target['SMTP']['From']}' >> #{path}/BookStack.env", ssh)
50
+ end
51
+
52
+ self.class.exec("chown #{USER}:#{USER} #{path}/BookStack.env", ssh)
53
+ self.class.exec("chmod 600 #{path}/BookStack.env", ssh)
54
+
55
+ ssh.scp.upload!(__dir__ + '/BookStack.container', path)
56
+ self.class.exec("systemctl --user --machine=#{USER}@ daemon-reload", ssh)
57
+ self.class.exec("systemctl --user --machine=#{USER}@ restart BookStack", ssh)
58
+
59
+ Framework::LinuxApp.ensurePackages([NGINX_PACKAGE], ssh)
60
+ Framework::LinuxApp.ensureServiceAutoStartOverSSH(NGINX_PACKAGE, ssh)
61
+ self.class.prepareNginxConfig(target, ssh)
62
+ self.writeNginxConfig(__dir__, 'BookStack', id, target, state, context, options)
63
+ self.deployNginxConfig(id, target, activeState, context, options)
64
+ Framework::LinuxApp.startServiceOverSSH(NGINX_PACKAGE, ssh)
65
+ end
66
+ else
67
+ # TODO
68
+ end
69
+ end
70
+
71
+ def configureMariaDB(settings, activeState, ssh)
72
+ password = SecureRandom.alphanumeric(20)
73
+ MariaDB.createRemoteUserAndDB(settings, USER, password, ssh)
74
+ password
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+
@@ -0,0 +1,17 @@
1
+
2
+
3
+ [Unit]
4
+ Description=Discourse Sidekiq container
5
+ After=local-fs.target
6
+
7
+ [Container]
8
+ Image=docker.io/bitnami/discourse:latest
9
+ Exec=/opt/bitnami/scripts/discourse-sidekiq/run.sh
10
+ EnvironmentFile=/var/lib/discourse/.config/containers/systemd/Discourse.env
11
+ Network=slirp4netns:allow_host_loopback=true
12
+ UserNS=keep-id:uid=999,gid=999
13
+ Volume=/var/lib/discourse/sidekiq:/bitnami/discourse
14
+ AutoUpdate=registry
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,41 @@
1
+
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/discourse.access.log;
26
+ error_log /var/log/nginx/discourse.error.log;
27
+
28
+ include config-lmm/errors.conf;
29
+ include config-lmm/security.conf;
30
+
31
+ location / {
32
+ <% if config['Server'] %>
33
+ proxy_pass <%= config['Server'] %>;
34
+ <% else %>
35
+ proxy_pass http://127.0.0.1:13100;
36
+ <% end %>
37
+
38
+ include config-lmm/proxy.conf;
39
+ }
40
+
41
+ }
@@ -0,0 +1,17 @@
1
+
2
+ [Unit]
3
+ Description=Discourse container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=Discourse
8
+ Image=docker.io/bitnami/discourse:latest
9
+ EnvironmentFile=/var/lib/discourse/.config/containers/systemd/Discourse.env
10
+ Network=slirp4netns:allow_host_loopback=true
11
+ PublishPort=127.0.0.1:13100:3000
12
+ UserNS=keep-id:uid=999,gid=999
13
+ Volume=/var/lib/discourse/data:/bitnami/discourse
14
+ AutoUpdate=registry
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,95 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class Discourse < Framework::NginxApp
5
+
6
+ USER = 'discourse'
7
+ HOME_DIR = '/var/lib/discourse'
8
+ HOST_IP = '10.0.2.2'
9
+
10
+ def actionDiscourseDeploy(id, target, activeState, context, options)
11
+ raise Framework::PluginProcessError.new('Domain field must be set!') unless target['Domain']
12
+
13
+ target['Database'] ||= {}
14
+ if target['Location'] && target['Location'] != '@me'
15
+ uri = Addressable::URI.parse(target['Location'])
16
+ raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
17
+
18
+ self.class.sshStart(uri) do |ssh|
19
+
20
+ dbPassword = self.configurePostgreSQL(target['Database'], ssh)
21
+ distroInfo = Framework::LinuxApp.currentDistroInfo(ssh)
22
+ Framework::LinuxApp.configurePodmanServiceOverSSH(USER, HOME_DIR, 'Discourse', distroInfo, ssh)
23
+ self.class.sshExec!(ssh, "su --login #{USER} --shell /bin/sh --command 'mkdir -p ~/data ~/sidekiq'")
24
+
25
+ path = Framework::LinuxApp::SYSTEMD_CONTAINERS_PATH.gsub('~', HOME_DIR)
26
+ self.class.exec("echo 'DISCOURSE_DATABASE_HOST=#{HOST_IP}' > #{path}/Discourse.env", ssh)
27
+ self.class.exec("echo 'DISCOURSE_DATABASE_NAME=#{USER}' >> #{path}/Discourse.env", ssh)
28
+ self.class.exec(" echo 'DISCOURSE_DATABASE_USER=#{USER}' >> #{path}/Discourse.env", ssh)
29
+ self.class.exec(" echo 'DISCOURSE_DATABASE_PASSWORD=#{dbPassword}' >> #{path}/Discourse.env", ssh)
30
+ self.class.exec("echo 'DISCOURSE_HOST=#{target['Domain']}' >> #{path}/Discourse.env", ssh)
31
+
32
+ self.class.exec("echo 'DISCOURSE_REDIS_HOST=#{HOST_IP}' >> #{path}/Discourse.env", ssh)
33
+ self.class.exec(" echo 'DISCOURSE_REDIS_PASSWORD=#{ENV['REDIS_PASSWORD']}' >> #{path}/Discourse.env", ssh)
34
+
35
+ if target['SMTP']
36
+ host = target['SMTP']['Host']
37
+ host = HOST_IP if ['localhost', '127.0.0.1'].include?(host)
38
+ self.class.exec("echo 'DISCOURSE_SMTP_HOST=#{host}' >> #{path}/Discourse.env", ssh)
39
+ self.class.exec("echo 'DISCOURSE_SMTP_PORT_NUMBER=#{target['SMTP']['Port']}' >> #{path}/Discourse.env", ssh)
40
+ self.class.exec(" echo 'DISCOURSE_SMTP_USER=#{target['SMTP']['Username']}' >> #{path}/Discourse.env", ssh)
41
+ self.class.exec(" echo 'DISCOURSE_SMTP_PASSWORD=#{ENV['DISCOURSE_SMTP_PASSWORD']}' >> #{path}/Discourse.env", ssh)
42
+ auth = target['SMTP']['Auth'].to_s.downcase
43
+ auth = 'plain' if auth.empty?
44
+ self.class.exec("echo 'DISCOURSE_SMTP_AUTH=#{auth}' >> #{path}/Discourse.env", ssh)
45
+ if target['SMTP']['Port'] == 465
46
+ self.class.exec("echo 'DISCOURSE_EXTRA_CONF_CONTENT=smtp_force_tls = true' >> #{path}/Discourse.env", ssh)
47
+ end
48
+ end
49
+
50
+ self.class.exec(" echo 'DISCOURSE_PRECOMPILE_ASSETS=no' >> #{path}/Discourse.env", ssh)
51
+ self.class.exec("echo 'CHEAP_SOURCE_MAPS=1' >> #{path}/Discourse.env", ssh)
52
+ self.class.exec("echo 'JOBS=1' >> #{path}/Discourse.env", ssh)
53
+
54
+ self.class.exec("chown #{USER}:#{USER} #{path}/Discourse.env", ssh)
55
+ self.class.exec("chmod 600 #{path}/Discourse.env", ssh)
56
+
57
+ ssh.scp.upload!(__dir__ + '/Discourse.container', path)
58
+ ssh.scp.upload!(__dir__ + '/Discourse-Sidekiq.container', path)
59
+ self.class.exec("systemctl --user --machine=#{USER}@ daemon-reload", ssh)
60
+ self.class.exec("systemctl --user --machine=#{USER}@ restart Discourse", ssh)
61
+ self.class.exec("systemctl --user --machine=#{USER}@ restart Discourse-Sidekiq", ssh)
62
+
63
+ Framework::LinuxApp.ensurePackages([NGINX_PACKAGE], ssh)
64
+ Framework::LinuxApp.ensureServiceAutoStartOverSSH(NGINX_PACKAGE, ssh)
65
+ self.class.prepareNginxConfig(target, ssh)
66
+ self.writeNginxConfig(__dir__, 'Discourse', id, target, state, context, options)
67
+ self.deployNginxConfig(id, target, activeState, context, options)
68
+ Framework::LinuxApp.startServiceOverSSH(NGINX_PACKAGE, ssh)
69
+
70
+ containers = JSON.parse(self.class.exec("su --login #{USER} --shell /usr/bin/sh --command 'podman ps --format json --filter name=^Discourse$'", ssh).strip)
71
+ raise 'Failed to find container!' if containers.empty?
72
+ if !target['Plugins'].to_a.empty?
73
+ target['Plugins'].each do |plugin|
74
+ self.class.exec("su --login #{USER} --shell /usr/bin/sh --command \"podman exec --workdir /opt/bitnami/discourse #{containers.first['Id']} sh -c 'RAILS_ENV=production bundle exec rake plugin:install repo=#{plugin}'\"", ssh, true)
75
+ end
76
+ end
77
+
78
+ self.class.exec("su --login #{USER} --shell /usr/bin/sh --command \"podman exec --workdir /opt/bitnami/discourse #{containers.first['Id']} sh -c 'RAILS_ENV=production CHEAP_SOURCE_MAPS=1 JOBS=1 bundle exec rake assets:precompile'\"", ssh)
79
+ end
80
+ else
81
+ # TODO
82
+ end
83
+ end
84
+
85
+ def configurePostgreSQL(settings, ssh)
86
+ password = SecureRandom.alphanumeric(20)
87
+ PostgreSQL.createRemoteUserAndDBOverSSH(settings, USER, password, ssh)
88
+ PostgreSQL.createExtensions(settings, USER, ['hstore', 'pg_trgm'], ssh)
89
+ password
90
+ end
91
+
92
+ end
93
+ end
94
+ end
95
+
@@ -29,6 +29,12 @@ module ConfigLMM
29
29
  cmd = "sed -i 's|^#mail_location =.*|mail_location = maildir:~/Mail|' #{DOVECOT_DIR}conf.d/10-mail.conf"
30
30
  self.class.sshExec!(ssh, cmd)
31
31
 
32
+ if !target['Protocols'].to_a.empty?
33
+ updateRemoteFile(ssh, DOVECOT_DIR + 'dovecot.conf', options) do |configLines|
34
+ configLines << "protocols = #{target['Protocols'].join(' ')}\n"
35
+ end
36
+ end
37
+
32
38
  updateRemoteFile(ssh, DOVECOT_DIR + 'conf.d/10-mail.conf', options) do |configLines|
33
39
  configLines << "mail_home = #{EMAIL_HOME}/emails/%u\n"
34
40
  configLines << "first_valid_uid = #{uid}\n"
@@ -68,8 +74,7 @@ module ConfigLMM
68
74
  configLines << "}\n"
69
75
  end
70
76
 
71
- self.class.sshExec!(ssh, "firewall-cmd -q --add-service='imaps'")
72
- self.class.sshExec!(ssh, "firewall-cmd -q --permanent --add-service='imaps'")
77
+ Framework::LinuxApp.firewallAddService('imaps', ssh)
73
78
 
74
79
  cmd = "sed -i 's|^!include auth-system.conf.ext|#!include auth-system.conf.ext|' #{DOVECOT_DIR}conf.d/10-auth.conf"
75
80
  self.class.sshExec!(ssh, cmd)
@@ -137,6 +142,24 @@ module ConfigLMM
137
142
  end
138
143
 
139
144
  plugins[:Linux].startService(SERVICE_NAME, target['Location'])
145
+
146
+ activeState['Status'] = State::STATUS_DEPLOYED
147
+ end
148
+
149
+ def cleanup(configs, state, context, options)
150
+ cleanupType(:Dovecot, configs, state, context, options) do |item, id, state, context, options, ssh|
151
+ Framework::LinuxApp.stopService(SERVICE_NAME, ssh, options[:dry])
152
+ Framework::LinuxApp.firewallRemoveService('imaps', ssh, options[:dry])
153
+ Framework::LinuxApp.removePackage(PACKAGE_NAME, ssh, options[:dry])
154
+
155
+ state.item(id)['Status'] = State::STATUS_DELETED unless options[:dry]
156
+
157
+ if options[:destroy]
158
+ Framework::LinuxApp.deleteUserAndGroup(EMAIL_USER, ssh, options[:dry])
159
+
160
+ state.item(id)['Status'] = State::STATUS_DESTROYED unless options[:dry]
161
+ end
162
+ end
140
163
  end
141
164
 
142
165
  def self.cutConfigSection(file, sectionStart, options, ssh)
@@ -0,0 +1,19 @@
1
+
2
+ [Unit]
3
+ Description=ERPNext Frontend container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=ERPNext-Frontend
8
+ Image=ConfigLM.moe/erpnext:v$VERSION
9
+ Exec=nginx-entrypoint.sh
10
+ EnvironmentFile=/var/lib/erpnext/.config/containers/systemd/ERPNext.env
11
+ PublishPort=127.0.0.1:18400:8080
12
+ Network=ERPNext
13
+ HostName=ERPNext-Frontend
14
+ UserNS=keep-id:uid=1000,gid=1000
15
+ Volume=/var/lib/erpnext/sites:/home/frappe/frappe-bench/sites
16
+ Volume=/var/lib/erpnext/logs:/home/frappe/frappe-bench/logs
17
+
18
+ [Install]
19
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,17 @@
1
+
2
+ [Unit]
3
+ Description=ERPNext Queue container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=ERPNext-Queue
8
+ Image=ConfigLM.moe/erpnext:v$VERSION
9
+ Exec=bench worker --queue long,default,short
10
+ EnvironmentFile=/var/lib/erpnext/.config/containers/systemd/ERPNext.env
11
+ Network=slirp4netns:allow_host_loopback=true
12
+ UserNS=keep-id:uid=1000,gid=1000
13
+ Volume=/var/lib/erpnext/sites:/home/frappe/frappe-bench/sites
14
+ Volume=/var/lib/erpnext/logs:/home/frappe/frappe-bench/logs
15
+
16
+ [Install]
17
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,17 @@
1
+
2
+ [Unit]
3
+ Description=ERPNext Scheduler container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=ERPNext-Scheduler
8
+ Image=ConfigLM.moe/erpnext:v$VERSION
9
+ Exec=bench schedule
10
+ EnvironmentFile=/var/lib/erpnext/.config/containers/systemd/ERPNext.env
11
+ UserNS=keep-id:uid=1000,gid=1000
12
+ Volume=/var/lib/erpnext/sites:/home/frappe/frappe-bench/sites
13
+ Volume=/var/lib/erpnext/logs:/home/frappe/frappe-bench/logs
14
+
15
+ [Install]
16
+ WantedBy=multi-user.target default.target
17
+
@@ -0,0 +1,19 @@
1
+
2
+ [Unit]
3
+ Description=ERPNext Websocket container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=ERPNext-Websocket
8
+ Image=ConfigLM.moe/erpnext:v$VERSION
9
+ Exec=node /home/frappe/frappe-bench/apps/frappe/socketio.js
10
+ EnvironmentFile=/var/lib/erpnext/.config/containers/systemd/ERPNext.env
11
+ Network=ERPNext
12
+ IP=10.90.50.11
13
+ HostName=ERPNext-Websocket
14
+ UserNS=keep-id:uid=1000,gid=1000
15
+ Volume=/var/lib/erpnext/sites:/home/frappe/frappe-bench/sites
16
+ Volume=/var/lib/erpnext/logs:/home/frappe/frappe-bench/logs
17
+
18
+ [Install]
19
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,18 @@
1
+
2
+ [Unit]
3
+ Description=ERPNext container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=ERPNext
8
+ Image=ConfigLM.moe/erpnext:v$VERSION
9
+ EnvironmentFile=/var/lib/erpnext/.config/containers/systemd/ERPNext.env
10
+ Network=ERPNext
11
+ IP=10.90.50.10
12
+ HostName=ERPNext
13
+ UserNS=keep-id:uid=1000,gid=1000
14
+ Volume=/var/lib/erpnext/sites:/home/frappe/frappe-bench/sites
15
+ Volume=/var/lib/erpnext/logs:/home/frappe/frappe-bench/logs
16
+
17
+ [Install]
18
+ WantedBy=multi-user.target default.target