openship 0.4.4 → 0.4.6

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 (318) hide show
  1. package/README.md +2 -24
  2. package/dist/bare-2HYDXQEH.js +23 -0
  3. package/dist/{bowser-MFQA6OS4.js → bowser-O3AEA6AN.js} +5 -1
  4. package/dist/{chunk-KD7VO4LG.js → chunk-2ZLR7ULL.js} +255 -65
  5. package/dist/{chunk-YHXCHW7Q.js → chunk-3JQTL4R4.js} +236 -99
  6. package/dist/{chunk-E77LJWJK.js → chunk-4TJC4JZQ.js} +20 -9
  7. package/dist/{chunk-YL67EWYZ.js → chunk-4Z5QO7N6.js} +6 -2
  8. package/dist/{chunk-WJPCBU7Z.js → chunk-5FNIWBFI.js} +10 -6
  9. package/dist/{chunk-Y37KHU7L.js → chunk-5MZ624VX.js} +5 -0
  10. package/dist/{chunk-QKFRZNKN.js → chunk-6APIPIJY.js} +8 -4
  11. package/dist/{chunk-JZLNHGLW.js → chunk-6APULWSU.js} +153 -133
  12. package/dist/{chunk-F4DFVG6Q.js → chunk-6B5YCFFV.js} +8 -4
  13. package/dist/{chunk-XVSAUO2I.js → chunk-AVIB7CT7.js} +5 -1
  14. package/dist/{chunk-TEYJYSMT.js → chunk-C5OHTYEF.js} +12 -6
  15. package/dist/{chunk-7UOB5GGQ.js → chunk-DYPH4HCI.js} +5 -0
  16. package/dist/{chunk-XIFRMKYT.js → chunk-EI575GHN.js} +6 -2
  17. package/dist/{chunk-4CQWT2WL.js → chunk-FPRHYBY2.js} +5 -0
  18. package/dist/chunk-FQGJZ2HS.js +248 -0
  19. package/dist/{chunk-JR2OCMJA.js → chunk-FRNAR33O.js} +6 -2
  20. package/dist/{chunk-JUJBBF5U.js → chunk-FXTBHTWY.js} +362 -42
  21. package/dist/chunk-ILFETZOM.js +131 -0
  22. package/dist/{chunk-5ZCKQ2MI.js → chunk-IXCHURVK.js} +7 -3
  23. package/dist/{chunk-6ERIMTBC.js → chunk-JYGD5WSN.js} +5 -0
  24. package/dist/{chunk-4VNS5WPM.js → chunk-KI2EY3WB.js} +4 -0
  25. package/dist/{chunk-3U4SPZD5.js → chunk-L2VOGB2X.js} +7 -104
  26. package/dist/{chunk-NB2VEIWG.js → chunk-MZ2WEHHY.js} +7 -3
  27. package/dist/{chunk-BUBSSKO6.js → chunk-O6ZZF52V.js} +5 -1
  28. package/dist/{chunk-LBFH7THC.js → chunk-OWED2GRM.js} +5 -1
  29. package/dist/{chunk-4MG3BVHE.js → chunk-OXWDY22G.js} +6 -2
  30. package/dist/{chunk-4FABQGQP.js → chunk-Q2CIRAOK.js} +5 -0
  31. package/dist/{chunk-4LEL3H2Z.js → chunk-RPHRPFEH.js} +5 -0
  32. package/dist/{chunk-46YJFP4S.js → chunk-SQ77DZOI.js} +1728 -1484
  33. package/dist/{chunk-S6DSRLNE.js → chunk-TGIINPVM.js} +15 -2
  34. package/dist/{chunk-U2DPZSXA.js → chunk-TYVME3XD.js} +9 -1
  35. package/dist/{chunk-HFYDYK4D.js → chunk-V6AN6XLZ.js} +18 -6
  36. package/dist/{chunk-ZKJOFWBJ.js → chunk-VDMQJHPI.js} +38 -15
  37. package/dist/{chunk-E6W2JMMU.js → chunk-VFINNTUL.js} +22 -1
  38. package/dist/{chunk-OJE6YWPQ.js → chunk-VWPEKOH6.js} +6 -2
  39. package/dist/{chunk-5N7VUGL6.js → chunk-W73X43R7.js} +6 -2
  40. package/dist/cloud-7QYOGF7F.js +11 -0
  41. package/dist/cloud-ZSKOS4GY.js +30 -0
  42. package/dist/detect-SH4FHFBI.js +36 -0
  43. package/dist/{dist-L3MPOWCA.js → dist-IQJ6H6UZ.js} +6 -2
  44. package/dist/{dist-es-KE56MWYO.js → dist-es-5DY7K32L.js} +9 -5
  45. package/dist/dist-es-JSTDUS7Q.js +26 -0
  46. package/dist/{dist-es-GTAMF674.js → dist-es-OSNJBLXB.js} +8 -4
  47. package/dist/{dist-es-NRZIQY5B.js → dist-es-RYHIPOTN.js} +17 -13
  48. package/dist/{dist-es-XQCOTGC7.js → dist-es-VNKHQVIB.js} +7 -3
  49. package/dist/{dist-es-WZCR7DHQ.js → dist-es-WV4AMOII.js} +11 -7
  50. package/dist/{dist-es-ZW3HFQUM.js → dist-es-X3B5QYKB.js} +9 -5
  51. package/dist/docker-U7AF5PTA.js +16 -0
  52. package/dist/docker-edge-executor-VHW3JPJS.js +13 -0
  53. package/dist/edge-import-V5CBA47C.js +45 -0
  54. package/dist/ensure-container-edge-CMYROUSI.js +33 -0
  55. package/dist/{event-streams-7LGDL56D.js → event-streams-EHRAKL75.js} +7 -3
  56. package/dist/executor-YPT6RH6O.js +30 -0
  57. package/dist/index.d.ts +2 -1
  58. package/dist/index.js +258 -245
  59. package/dist/{loadSso-N273C5I7.js → loadSso-46FQRIZS.js} +13 -9
  60. package/dist/local-executor-V7U5VOZW.js +12 -0
  61. package/dist/nginx-2GTRUIDE.js +24 -0
  62. package/dist/noop-FMMFG2EZ.js +11 -0
  63. package/dist/{openresty-lua-HAO4EUDY.js → openresty-lua-4WLGDSER.js} +13 -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 +55574 -53675
  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-HQIZLUOJ.js +20 -0
  304. package/dist/{signin-DR462ERO.js → signin-ITOPKKUB.js} +13 -9
  305. package/dist/{sso-oidc-VBZLJUK5.js → sso-oidc-NON2PD7Y.js} +13 -9
  306. package/dist/{sts-EJLZ4Z77.js → sts-HHFFSAY2.js} +12 -8
  307. package/package.json +1 -1
  308. package/dist/bare-ITM45QBX.js +0 -18
  309. package/dist/cloud-JMPHO3CW.js +0 -7
  310. package/dist/cloud-W5G6YXAY.js +0 -24
  311. package/dist/dist-es-4C322O5D.js +0 -22
  312. package/dist/docker-3FVJMFC3.js +0 -12
  313. package/dist/docker-edge-executor-ZIWAZWPL.js +0 -9
  314. package/dist/edge-import-Y34YL6JK.js +0 -39
  315. package/dist/executor-J7BHW5HF.js +0 -26
  316. package/dist/nginx-YZWKYKQU.js +0 -16
  317. package/dist/noop-RRNCIUI5.js +0 -7
  318. package/dist/setup-5X3WEEHA.js +0 -14
