openship 0.4.5 → 0.4.7

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 (314) hide show
  1. package/README.md +2 -24
  2. package/dist/bare-2HYDXQEH.js +23 -0
  3. package/dist/{bowser-L6PVH37V.js → bowser-O3AEA6AN.js} +3 -1
  4. package/dist/{chunk-YFLQGDN2.js → chunk-4TJC4JZQ.js} +18 -9
  5. package/dist/{chunk-JXQCFTDV.js → chunk-4Z5QO7N6.js} +4 -2
  6. package/dist/{chunk-JJVSOE4I.js → chunk-5MZ624VX.js} +2 -0
  7. package/dist/{chunk-TDFK6ISF.js → chunk-6APIPIJY.js} +6 -4
  8. package/dist/{chunk-3LCRNQ2C.js → chunk-6APULWSU.js} +151 -133
  9. package/dist/{chunk-FTBXJ2VA.js → chunk-6B5YCFFV.js} +6 -4
  10. package/dist/{chunk-MDFYN2QH.js → chunk-AVIB7CT7.js} +3 -1
  11. package/dist/{chunk-W7KGHYLV.js → chunk-DYPH4HCI.js} +2 -0
  12. package/dist/{chunk-5WI7ZXOC.js → chunk-EI575GHN.js} +4 -2
  13. package/dist/{chunk-Z6EKX6CX.js → chunk-FPRHYBY2.js} +2 -0
  14. package/dist/{chunk-FZJX6XP7.js → chunk-FRNAR33O.js} +4 -2
  15. package/dist/{chunk-XRWV5ZNR.js → chunk-FXTBHTWY.js} +360 -42
  16. package/dist/chunk-ILFETZOM.js +131 -0
  17. package/dist/{chunk-E3ZPCNPU.js → chunk-IMBD5PZ5.js} +251 -105
  18. package/dist/{chunk-VJTXRCTT.js → chunk-IPYPMI3S.js} +10 -6
  19. package/dist/{chunk-PARRPUXN.js → chunk-IXCHURVK.js} +5 -3
  20. package/dist/{chunk-YJAS5RQX.js → chunk-JYGD5WSN.js} +2 -0
  21. package/dist/{chunk-JWBXYO24.js → chunk-KI2EY3WB.js} +2 -0
  22. package/dist/{chunk-YVXF6GGR.js → chunk-L2VOGB2X.js} +5 -104
  23. package/dist/{chunk-K5OH5U7N.js → chunk-MZ2WEHHY.js} +5 -3
  24. package/dist/{chunk-SJBR527P.js → chunk-O6ZZF52V.js} +3 -1
  25. package/dist/{chunk-VLPJLXXB.js → chunk-OWED2GRM.js} +3 -1
  26. package/dist/{chunk-C4FOQJ63.js → chunk-OXWDY22G.js} +4 -2
  27. package/dist/{chunk-NPVJYR7M.js → chunk-Q2CIRAOK.js} +2 -0
  28. package/dist/chunk-R5BHFFAV.js +282 -0
  29. package/dist/{chunk-JYAGTMSJ.js → chunk-RPHRPFEH.js} +2 -0
  30. package/dist/{chunk-R74UB4X4.js → chunk-SJIFLLX4.js} +397 -85
  31. package/dist/{chunk-HYL4QYZM.js → chunk-TGIINPVM.js} +13 -2
  32. package/dist/{chunk-SYCZE6FQ.js → chunk-TYVME3XD.js} +7 -1
  33. package/dist/{chunk-UMYQALBH.js → chunk-UXWAVM4C.js} +1788 -1484
  34. package/dist/{chunk-HDV4NSA3.js → chunk-V6AN6XLZ.js} +16 -6
  35. package/dist/{chunk-3YCMJRDZ.js → chunk-VDMQJHPI.js} +36 -15
  36. package/dist/{chunk-RZPDJC2J.js → chunk-VFINNTUL.js} +20 -1
  37. package/dist/{chunk-65KKUJBR.js → chunk-VWPEKOH6.js} +4 -2
  38. package/dist/{chunk-GHXGCANU.js → chunk-W73X43R7.js} +4 -2
  39. package/dist/{chunk-7EYFCKZX.js → chunk-XFCDYZUQ.js} +8 -6
  40. package/dist/cloud-2KXREXMO.js +30 -0
  41. package/dist/{cloud-7IGG45CF.js → cloud-7QYOGF7F.js} +4 -2
  42. package/dist/detect-2XSCFFTF.js +44 -0
  43. package/dist/{dist-NQB6QGX3.js → dist-IQJ6H6UZ.js} +4 -2
  44. package/dist/{dist-es-DCR6UJMP.js → dist-es-5DY7K32L.js} +7 -5
  45. package/dist/{dist-es-TYYZ5KEF.js → dist-es-JSTDUS7Q.js} +7 -5
  46. package/dist/{dist-es-7EZIM4JU.js → dist-es-OSNJBLXB.js} +6 -4
  47. package/dist/{dist-es-6RSPR56Q.js → dist-es-RYHIPOTN.js} +15 -13
  48. package/dist/{dist-es-33Z3Y6VE.js → dist-es-VNKHQVIB.js} +5 -3
  49. package/dist/{dist-es-D6QQVWRZ.js → dist-es-WV4AMOII.js} +9 -7
  50. package/dist/{dist-es-GMEQINIE.js → dist-es-X3B5QYKB.js} +7 -5
  51. package/dist/docker-LODK5Y2S.js +16 -0
  52. package/dist/docker-edge-executor-VHW3JPJS.js +13 -0
  53. package/dist/edge-import-ZQ4Y5HEA.js +45 -0
  54. package/dist/ensure-container-edge-ED25XL3Z.js +31 -0
  55. package/dist/{event-streams-G7F6Q7QY.js → event-streams-EHRAKL75.js} +5 -3
  56. package/dist/{executor-O5NR7LMP.js → executor-YPT6RH6O.js} +9 -7
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.js +279 -245
  59. package/dist/{loadSso-LH753OSJ.js → loadSso-46FQRIZS.js} +11 -9
  60. package/dist/local-executor-V7U5VOZW.js +12 -0
  61. package/dist/nginx-XWJAOGS4.js +24 -0
  62. package/dist/{noop-6KC6HRDB.js → noop-FMMFG2EZ.js} +4 -2
  63. package/dist/{openresty-lua-KFCOJAG4.js → openresty-lua-4WLGDSER.js} +11 -3
  64. package/dist/server/engine/ChangeLog +2289 -0
  65. package/dist/server/engine/Documentations +5 -0
  66. package/dist/server/engine/LICENSE +674 -0
  67. package/dist/server/engine/README.md +34 -0
  68. package/dist/server/engine/conf/amavisd +150 -0
  69. package/dist/server/engine/conf/clamav +71 -0
  70. package/dist/server/engine/conf/core +632 -0
  71. package/dist/server/engine/conf/dovecot +151 -0
  72. package/dist/server/engine/conf/fail2ban +72 -0
  73. package/dist/server/engine/conf/global +698 -0
  74. package/dist/server/engine/conf/iredapd +52 -0
  75. package/dist/server/engine/conf/logwatch +28 -0
  76. package/dist/server/engine/conf/postfix +97 -0
  77. package/dist/server/engine/conf/postgresql +93 -0
  78. package/dist/server/engine/conf/spamassassin +39 -0
  79. package/dist/server/engine/dialog/config_via_dialog.sh +258 -0
  80. package/dist/server/engine/dialog/optional_components.sh +122 -0
  81. package/dist/server/engine/dialog/pgsql_config.sh +52 -0
  82. package/dist/server/engine/dialog/virtual_domain_config.sh +94 -0
  83. package/dist/server/engine/functions/amavisd.sh +267 -0
  84. package/dist/server/engine/functions/backend.sh +85 -0
  85. package/dist/server/engine/functions/clamav.sh +100 -0
  86. package/dist/server/engine/functions/cleanup.sh +393 -0
  87. package/dist/server/engine/functions/dovecot.sh +583 -0
  88. package/dist/server/engine/functions/fail2ban.sh +209 -0
  89. package/dist/server/engine/functions/iredapd.sh +356 -0
  90. package/dist/server/engine/functions/optional_components.sh +24 -0
  91. package/dist/server/engine/functions/packages.sh +522 -0
  92. package/dist/server/engine/functions/packages_freebsd.sh +179 -0
  93. package/dist/server/engine/functions/postfix.sh +457 -0
  94. package/dist/server/engine/functions/postgresql.sh +239 -0
  95. package/dist/server/engine/functions/spamassassin.sh +49 -0
  96. package/dist/server/engine/functions/system_accounts.sh +128 -0
  97. package/dist/server/engine/iRedMail.sh +144 -0
  98. package/dist/server/engine/pkgs/get_all.sh +338 -0
  99. package/dist/server/engine/pkgs/pkgs.freebsd.sha256 +3 -0
  100. package/dist/server/engine/pkgs/pkgs.openbsd.sha256 +6 -0
  101. package/dist/server/engine/pkgs/pkgs.sha256 +1 -0
  102. package/dist/server/engine/samples/amavisd/amavisd.conf +677 -0
  103. package/dist/server/engine/samples/amavisd/amavisd.mysql +278 -0
  104. package/dist/server/engine/samples/amavisd/amavisd.pgsql +253 -0
  105. package/dist/server/engine/samples/amavisd/amavisd.schema +942 -0
  106. package/dist/server/engine/samples/amavisd/default_spam_policy.sql +46 -0
  107. package/dist/server/engine/samples/dovecot/dovecot-2.4-mariadb.conf +758 -0
  108. package/dist/server/engine/samples/dovecot/dovecot-2.4-openldap.conf +767 -0
  109. package/dist/server/engine/samples/dovecot/dovecot-2.4-pgsql.conf +762 -0
  110. package/dist/server/engine/samples/dovecot/dovecot-last-login.conf +50 -0
  111. package/dist/server/engine/samples/dovecot/dovecot-ldap.conf +21 -0
  112. package/dist/server/engine/samples/dovecot/dovecot-share-folder.conf +22 -0
  113. package/dist/server/engine/samples/dovecot/dovecot-sql.conf +28 -0
  114. package/dist/server/engine/samples/dovecot/dovecot-used-quota.conf +13 -0
  115. package/dist/server/engine/samples/dovecot/dovecot.conf +484 -0
  116. package/dist/server/engine/samples/dovecot/dovecot.sieve +7 -0
  117. package/dist/server/engine/samples/dovecot/dovecot2-quota-warning.sh +38 -0
  118. package/dist/server/engine/samples/dovecot/sql/imap_share_folder.mysql +16 -0
  119. package/dist/server/engine/samples/dovecot/sql/last_login.mysql +12 -0
  120. package/dist/server/engine/samples/dovecot/sql/used_quota.mysql +26 -0
  121. package/dist/server/engine/samples/dovecot/systemd/override.conf +3 -0
  122. package/dist/server/engine/samples/fail2ban/action.d/banned_db.conf +9 -0
  123. package/dist/server/engine/samples/fail2ban/bin/fail2ban_banned_db +268 -0
  124. package/dist/server/engine/samples/fail2ban/fail2ban.local +17 -0
  125. package/dist/server/engine/samples/fail2ban/filter.d/dovecot.iredmail.conf +5 -0
  126. package/dist/server/engine/samples/fail2ban/filter.d/postfix-pregreet.iredmail.conf +16 -0
  127. package/dist/server/engine/samples/fail2ban/filter.d/postfix.iredmail.conf +18 -0
  128. package/dist/server/engine/samples/fail2ban/filter.d/roundcube.iredmail.conf +9 -0
  129. package/dist/server/engine/samples/fail2ban/jail.d/dovecot.local +8 -0
  130. package/dist/server/engine/samples/fail2ban/jail.d/nginx-http-auth.local +8 -0
  131. package/dist/server/engine/samples/fail2ban/jail.d/postfix-pregreet.local +9 -0
  132. package/dist/server/engine/samples/fail2ban/jail.d/postfix.local +8 -0
  133. package/dist/server/engine/samples/fail2ban/jail.d/roundcube.local +9 -0
  134. package/dist/server/engine/samples/fail2ban/jail.d/sogo.local +8 -0
  135. package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +8 -0
  136. package/dist/server/engine/samples/fail2ban/jail.local +6 -0
  137. package/dist/server/engine/samples/fail2ban/openbsd/rc +27 -0
  138. package/dist/server/engine/samples/fail2ban/sql/fail2ban.mysql +49 -0
  139. package/dist/server/engine/samples/fail2ban/sql/fail2ban.pgsql +48 -0
  140. package/dist/server/engine/samples/firewall/firewalld/services/ssh.xml +6 -0
  141. package/dist/server/engine/samples/firewall/firewalld/zones/iredmail.xml +15 -0
  142. package/dist/server/engine/samples/firewall/iptables/ip6tables.init.debian +362 -0
  143. package/dist/server/engine/samples/firewall/iptables/ip6tables.rules +53 -0
  144. package/dist/server/engine/samples/firewall/iptables/iptables.init.debian +363 -0
  145. package/dist/server/engine/samples/firewall/iptables/iptables.rules +54 -0
  146. package/dist/server/engine/samples/firewall/nftables.conf +58 -0
  147. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/dovecot.conf +3 -0
  148. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/iredapd.conf +1 -0
  149. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/mlmmjadmin.conf +1 -0
  150. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/php-fpm.conf +2 -0
  151. package/dist/server/engine/samples/freebsd/newsyslog.conf.d/slapd.conf +1 -0
  152. package/dist/server/engine/samples/freebsd/syslog.d/dovecot.conf +2 -0
  153. package/dist/server/engine/samples/freebsd/syslog.d/fail2ban.conf +2 -0
  154. package/dist/server/engine/samples/freebsd/syslog.d/iredapd.conf +2 -0
  155. package/dist/server/engine/samples/freebsd/syslog.d/mlmmjadmin.conf +2 -0
  156. package/dist/server/engine/samples/freebsd/syslog.d/php-fpm.conf +2 -0
  157. package/dist/server/engine/samples/freebsd/syslog.d/slapd.conf +2 -0
  158. package/dist/server/engine/samples/iredmail/iredmail.mysql +491 -0
  159. package/dist/server/engine/samples/iredmail/iredmail.pgsql +465 -0
  160. package/dist/server/engine/samples/iredmail/iredmail.schema +456 -0
  161. package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.3.pgsql +40 -0
  162. package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.4.pgsql +18 -0
  163. package/dist/server/engine/samples/logrotate/dovecot +13 -0
  164. package/dist/server/engine/samples/logrotate/iredapd +13 -0
  165. package/dist/server/engine/samples/logrotate/mlmmjadmin +13 -0
  166. package/dist/server/engine/samples/logrotate/openldap +19 -0
  167. package/dist/server/engine/samples/logrotate/php-fpm +13 -0
  168. package/dist/server/engine/samples/openbsd/ldapd.conf +58 -0
  169. package/dist/server/engine/samples/openbsd/pf.conf +28 -0
  170. package/dist/server/engine/samples/postfix/command_filter.pcre +3 -0
  171. package/dist/server/engine/samples/postfix/freebsd/mailer.conf +9 -0
  172. package/dist/server/engine/samples/postfix/helo_access.pcre +180 -0
  173. package/dist/server/engine/samples/postfix/ldap/catchall_maps.cf +11 -0
  174. package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_domain.cf +11 -0
  175. package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_user.cf +11 -0
  176. package/dist/server/engine/samples/postfix/ldap/relay_domains.cf +11 -0
  177. package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_domain.cf +11 -0
  178. package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_user.cf +11 -0
  179. package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_domain.cf +11 -0
  180. package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_user.cf +11 -0
  181. package/dist/server/engine/samples/postfix/ldap/sender_login_maps.cf +11 -0
  182. package/dist/server/engine/samples/postfix/ldap/transport_maps_domain.cf +11 -0
  183. package/dist/server/engine/samples/postfix/ldap/transport_maps_user.cf +11 -0
  184. package/dist/server/engine/samples/postfix/ldap/virtual_alias_maps.cf +11 -0
  185. package/dist/server/engine/samples/postfix/ldap/virtual_group_maps.cf +11 -0
  186. package/dist/server/engine/samples/postfix/ldap/virtual_group_members_maps.cf +11 -0
  187. package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_domains.cf +11 -0
  188. package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_maps.cf +12 -0
  189. package/dist/server/engine/samples/postfix/main.cf +282 -0
  190. package/dist/server/engine/samples/postfix/main.cf.amavisd +7 -0
  191. package/dist/server/engine/samples/postfix/main.cf.dovecot +8 -0
  192. package/dist/server/engine/samples/postfix/main.cf.ldap +39 -0
  193. package/dist/server/engine/samples/postfix/main.cf.mlmmj +5 -0
  194. package/dist/server/engine/samples/postfix/main.cf.mysql +39 -0
  195. package/dist/server/engine/samples/postfix/main.cf.pgsql +39 -0
  196. package/dist/server/engine/samples/postfix/main.cf.postscreen +30 -0
  197. package/dist/server/engine/samples/postfix/master.cf +87 -0
  198. package/dist/server/engine/samples/postfix/mysql/catchall_maps.cf +5 -0
  199. package/dist/server/engine/samples/postfix/mysql/domain_alias_catchall_maps.cf +5 -0
  200. package/dist/server/engine/samples/postfix/mysql/domain_alias_maps.cf +5 -0
  201. package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_domain.cf +5 -0
  202. package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_user.cf +5 -0
  203. package/dist/server/engine/samples/postfix/mysql/relay_domains.cf +5 -0
  204. package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_domain.cf +5 -0
  205. package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_user.cf +5 -0
  206. package/dist/server/engine/samples/postfix/mysql/sender_dependent_relayhost_maps.cf +6 -0
  207. package/dist/server/engine/samples/postfix/mysql/sender_login_maps.cf +5 -0
  208. package/dist/server/engine/samples/postfix/mysql/transport_maps_domain.cf +5 -0
  209. package/dist/server/engine/samples/postfix/mysql/transport_maps_maillist.cf +5 -0
  210. package/dist/server/engine/samples/postfix/mysql/transport_maps_user.cf +5 -0
  211. package/dist/server/engine/samples/postfix/mysql/virtual_alias_maps.cf +5 -0
  212. package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_domains.cf +5 -0
  213. package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_maps.cf +5 -0
  214. package/dist/server/engine/samples/postfix/pgsql/catchall_maps.cf +5 -0
  215. package/dist/server/engine/samples/postfix/pgsql/domain_alias_catchall_maps.cf +5 -0
  216. package/dist/server/engine/samples/postfix/pgsql/domain_alias_maps.cf +5 -0
  217. package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_domain.cf +5 -0
  218. package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_user.cf +5 -0
  219. package/dist/server/engine/samples/postfix/pgsql/relay_domains.cf +5 -0
  220. package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_domain.cf +5 -0
  221. package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_user.cf +5 -0
  222. package/dist/server/engine/samples/postfix/pgsql/sender_dependent_relayhost_maps.cf +6 -0
  223. package/dist/server/engine/samples/postfix/pgsql/sender_login_maps.cf +5 -0
  224. package/dist/server/engine/samples/postfix/pgsql/transport_maps_domain.cf +5 -0
  225. package/dist/server/engine/samples/postfix/pgsql/transport_maps_maillist.cf +5 -0
  226. package/dist/server/engine/samples/postfix/pgsql/transport_maps_user.cf +5 -0
  227. package/dist/server/engine/samples/postfix/pgsql/virtual_alias_maps.cf +5 -0
  228. package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_domains.cf +5 -0
  229. package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_maps.cf +5 -0
  230. package/dist/server/engine/samples/postfix/postscreen_access.cidr +6 -0
  231. package/dist/server/engine/samples/postgresql/sql/add_first_domain_and_user.sql +34 -0
  232. package/dist/server/engine/samples/postgresql/sql/grant_permissions.sql +14 -0
  233. package/dist/server/engine/samples/postgresql/sql/init_vmail_db.sql +15 -0
  234. package/dist/server/engine/samples/rsyslog.d/1-iredmail-dovecot.conf +23 -0
  235. package/dist/server/engine/samples/rsyslog.d/1-iredmail-fail2ban.conf +2 -0
  236. package/dist/server/engine/samples/rsyslog.d/1-iredmail-iredapd.conf +12 -0
  237. package/dist/server/engine/samples/rsyslog.d/1-iredmail-mlmmjadmin.conf +12 -0
  238. package/dist/server/engine/samples/rsyslog.d/1-iredmail-openldap.conf +2 -0
  239. package/dist/server/engine/samples/rsyslog.d/1-iredmail-phpfpm.conf +3 -0
  240. package/dist/server/engine/samples/spamassassin/local.cf +186 -0
  241. package/dist/server/engine/samples/spamassassin/razor.conf +1 -0
  242. package/dist/server/engine/samples/systemd/clamd.service.d/override.conf +2 -0
  243. package/dist/server/engine/samples/systemd/mariadb.service.d/override.conf +3 -0
  244. package/dist/server/engine/samples/systemd/slapd.service.d/override.conf +6 -0
  245. package/dist/server/engine/samples/yum/CentOS-Linux-AppStream.repo +6 -0
  246. package/dist/server/engine/samples/yum/CentOS-Linux-PowerTools.repo +6 -0
  247. package/dist/server/engine/samples/yum/CentOS-Stream-AppStream.repo +7 -0
  248. package/dist/server/engine/samples/yum/CentOS-Stream-PowerTools.repo +7 -0
  249. package/dist/server/engine/samples/yum/symas-openldap.repo +6 -0
  250. package/dist/server/engine/tools/add_enabled_service.py +60 -0
  251. package/dist/server/engine/tools/backup_mysql.sh +238 -0
  252. package/dist/server/engine/tools/backup_openldap.sh +230 -0
  253. package/dist/server/engine/tools/backup_pgsql.sh +217 -0
  254. package/dist/server/engine/tools/backup_sogo.sh +156 -0
  255. package/dist/server/engine/tools/create_mail_user_OpenLDAP.py +335 -0
  256. package/dist/server/engine/tools/create_mail_user_OpenLDAP.sh +287 -0
  257. package/dist/server/engine/tools/create_mail_user_SQL.sh +127 -0
  258. package/dist/server/engine/tools/enable_postscreen.sh +144 -0
  259. package/dist/server/engine/tools/fail2ban_unban_ip.sh +39 -0
  260. package/dist/server/engine/tools/find_sasl_login_ip.sh +20 -0
  261. package/dist/server/engine/tools/find_top_sasl_usernames.sh +28 -0
  262. package/dist/server/engine/tools/generate_ssl_keys.sh +85 -0
  263. package/dist/server/engine/tools/kill_high_cpu_sogo_process.sh +32 -0
  264. package/dist/server/engine/tools/ldap_assign_user_to_groups.py +151 -0
  265. package/dist/server/engine/tools/ldap_move_members_to_another_group.py +129 -0
  266. package/dist/server/engine/tools/migrate_sql_alias_table.py +177 -0
  267. package/dist/server/engine/update/0.9.8/amavisd.mysql +29 -0
  268. package/dist/server/engine/update/0.9.8/amavisd.pgsql +39 -0
  269. package/dist/server/engine/update/0.9.8/iredmail.mysql +53 -0
  270. package/dist/server/engine/update/0.9.8/iredmail.pgsql +61 -0
  271. package/dist/server/engine/update/0.9.9/iredmail.mysql +2 -0
  272. package/dist/server/engine/update/0.9.9/iredmail.pgsql +2 -0
  273. package/dist/server/engine/update/1.0/iredmail.mysql +2 -0
  274. package/dist/server/engine/update/1.0/iredmail.pgsql +2 -0
  275. package/dist/server/engine/update/1.2/last_login.mysql +16 -0
  276. package/dist/server/engine/update/1.4.0/iredmail.mysql +22 -0
  277. package/dist/server/engine/update/1.4.0/iredmail.pgsql +20 -0
  278. package/dist/server/engine/update/1.4.1/iredmail.mysql +3 -0
  279. package/dist/server/engine/update/1.4.1/iredmail.pgsql +3 -0
  280. package/dist/server/engine/update/1.4.1/sogo.mysql +11 -0
  281. package/dist/server/engine/update/1.4.1/sogo.pgsql +30 -0
  282. package/dist/server/engine/update/1.4.1/update-ldap.py +66 -0
  283. package/dist/server/engine/update/1.4.2/iredmail.mysql +9 -0
  284. package/dist/server/engine/update/1.4.2/iredmail.pgsql +9 -0
  285. package/dist/server/engine/update/1.4.2/sogo.pgsql +30 -0
  286. package/dist/server/engine/update/1.6.0/sogo_view.pgsql +27 -0
  287. package/dist/server/engine/update/1.6.3/iredmail.mysql +1 -0
  288. package/dist/server/engine/update/1.7.0/fail2ban.mysql +11 -0
  289. package/dist/server/engine/update/1.7.0/fail2ban.pgsql +12 -0
  290. package/dist/server/engine/update/1.7.1/amavisd.mysql +1 -0
  291. package/dist/server/engine/update/1.7.2/vmail.mysql +30 -0
  292. package/dist/server/engine/update/1.7.3/deleted_mailboxes.mysql +43 -0
  293. package/dist/server/engine/update/1.7.3/iredadmin.mysql +26 -0
  294. package/dist/server/engine/update/1.7.3/vmail.mysql +79 -0
  295. package/dist/server/engine/update/1.7.3/vmail.pgsql +9 -0
  296. package/dist/server/engine/update/README.md +1 -0
  297. package/dist/server/engine/update/ldap/README.md +40 -0
  298. package/dist/server/engine/update/ldap/update-ldap-dovecot-2.3.py +69 -0
  299. package/dist/server/index.js +55793 -53701
  300. package/dist/server/migrations/0071_gh_device_token.sql +3 -0
  301. package/dist/server/migrations/0072_gh_device_token_method.sql +1 -0
  302. package/dist/server/migrations/meta/_journal.json +14 -0
  303. package/dist/setup-2DMFIA74.js +20 -0
  304. package/dist/{signin-PVC7MBZJ.js → signin-ITOPKKUB.js} +11 -9
  305. package/dist/{sso-oidc-ZXY77BSL.js → sso-oidc-NON2PD7Y.js} +11 -9
  306. package/dist/{sts-LX5L4UU3.js → sts-HHFFSAY2.js} +10 -8
  307. package/package.json +1 -1
  308. package/dist/bare-CNSFDNSZ.js +0 -20
  309. package/dist/cloud-655YYCEW.js +0 -26
  310. package/dist/docker-UWQPDEGW.js +0 -14
  311. package/dist/docker-edge-executor-ZLAYE5FE.js +0 -11
  312. package/dist/edge-import-AFID2EN3.js +0 -41
  313. package/dist/nginx-M36GDYL7.js +0 -18
  314. package/dist/setup-ZXXB5S22.js +0 -16
