ConfigLMM 0.3.0 → 0.5.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 (250) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +70 -0
  3. data/CNAME +1 -0
  4. data/Examples/.lmm.state.yaml +159 -0
  5. data/Examples/ConfigLMM.mm.yaml +32 -0
  6. data/Examples/Implemented.mm.yaml +252 -4
  7. data/Examples/SmallBusiness.mm.yaml +492 -0
  8. data/Plugins/Apps/Answer/answer.lmm.rb +165 -0
  9. data/Plugins/Apps/Answer/answer@.service +40 -0
  10. data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.conf.erb +0 -3
  11. data/Plugins/Apps/ArchiSteamFarm/ArchiSteamFarm.lmm.rb +0 -1
  12. data/Plugins/Apps/Authentik/Authentik-ProxyOutpost.container +20 -0
  13. data/Plugins/Apps/Authentik/Authentik-Server.container +7 -1
  14. data/Plugins/Apps/Authentik/Authentik-Worker.container +7 -1
  15. data/Plugins/Apps/Authentik/Authentik.conf.erb +18 -6
  16. data/Plugins/Apps/Authentik/Authentik.lmm.rb +232 -45
  17. data/Plugins/Apps/BookStack/BookStack.conf.erb +38 -0
  18. data/Plugins/Apps/BookStack/BookStack.container +20 -0
  19. data/Plugins/Apps/BookStack/BookStack.lmm.rb +91 -0
  20. data/Plugins/Apps/Cassandra/Cassandra.lmm.rb +9 -19
  21. data/Plugins/Apps/ClickHouse/ClickHouse.container +28 -0
  22. data/Plugins/Apps/ClickHouse/ClickHouse.lmm.rb +113 -0
  23. data/Plugins/Apps/ClickHouse/Config/listen.yaml +2 -0
  24. data/Plugins/Apps/ClickHouse/Config/logger.yaml +8 -0
  25. data/Plugins/Apps/ClickHouse/Config/zookeepers.yaml +5 -0
  26. data/Plugins/Apps/ClickHouse/Connection.rb +96 -0
  27. data/Plugins/Apps/Discourse/Discourse-Sidekiq.container +22 -0
  28. data/Plugins/Apps/Discourse/Discourse.conf.erb +38 -0
  29. data/Plugins/Apps/Discourse/Discourse.container +21 -0
  30. data/Plugins/Apps/Discourse/Discourse.lmm.rb +156 -0
  31. data/Plugins/Apps/Dovecot/Dovecot.lmm.rb +87 -52
  32. data/Plugins/Apps/ERPNext/ERPNext-Frontend.container +24 -0
  33. data/Plugins/Apps/ERPNext/ERPNext-Queue.container +22 -0
  34. data/Plugins/Apps/ERPNext/ERPNext-Scheduler.container +22 -0
  35. data/Plugins/Apps/ERPNext/ERPNext-Websocket.container +24 -0
  36. data/Plugins/Apps/ERPNext/ERPNext.container +23 -0
  37. data/Plugins/Apps/ERPNext/ERPNext.lmm.rb +204 -0
  38. data/Plugins/Apps/ERPNext/ERPNext.network +12 -0
  39. data/Plugins/Apps/ERPNext/sites/apps.json +10 -0
  40. data/Plugins/Apps/ERPNext/sites/apps.txt +3 -0
  41. data/Plugins/Apps/ERPNext/sites/common_site_config.json +11 -0
  42. data/Plugins/Apps/GitLab/GitLab.container +9 -2
  43. data/Plugins/Apps/GitLab/GitLab.lmm.rb +52 -33
  44. data/Plugins/Apps/Homepage/Homepage.conf.erb +86 -0
  45. data/Plugins/Apps/Homepage/Homepage.container +19 -0
  46. data/Plugins/Apps/Homepage/Homepage.lmm.rb +54 -0
  47. data/Plugins/Apps/IPFS/IPFS.conf.erb +0 -3
  48. data/Plugins/Apps/IPFS/IPFS.lmm.rb +0 -1
  49. data/Plugins/Apps/InfluxDB/InfluxDB.conf.erb +0 -3
  50. data/Plugins/Apps/InfluxDB/InfluxDB.lmm.rb +0 -1
  51. data/Plugins/Apps/Jackett/Jackett.conf.erb +0 -3
  52. data/Plugins/Apps/Jackett/Jackett.lmm.rb +0 -1
  53. data/Plugins/Apps/Jellyfin/Jellyfin.conf.erb +0 -3
  54. data/Plugins/Apps/Jellyfin/Jellyfin.lmm.rb +0 -1
  55. data/Plugins/Apps/LetsEncrypt/LetsEncrypt.lmm.rb +78 -0
  56. data/Plugins/Apps/LetsEncrypt/hooks/dovecot.sh +2 -0
  57. data/Plugins/Apps/LetsEncrypt/hooks/nginx.sh +2 -0
  58. data/Plugins/Apps/LetsEncrypt/hooks/postfix.sh +2 -0
  59. data/Plugins/Apps/LetsEncrypt/renew-certificates.service +7 -0
  60. data/Plugins/Apps/LetsEncrypt/renew-certificates.timer +12 -0
  61. data/Plugins/Apps/LetsEncrypt/rfc2136.ini +11 -0
  62. data/Plugins/Apps/LibreTranslate/LibreTranslate.container +21 -0
  63. data/Plugins/Apps/LibreTranslate/LibreTranslate.lmm.rb +34 -0
  64. data/Plugins/Apps/Lobsters/Containerfile +81 -0
  65. data/Plugins/Apps/Lobsters/Lobsters-Tasks.container +26 -0
  66. data/Plugins/Apps/Lobsters/Lobsters.conf.erb +99 -0
  67. data/Plugins/Apps/Lobsters/Lobsters.container +27 -0
  68. data/Plugins/Apps/Lobsters/Lobsters.lmm.rb +196 -0
  69. data/Plugins/Apps/Lobsters/crontab +3 -0
  70. data/Plugins/Apps/Lobsters/database.yml +26 -0
  71. data/Plugins/Apps/Lobsters/entrypoint.sh +30 -0
  72. data/Plugins/Apps/Lobsters/generateCredentials.rb +19 -0
  73. data/Plugins/Apps/Lobsters/lobsters-cron.sh +25 -0
  74. data/Plugins/Apps/Lobsters/lobsters-daily.sh +23 -0
  75. data/Plugins/Apps/Lobsters/puma.rb +49 -0
  76. data/Plugins/Apps/MariaDB/Connection.rb +55 -0
  77. data/Plugins/Apps/MariaDB/MariaDB.lmm.rb +122 -0
  78. data/Plugins/Apps/Mastodon/Mastodon-Sidekiq.container +22 -0
  79. data/Plugins/Apps/Mastodon/Mastodon-Streaming.container +20 -0
  80. data/Plugins/Apps/Mastodon/Mastodon.conf.erb +34 -45
  81. data/Plugins/Apps/Mastodon/Mastodon.container +28 -0
  82. data/Plugins/Apps/Mastodon/Mastodon.lmm.rb +240 -5
  83. data/Plugins/Apps/Mastodon/configlmm.rake +30 -0
  84. data/Plugins/Apps/Mastodon/entrypoint.sh +16 -0
  85. data/Plugins/Apps/Matrix/Element.container +19 -0
  86. data/Plugins/Apps/Matrix/Matrix.conf.erb +47 -9
  87. data/Plugins/Apps/Matrix/Matrix.lmm.rb +119 -5
  88. data/Plugins/Apps/Matrix/Synapse.container +22 -0
  89. data/Plugins/Apps/Matrix/config.json +50 -0
  90. data/Plugins/Apps/Matrix/homeserver.yaml +70 -0
  91. data/Plugins/Apps/Matrix/log.config +30 -0
  92. data/Plugins/Apps/Netdata/Netdata.conf.erb +0 -3
  93. data/Plugins/Apps/Netdata/Netdata.lmm.rb +0 -1
  94. data/Plugins/Apps/Nextcloud/Nextcloud.conf.erb +3 -4
  95. data/Plugins/Apps/Nextcloud/Nextcloud.lmm.rb +155 -48
  96. data/Plugins/Apps/Nextcloud/autoconfig.php +13 -0
  97. data/Plugins/Apps/Nextcloud/config.php +10 -1
  98. data/Plugins/Apps/Nextcloud/nextcloudcron.service +8 -0
  99. data/Plugins/Apps/Nextcloud/nextcloudcron.timer +10 -0
  100. data/Plugins/Apps/Nginx/Connection.rb +93 -0
  101. data/Plugins/Apps/Nginx/conf.d/configlmm.conf +54 -4
  102. data/Plugins/Apps/Nginx/conf.d/languages.conf +21 -0
  103. data/Plugins/Apps/Nginx/config-lmm/errors.conf +33 -22
  104. data/Plugins/Apps/Nginx/config-lmm/gateway-errors.conf +20 -0
  105. data/Plugins/Apps/Nginx/config-lmm/proxy.conf +6 -2
  106. data/Plugins/Apps/Nginx/main.conf.erb +7 -3
  107. data/Plugins/Apps/Nginx/nginx.conf +2 -2
  108. data/Plugins/Apps/Nginx/nginx.lmm.rb +103 -81
  109. data/Plugins/Apps/Nginx/proxy.conf.erb +24 -6
  110. data/Plugins/Apps/Odoo/Odoo.conf.erb +0 -3
  111. data/Plugins/Apps/Odoo/Odoo.container +7 -1
  112. data/Plugins/Apps/Odoo/Odoo.lmm.rb +4 -5
  113. data/Plugins/Apps/Ollama/Ollama.container +26 -0
  114. data/Plugins/Apps/Ollama/Ollama.lmm.rb +73 -0
  115. data/Plugins/Apps/OpenTelemetry/Config/config.yaml +704 -0
  116. data/Plugins/Apps/OpenTelemetry/OpenTelemetry.lmm.rb +154 -0
  117. data/Plugins/Apps/OpenVidu/Ingress.container +23 -0
  118. data/Plugins/Apps/{GitLab/GitLab.conf.erb → OpenVidu/OpenVidu.conf.erb} +8 -3
  119. data/Plugins/Apps/OpenVidu/OpenVidu.container +21 -0
  120. data/Plugins/Apps/OpenVidu/OpenVidu.lmm.rb +94 -0
  121. data/Plugins/Apps/OpenVidu/OpenViduCall.conf.erb +32 -0
  122. data/Plugins/Apps/OpenVidu/OpenViduCall.container +20 -0
  123. data/Plugins/Apps/OpenVidu/ingress.yaml +10 -0
  124. data/Plugins/Apps/OpenVidu/livekit.yaml +13 -0
  125. data/Plugins/Apps/PHP-FPM/Connection.rb +91 -0
  126. data/Plugins/Apps/PHP-FPM/PHP-FPM.lmm.rb +31 -4
  127. data/Plugins/Apps/Peppermint/Peppermint.conf.erb +2 -9
  128. data/Plugins/Apps/Peppermint/Peppermint.container +7 -1
  129. data/Plugins/Apps/Peppermint/Peppermint.lmm.rb +29 -33
  130. data/Plugins/Apps/Perplexica/Perplexica.container +25 -0
  131. data/Plugins/Apps/Perplexica/Perplexica.lmm.rb +92 -0
  132. data/Plugins/Apps/Perplexica/config.toml +26 -0
  133. data/Plugins/Apps/Podman/Connection.rb +24 -0
  134. data/Plugins/Apps/Podman/Podman.lmm.rb +80 -0
  135. data/Plugins/Apps/Podman/storage.conf +6 -0
  136. data/Plugins/Apps/Postfix/Postfix.lmm.rb +249 -145
  137. data/Plugins/Apps/PostgreSQL/Connection.rb +97 -0
  138. data/Plugins/Apps/PostgreSQL/PostgreSQL.lmm.rb +204 -99
  139. data/Plugins/Apps/Pterodactyl/Pterodactyl.conf.erb +0 -3
  140. data/Plugins/Apps/Pterodactyl/Pterodactyl.lmm.rb +0 -2
  141. data/Plugins/Apps/Pterodactyl/Wings.conf.erb +0 -3
  142. data/Plugins/Apps/RVM/RVM.lmm.rb +57 -0
  143. data/Plugins/Apps/Roundcube/Roundcube.conf.erb +72 -0
  144. data/Plugins/Apps/Roundcube/Roundcube.lmm.rb +141 -0
  145. data/Plugins/Apps/SSH/SSH.lmm.rb +9 -15
  146. data/Plugins/Apps/SearXNG/SearXNG.container +22 -0
  147. data/Plugins/Apps/SearXNG/SearXNG.lmm.rb +79 -0
  148. data/Plugins/Apps/SearXNG/limiter.toml +40 -0
  149. data/Plugins/Apps/SearXNG/settings.yml +2 -0
  150. data/Plugins/Apps/SigNoz/Config/alerts.yml +11 -0
  151. data/Plugins/Apps/SigNoz/Config/otel-collector-config.yaml +110 -0
  152. data/Plugins/Apps/SigNoz/Config/otel-collector-opamp-config.yaml +1 -0
  153. data/Plugins/Apps/SigNoz/Config/prometheus.yml +18 -0
  154. data/Plugins/Apps/SigNoz/SigNoz-Collector.container +23 -0
  155. data/Plugins/Apps/SigNoz/SigNoz-Migrator.container +17 -0
  156. data/Plugins/Apps/SigNoz/SigNoz.conf.erb +61 -0
  157. data/Plugins/Apps/SigNoz/SigNoz.container +26 -0
  158. data/Plugins/Apps/SigNoz/SigNoz.lmm.rb +319 -0
  159. data/Plugins/Apps/Solr/log4j2.xml +89 -0
  160. data/Plugins/Apps/Solr/solr.lmm.rb +82 -0
  161. data/Plugins/Apps/Sunshine/Sunshine.conf.erb +0 -3
  162. data/Plugins/Apps/Sunshine/Sunshine.lmm.rb +0 -1
  163. data/Plugins/Apps/Tunnel/tunnel.lmm.rb +59 -0
  164. data/Plugins/Apps/Tunnel/tunnelTCP.service +9 -0
  165. data/Plugins/Apps/Tunnel/tunnelTCP.socket +9 -0
  166. data/Plugins/Apps/Tunnel/tunnelUDP.service +9 -0
  167. data/Plugins/Apps/Tunnel/tunnelUDP.socket +9 -0
  168. data/Plugins/Apps/UVdesk/UVdesk.conf.erb +0 -3
  169. data/Plugins/Apps/Umami/Umami.container +19 -0
  170. data/Plugins/Apps/Umami/Umami.lmm.rb +108 -0
  171. data/Plugins/Apps/Valkey/Valkey.lmm.rb +64 -20
  172. data/Plugins/Apps/Vaultwarden/Vaultwarden.conf.erb +9 -6
  173. data/Plugins/Apps/Vaultwarden/Vaultwarden.container +7 -1
  174. data/Plugins/Apps/Vaultwarden/Vaultwarden.lmm.rb +67 -28
  175. data/Plugins/Apps/Wiki.js/Wiki.js.conf.erb +39 -0
  176. data/Plugins/Apps/Wiki.js/Wiki.js.container +20 -0
  177. data/Plugins/Apps/Wiki.js/Wiki.js.lmm.rb +55 -0
  178. data/Plugins/Apps/YaCy/YaCy.conf.erb +93 -0
  179. data/Plugins/Apps/YaCy/YaCy.container +21 -0
  180. data/Plugins/Apps/YaCy/YaCy.lmm.rb +160 -0
  181. data/Plugins/Apps/ZooKeeper/ZooKeeper.container +24 -0
  182. data/Plugins/Apps/ZooKeeper/ZooKeeper.lmm.rb +68 -0
  183. data/Plugins/Apps/bitmagnet/bitmagnet.conf.erb +0 -3
  184. data/Plugins/Apps/bitmagnet/bitmagnet.lmm.rb +0 -1
  185. data/Plugins/Apps/gollum/gollum.conf.erb +40 -4
  186. data/Plugins/Apps/gollum/gollum.container +10 -1
  187. data/Plugins/Apps/gollum/gollum.lmm.rb +56 -47
  188. data/Plugins/Apps/llama.cpp/llama.cpp.container +28 -0
  189. data/Plugins/Apps/llama.cpp/llama.cpp.lmm.rb +90 -0
  190. data/Plugins/Apps/vLLM/vLLM.container +32 -0
  191. data/Plugins/Apps/vLLM/vLLM.lmm.rb +89 -0
  192. data/Plugins/OS/General/Utils.lmm.rb +26 -0
  193. data/Plugins/OS/Linux/Connection.rb +472 -0
  194. data/Plugins/OS/Linux/Debian/preseed.cfg.erb +81 -0
  195. data/Plugins/OS/Linux/Distributions.yaml +32 -0
  196. data/Plugins/OS/Linux/Flavours.yaml +24 -0
  197. data/Plugins/OS/Linux/Grub/grub.cfg +10 -0
  198. data/Plugins/OS/Linux/HTTP.rb +32 -0
  199. data/Plugins/OS/Linux/Linux.lmm.rb +708 -174
  200. data/Plugins/OS/Linux/Packages.yaml +67 -3
  201. data/Plugins/OS/Linux/Proxmox/answer.toml.erb +30 -0
  202. data/Plugins/OS/Linux/Services.yaml +8 -0
  203. data/Plugins/OS/Linux/Shell.rb +70 -0
  204. data/Plugins/OS/Linux/Syslinux/default +8 -0
  205. data/Plugins/OS/Linux/WireGuard/WireGuard.lmm.rb +93 -40
  206. data/Plugins/OS/Linux/WireGuard/wg0.conf.erb +3 -0
  207. data/Plugins/OS/Linux/openSUSE/autoinst.xml.erb +29 -3
  208. data/Plugins/OS/Linux/systemd/systemd.lmm.rb +13 -11
  209. data/Plugins/OS/Routers/Aruba/ArubaInstant.lmm.rb +6 -5
  210. data/Plugins/Platforms/GitHub.lmm.rb +73 -28
  211. data/Plugins/Platforms/GoDaddy/GoDaddy.lmm.rb +10 -7
  212. data/Plugins/Platforms/Proxmox/Proxmox.lmm.rb +402 -0
  213. data/Plugins/Platforms/Proxmox/XTerm.rb +321 -0
  214. data/Plugins/Platforms/libvirt/libvirt.lmm.rb +41 -15
  215. data/Plugins/Platforms/porkbun.lmm.rb +12 -2
  216. data/Plugins/Platforms/porkbun_spec.rb +2 -2
  217. data/Plugins/Services/DNS/AmberBit.lmm.rb +1 -1
  218. data/Plugins/Services/DNS/ArubaItDNS.lmm.rb +1 -1
  219. data/Plugins/Services/DNS/NICLV.lmm.rb +1 -1
  220. data/Plugins/Services/DNS/PowerDNS.lmm.rb +130 -41
  221. data/Plugins/Services/DNS/tonic.lmm.rb +22 -12
  222. data/bootstrap.sh +41 -3
  223. data/lib/ConfigLMM/Framework/plugins/dns.rb +4 -3
  224. data/lib/ConfigLMM/Framework/plugins/linuxApp.rb +187 -144
  225. data/lib/ConfigLMM/Framework/plugins/nginxApp.rb +54 -6
  226. data/lib/ConfigLMM/Framework/plugins/plugin.rb +68 -140
  227. data/lib/ConfigLMM/Framework/plugins/store.rb +4 -4
  228. data/lib/ConfigLMM/Framework/variables.rb +75 -0
  229. data/lib/ConfigLMM/Framework.rb +1 -0
  230. data/lib/ConfigLMM/cli.rb +13 -5
  231. data/lib/ConfigLMM/commands/cleanup.rb +1 -0
  232. data/lib/ConfigLMM/commands/configsCommand.rb +38 -5
  233. data/lib/ConfigLMM/commands/diff.rb +33 -9
  234. data/lib/ConfigLMM/context.rb +22 -3
  235. data/lib/ConfigLMM/io/configList.rb +85 -7
  236. data/lib/ConfigLMM/io/connection.rb +143 -0
  237. data/lib/ConfigLMM/io/dhcp.rb +330 -0
  238. data/lib/ConfigLMM/io/http.rb +78 -0
  239. data/lib/ConfigLMM/io/local.rb +207 -0
  240. data/lib/ConfigLMM/io/pxe.rb +92 -0
  241. data/lib/ConfigLMM/io/ssh.rb +156 -0
  242. data/lib/ConfigLMM/io/tftp.rb +105 -0
  243. data/lib/ConfigLMM/io.rb +2 -0
  244. data/lib/ConfigLMM/secrets/envStore.rb +39 -0
  245. data/lib/ConfigLMM/secrets/fileStore.rb +43 -0
  246. data/lib/ConfigLMM/state.rb +12 -3
  247. data/lib/ConfigLMM/version.rb +2 -1
  248. data/lib/ConfigLMM.rb +1 -0
  249. data/{Examples → scripts}/configlmmAuth.sh +7 -5
  250. metadata +257 -9
