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.
- package/README.md +2 -24
- package/dist/bare-2HYDXQEH.js +23 -0
- package/dist/{bowser-MFQA6OS4.js → bowser-O3AEA6AN.js} +5 -1
- package/dist/{chunk-KD7VO4LG.js → chunk-2ZLR7ULL.js} +255 -65
- package/dist/{chunk-YHXCHW7Q.js → chunk-3JQTL4R4.js} +236 -99
- package/dist/{chunk-E77LJWJK.js → chunk-4TJC4JZQ.js} +20 -9
- package/dist/{chunk-YL67EWYZ.js → chunk-4Z5QO7N6.js} +6 -2
- package/dist/{chunk-WJPCBU7Z.js → chunk-5FNIWBFI.js} +10 -6
- package/dist/{chunk-Y37KHU7L.js → chunk-5MZ624VX.js} +5 -0
- package/dist/{chunk-QKFRZNKN.js → chunk-6APIPIJY.js} +8 -4
- package/dist/{chunk-JZLNHGLW.js → chunk-6APULWSU.js} +153 -133
- package/dist/{chunk-F4DFVG6Q.js → chunk-6B5YCFFV.js} +8 -4
- package/dist/{chunk-XVSAUO2I.js → chunk-AVIB7CT7.js} +5 -1
- package/dist/{chunk-TEYJYSMT.js → chunk-C5OHTYEF.js} +12 -6
- package/dist/{chunk-7UOB5GGQ.js → chunk-DYPH4HCI.js} +5 -0
- package/dist/{chunk-XIFRMKYT.js → chunk-EI575GHN.js} +6 -2
- package/dist/{chunk-4CQWT2WL.js → chunk-FPRHYBY2.js} +5 -0
- package/dist/chunk-FQGJZ2HS.js +248 -0
- package/dist/{chunk-JR2OCMJA.js → chunk-FRNAR33O.js} +6 -2
- package/dist/{chunk-JUJBBF5U.js → chunk-FXTBHTWY.js} +362 -42
- package/dist/chunk-ILFETZOM.js +131 -0
- package/dist/{chunk-5ZCKQ2MI.js → chunk-IXCHURVK.js} +7 -3
- package/dist/{chunk-6ERIMTBC.js → chunk-JYGD5WSN.js} +5 -0
- package/dist/{chunk-4VNS5WPM.js → chunk-KI2EY3WB.js} +4 -0
- package/dist/{chunk-3U4SPZD5.js → chunk-L2VOGB2X.js} +7 -104
- package/dist/{chunk-NB2VEIWG.js → chunk-MZ2WEHHY.js} +7 -3
- package/dist/{chunk-BUBSSKO6.js → chunk-O6ZZF52V.js} +5 -1
- package/dist/{chunk-LBFH7THC.js → chunk-OWED2GRM.js} +5 -1
- package/dist/{chunk-4MG3BVHE.js → chunk-OXWDY22G.js} +6 -2
- package/dist/{chunk-4FABQGQP.js → chunk-Q2CIRAOK.js} +5 -0
- package/dist/{chunk-4LEL3H2Z.js → chunk-RPHRPFEH.js} +5 -0
- package/dist/{chunk-46YJFP4S.js → chunk-SQ77DZOI.js} +1728 -1484
- package/dist/{chunk-S6DSRLNE.js → chunk-TGIINPVM.js} +15 -2
- package/dist/{chunk-U2DPZSXA.js → chunk-TYVME3XD.js} +9 -1
- package/dist/{chunk-HFYDYK4D.js → chunk-V6AN6XLZ.js} +18 -6
- package/dist/{chunk-ZKJOFWBJ.js → chunk-VDMQJHPI.js} +38 -15
- package/dist/{chunk-E6W2JMMU.js → chunk-VFINNTUL.js} +22 -1
- package/dist/{chunk-OJE6YWPQ.js → chunk-VWPEKOH6.js} +6 -2
- package/dist/{chunk-5N7VUGL6.js → chunk-W73X43R7.js} +6 -2
- package/dist/cloud-7QYOGF7F.js +11 -0
- package/dist/cloud-ZSKOS4GY.js +30 -0
- package/dist/detect-SH4FHFBI.js +36 -0
- package/dist/{dist-L3MPOWCA.js → dist-IQJ6H6UZ.js} +6 -2
- package/dist/{dist-es-KE56MWYO.js → dist-es-5DY7K32L.js} +9 -5
- package/dist/dist-es-JSTDUS7Q.js +26 -0
- package/dist/{dist-es-GTAMF674.js → dist-es-OSNJBLXB.js} +8 -4
- package/dist/{dist-es-NRZIQY5B.js → dist-es-RYHIPOTN.js} +17 -13
- package/dist/{dist-es-XQCOTGC7.js → dist-es-VNKHQVIB.js} +7 -3
- package/dist/{dist-es-WZCR7DHQ.js → dist-es-WV4AMOII.js} +11 -7
- package/dist/{dist-es-ZW3HFQUM.js → dist-es-X3B5QYKB.js} +9 -5
- package/dist/docker-U7AF5PTA.js +16 -0
- package/dist/docker-edge-executor-VHW3JPJS.js +13 -0
- package/dist/edge-import-V5CBA47C.js +45 -0
- package/dist/ensure-container-edge-CMYROUSI.js +33 -0
- package/dist/{event-streams-7LGDL56D.js → event-streams-EHRAKL75.js} +7 -3
- package/dist/executor-YPT6RH6O.js +30 -0
- package/dist/index.d.ts +2 -1
- package/dist/index.js +258 -245
- package/dist/{loadSso-N273C5I7.js → loadSso-46FQRIZS.js} +13 -9
- package/dist/local-executor-V7U5VOZW.js +12 -0
- package/dist/nginx-2GTRUIDE.js +24 -0
- package/dist/noop-FMMFG2EZ.js +11 -0
- package/dist/{openresty-lua-HAO4EUDY.js → openresty-lua-4WLGDSER.js} +13 -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 +55574 -53675
- 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-HQIZLUOJ.js +20 -0
- package/dist/{signin-DR462ERO.js → signin-ITOPKKUB.js} +13 -9
- package/dist/{sso-oidc-VBZLJUK5.js → sso-oidc-NON2PD7Y.js} +13 -9
- package/dist/{sts-EJLZ4Z77.js → sts-HHFFSAY2.js} +12 -8
- package/package.json +1 -1
- package/dist/bare-ITM45QBX.js +0 -18
- package/dist/cloud-JMPHO3CW.js +0 -7
- package/dist/cloud-W5G6YXAY.js +0 -24
- package/dist/dist-es-4C322O5D.js +0 -22
- package/dist/docker-3FVJMFC3.js +0 -12
- package/dist/docker-edge-executor-ZIWAZWPL.js +0 -9
- package/dist/edge-import-Y34YL6JK.js +0 -39
- package/dist/executor-J7BHW5HF.js +0 -26
- package/dist/nginx-YZWKYKQU.js +0 -16
- package/dist/noop-RRNCIUI5.js +0 -7
- 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-
|
|
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-
|
|
16
|
+
} from "./chunk-VFINNTUL.js";
|
|
13
17
|
import {
|
|
14
18
|
PROXY_GZIP_TYPES,
|
|
15
19
|
safeErrorMessage,
|
|
16
20
|
sanitizeProxySettings
|
|
17
|
-
} from "./chunk-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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
|
-
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
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
|
-
|
|
505
|
-
|
|
506
|
-
|
|
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.
|
|
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 {
|
|
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
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
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
|
-
|
|
577
|
-
|
|
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
|
+
};
|