@@ -1,8 +1,10 @@
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v bun || command -v node)" "$0" "$@"
1
3
  import { createRequire as __ospCreateRequire } from "node:module";
2
4
  const require = __ospCreateRequire(import.meta.url);
3
5
  import {
4
6
  sq
5
- } from "./chunk-Z6EKX6CX.js";
7
+ } from "./chunk-FPRHYBY2.js";
6
8
  import {
7
9
  ACME_CHALLENGE_LOCATION,
8
10
  ACME_HTTP01_PORT,
@@ -11,12 +13,12 @@ import {
11
13
  buildReloadCommand,
12
14
  detectOpenRestyPaths,
13
15
  luaSourceAvailable
14
- } from "./chunk-RZPDJC2J.js";
16
+ } from "./chunk-VFINNTUL.js";
15
17
  import {
16
18
  PROXY_GZIP_TYPES,
17
19
  safeErrorMessage,
18
20
  sanitizeProxySettings
19
- } from "./chunk-3LCRNQ2C.js";
21
+ } from "./chunk-6APULWSU.js";
20
22
 
21
23
  // ../../packages/adapters/src/infra/nginx.ts
22
24
  import {
@@ -25,12 +27,227 @@ import {
25
27
  rm as fsRm,
26
28
  mkdir as fsMkdir,
27
29
  readFile as fsReadFile,
28
- rename as fsRename
30
+ rename as fsRename,
31
+ stat as fsStat
29
32
  } from "fs/promises";
30
33
  import { execFile as cpExecFile } from "child_process";
31
- import { randomBytes, X509Certificate, createPrivateKey } from "crypto";
34
+ import { randomBytes } from "crypto";
32
35
  import { promisify } from "util";
33
36
  import { dirname, join } from "path";
37
+
38
+ // ../../packages/adapters/src/system/proxy/cert-material.ts
39
+ import { X509Certificate, createPrivateKey } from "crypto";
40
+
41
+ // ../../packages/adapters/src/system/edge-container-executor.ts
42
+ function containerCommand(container, command) {
43
+ return `docker exec ${sq(container)} sh -c ${sq(command)}`;
44
+ }
45
+ function resolveRef(ref) {
46
+ return typeof ref === "function" ? ref() : ref;
47
+ }
48
+ async function readMaybeInContainer(exec, path, container) {
49
+ const direct = await exec.readFile(path).catch(() => "");
50
+ if (direct.trim()) return direct;
51
+ const name = await resolveRef(container);
52
+ if (!name) return "";
53
+ return exec.exec(containerCommand(name, `cat ${sq(path)}`)).catch(() => "");
54
+ }
55
+ async function writeEdgeFileIn(inner, container, path, content) {
56
+ await inner.writeFile(path, content);
57
+ if (!container) return;
58
+ const visible = await inner.exec(containerCommand(container, `test -e ${sq(path)} && echo visible`)).catch(() => "");
59
+ if (visible.trim() === "visible") return;
60
+ const dir = path.replace(/\/[^/]*$/, "");
61
+ if (dir && dir !== path) {
62
+ await inner.exec(containerCommand(container, `mkdir -p ${sq(dir)}`)).catch(() => {
63
+ });
64
+ }
65
+ await inner.exec(`docker cp ${sq(path)} ${sq(`${container}:${path}`)}`).catch(() => {
66
+ });
67
+ }
68
+ function edgeContainerExecutor(inner, container, opts) {
69
+ const inContainer = (command) => containerCommand(container, command);
70
+ const overrides = {
71
+ exec: (command, execOpts) => inner.exec(inContainer(command), execOpts),
72
+ streamExec: (command, onLog) => inner.streamExec(inContainer(command), onLog),
73
+ // A rename is a FILE op, so it follows the files, never the commands. Stated
74
+ // here rather than left to the Proxy's passthrough: if the inner executor has no
75
+ // `rename`, the fallback must still be the INNER shell (the host) — routing it
76
+ // through this decorator's `exec` would put a `mv` inside the container, where
77
+ // the host path it was handed does not exist.
78
+ rename: async (from, to) => {
79
+ if (inner.rename) return inner.rename(from, to);
80
+ await inner.exec(`mv ${sq(from)} ${sq(to)}`);
81
+ }
82
+ };
83
+ if (opts?.files === "auto") {
84
+ Object.assign(overrides, {
85
+ readFile: (path) => readMaybeInContainer(inner, path, container),
86
+ writeFile: (path, content) => writeEdgeFileIn(inner, container, path, content),
87
+ exists: async (path) => {
88
+ if (await inner.exists(path).catch(() => false)) return true;
89
+ const { code } = await inner.streamExec(inContainer(`test -e ${sq(path)}`), () => {
90
+ });
91
+ return code === 0;
92
+ },
93
+ // The rename half of an atomic write must land in the same namespace as the
94
+ // write itself, so it follows readFile/writeFile — host first, container only
95
+ // when the host can't see the file.
96
+ rename: async (from, to) => {
97
+ if (await inner.exists(from).catch(() => false)) {
98
+ if (inner.rename) return inner.rename(from, to);
99
+ await inner.exec(`mv ${sq(from)} ${sq(to)}`);
100
+ return;
101
+ }
102
+ await inner.exec(inContainer(`mv ${sq(from)} ${sq(to)}`));
103
+ },
104
+ // mkdir/rm are idempotent and rare (route register/deregister), so both homes
105
+ // get them unconditionally rather than paying a probe to decide.
106
+ mkdir: async (path) => {
107
+ await inner.mkdir(path);
108
+ await inner.exec(inContainer(`mkdir -p ${sq(path)}`)).catch(() => {
109
+ });
110
+ },
111
+ rm: async (path) => {
112
+ await inner.rm(path);
113
+ await inner.exec(inContainer(`rm -rf ${sq(path)}`)).catch(() => {
114
+ });
115
+ }
116
+ });
117
+ }
118
+ return new Proxy(inner, {
119
+ get(target, prop) {
120
+ if (Object.prototype.hasOwnProperty.call(overrides, prop)) {
121
+ return overrides[prop];
122
+ }
123
+ const value = Reflect.get(target, prop, target);
124
+ return typeof value === "function" ? value.bind(target) : value;
125
+ }
126
+ });
127
+ }
128
+
129
+ // ../../packages/adapters/src/system/proxy/cert-material.ts
130
+ var ACME_ISSUERS = [
131
+ "let's encrypt",
132
+ "lets encrypt",
133
+ "letsencrypt",
134
+ "zerossl",
135
+ "google trust services",
136
+ "buypass",
137
+ "actalis",
138
+ "ssl.com"
139
+ ];
140
+ function issuerLabel(x509) {
141
+ const fields = /* @__PURE__ */ new Map();
142
+ for (const line of x509.issuer.split("\n")) {
143
+ const eq = line.indexOf("=");
144
+ if (eq > 0) fields.set(line.slice(0, eq).trim().toUpperCase(), line.slice(eq + 1).trim());
145
+ }
146
+ return fields.get("CN") ?? fields.get("O") ?? x509.issuer.replace(/\n/g, ", ");
147
+ }
148
+ function isAcmeIssuer(x509) {
149
+ const haystack = `${x509.issuer}`.toLowerCase();
150
+ return ACME_ISSUERS.some((ca) => haystack.includes(ca));
151
+ }
152
+ function certNames(x509) {
153
+ const names = [];
154
+ for (const entry of (x509.subjectAltName ?? "").split(",")) {
155
+ const [kind, ...rest] = entry.trim().split(":");
156
+ if (kind?.toUpperCase() === "DNS" && rest.length) names.push(rest.join(":").trim().toLowerCase());
157
+ }
158
+ if (names.length === 0) {
159
+ const cn = x509.subject.split("\n").find((l) => l.trim().toUpperCase().startsWith("CN="));
160
+ if (cn) names.push(cn.slice(cn.indexOf("=") + 1).trim().toLowerCase());
161
+ }
162
+ return names;
163
+ }
164
+ function coversHost(x509, host) {
165
+ const target = host.toLowerCase();
166
+ if (x509.checkHost(target)) return true;
167
+ return certNames(x509).some((name) => {
168
+ if (name === target) return true;
169
+ if (!name.startsWith("*.")) return false;
170
+ const suffix = name.slice(1);
171
+ return target.endsWith(suffix) && !target.slice(0, -suffix.length).includes(".");
172
+ });
173
+ }
174
+ function validateCertFor(host, pems, source) {
175
+ const certPem = pems.certPem ?? "";
176
+ const keyPem = pems.keyPem ?? "";
177
+ if (!certPem.trim() || !keyPem.trim()) {
178
+ return { cert: null, reason: `${source}: cert or key is empty` };
179
+ }
180
+ let x509;
181
+ try {
182
+ x509 = new X509Certificate(certPem);
183
+ } catch (err) {
184
+ return { cert: null, reason: `${source}: unreadable certificate (${safeErrorMessage(err)})` };
185
+ }
186
+ try {
187
+ if (!x509.checkPrivateKey(createPrivateKey(keyPem))) {
188
+ return { cert: null, reason: `${source}: private key does not match the certificate` };
189
+ }
190
+ } catch (err) {
191
+ return { cert: null, reason: `${source}: unreadable private key (${safeErrorMessage(err)})` };
192
+ }
193
+ if (!coversHost(x509, host)) {
194
+ return {
195
+ cert: null,
196
+ reason: `${source}: certificate covers ${certNames(x509).join(", ") || "no DNS names"} \u2014 not ${host}`
197
+ };
198
+ }
199
+ const notAfter = new Date(x509.validTo);
200
+ if (!Number.isFinite(notAfter.getTime())) {
201
+ return { cert: null, reason: `${source}: certificate has an unreadable expiry` };
202
+ }
203
+ if (notAfter.getTime() <= Date.now()) {
204
+ return { cert: null, reason: `${source}: certificate expired ${notAfter.toISOString()}` };
205
+ }
206
+ return {
207
+ cert: {
208
+ certPem,
209
+ keyPem,
210
+ source,
211
+ expiresAt: notAfter.toISOString(),
212
+ issuer: issuerLabel(x509),
213
+ covers: certNames(x509),
214
+ renewable: isAcmeIssuer(x509)
215
+ }
216
+ };
217
+ }
218
+ function isSafeCertPath(p) {
219
+ return /^\/[A-Za-z0-9._/-]+$/.test(p) && !p.includes("..");
220
+ }
221
+ async function readDeclaredPair(exec, certPath, keyPath, container) {
222
+ if (!isSafeCertPath(certPath) || !isSafeCertPath(keyPath)) return null;
223
+ const certPem = await readMaybeInContainer(exec, certPath, container);
224
+ const keyPem = await readMaybeInContainer(exec, keyPath, container);
225
+ return certPem && keyPem ? { certPem, keyPem } : null;
226
+ }
227
+
228
+ // ../../packages/adapters/src/system/output-exists.ts
229
+ function buildCommand(servedPath) {
230
+ const p = servedPath.replace(/'/g, "'\\''");
231
+ return [
232
+ `if [ -e '${p}' ]; then echo FOUND; fi`,
233
+ `if [ -f '${p}' ] || [ -f '${p}/index.html' ]; then echo INDEX; fi`,
234
+ "true"
235
+ ].join("; ");
236
+ }
237
+ async function probeStaticOutput(executor, servedPath) {
238
+ try {
239
+ const out = await executor.exec(buildCommand(servedPath), { timeout: 5e3 });
240
+ return {
241
+ found: /(^|\n)FOUND(\r?\n|$)/.test(out),
242
+ hasIndex: /(^|\n)INDEX(\r?\n|$)/.test(out),
243
+ checked: true
244
+ };
245
+ } catch {
246
+ return { found: false, hasIndex: false, checked: false };
247
+ }
248
+ }
249
+
250
+ // ../../packages/adapters/src/infra/nginx.ts
34
251
  var PROXY_HEADERS = `proxy_set_header Host $host;
35
252
  proxy_set_header X-Real-IP $remote_addr;
36
253
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -118,11 +335,19 @@ function assertValidUpstream(targetUrl) {
118
335
  throw new Error(`Invalid proxy target (must be http/https URL): ${targetUrl}`);
119
336
  }
120
337
  }
121
- function assertValidStaticRoot(root) {
338
+ var MANAGED_STATIC_BASE = "/opt/openship";
339
+ function assertValidStaticRoot(root, opts) {
122
340
  assertNoNginxInjection(root, "static root");
123
341
  if (!root.startsWith("/") || root.includes("..")) {
124
342
  throw new Error(`Invalid static root (must be an absolute path, no traversal): ${root}`);
125
343
  }
344
+ if (opts?.adopted) return;
345
+ const confined = root === MANAGED_STATIC_BASE || root.startsWith(`${MANAGED_STATIC_BASE}/`);
346
+ if (!confined) {
347
+ throw new Error(
348
+ `Refusing to serve static root outside ${MANAGED_STATIC_BASE}: ${root}. Openship-managed routes serve only their own build output; pass staticRootAdopted when importing a root an existing proxy already serves.`
349
+ );
350
+ }
126
351
  }
127
352
  var execFileAsync = promisify(cpExecFile);
128
353
  function summarizeCertbotFailure(output, domain) {
@@ -151,12 +376,14 @@ var NginxProvider = class {
151
376
  certDir;
152
377
  executor;
153
378
  reloadCommand;
379
+ pinPaths;
154
380
  constructor(opts) {
155
381
  this.sitesDir = opts.paths.sitesDir;
156
382
  this.acmeEmail = opts.acmeEmail;
157
383
  this.certDir = opts.certDir ?? DEFAULT_CERT_DIR;
158
384
  this.executor = opts.executor ?? null;
159
385
  this.reloadCommand = buildReloadCommand(opts.paths);
386
+ this.pinPaths = opts.pinPaths ?? false;
160
387
  }
161
388
  // ── File operation helpers (dual-path: local or remote) ──────────────
162
389
  async _writeFile(path, content) {
@@ -164,7 +391,11 @@ var NginxProvider = class {
164
391
  if (this.executor) {
165
392
  try {
166
393
  await this.executor.writeFile(tmpPath, content);
167
- await this.executor.exec(`mv ${sq(tmpPath)} ${sq(path)}`);
394
+ if (this.executor.rename) {
395
+ await this.executor.rename(tmpPath, path);
396
+ } else {
397
+ await this.executor.exec(`mv ${sq(tmpPath)} ${sq(path)}`);
398
+ }
168
399
  } catch (err) {
169
400
  await this.executor.rm(tmpPath).catch(() => void 0);
170
401
  throw err;
@@ -214,6 +445,37 @@ var NginxProvider = class {
214
445
  await fsMkdir(path, { recursive: true });
215
446
  }
216
447
  }
448
+ /**
449
+ * Put `fullchain.pem` + `privkey.pem` in `dir` as ONE atomic step.
450
+ *
451
+ * Both files are written into a temp sibling dir, then the pair is moved into
452
+ * place together. A cert and its key are only valid as a set — writing them
453
+ * individually means a crash, a full disk, or a dropped SSH connection between
454
+ * the two leaves the new cert next to the previous key, and `openresty -t` then
455
+ * fails for the WHOLE edge (every domain, not just this one) until someone
456
+ * notices. `mv -T`/rename of the staged dir also means a concurrent reload sees
457
+ * either the old pair or the new one, never a half-written PEM.
458
+ */
459
+ async stageCertDir(dir, cert) {
460
+ const staging = `${dir}.staging-${process.pid}-${randomBytes(4).toString("hex")}`;
461
+ try {
462
+ await this._mkdir(staging);
463
+ await this._writeFile(join(staging, "fullchain.pem"), cert.certPem);
464
+ await this._writeFile(join(staging, "privkey.pem"), cert.keyPem);
465
+ if (this.executor) {
466
+ await this._mkdir(dir);
467
+ await this.executor.exec(
468
+ `mv -f ${sq(join(staging, "fullchain.pem"))} ${sq(join(staging, "privkey.pem"))} ${sq(dir)}/`
469
+ );
470
+ } else {
471
+ await fsMkdir(dir, { recursive: true });
472
+ await fsRename(join(staging, "fullchain.pem"), join(dir, "fullchain.pem"));
473
+ await fsRename(join(staging, "privkey.pem"), join(dir, "privkey.pem"));
474
+ }
475
+ } finally {
476
+ await this._rm(staging).catch(() => void 0);
477
+ }
478
+ }
217
479
  async _exec(command, args = []) {
218
480
  if (this.executor) {
219
481
  const full = args.length ? `${command} ${args.map(sq).join(" ")}` : command;
@@ -228,6 +490,28 @@ var NginxProvider = class {
228
490
  throw new Error(detail || e.message || String(err));
229
491
  }
230
492
  }
493
+ /**
494
+ * Does this static `root` resolve where OpenResty looks? (RoutingProvider)
495
+ *
496
+ * Runs the probe through `this.executor` — the SAME handle vhost writes and
497
+ * `openresty -s reload` go through — so it answers from the edge's own vantage
498
+ * point. That is the whole value: for a containerized edge the executor's `exec`
499
+ * lands INSIDE the container (DockerEdgeExecutor / edgeContainerExecutor), so a
500
+ * `root` that exists on the host but was never bind-mounted reads as missing
501
+ * here, which is exactly the 404 a host-side probe cannot see. With no executor
502
+ * the edge is bare-local and shares this process's filesystem, so node:fs is
503
+ * already the right vantage point.
504
+ *
505
+ * Advisory: never throws.
506
+ */
507
+ async probeStaticRoot(servedPath) {
508
+ if (this.executor) return probeStaticOutput(this.executor, servedPath);
509
+ const found = await this._exists(servedPath);
510
+ if (!found) return { found: false, hasIndex: false, checked: true };
511
+ const isFile = await fsStat(servedPath).then((s) => s.isFile()).catch(() => false);
512
+ const hasIndex = isFile || await this._exists(join(servedPath, "index.html"));
513
+ return { found: true, hasIndex, checked: true };
514
+ }
231
515
  /**
232
516
  * Run certbot, optionally STREAMING its output line-by-line to `onLog` (the
233
517
  * live verify modal). Falls back to buffered `_exec` when no callback / no
@@ -281,7 +565,7 @@ var NginxProvider = class {
281
565
  const slug = this.domainSlug(route.domain);
282
566
  const configPath = join(this.sitesDir, `${slug}.conf`);
283
567
  if ("staticRoot" in route && route.staticRoot) {
284
- assertValidStaticRoot(route.staticRoot);
568
+ assertValidStaticRoot(route.staticRoot, { adopted: route.staticRootAdopted });
285
569
  } else {
286
570
  assertValidUpstream(route.targetUrl);
287
571
  }
@@ -459,7 +743,12 @@ ${webhookLocation}${extraLocations}
459
743
  }
460
744
  const rootMatch = existing.match(/root\s+([^;]+);/);
461
745
  if (rootMatch) {
462
- await this.registerRoute({ domain, staticRoot: rootMatch[1], tls: true });
746
+ await this.registerRoute({
747
+ domain,
748
+ staticRoot: rootMatch[1],
749
+ staticRootAdopted: true,
750
+ tls: true
751
+ });
463
752
  return this.ensureIssued(domain, certonlyOut);
464
753
  }
465
754
  } catch {
@@ -492,32 +781,41 @@ ${tail}` : "")
492
781
  if (!await this.certsExist(domain)) {
493
782
  return this.provisionCert(domain);
494
783
  }
784
+ if (!await this.hasCertbotLineage(domain)) {
785
+ return this.provisionCert(domain, { force: true });
786
+ }
495
787
  await this._exec("certbot", ["renew", "--cert-name", domain, "--non-interactive"]);
496
788
  await this.reload();
497
789
  return this.readCertInfo(domain);
498
790
  }