@@ -1,40 +1,46 @@
1
1
 
2
2
  module ConfigLMM
3
3
  module LMM
4
- class GitLab < Framework::NginxApp
4
+ class GitLab < Framework::Plugin
5
5
 
6
6
  HOME_DIR = '/var/lib/gitlab'
7
+ IMAGE_ID = 'docker.io/gitlab/gitlab-ce:latest'
7
8
 
8
9
  def actionGitLabDeploy(id, target, activeState, context, options)
10
+ raise Framework::PluginProcessError.new('Domain field must be set!') unless target['Domain']
9
11
 
10
- if target['Location'] && target['Location'] != '@me'
11
- uri = Addressable::URI.parse(target['Location'])
12
- raise Framework::PluginProcessError.new("#{id}: Unknown Protocol: #{uri.scheme}!") if uri.scheme != 'ssh'
12
+ self.withConnection(target['Location'], target) do |connection|
13
+ Linux.withConnection(connection) do |linuxConnection|
13
14
 
14
- self.class.sshStart(uri) do |ssh|
15
- self.prepareConfig(target, ssh)
16
-
17
- distroInfo = Framework::LinuxApp.currentDistroInfo(ssh)
18
- self.class.sshExec!(ssh, "mkdir -p #{HOME_DIR}/config")
19
- self.class.sshExec!(ssh, "mkdir -p #{HOME_DIR}/logs")
20
- self.class.sshExec!(ssh, "mkdir -p #{HOME_DIR}/data")
21
- self.class.sshExec!(ssh, "mkdir -p #{HOME_DIR}/backups")
15
+ linuxConnection.createDirs(options, "#{HOME_DIR}/config", "#{HOME_DIR}/logs", "#{HOME_DIR}/data", "#{HOME_DIR}/backups")
22
16
 