@@ -1,6 +1,10 @@
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);
1
5
  import {
2
6
  sq
3
- } from "./chunk-4CQWT2WL.js";
7
+ } from "./chunk-FPRHYBY2.js";
4
8
  import {
5
9
  ACME_CHALLENGE_LOCATION,
6
10
  ACME_HTTP01_PORT,
@@ -9,12 +13,12 @@ import {
9
13
  buildReloadCommand,
10
14
  detectOpenRestyPaths,
11
15
  luaSourceAvailable
12
- } from "./chunk-E6W2JMMU.js";
16
+ } from "./chunk-VFINNTUL.js";
13
17
  import {
14
18
  PROXY_GZIP_TYPES,
15
19
  safeErrorMessage,
16
20
  sanitizeProxySettings
17
- } from "./chunk-JZLNHGLW.js";
21
+ } from "./chunk-6APULWSU.js";
18
22
 
19
23
  // ../../packages/adapters/src/infra/nginx.ts
20
24
  import {
@@ -23,12 +27,227 @@ import {
23
27
  rm as fsRm,
24
28
  mkdir as fsMkdir,
25
29
  readFile as fsReadFile,
26
- rename as fsRename
30
+ rename as fsRename,
31
+ stat as fsStat
27
32
  } from "fs/promises";
28
33
  import { execFile as cpExecFile } from "child_process";
29
- import { randomBytes, X509Certificate, createPrivateKey } from "crypto";
34
+ import { randomBytes } from "crypto";
30
35
  import { promisify } from "util";
31
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
32
251
  var PROXY_HEADERS = `proxy_set_header Host $host;
33
252
  proxy_set_header X-Real-IP $remote_addr;
34
253
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
@@ -116,11 +335,19 @@ function assertValidUpstream(targetUrl) {
116
335
  throw new Error(`Invalid proxy target (must be http/https URL): ${targetUrl}`);
117
336
  }
118
337
  }
119
- function assertValidStaticRoot(root) {
338
+ var MANAGED_STATIC_BASE = "/opt/openship";
339
+ function assertValidStaticRoot(root, opts) {
120
340
  assertNoNginxInjection(root, "static root");
121
341
  if (!root.startsWith("/") || root.includes("..")) {
122
342
  throw new Error(`Invalid static root (must be an absolute path, no traversal): ${root}`);
123
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
+ }
124
351
  }
125
352
  var execFileAsync = promisify(cpExecFile);
126
353
  function summarizeCertbotFailure(output, domain) {
@@ -149,12 +376,14 @@ var NginxProvider = class {
149
376
  certDir;
150
377
  executor;
151
378
  reloadCommand;
379
+ pinPaths;
152
380
  constructor(opts) {
153
381
  this.sitesDir = opts.paths.sitesDir;
154
382
  this.acmeEmail = opts.acmeEmail;
155
383
  this.certDir = opts.certDir ?? DEFAULT_CERT_DIR;
156
384
  this.executor = opts.executor ?? null;
157
385
  this.reloadCommand = buildReloadCommand(opts.paths);
386
+ this.pinPaths = opts.pinPaths ?? false;
158
387
  }
159
388
  // ── File operation helpers (dual-path: local or remote) ──────────────
160
389
  async _writeFile(path, content) {
@@ -162,7 +391,11 @@ var NginxProvider = class {
162
391
  if (this.executor) {
163
392
  try {
164
393
  await this.executor.writeFile(tmpPath, content);
165
- 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
+ }
166
399
  } catch (err) {
167
400
  await this.executor.rm(tmpPath).catch(() => void 0);
168
401
  throw err;
@@ -212,6 +445,37 @@ var NginxProvider = class {
212
445
  await fsMkdir(path, { recursive: true });
213
446
  }
214
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
+ }
215
479
  async _exec(command, args = []) {
216
480
  if (this.executor) {
217
481
  const full = args.length ? `${command} ${args.map(sq).join(" ")}` : command;
@@ -226,6 +490,28 @@ var NginxProvider = class {
226
490
  throw new Error(detail || e.message || String(err));
227
491
  }
228
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
+ }
229
515
  /**
230
516
  * Run certbot, optionally STREAMING its output line-by-line to `onLog` (the
231
517
  * live verify modal). Falls back to buffered `_exec` when no callback / no
@@ -279,7 +565,7 @@ var NginxProvider = class {
279
565
  const slug = this.domainSlug(route.domain);
280
566
  const configPath = join(this.sitesDir, `${slug}.conf`);
281
567
  if ("staticRoot" in route && route.staticRoot) {
282
- assertValidStaticRoot(route.staticRoot);
568
+ assertValidStaticRoot(route.staticRoot, { adopted: route.staticRootAdopted });
283
569
  } else {
284
570
  assertValidUpstream(route.targetUrl);
285
571
  }
@@ -457,7 +743,12 @@ ${webhookLocation}${extraLocations}
457
743
  }
458
744
  const rootMatch = existing.match(/root\s+([^;]+);/);
459
745
  if (rootMatch) {
460
- 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
+ });
461
752
  return this.ensureIssued(domain, certonlyOut);
462
753
  }
463
754
  } catch {
@@ -490,32 +781,41 @@ ${tail}` : "")
490
781
  if (!await this.certsExist(domain)) {
491
782
  return this.provisionCert(domain);
492
783
  }
