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.
- package/README.md +2 -24
- package/dist/bare-2HYDXQEH.js +23 -0
- package/dist/{bowser-L6PVH37V.js → bowser-O3AEA6AN.js} +3 -1
- package/dist/{chunk-YFLQGDN2.js → chunk-4TJC4JZQ.js} +18 -9
- package/dist/{chunk-JXQCFTDV.js → chunk-4Z5QO7N6.js} +4 -2
- package/dist/{chunk-JJVSOE4I.js → chunk-5MZ624VX.js} +2 -0
- package/dist/{chunk-TDFK6ISF.js → chunk-6APIPIJY.js} +6 -4
- package/dist/{chunk-3LCRNQ2C.js → chunk-6APULWSU.js} +151 -133
- package/dist/{chunk-FTBXJ2VA.js → chunk-6B5YCFFV.js} +6 -4
- package/dist/{chunk-MDFYN2QH.js → chunk-AVIB7CT7.js} +3 -1
- package/dist/{chunk-W7KGHYLV.js → chunk-DYPH4HCI.js} +2 -0
- package/dist/{chunk-5WI7ZXOC.js → chunk-EI575GHN.js} +4 -2
- package/dist/{chunk-Z6EKX6CX.js → chunk-FPRHYBY2.js} +2 -0
- package/dist/{chunk-FZJX6XP7.js → chunk-FRNAR33O.js} +4 -2
- package/dist/{chunk-XRWV5ZNR.js → chunk-FXTBHTWY.js} +360 -42
- package/dist/chunk-ILFETZOM.js +131 -0
- package/dist/{chunk-E3ZPCNPU.js → chunk-IMBD5PZ5.js} +251 -105
- package/dist/{chunk-VJTXRCTT.js → chunk-IPYPMI3S.js} +10 -6
- package/dist/{chunk-PARRPUXN.js → chunk-IXCHURVK.js} +5 -3
- package/dist/{chunk-YJAS5RQX.js → chunk-JYGD5WSN.js} +2 -0
- package/dist/{chunk-JWBXYO24.js → chunk-KI2EY3WB.js} +2 -0
- package/dist/{chunk-YVXF6GGR.js → chunk-L2VOGB2X.js} +5 -104
- package/dist/{chunk-K5OH5U7N.js → chunk-MZ2WEHHY.js} +5 -3
- package/dist/{chunk-SJBR527P.js → chunk-O6ZZF52V.js} +3 -1
- package/dist/{chunk-VLPJLXXB.js → chunk-OWED2GRM.js} +3 -1
- package/dist/{chunk-C4FOQJ63.js → chunk-OXWDY22G.js} +4 -2
- package/dist/{chunk-NPVJYR7M.js → chunk-Q2CIRAOK.js} +2 -0
- package/dist/chunk-R5BHFFAV.js +282 -0
- package/dist/{chunk-JYAGTMSJ.js → chunk-RPHRPFEH.js} +2 -0
- package/dist/{chunk-R74UB4X4.js → chunk-SJIFLLX4.js} +397 -85
- package/dist/{chunk-HYL4QYZM.js → chunk-TGIINPVM.js} +13 -2
- package/dist/{chunk-SYCZE6FQ.js → chunk-TYVME3XD.js} +7 -1
- package/dist/{chunk-UMYQALBH.js → chunk-UXWAVM4C.js} +1788 -1484
- package/dist/{chunk-HDV4NSA3.js → chunk-V6AN6XLZ.js} +16 -6
- package/dist/{chunk-3YCMJRDZ.js → chunk-VDMQJHPI.js} +36 -15
- package/dist/{chunk-RZPDJC2J.js → chunk-VFINNTUL.js} +20 -1
- package/dist/{chunk-65KKUJBR.js → chunk-VWPEKOH6.js} +4 -2
- package/dist/{chunk-GHXGCANU.js → chunk-W73X43R7.js} +4 -2
- package/dist/{chunk-7EYFCKZX.js → chunk-XFCDYZUQ.js} +8 -6
- package/dist/cloud-2KXREXMO.js +30 -0
- package/dist/{cloud-7IGG45CF.js → cloud-7QYOGF7F.js} +4 -2
- package/dist/detect-2XSCFFTF.js +44 -0
- package/dist/{dist-NQB6QGX3.js → dist-IQJ6H6UZ.js} +4 -2
- package/dist/{dist-es-DCR6UJMP.js → dist-es-5DY7K32L.js} +7 -5
- package/dist/{dist-es-TYYZ5KEF.js → dist-es-JSTDUS7Q.js} +7 -5
- package/dist/{dist-es-7EZIM4JU.js → dist-es-OSNJBLXB.js} +6 -4
- package/dist/{dist-es-6RSPR56Q.js → dist-es-RYHIPOTN.js} +15 -13
- package/dist/{dist-es-33Z3Y6VE.js → dist-es-VNKHQVIB.js} +5 -3
- package/dist/{dist-es-D6QQVWRZ.js → dist-es-WV4AMOII.js} +9 -7
- package/dist/{dist-es-GMEQINIE.js → dist-es-X3B5QYKB.js} +7 -5
- package/dist/docker-LODK5Y2S.js +16 -0
- package/dist/docker-edge-executor-VHW3JPJS.js +13 -0
- package/dist/edge-import-ZQ4Y5HEA.js +45 -0
- package/dist/ensure-container-edge-ED25XL3Z.js +31 -0
- package/dist/{event-streams-G7F6Q7QY.js → event-streams-EHRAKL75.js} +5 -3
- package/dist/{executor-O5NR7LMP.js → executor-YPT6RH6O.js} +9 -7
- package/dist/index.d.ts +2 -1
- package/dist/index.js +279 -245
- package/dist/{loadSso-LH753OSJ.js → loadSso-46FQRIZS.js} +11 -9
- package/dist/local-executor-V7U5VOZW.js +12 -0
- package/dist/nginx-XWJAOGS4.js +24 -0
- package/dist/{noop-6KC6HRDB.js → noop-FMMFG2EZ.js} +4 -2
- package/dist/{openresty-lua-KFCOJAG4.js → openresty-lua-4WLGDSER.js} +11 -3
- package/dist/server/engine/ChangeLog +2289 -0
- package/dist/server/engine/Documentations +5 -0
- package/dist/server/engine/LICENSE +674 -0
- package/dist/server/engine/README.md +34 -0
- package/dist/server/engine/conf/amavisd +150 -0
- package/dist/server/engine/conf/clamav +71 -0
- package/dist/server/engine/conf/core +632 -0
- package/dist/server/engine/conf/dovecot +151 -0
- package/dist/server/engine/conf/fail2ban +72 -0
- package/dist/server/engine/conf/global +698 -0
- package/dist/server/engine/conf/iredapd +52 -0
- package/dist/server/engine/conf/logwatch +28 -0
- package/dist/server/engine/conf/postfix +97 -0
- package/dist/server/engine/conf/postgresql +93 -0
- package/dist/server/engine/conf/spamassassin +39 -0
- package/dist/server/engine/dialog/config_via_dialog.sh +258 -0
- package/dist/server/engine/dialog/optional_components.sh +122 -0
- package/dist/server/engine/dialog/pgsql_config.sh +52 -0
- package/dist/server/engine/dialog/virtual_domain_config.sh +94 -0
- package/dist/server/engine/functions/amavisd.sh +267 -0
- package/dist/server/engine/functions/backend.sh +85 -0
- package/dist/server/engine/functions/clamav.sh +100 -0
- package/dist/server/engine/functions/cleanup.sh +393 -0
- package/dist/server/engine/functions/dovecot.sh +583 -0
- package/dist/server/engine/functions/fail2ban.sh +209 -0
- package/dist/server/engine/functions/iredapd.sh +356 -0
- package/dist/server/engine/functions/optional_components.sh +24 -0
- package/dist/server/engine/functions/packages.sh +522 -0
- package/dist/server/engine/functions/packages_freebsd.sh +179 -0
- package/dist/server/engine/functions/postfix.sh +457 -0
- package/dist/server/engine/functions/postgresql.sh +239 -0
- package/dist/server/engine/functions/spamassassin.sh +49 -0
- package/dist/server/engine/functions/system_accounts.sh +128 -0
- package/dist/server/engine/iRedMail.sh +144 -0
- package/dist/server/engine/pkgs/get_all.sh +338 -0
- package/dist/server/engine/pkgs/pkgs.freebsd.sha256 +3 -0
- package/dist/server/engine/pkgs/pkgs.openbsd.sha256 +6 -0
- package/dist/server/engine/pkgs/pkgs.sha256 +1 -0
- package/dist/server/engine/samples/amavisd/amavisd.conf +677 -0
- package/dist/server/engine/samples/amavisd/amavisd.mysql +278 -0
- package/dist/server/engine/samples/amavisd/amavisd.pgsql +253 -0
- package/dist/server/engine/samples/amavisd/amavisd.schema +942 -0
- package/dist/server/engine/samples/amavisd/default_spam_policy.sql +46 -0
- package/dist/server/engine/samples/dovecot/dovecot-2.4-mariadb.conf +758 -0
- package/dist/server/engine/samples/dovecot/dovecot-2.4-openldap.conf +767 -0
- package/dist/server/engine/samples/dovecot/dovecot-2.4-pgsql.conf +762 -0
- package/dist/server/engine/samples/dovecot/dovecot-last-login.conf +50 -0
- package/dist/server/engine/samples/dovecot/dovecot-ldap.conf +21 -0
- package/dist/server/engine/samples/dovecot/dovecot-share-folder.conf +22 -0
- package/dist/server/engine/samples/dovecot/dovecot-sql.conf +28 -0
- package/dist/server/engine/samples/dovecot/dovecot-used-quota.conf +13 -0
- package/dist/server/engine/samples/dovecot/dovecot.conf +484 -0
- package/dist/server/engine/samples/dovecot/dovecot.sieve +7 -0
- package/dist/server/engine/samples/dovecot/dovecot2-quota-warning.sh +38 -0
- package/dist/server/engine/samples/dovecot/sql/imap_share_folder.mysql +16 -0
- package/dist/server/engine/samples/dovecot/sql/last_login.mysql +12 -0
- package/dist/server/engine/samples/dovecot/sql/used_quota.mysql +26 -0
- package/dist/server/engine/samples/dovecot/systemd/override.conf +3 -0
- package/dist/server/engine/samples/fail2ban/action.d/banned_db.conf +9 -0
- package/dist/server/engine/samples/fail2ban/bin/fail2ban_banned_db +268 -0
- package/dist/server/engine/samples/fail2ban/fail2ban.local +17 -0
- package/dist/server/engine/samples/fail2ban/filter.d/dovecot.iredmail.conf +5 -0
- package/dist/server/engine/samples/fail2ban/filter.d/postfix-pregreet.iredmail.conf +16 -0
- package/dist/server/engine/samples/fail2ban/filter.d/postfix.iredmail.conf +18 -0
- package/dist/server/engine/samples/fail2ban/filter.d/roundcube.iredmail.conf +9 -0
- package/dist/server/engine/samples/fail2ban/jail.d/dovecot.local +8 -0
- package/dist/server/engine/samples/fail2ban/jail.d/nginx-http-auth.local +8 -0
- package/dist/server/engine/samples/fail2ban/jail.d/postfix-pregreet.local +9 -0
- package/dist/server/engine/samples/fail2ban/jail.d/postfix.local +8 -0
- package/dist/server/engine/samples/fail2ban/jail.d/roundcube.local +9 -0
- package/dist/server/engine/samples/fail2ban/jail.d/sogo.local +8 -0
- package/dist/server/engine/samples/fail2ban/jail.d/sshd.local +8 -0
- package/dist/server/engine/samples/fail2ban/jail.local +6 -0
- package/dist/server/engine/samples/fail2ban/openbsd/rc +27 -0
- package/dist/server/engine/samples/fail2ban/sql/fail2ban.mysql +49 -0
- package/dist/server/engine/samples/fail2ban/sql/fail2ban.pgsql +48 -0
- package/dist/server/engine/samples/firewall/firewalld/services/ssh.xml +6 -0
- package/dist/server/engine/samples/firewall/firewalld/zones/iredmail.xml +15 -0
- package/dist/server/engine/samples/firewall/iptables/ip6tables.init.debian +362 -0
- package/dist/server/engine/samples/firewall/iptables/ip6tables.rules +53 -0
- package/dist/server/engine/samples/firewall/iptables/iptables.init.debian +363 -0
- package/dist/server/engine/samples/firewall/iptables/iptables.rules +54 -0
- package/dist/server/engine/samples/firewall/nftables.conf +58 -0
- package/dist/server/engine/samples/freebsd/newsyslog.conf.d/dovecot.conf +3 -0
- package/dist/server/engine/samples/freebsd/newsyslog.conf.d/iredapd.conf +1 -0
- package/dist/server/engine/samples/freebsd/newsyslog.conf.d/mlmmjadmin.conf +1 -0
- package/dist/server/engine/samples/freebsd/newsyslog.conf.d/php-fpm.conf +2 -0
- package/dist/server/engine/samples/freebsd/newsyslog.conf.d/slapd.conf +1 -0
- package/dist/server/engine/samples/freebsd/syslog.d/dovecot.conf +2 -0
- package/dist/server/engine/samples/freebsd/syslog.d/fail2ban.conf +2 -0
- package/dist/server/engine/samples/freebsd/syslog.d/iredapd.conf +2 -0
- package/dist/server/engine/samples/freebsd/syslog.d/mlmmjadmin.conf +2 -0
- package/dist/server/engine/samples/freebsd/syslog.d/php-fpm.conf +2 -0
- package/dist/server/engine/samples/freebsd/syslog.d/slapd.conf +2 -0
- package/dist/server/engine/samples/iredmail/iredmail.mysql +491 -0
- package/dist/server/engine/samples/iredmail/iredmail.pgsql +465 -0
- package/dist/server/engine/samples/iredmail/iredmail.schema +456 -0
- package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.3.pgsql +40 -0
- package/dist/server/engine/samples/iredmail/used_quota_triggers_dovecot_2.4.pgsql +18 -0
- package/dist/server/engine/samples/logrotate/dovecot +13 -0
- package/dist/server/engine/samples/logrotate/iredapd +13 -0
- package/dist/server/engine/samples/logrotate/mlmmjadmin +13 -0
- package/dist/server/engine/samples/logrotate/openldap +19 -0
- package/dist/server/engine/samples/logrotate/php-fpm +13 -0
- package/dist/server/engine/samples/openbsd/ldapd.conf +58 -0
- package/dist/server/engine/samples/openbsd/pf.conf +28 -0
- package/dist/server/engine/samples/postfix/command_filter.pcre +3 -0
- package/dist/server/engine/samples/postfix/freebsd/mailer.conf +9 -0
- package/dist/server/engine/samples/postfix/helo_access.pcre +180 -0
- package/dist/server/engine/samples/postfix/ldap/catchall_maps.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_domain.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/recipient_bcc_maps_user.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/relay_domains.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_domain.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/sender_bcc_maps_user.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_domain.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/sender_dependent_relayhost_maps_user.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/sender_login_maps.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/transport_maps_domain.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/transport_maps_user.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/virtual_alias_maps.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/virtual_group_maps.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/virtual_group_members_maps.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_domains.cf +11 -0
- package/dist/server/engine/samples/postfix/ldap/virtual_mailbox_maps.cf +12 -0
- package/dist/server/engine/samples/postfix/main.cf +282 -0
- package/dist/server/engine/samples/postfix/main.cf.amavisd +7 -0
- package/dist/server/engine/samples/postfix/main.cf.dovecot +8 -0
- package/dist/server/engine/samples/postfix/main.cf.ldap +39 -0
- package/dist/server/engine/samples/postfix/main.cf.mlmmj +5 -0
- package/dist/server/engine/samples/postfix/main.cf.mysql +39 -0
- package/dist/server/engine/samples/postfix/main.cf.pgsql +39 -0
- package/dist/server/engine/samples/postfix/main.cf.postscreen +30 -0
- package/dist/server/engine/samples/postfix/master.cf +87 -0
- package/dist/server/engine/samples/postfix/mysql/catchall_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/domain_alias_catchall_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/domain_alias_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_domain.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/recipient_bcc_maps_user.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/relay_domains.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_domain.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/sender_bcc_maps_user.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/sender_dependent_relayhost_maps.cf +6 -0
- package/dist/server/engine/samples/postfix/mysql/sender_login_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/transport_maps_domain.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/transport_maps_maillist.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/transport_maps_user.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/virtual_alias_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_domains.cf +5 -0
- package/dist/server/engine/samples/postfix/mysql/virtual_mailbox_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/catchall_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/domain_alias_catchall_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/domain_alias_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_domain.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/recipient_bcc_maps_user.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/relay_domains.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_domain.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/sender_bcc_maps_user.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/sender_dependent_relayhost_maps.cf +6 -0
- package/dist/server/engine/samples/postfix/pgsql/sender_login_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/transport_maps_domain.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/transport_maps_maillist.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/transport_maps_user.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/virtual_alias_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_domains.cf +5 -0
- package/dist/server/engine/samples/postfix/pgsql/virtual_mailbox_maps.cf +5 -0
- package/dist/server/engine/samples/postfix/postscreen_access.cidr +6 -0
- package/dist/server/engine/samples/postgresql/sql/add_first_domain_and_user.sql +34 -0
- package/dist/server/engine/samples/postgresql/sql/grant_permissions.sql +14 -0
- package/dist/server/engine/samples/postgresql/sql/init_vmail_db.sql +15 -0
- package/dist/server/engine/samples/rsyslog.d/1-iredmail-dovecot.conf +23 -0
- package/dist/server/engine/samples/rsyslog.d/1-iredmail-fail2ban.conf +2 -0
- package/dist/server/engine/samples/rsyslog.d/1-iredmail-iredapd.conf +12 -0
- package/dist/server/engine/samples/rsyslog.d/1-iredmail-mlmmjadmin.conf +12 -0
- package/dist/server/engine/samples/rsyslog.d/1-iredmail-openldap.conf +2 -0
- package/dist/server/engine/samples/rsyslog.d/1-iredmail-phpfpm.conf +3 -0
- package/dist/server/engine/samples/spamassassin/local.cf +186 -0
- package/dist/server/engine/samples/spamassassin/razor.conf +1 -0
- package/dist/server/engine/samples/systemd/clamd.service.d/override.conf +2 -0
- package/dist/server/engine/samples/systemd/mariadb.service.d/override.conf +3 -0
- package/dist/server/engine/samples/systemd/slapd.service.d/override.conf +6 -0
- package/dist/server/engine/samples/yum/CentOS-Linux-AppStream.repo +6 -0
- package/dist/server/engine/samples/yum/CentOS-Linux-PowerTools.repo +6 -0
- package/dist/server/engine/samples/yum/CentOS-Stream-AppStream.repo +7 -0
- package/dist/server/engine/samples/yum/CentOS-Stream-PowerTools.repo +7 -0
- package/dist/server/engine/samples/yum/symas-openldap.repo +6 -0
- package/dist/server/engine/tools/add_enabled_service.py +60 -0
- package/dist/server/engine/tools/backup_mysql.sh +238 -0
- package/dist/server/engine/tools/backup_openldap.sh +230 -0
- package/dist/server/engine/tools/backup_pgsql.sh +217 -0
- package/dist/server/engine/tools/backup_sogo.sh +156 -0
- package/dist/server/engine/tools/create_mail_user_OpenLDAP.py +335 -0
- package/dist/server/engine/tools/create_mail_user_OpenLDAP.sh +287 -0
- package/dist/server/engine/tools/create_mail_user_SQL.sh +127 -0
- package/dist/server/engine/tools/enable_postscreen.sh +144 -0
- package/dist/server/engine/tools/fail2ban_unban_ip.sh +39 -0
- package/dist/server/engine/tools/find_sasl_login_ip.sh +20 -0
- package/dist/server/engine/tools/find_top_sasl_usernames.sh +28 -0
- package/dist/server/engine/tools/generate_ssl_keys.sh +85 -0
- package/dist/server/engine/tools/kill_high_cpu_sogo_process.sh +32 -0
- package/dist/server/engine/tools/ldap_assign_user_to_groups.py +151 -0
- package/dist/server/engine/tools/ldap_move_members_to_another_group.py +129 -0
- package/dist/server/engine/tools/migrate_sql_alias_table.py +177 -0
- package/dist/server/engine/update/0.9.8/amavisd.mysql +29 -0
- package/dist/server/engine/update/0.9.8/amavisd.pgsql +39 -0
- package/dist/server/engine/update/0.9.8/iredmail.mysql +53 -0
- package/dist/server/engine/update/0.9.8/iredmail.pgsql +61 -0
- package/dist/server/engine/update/0.9.9/iredmail.mysql +2 -0
- package/dist/server/engine/update/0.9.9/iredmail.pgsql +2 -0
- package/dist/server/engine/update/1.0/iredmail.mysql +2 -0
- package/dist/server/engine/update/1.0/iredmail.pgsql +2 -0
- package/dist/server/engine/update/1.2/last_login.mysql +16 -0
- package/dist/server/engine/update/1.4.0/iredmail.mysql +22 -0
- package/dist/server/engine/update/1.4.0/iredmail.pgsql +20 -0
- package/dist/server/engine/update/1.4.1/iredmail.mysql +3 -0
- package/dist/server/engine/update/1.4.1/iredmail.pgsql +3 -0
- package/dist/server/engine/update/1.4.1/sogo.mysql +11 -0
- package/dist/server/engine/update/1.4.1/sogo.pgsql +30 -0
- package/dist/server/engine/update/1.4.1/update-ldap.py +66 -0
- package/dist/server/engine/update/1.4.2/iredmail.mysql +9 -0
- package/dist/server/engine/update/1.4.2/iredmail.pgsql +9 -0
- package/dist/server/engine/update/1.4.2/sogo.pgsql +30 -0
- package/dist/server/engine/update/1.6.0/sogo_view.pgsql +27 -0
- package/dist/server/engine/update/1.6.3/iredmail.mysql +1 -0
- package/dist/server/engine/update/1.7.0/fail2ban.mysql +11 -0
- package/dist/server/engine/update/1.7.0/fail2ban.pgsql +12 -0
- package/dist/server/engine/update/1.7.1/amavisd.mysql +1 -0
- package/dist/server/engine/update/1.7.2/vmail.mysql +30 -0
- package/dist/server/engine/update/1.7.3/deleted_mailboxes.mysql +43 -0
- package/dist/server/engine/update/1.7.3/iredadmin.mysql +26 -0
- package/dist/server/engine/update/1.7.3/vmail.mysql +79 -0
- package/dist/server/engine/update/1.7.3/vmail.pgsql +9 -0
- package/dist/server/engine/update/README.md +1 -0
- package/dist/server/engine/update/ldap/README.md +40 -0
- package/dist/server/engine/update/ldap/update-ldap-dovecot-2.3.py +69 -0
- package/dist/server/index.js +55793 -53701
- package/dist/server/migrations/0071_gh_device_token.sql +3 -0
- package/dist/server/migrations/0072_gh_device_token_method.sql +1 -0
- package/dist/server/migrations/meta/_journal.json +14 -0
- package/dist/setup-2DMFIA74.js +20 -0
- package/dist/{signin-PVC7MBZJ.js → signin-ITOPKKUB.js} +11 -9
- package/dist/{sso-oidc-ZXY77BSL.js → sso-oidc-NON2PD7Y.js} +11 -9
- package/dist/{sts-LX5L4UU3.js → sts-HHFFSAY2.js} +10 -8
- package/package.json +1 -1
- package/dist/bare-CNSFDNSZ.js +0 -20
- package/dist/cloud-655YYCEW.js +0 -26
- package/dist/docker-UWQPDEGW.js +0 -14
- package/dist/docker-edge-executor-ZLAYE5FE.js +0 -11
- package/dist/edge-import-AFID2EN3.js +0 -41
- package/dist/nginx-M36GDYL7.js +0 -18
- 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-
|
|
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-
|
|
16
|
+
} from "./chunk-VFINNTUL.js";
|
|
15
17
|
import {
|
|
16
18
|
PROXY_GZIP_TYPES,
|
|
17
19
|
safeErrorMessage,
|
|
18
20
|
sanitizeProxySettings
|
|
19
|
-
} from "./chunk-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
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
|
-
|
|
507
|
-
|
|
508
|
-
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
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
|
-
|
|
579
|
-
|
|
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
|
+
};
|