23
17
  path = '/etc/containers/systemd'
24
- ssh.scp.upload!(__dir__ + '/GitLab.container', path)
25
- self.class.sshExec!(ssh, "systemctl daemon-reload")
26
- self.class.sshExec!(ssh, "systemctl start GitLab")
18
+ linuxConnection.upload(__dir__ + '/GitLab.container', path, options)
19
+
20
+ if !target.key?('Proxy') || target['Proxy']
21
+ Nginx.withConnection(linuxConnection) do |nginxConnection|
22
+ nginxConnection.provisionProxy('http://127.0.0.1:18100', 'GitLab', target, activeState, context, options)
23
+ end
24
+ elsif target.key?('Proxy') && target['Proxy'] == false
25
+ linuxConnection.fileReplace("#{path}/GitLab.container", 'PublishPort=127.0.0.1:18100:', 'PublishPort=0.0.0.0:18100:', options)
26
+ linuxConnection.firewallAddPort('18100/tcp', options)
27
+ end
27
28
 
28
- Framework::LinuxApp.ensureServiceAutoStartOverSSH(NGINX_PACKAGE, ssh)
29
- self.writeNginxConfig(__dir__, 'GitLab', id, target, state, context, options)
30
- self.deployNginxConfig(id, target, activeState, context, options)
31
- Framework::LinuxApp.startServiceOverSSH(NGINX_PACKAGE, ssh)
29
+ linuxConnection.reloadServiceManager(options)
30
+ linuxConnection.restartService('GitLab', options)
32
31
 