784
+ if (!await this.hasCertbotLineage(domain)) {
785
+ return this.provisionCert(domain, { force: true });
786
+ }
493
787
  await this._exec("certbot", ["renew", "--cert-name", domain, "--non-interactive"]);
494
788
  await this.reload();
495
789
  return this.readCertInfo(domain);
496
790
  }
497
791
  /**
498
- * Install an operator-supplied certificate (no ACME). Validates the PEM pair,
499
- * writes it to the same on-disk path certbot uses, then re-registers the vhost
500
- * 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.
501
811
  */
502
812
  async installCert(domain, cert) {
503
813
  assertValidDomain(domain);
504
- let expiresAt;
505
- try {
506
- const x509 = new X509Certificate(cert.certPem);
507
- const key = createPrivateKey(cert.keyPem);
508
- if (!x509.checkPrivateKey(key)) {
509
- throw new Error("private key does not match certificate");
510
- }
511
- expiresAt = new Date(x509.validTo).toISOString();
512
- } catch (err) {
513
- throw new Error(`Invalid certificate: ${safeErrorMessage(err)}`);
514
- }
814
+ const candidate = validateCertFor(domain, cert, "supplied certificate");
815
+ if (!candidate.cert) throw new Error(`Invalid certificate: ${candidate.reason}`);
816
+ const { expiresAt } = candidate.cert;
515
817
  const dir = join(this.certDir, domain);
516
- await this._mkdir(dir);
517
- await this._writeFile(join(dir, "fullchain.pem"), cert.certPem);
518
- await this._writeFile(join(dir, "privkey.pem"), cert.keyPem);
818
+ await this.stageCertDir(dir, cert);
519
819
  const slug = this.domainSlug(domain);
520
820
  try {
521
821
  const state = await this._readFile(this.routeStatePath(slug));
@@ -523,7 +823,13 @@ ${tail}` : "")
523
823
  await this.registerRoute({ ...saved, domain, tls: true });
524
824
  } catch {
525
825
  }
526
- 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
+ };
527
833
  }
528
834
  // ── Helpers ──────────────────────────────────────────────────────────
529
835
  domainSlug(domain) {
@@ -538,11 +844,13 @@ ${tail}` : "")
538
844
  */
539
845
  async reload() {
540
846
  if (this.executor) {
541
- try {
542
- const freshPaths = await detectOpenRestyPaths(this.executor);
543
- this.sitesDir = freshPaths.sitesDir;
544
- this.reloadCommand = buildReloadCommand(freshPaths);
545
- } 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
+ }
546
854
  }
547
855
  await this.executor.exec(this.reloadCommand);
548
856
  return;
@@ -572,20 +880,25 @@ ${tail}` : "")
572
880
  if (!await this.certsExist(domain)) {
573
881
  return { domain, expiresAt: "", issuer: "certbot", verified: false, reason: "missing" };
574
882
  }
883
+ let certPem;
884
+ let keyPem;
575
885
  try {
576
- const certPath = join(this.certDir, domain, "fullchain.pem");
577
- const pem = await this._readFile(certPath);
578
- const { X509Certificate: X509Certificate2 } = await import("crypto");
579
- const cert = new X509Certificate2(pem);
580
- return {
581
- domain,
582
- expiresAt: new Date(cert.validTo).toISOString(),
583
- issuer: "certbot",
584
- verified: true
585
- };
886
+ certPem = await this._readFile(join(this.certDir, domain, "fullchain.pem"));
887
+ keyPem = await this._readFile(join(this.certDir, domain, "privkey.pem"));
586
888
  } catch {
587
889
  return { domain, expiresAt: "", issuer: "certbot", verified: false, reason: "read_error" };
588
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
+ };
589
902
  }
590
903
  /**
591
904
  * Read-only check that the Let's Encrypt cert is actually present + valid on
@@ -739,8 +1052,15 @@ ${geoEntries.join("\n")}
739
1052
  };
740
1053
 
741
1054
  export {
1055
+ containerCommand,
1056
+ readMaybeInContainer,
1057
+ edgeContainerExecutor,
1058
+ validateCertFor,
1059
+ isSafeCertPath,
1060
+ readDeclaredPair,
742
1061
  renderProxyOptions,
743
1062
  renderGeoEntries,
1063
+ MANAGED_STATIC_BASE,
744
1064
  summarizeCertbotFailure,
745
1065
  NginxProvider
746
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
+ };