499
791
  /**
500
- * Install an operator-supplied certificate (no ACME). Validates the PEM pair,
501
- * writes it to the same on-disk path certbot uses, then re-registers the vhost
502
- * with TLS mirroring provisionCert's tail so composite routes are preserved.
792
+ * Does certbot track a renewal lineage for this domain? `/etc/letsencrypt/renewal/
793
+ * <domain>.conf` is the per-lineage record certbot writes on first issuance and
794
+ * reads on every `renew`, so its presence is the authoritative answer — the cert
795
+ * FILES existing is not (they can be adopted, or copied in by hand).
796
+ */
797
+ async hasCertbotLineage(domain) {
798
+ const renewalPath = join(dirname(this.certDir), "renewal", `${domain}.conf`);
799
+ return this._exists(renewalPath);
800
+ }
801
+ /**
802
+ * Install an operator-supplied or adopted certificate (no ACME). Validates the
803
+ * PEM pair against the domain, writes it to the same on-disk path certbot uses,
804
+ * then re-registers the vhost with TLS — mirroring provisionCert's tail so
805
+ * composite routes are preserved.
806
+ *
807
+ * The validation is deliberately here, at the last gate before disk, and not
808
+ * only in the callers: every path that puts a cert on this box (operator upload,
809
+ * migration carry, cert reuse) funnels through this method, so a check here can't
810
+ * be bypassed by a new caller that forgets it.
503
811
  */