33
- configFile = '/var/lib/gitlab/config/gitlab.rb'
34
- while !self.class.remoteFilePresent?(configFile, ssh)
35
- sleep(2)
32
+ configFile = HOME_DIR + '/config/gitlab.rb'
33
+ if options['dry']
34
+ linuxConnection.filePresent?(configFile, options)
35
+ else
36
+ counter = 200
37
+ while !linuxConnection.filePresent?(configFile, options)
38
+ counter -= 1
39
+ raise "Timeout while waiting for #{configFile}!" if counter <= 0
40
+ sleep(2)
41
+ end
36
42
  end
37
- updateRemoteFile(ssh, configFile, options, true) do |fileLines|
43
+ linuxConnection.updateFile(configFile, options, true) do |fileLines|
38
44
  fileLines << "external_url 'https://#{target['Domain']}'\n"
39
45
  fileLines << "letsencrypt['enable'] = false\n"
40
46
  fileLines << "nginx['listen_port'] = 80\n"
@@ -44,9 +50,9 @@ module ConfigLMM
44
50
  fileLines << "mattermost_nginx['listen_port'] = 80\n"
45
51
  fileLines << "mattermost_nginx['listen_https'] = false\n"
46
52
  if target['SMTP']
47
- fileLines << "gitlab_rails['smtp_address'] = '#{target['SMTP']['HostName']}'\n"
53
+ fileLines << "gitlab_rails['smtp_address'] = '#{target['SMTP']['Host']}'\n"
48
54
  fileLines << "gitlab_rails['smtp_port'] = '#{target['SMTP']['Port']}'\n"