504
812
  async installCert(domain, cert) {
505
813
  assertValidDomain(domain);
506
- let expiresAt;
507
- try {
508
- const x509 = new X509Certificate(cert.certPem);
509
- const key = createPrivateKey(cert.keyPem);
510
- if (!x509.checkPrivateKey(key)) {
511
- throw new Error("private key does not match certificate");
512
- }
513
- expiresAt = new Date(x509.validTo).toISOString();
514
- } catch (err) {
515
- throw new Error(`Invalid certificate: ${safeErrorMessage(err)}`);
516
- }
814
+ const candidate = validateCertFor(domain, cert, "supplied certificate");
815
+ if (!candidate.cert) throw new Error(`Invalid certificate: ${candidate.reason}`);
816
+ const { expiresAt } = candidate.cert;
517
817
  const dir = join(this.certDir, domain);
518
- await this._mkdir(dir);
519
- await this._writeFile(join(dir, "fullchain.pem"), cert.certPem);
520
- await this._writeFile(join(dir, "privkey.pem"), cert.keyPem);
818
+ await this.stageCertDir(dir, cert);
521
819
  const slug = this.domainSlug(domain);
522
820
  try {
523
821
  const state = await this._readFile(this.routeStatePath(slug));
@@ -525,7 +823,13 @@ ${tail}` : "")
525
823
  await this.registerRoute({ ...saved, domain, tls: true });
526
824
  } catch {
527
825
  }
528
- return { domain, expiresAt, issuer: "manual", verified: true, reason: "issued" };
826
+ return {
827
+ domain,
828
+ expiresAt,
829
+ issuer: candidate.cert.renewable ? candidate.cert.issuer : "manual",
830
+ verified: true,
831
+ reason: "issued"
832
+ };
529
833
  }
530
834
  // ── Helpers ──────────────────────────────────────────────────────────
531
835
  domainSlug(domain) {
@@ -540,11 +844,13 @@ ${tail}` : "")
540
844
  */
541
845
  async reload() {
542
846
  if (this.executor) {
543
- try {
544
- const freshPaths = await detectOpenRestyPaths(this.executor);
545
- this.sitesDir = freshPaths.sitesDir;
546
- this.reloadCommand = buildReloadCommand(freshPaths);
547
- } catch {
847
+ if (!this.pinPaths) {
848
+ try {
849
+ const freshPaths = await detectOpenRestyPaths(this.executor);
850
+ this.sitesDir = freshPaths.sitesDir;
851
+ this.reloadCommand = buildReloadCommand(freshPaths);
852
+ } catch {
853
+ }
548
854
  }
549
855
  await this.executor.exec(this.reloadCommand);
550
856
  return;
@@ -574,20 +880,25 @@ ${tail}` : "")
574
880
  if (!await this.certsExist(domain)) {
575
881
  return { domain, expiresAt: "", issuer: "certbot", verified: false, reason: "missing" };
576
882
  }
883
+ let certPem;
884
+ let keyPem;
577
885
  try {
578
- const certPath = join(this.certDir, domain, "fullchain.pem");
579
- const pem = await this._readFile(certPath);
580
- const { X509Certificate: X509Certificate2 } = await import("crypto");
581
- const cert = new X509Certificate2(pem);
582
- return {
583
- domain,
584
- expiresAt: new Date(cert.validTo).toISOString(),
585
- issuer: "certbot",
586
- verified: true
587
- };
886
+ certPem = await this._readFile(join(this.certDir, domain, "fullchain.pem"));
887
+ keyPem = await this._readFile(join(this.certDir, domain, "privkey.pem"));
588
888
  } catch {
589
889
  return { domain, expiresAt: "", issuer: "certbot", verified: false, reason: "read_error" };
590
890
  }
891
+ const candidate = validateCertFor(domain, { certPem, keyPem }, "on-disk certificate");
892
+ if (!candidate.cert) {
893
+ console.warn(`[SSL] unusable cert on disk for ${domain}: ${candidate.reason}`);
894
+ return { domain, expiresAt: "", issuer: "certbot", verified: false, reason: "invalid" };
895
+ }
896
+ return {
897
+ domain,
898
+ expiresAt: candidate.cert.expiresAt,
899
+ issuer: candidate.cert.issuer || "certbot",
900
+ verified: true
901
+ };
591
902
  }
592
903
  /**
593
904
  * Read-only check that the Let's Encrypt cert is actually present + valid on
@@ -741,8 +1052,15 @@ ${geoEntries.join("\n")}
741
1052
  };
742
1053
 
743
1054
  export {
1055
+ containerCommand,
1056
+ readMaybeInContainer,
1057
+ edgeContainerExecutor,
1058
+ validateCertFor,
1059
+ isSafeCertPath,
1060
+ readDeclaredPair,
744
1061
  renderProxyOptions,
745
1062
  renderGeoEntries,
1063
+ MANAGED_STATIC_BASE,
746
1064
  summarizeCertbotFailure,
747
1065
  NginxProvider
748
1066
  };
@@ -0,0 +1,131 @@
1
+ #!/usr/bin/env sh
2
+ ":" //# ; exec "$(command -v bun || command -v node)" "$0" "$@"
3
+ import { createRequire as __ospCreateRequire } from "node:module";
4
+ const require = __ospCreateRequire(import.meta.url);
5
+
6
+ // ../../packages/adapters/src/system/port-listen.ts
7
+ var PROC_NET_TCP_CMD = "cat /proc/net/tcp 2>/dev/null; cat /proc/net/tcp6 2>/dev/null; true";
8
+ var TCP_LISTEN = "0A";
9
+ function parseListeningPorts(procText) {
10
+ const ports = /* @__PURE__ */ new Set();
11
+ for (const rawLine of procText.split("\n")) {
12
+ const line = rawLine.trim();
13
+ if (!line) continue;
14
+ const fields = line.split(/\s+/);
15
+ if (fields.length < 4) continue;
16
+ if (fields[3] !== TCP_LISTEN) continue;
17
+ const local = fields[1];
18
+ const colon = local.lastIndexOf(":");
19
+ if (colon === -1) continue;
20
+ const hexPort = local.slice(colon + 1);
21
+ if (!/^[0-9A-Fa-f]{1,4}$/.test(hexPort)) continue;
22
+ const port = parseInt(hexPort, 16);
23
+ if (port > 0) ports.add(port);
24
+ }
25
+ return ports;
26
+ }
27
+ async function probePortListeningOnce(executor, port) {
28
+ try {
29
+ const out = await executor.exec(PROC_NET_TCP_CMD, { timeout: 5e3 });
30
+ return parseListeningPorts(out).has(port);
31
+ } catch {
32
+ return null;
33
+ }
34
+ }
35
+ function delay(ms) {
36
+ return new Promise((resolve) => setTimeout(resolve, ms));
37
+ }
38
+ async function waitForPortListening(executor, port, opts = {}) {
39
+ const timeoutMs = opts.timeoutMs ?? 45e3;
40
+ const intervalMs = opts.intervalMs ?? 1e3;
41
+ const deadline = Date.now() + timeoutMs;
42
+ let anyConclusive = false;
43
+ try {
44
+ for (; ; ) {
45
+ const result = await probePortListeningOnce(executor, port);
46
+ if (result === true) return { listening: true, checked: true };
47
+ if (result === false) anyConclusive = true;
48
+ const remaining = deadline - Date.now();
49
+ if (remaining <= 0) break;
50
+ await delay(Math.min(intervalMs, remaining));
51
+ }
52
+ } catch {
53
+ return { listening: false, checked: false };
54
+ }
55
+ return anyConclusive ? { listening: false, checked: true } : { listening: false, checked: false };
56
+ }
57
+
58
+ // ../../packages/adapters/src/runtime/port-conflict.ts
59
+ async function tryExec(executor, command) {
60
+ try {
61
+ return await executor.exec(command);
62
+ } catch {
63
+ return null;
64
+ }
65
+ }
66
+ async function resolveSystemdUnit(executor, pid) {
67
+ const cgroup = await tryExec(executor, `cat /proc/${pid}/cgroup 2>/dev/null || true`);
68
+ const unitMatch = cgroup?.match(/(?:^|\/)([^/\n]+\.service)(?:$|\n|\/)/m) ?? cgroup?.match(/(?:^|\/)([^/\n]+\.service)(?:$|\n|\/)/m);
69
+ const systemdUnit = unitMatch?.[1]?.trim();
70
+ if (!systemdUnit || !/^[A-Za-z0-9@._:\\-]+\.service$/.test(systemdUnit)) {
71
+ return {};
72
+ }
73
+ const description = await tryExec(
74
+ executor,
75
+ `systemctl show ${systemdUnit} --property=Description --value 2>/dev/null || true`
76
+ );
77
+ const managedMatch = systemdUnit.match(/^openship-(.+)\.service$/);
78
+ return {
79
+ systemdUnit,
80
+ systemdDescription: description?.trim() || void 0,
81
+ deploymentId: managedMatch?.[1],
82
+ isManagedDeployment: Boolean(managedMatch)
83
+ };
84
+ }
85
+ async function resolveListener(executor, port) {
86
+ const out = await tryExec(
87
+ executor,
88
+ `ss -tlnp sport = :${port} 2>/dev/null | grep LISTEN || lsof -ti tcp:${port} -sTCP:LISTEN 2>/dev/null || true`
89
+ );
90
+ if (out) {
91
+ const ssMatch = out.match(/pid=(\d+)/);
92
+ const lsofMatch = !ssMatch ? out.match(/^\s*(\d+)\s*$/m) : null;
93
+ const pid = ssMatch ? parseInt(ssMatch[1], 10) : lsofMatch ? parseInt(lsofMatch[1], 10) : null;
94
+ if (pid) return { pid, occupied: true };
95
+ }
96
+ const listening = await probePortListeningOnce(executor, port);
97
+ return { pid: null, occupied: listening === true };
98
+ }
99
+ async function probeListeningPort(executor, port) {
100
+ try {
101
+ const { pid, occupied } = await resolveListener(executor, port);
102
+ if (!occupied) return null;
103
+ if (pid === null) return { pid: null, command: "unknown listener" };
104
+ return await describeProcess(executor, pid);
105
+ } catch {
106
+ return null;
107
+ }
108
+ }
109
+ async function describeProcess(executor, pid) {
110
+ let command = `PID ${pid}`;
111
+ let rawCommand;
112
+ const args = await tryExec(executor, `ps -p ${pid} -o args= 2>/dev/null || true`);
113
+ if (args?.trim()) {
114
+ rawCommand = args.trim();
115
+ command = `${rawCommand} (PID ${pid})`;
116
+ } else {
117
+ const cmd = await tryExec(executor, `ps -p ${pid} -o comm= 2>/dev/null || true`);
118
+ if (cmd?.trim()) {
119
+ rawCommand = cmd.trim();
120
+ command = `${rawCommand} (PID ${pid})`;
121
+ }
122
+ }
123
+ const systemd = await resolveSystemdUnit(executor, pid);
124
+ return { pid, command, rawCommand, ...systemd };
125
+ }
126
+
127
+ export {
128
+ waitForPortListening,
129
+ probeListeningPort,
130
+ describeProcess
131
+ };