49
- fileLines << "gitlab_rails['smtp_user_name'] = '#{target['SMTP']['User']}'\n"
55
+ fileLines << "gitlab_rails['smtp_user_name'] = '#{target['SMTP']['Username']}'\n"
50
56
  if target['SMTP']['TLS']
51
57
  fileLines << "gitlab_rails['smtp_tls'] = true\n"
52
58
  fileLines << "gitlab_rails['smtp_openssl_verify_mode'] = 'peer'\n"
@@ -54,18 +60,31 @@ module ConfigLMM
54
60
  end
55
61
  end
56
62
 
57
- self.class.sshExec!(ssh, "systemctl restart GitLab")
63
+ linuxConnection.restartService('GitLab', options)
58
64
  end
59
- else
60
- # TODO
61
65
  end
62
66
  end
63
67
 
64
- def prepareConfig(target, ssh)
65
- raise Framework::PluginProcessError.new('Domain field must be set!') unless target['Domain']
66
-
67
- Framework::LinuxApp.ensurePackages([NGINX_PACKAGE], ssh)
68
- self.class.prepareNginxConfig(target, ssh)
68
+ def cleanup(configs, state, context, options)
69
+ cleanupType(:GitLab, configs, state, context, options) do |item, id, state, context, options, connection|
70
+ Linux.withConnection(connection) do |linuxConnection|
71
+ if item['Config']['Proxy'].nil? || item['Config']['Proxy']
72
+ Nginx.withConnection(linuxConnection) do |nginxConnection|
73
+ nginxConnection.cleanupConfig('GitLab', context, options)
74
+ nginxConnection.reload(options)
75
+ end
76
+ end
77
+ linuxConnection.firewallRemovePort('18100/tcp', options)
78
+ linuxConnection.stopService('GitLab', options)
79
+ linuxConnection.rm('/etc/containers/systemd/GitLab.container', options[:dry])
80
+ linuxConnection.exec("podman rmi #{IMAGE_ID}", true, options)
81
+ state.item(id)['Status'] = State::STATUS_DELETED unless options[:dry]
82
+ if options[:destroy]
83
+ connection.rm('/var/lib/gitlab', options[:dry])
84
+ state.item(id)['Status'] = State::STATUS_DESTROYED unless options[:dry]
85
+ end
86
+ end
87
+ end
69
88
  end
70
89
 
71
90
  end
@@ -0,0 +1,86 @@
1
+
2
+ server {
3
+
4
+ <% if config['NginxVersion'] >= 1.25 %>
5
+ <% if !config['TLS'] %>
6
+ listen <%= config['Port'] %>;
7
+ listen [::]:<%= config['Port'] %>;
8
+ <% else %>
9
+ listen <%= config['Port'] %> ssl;
10
+ listen [::]:<%= config['Port'] %> ssl;
11
+
12
+ include config-lmm/ssl.conf;
13
+ <% end %>
14
+ http2 on;
15
+ http3 on;
16
+ quic_retry on;
17
+ add_header Alt-Svc 'h3=":443"; ma=86400';
18
+ <% else %>
19
+ <% if !config['TLS'] %>
20
+ listen <%= config['Port'] %>;
21
+ listen [::]:<%= config['Port'] %>;
22
+ <% else %>
23
+ listen <%= config['Port'] %> ssl http2;
24
+ listen [::]:<%= config['Port'] %> ssl http2;
25
+
26
+ include config-lmm/ssl.conf;
27
+ <% end %>
28
+ <% end %>
29
+
30
+ server_name <%= config['Domain'] %>;
31
+
32
+ <% if config['AuthentikDomain'].nil? %>
33
+ include config-lmm/private.conf;
34
+ <% end %>
35
+
36
+ include config-lmm/errors.conf;
37
+
38
+ <% if config['CertName'] %>
39
+ ssl_certificate "/etc/letsencrypt/live/<%= config['CertName'] %>/fullchain.pem";
40
+ ssl_certificate_key "/etc/letsencrypt/live/<%= config['CertName'] %>/privkey.pem";
41
+ ssl_trusted_certificate "/etc/letsencrypt/live/<%= config['CertName'] %>/chain.pem";
42
+ <% end %>
43
+
44
+ location / {
45
+ proxy_pass <%= config['Server'] %>;
46
+
47
+ <% if config['AuthentikDomain'] %>
48
+ error_page 401 = @authenticate;
49
+ include config-lmm/errors.conf;
50
+
51
+ auth_request /outpost.goauthentik.io/auth/nginx;
52
+
53
+ # translate headers from the outposts back to the actual upstream
54
+ auth_request_set $authentik_username $upstream_http_x_authentik_username;
55
+ auth_request_set $authentik_groups $upstream_http_x_authentik_groups;
56
+ auth_request_set $authentik_email $upstream_http_x_authentik_email;
57
+ auth_request_set $authentik_name $upstream_http_x_authentik_name;
58
+ auth_request_set $authentik_uid $upstream_http_x_authentik_uid;
59
+
60
+ proxy_set_header REMOTE_USER $authentik_username;
61
+ proxy_set_header REMOTE_GROUPS $authentik_groups;
62
+ proxy_set_header REMOTE_EMAIL $authentik_email;
63
+ proxy_set_header REMOTE_NAME $authentik_name;
64
+ proxy_set_header REMOTE_UID $authentik_uid;
65
+ <% end %>
66
+
67
+ include config-lmm/proxy.conf;
68
+ }
69
+
70
+ <% if config['AuthentikDomain'] %>
71
+ location /outpost.goauthentik.io {
72
+ proxy_pass https://<%= config['AuthentikDomain'] %>/outpost.goauthentik.io;
73
+ proxy_ssl_protocols TLSv1.2 TLSv1.3;
74
+ proxy_set_header X-Authentik-Host $host;
75
+ proxy_set_header X-Original-URL $scheme://$http_host$request_uri;
76
+ proxy_pass_request_body off;
77
+ proxy_set_header Content-Length "";
78
+ }
79
+
80
+ location @authenticate {
81
+ internal;
82
+ return 302 /outpost.goauthentik.io/start?rd=$request_uri;
83
+ }
84
+ <% end %>
85
+ }
86
+
@@ -0,0 +1,19 @@
1
+
2
+ [Unit]
3
+ Description=Homepage container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=Homepage
8
+ Image=ghcr.io/gethomepage/homepage:latest
9
+ PublishPort=127.0.0.1:13400:3000
10
+ LogDriver=journald
11
+ AutoUpdate=registry
12
+ Volume=/var/lib/homepage/config:/app/config
13
+
14
+ [Service]
15
+ TimeoutStartSec=3min
16
+ Restart=on-failure
17
+
18
+ [Install]
19
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,54 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class Homepage < Framework::Plugin
5
+
6
+ NAME = 'Homepage'
7
+ USER = 'homepage'
8
+ HOME_DIR = '/var/lib/homepage'
9
+
10
+ def actionHomepageBuild(id, target, activeState, context, options)
11
+ Nginx.withConnection(local) do |nginxConnection|
12
+ nginxConnection.writeConfig(__dir__, NAME, target, state, context, options)
13
+ end
14
+ end
15
+
16
+ def actionHomepageDeploy(id, target, activeState, context, options)
17
+ self.withConnection(target['Location'], target) do |connection|
18
+ Linux.withConnection(connection) do |linuxConnection|
19
+ if !target.key?('Proxy') || target['Proxy'] != 'only'
20
+ Podman.ensurePresent(linuxConnection, options)
21
+ Podman.createUser(USER, HOME_DIR, 'Homepage', linuxConnection, options)
22
+ linuxConnection.withUserShell(USER) do |shell|
23
+ shell.createDirs(options, '~/config')
24
+ end
25
+
26
+ configPath = './Homepage'
27
+ configPath = target['ConfigPath'] if target['ConfigPath']
28
+ Dir[configPath + '/*'].each do |file|
29
+ linuxConnection.upload(file, HOME_DIR + '/config/', options)
30
+ end
31
+
32
+ path = Podman.containersPath(HOME_DIR)
33
+ linuxConnection.upload(__dir__ + '/Homepage.container', path, options)
34
+ if target.key?('Proxy') && target['Proxy'] == false
35
+ linuxConnection.exec("sed -i 's|PublishPort=127.0.0.1:13400:|PublishPort=0.0.0.0:13400:|' #{path}/Homepage.container", false, options)
36
+ linuxConnection.firewallAddPort('13400/tcp', options)
37
+ end
38
+
39
+ linuxConnection.reloadUserServices(USER, options)
40
+ linuxConnection.restartUserService(USER, 'Homepage', options)
41
+ end
42
+ if !target.key?('Proxy') || !!target['Proxy']
43
+ Nginx.withConnection(linuxConnection) do |nginxConnection|
44
+ target['ConfigName'] = target['Name']
45
+ nginxConnection.provision(__dir__, NAME, target, activeState, context, options)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ end
53
+ end
54
+ end
@@ -20,9 +20,6 @@ server {
20
20
 
21
21
  server_name <%= config['Domain'] %>;
22
22
 
23
- access_log /var/log/nginx/ipfs.access.log;
24
- error_log /var/log/nginx/ipfs.error.log;
25
-
26
23
  include config-lmm/private.conf;
27
24
  include config-lmm/errors.conf;
28
25
 
@@ -14,7 +14,6 @@ module ConfigLMM
14
14
  def actionIPFSDeploy(id, target, activeState, context, options)
15
15
  if !target['Location'] || target['Location'] == '@me'
16
16
  deployNginxConfig(id, target, activeState, context, options)
17
- activeState['Location'] = '@me'
18
17
  end
19
18
  end
20
19
 
@@ -16,9 +16,6 @@ server {
16
16
 
17
17
  server_name <%= config['Domain'] %>;
18
18
 
19
- access_log /var/log/nginx/influxdb.access.log;
20
- error_log /var/log/nginx/influxdb.error.log;
21
-
22
19
  include config-lmm/private.conf;
23
20
  include config-lmm/errors.conf;
24
21
 
@@ -10,7 +10,6 @@ module ConfigLMM
10
10
  def actionInfluxDBDeploy(id, target, activeState, context, options)
11
11
  if !target['Location'] || target['Location'] == '@me'
12
12
  deployNginxConfig(id, target, activeState, context, options)
13
- activeState['Location'] = '@me'
14
13
  end
15
14
  end
16
15
 
@@ -16,9 +16,6 @@ server {
16
16
 
17
17
  server_name <%= config['Domain'] %>;
18
18
 
19
- access_log /var/log/nginx/jackett.access.log;
20
- error_log /var/log/nginx/jackett.error.log;
21
-
22
19
  include config-lmm/private.conf;
23
20
  include config-lmm/errors.conf;
24
21
 
@@ -10,7 +10,6 @@ module ConfigLMM
10
10
  def actionJackettDeploy(id, target, activeState, context, options)
11
11
  if !target['Location'] || target['Location'] == '@me'
12
12
  deployNginxConfig(id, target, activeState, context, options)
13
- activeState['Location'] = '@me'
14
13
  end
15
14
  end
16
15
 
@@ -17,9 +17,6 @@ server {
17
17
 
18
18
  server_name <%= config['Domain'] %>;
19
19
 
20
- access_log /var/log/nginx/jellyfin.access.log;
21
- error_log /var/log/nginx/jellyfin.error.log;
22
-
23
20
  include config-lmm/errors.conf;
24
21
  include config-lmm/ssl.conf;
25
22
 
@@ -14,7 +14,6 @@ module ConfigLMM
14
14
  def actionJellyfinDeploy(id, target, activeState, context, options)
15
15
  if !target['Location'] || target['Location'] == '@me'
16
16
  deployNginxConfig(id, target, activeState, context, options)
17
- activeState['Location'] = '@me'
18
17
  end
19
18
  end
20
19
 
@@ -0,0 +1,78 @@
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.withConnection(target['Location'], target) do |connection|
11
+ Linux.withConnection(connection) do |linuxConnection|
12
+ linuxConnection.ensurePackage(PACKAGE_NAME, options)
13
+
14
+ linuxConnection.upload(__dir__ + '/rfc2136.ini', CONFIG_DIR, options)
15
+ linuxConnection.upload(__dir__ + '/renew-certificates.service', '/etc/systemd/system/', options)
16
+ linuxConnection.upload(__dir__ + '/renew-certificates.timer', '/etc/systemd/system/', options)
17
+ linuxConnection.createDirs(options, CONFIG_DIR + "renewal-hooks/deploy")
18
+ target['Hooks'].to_a.each do |hook|
19
+ linuxConnection.upload(__dir__ + '/hooks/' + hook + '.sh', "#{CONFIG_DIR}renewal-hooks/deploy/", options)
20
+ end
21
+ linuxConnection.exec("chmod +x #{CONFIG_DIR}renewal-hooks/deploy/*.sh", false, options)
22
+ linuxConnection.fileReplace(CONFIG_DIR + 'rfc2136.ini', '\$IP', target['DNS']['IP'] , options)
23
+
24
+ secretId, secretName = target['DNS']['SecretId'].to_s.split('.')
25
+ key = nil
26
+ key = context.secrets.load(secretId, secretName) if secretId && secretName
27
+ key = context.secrets.load('LETSENCRYPT', 'DNS_SECRET') if key.nil?
28
+ raise Framework::PluginProcessError.new('LetsEncrypt missing RFC2136 TSIG key! Specify DNS.SecretId or LETSENCRYPT_DNS_SECRET env variable') unless key
29
+
30
+ linuxConnection.fileReplace(CONFIG_DIR + 'rfc2136.ini', '\$SECRET', key, options)
31
+ linuxConnection.setPrivate(CONFIG_DIR + 'rfc2136.ini', options)
32
+ if target['Domain']
33
+ createCertificate('Wildcard', target['Domain'], target, linuxConnection, options)
34
+ end
35
+ target['Certificates'].to_h.each do |name, domains|
36
+ createCertificate(name, domains, target, linuxConnection, options)
37
+ end
38
+
39
+ linuxConnection.reloadServiceManager(options)
40
+ linuxConnection.ensureServiceAutoStart('renew-certificates.timer', options)
41
+ linuxConnection.startService('renew-certificates.timer', options)
42
+
43
+ target['Hooks'].to_a.each do |hook|
44
+ linuxConnection.exec("#{CONFIG_DIR}renewal-hooks/deploy/#{hook}.sh", false, options)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ def createCertificate(name, domains, target, connection, options)
51
+ return if connection.fileLink?("#{CONFIG_DIR}live/#{name}/fullchain.pem", options)
52
+ connection.exec("rm -rf #{CONFIG_DIR}live/#{name}", false, options)
53
+
54
+ domainList = []
55
+ domains = [domains] unless domains.is_a?(Array)
56
+ domains.each do |domain|
57
+ domainList << '--domains "' + Addressable::IDNA.to_ascii(domain) + '"'
58
+ if addBaseDomain?(domain, domains)
59
+ domainList << '--domains "' + Addressable::IDNA.to_ascii(domain[2..-1]) + '"'
60
+ end
61
+ end
62
+ extra = ''
63
+ extra = '--dns-rfc2136-propagation-seconds ' + target['DNS']['Propagation'].to_s if target['DNS']['Propagation']
64
+
65
+ connection.exec("certbot certonly --dns-rfc2136 --dns-rfc2136-credentials=#{CONFIG_DIR}rfc2136.ini #{extra} --non-interactive --agree-tos --email #{target['EMail']} --cert-name '#{name}' #{domainList.join(' ')}", false, options)
66
+ end
67
+
68
+ def addBaseDomain?(domain, domains)
69
+ return false unless domain.start_with?('*.')
70
+ match = '*.' + domain[2..-1].split('.')[1..].join('.')
71
+ return false if match == '*.' || domains.any? { |d| d.casecmp?(match) }
72
+ true
73
+ end
74
+
75
+ end
76
+
77
+ end
78
+ 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,21 @@
1
+
2
+ [Unit]
3
+ Description=LibreTranslate container
4
+ After=local-fs.target
5
+
6
+ [Container]
7
+ ContainerName=LibreTranslate
8
+ Image=docker.io/libretranslate/libretranslate:latest
9
+ Exec=--host 0.0.0.0 $ARGS
10
+ PublishPort=127.0.0.1:15100:5000
11
+ UserNS=keep-id:uid=1032,gid=1032
12
+ Volume=/var/lib/libretranslate/.local:/home/libretranslate/.local
13
+ LogDriver=journald
14
+ AutoUpdate=registry
15
+
16
+ [Service]
17
+ TimeoutStartSec=32min
18
+ Restart=on-failure
19
+
20
+ [Install]
21
+ WantedBy=multi-user.target default.target
@@ -0,0 +1,34 @@
1
+
2
+ module ConfigLMM
3
+ module LMM
4
+ class LibreTranslate < Framework::Plugin
5
+
6
+ USER = 'libretranslate'
7
+ HOME_DIR = '/var/lib/libretranslate'
8
+
9
+ def actionLibreTranslateDeploy(id, target, activeState, context, options)
10
+
11
+ self.withConnection(target['Location'], target) do |connection|
12
+ Linux.withConnection(connection) do |linuxConnection|
13
+ Podman.ensurePresent(linuxConnection, options)
14
+ Podman.createUser(USER, HOME_DIR, 'LibreTranslate', linuxConnection, options)
15
+
16
+ path = Podman.containersPath(HOME_DIR)
17
+
18
+ linuxConnection.upload(__dir__ + '/LibreTranslate.container', path, options)
19
+
20
+ args = ['--metrics']
21
+ linuxConnection.fileReplace("#{path}/LibreTranslate.container", '\$ARGS', args.join(' '), options)
22
+ if target['Listen']
23
+ linuxConnection.fileReplace("#{path}/LibreTranslate.container", '127.0.0.1', target['Listen'], options)
24
+ end
25
+
26
+ linuxConnection.reloadUserServices(USER, options)
27
+ linuxConnection.restartUserService(USER, 'LibreTranslate', options)
28
+ end
29
+ end
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,81 @@
1
+ # Use an official Ruby image as a base
2
+ ARG RUBY_VERSION
3
+ FROM ruby:${RUBY_VERSION} AS base
4
+
5
+ # Install base packages
6
+ RUN apt-get update -qq && \
7
+ apt-get install --no-install-recommends -y curl libjemalloc2 libvips sqlite3 mariadb-client sendmail vim && \
8
+ rm -rf /var/lib/apt/lists /var/cache/apt/archives
9
+
10
+ # Latest releases available at https://github.com/aptible/supercronic/releases
11
+ ENV SUPERCRONIC_URL=https://github.com/aptible/supercronic/releases/download/v0.2.33/supercronic-linux-amd64 \
12
+ SUPERCRONIC_SHA1SUM=71b0d58cc53f6bd72cf2f293e09e294b79c666d8 \
13
+ SUPERCRONIC=supercronic-linux-amd64
14
+
15
+ RUN curl -fsSLO "$SUPERCRONIC_URL" \
16
+ && echo "${SUPERCRONIC_SHA1SUM} ${SUPERCRONIC}" | sha1sum -c - \
17
+ && chmod +x "$SUPERCRONIC" \
18
+ && mv "$SUPERCRONIC" "/usr/local/bin/${SUPERCRONIC}" \
19
+ && ln -s "/usr/local/bin/${SUPERCRONIC}" /usr/local/bin/supercronic
20
+
21
+ ENV BUNDLE_DEPLOYMENT="1" BUNDLE_PATH="/usr/local/bundle" BUNDLE_WITHOUT="development"
22
+
23
+ # Throw-away build stage to reduce size of final image
24
+ FROM base AS build
25
+
26
+ # Install packages needed to build gems
27
+ RUN apt-get update -qq && \
28
+ apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config && \
29
+ rm -rf /var/lib/apt/lists /var/cache/apt/archives
30
+
31
+ # Set working directory
32
+ WORKDIR /lobsters
33
+
34
+ COPY . .
35
+
36
+ # Install application gems
37
+ RUN bundle install && \
38
+ rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git
39
+
40
+ RUN mkdir -p /srv/lobste.rs/log && \
41
+ ln -sf /lobsters /srv/lobste.rs/http
42
+
43
+ # Temporary generate secrets
44
+ RUN RAILS_ENV=production ./bin/rails credentials:edit
45
+
46
+ # Precompiling assets for production without requiring secret RAILS_MASTER_KEY
47
+ RUN SECRET_KEY_BASE_DUMMY=1 RAILS_ENV=production ./bin/rails assets:precompile
48
+
49
+ # Remove secrets
50
+ RUN rm -rf ./config/credentials.yml.enc ./config/master.key ./config/database.yml ./tmp/* /srv/lobste.rs/log/*
51
+
52
+ # Final stage for app image
53
+ FROM base
54
+
55
+ WORKDIR /lobsters
56
+
57
+ # Copy built artifacts: gems, application
58
+ COPY --from=build "${BUNDLE_PATH}" "${BUNDLE_PATH}"
59
+ COPY --from=build /lobsters /lobsters
60
+ COPY --from=build /srv/lobste.rs /srv/lobste.rs
61
+
62
+ COPY crontab /etc/
63
+
64
+ RUN ln -sf /config/master.key /lobsters/config/master.key && \
65
+ ln -sf /config/credentials.yml.enc /lobsters/config/credentials.yml.enc && \
66
+ ln -sf /config/database.yml /lobsters/config/database.yml
67
+
68
+ RUN mv /lobsters/public /lobsters/public_source && ln -sf /srv/lobsters/public /lobsters/public
69
+
70
+ # Run and own only the runtime files as a non-root user for security
71
+ RUN groupadd --system --gid 1000 lobsters && \
72
+ useradd lobsters --uid 1000 --gid 1000 --create-home --shell /bin/bash && \
73
+ chown -R lobsters:lobsters /lobsters/tmp /srv/lobste.rs/log
74
+
75
+ ENV LD_PRELOAD="/usr/lib/x86_64-linux-gnu/libjemalloc.so.2"
76
+
77
+ # Expose port for Rails server
78
+ EXPOSE 9292
79
+
80
+ ENTRYPOINT ["/lobsters/entrypoint.sh"]
81
+ CMD ["server"]