diff --git a/Cargo.lock b/Cargo.lock index 484e72d1..1df3cd40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7398,6 +7398,7 @@ dependencies = [ "migration", "pop3", "registry", + "rustls 0.23.37", "services", "smtp", "smtp-proto", diff --git a/Dockerfile b/Dockerfile index 3309e048..993406a5 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,3 @@ -# Stalwart Dockerfile -# Credits: https://github.com/33KK - FROM --platform=$BUILDPLATFORM docker.io/lukemathwalker/cargo-chef:latest-rust-slim-trixie AS chef WORKDIR /build @@ -28,14 +25,22 @@ RUN RUSTFLAGS="$(cat /flags.txt)" cargo build --target "$(cat /target.txt)" --re RUN mv "/build/target/$(cat /target.txt)/release" "/output" FROM docker.io/debian:trixie-slim -WORKDIR /opt/stalwart RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ - apt-get install -yq --no-install-recommends ca-certificates -COPY --from=builder /output/stalwart /usr/local/bin -COPY ./resources/docker/entrypoint.sh /usr/local/bin/entrypoint.sh -RUN chmod -R 755 /usr/local/bin -CMD ["/usr/local/bin/stalwart"] -VOLUME [ "/opt/stalwart" ] + apt-get install -yq --no-install-recommends ca-certificates curl libcap2-bin && \ + rm -rf /var/lib/apt/lists/* && \ + groupadd -r -g 2000 stalwart && \ + useradd -r -u 2000 -g 2000 -s /usr/sbin/nologin -M stalwart && \ + mkdir -p /etc/stalwart /var/lib/stalwart && \ + chown stalwart:stalwart /etc/stalwart /var/lib/stalwart +COPY --from=builder --chmod=0755 /output/stalwart /usr/local/bin/stalwart +RUN setcap 'cap_net_bind_service=+ep' /usr/local/bin/stalwart +USER stalwart +WORKDIR /var/lib/stalwart +VOLUME ["/etc/stalwart", "/var/lib/stalwart"] EXPOSE 443 25 110 587 465 143 993 995 4190 8080 -ENTRYPOINT ["/bin/sh", "/usr/local/bin/entrypoint.sh"] +ENV STALWART_HEALTHCHECK_URL=https://127.0.0.1:443/healthz/live +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD curl -fsSk "$STALWART_HEALTHCHECK_URL" || curl -fsS http://127.0.0.1:8080/healthz/live || exit 1 +ENTRYPOINT ["/usr/local/bin/stalwart"] +CMD ["--config", "/etc/stalwart/config.json"] diff --git a/Dockerfile.build b/Dockerfile.build index f760fcec..997c43e7 100644 --- a/Dockerfile.build +++ b/Dockerfile.build @@ -150,28 +150,44 @@ COPY --from=builder /app/artifact / # Runtime image for GNU targets # ***************** FROM --platform=$TARGETPLATFORM docker.io/library/debian:trixie-slim AS gnu -WORKDIR /opt/stalwart RUN export DEBIAN_FRONTEND=noninteractive && \ apt-get update && \ - apt-get install -yq --no-install-recommends ca-certificates tzdata -COPY --from=builder /app/artifact/stalwart /usr/local/bin -COPY ./resources/docker/entrypoint.sh /usr/local/bin/entrypoint.sh -RUN chmod -R 755 /usr/local/bin -CMD ["/usr/local/bin/stalwart"] -VOLUME [ "/opt/stalwart" ] + apt-get install -yq --no-install-recommends ca-certificates curl tzdata libcap2-bin && \ + rm -rf /var/lib/apt/lists/* && \ + groupadd -r -g 2000 stalwart && \ + useradd -r -u 2000 -g 2000 -s /usr/sbin/nologin -M stalwart && \ + mkdir -p /etc/stalwart /var/lib/stalwart && \ + chown stalwart:stalwart /etc/stalwart /var/lib/stalwart +COPY --from=builder --chmod=0755 /app/artifact/stalwart /usr/local/bin/stalwart +RUN setcap 'cap_net_bind_service=+ep' /usr/local/bin/stalwart +USER stalwart +WORKDIR /var/lib/stalwart +VOLUME ["/etc/stalwart", "/var/lib/stalwart"] EXPOSE 443 25 110 587 465 143 993 995 4190 8080 -ENTRYPOINT ["/bin/sh", "/usr/local/bin/entrypoint.sh"] +ENV STALWART_HEALTHCHECK_URL=https://127.0.0.1:443/healthz/live +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD curl -fsSk "$STALWART_HEALTHCHECK_URL" || curl -fsS http://127.0.0.1:8080/healthz/live || exit 1 +ENTRYPOINT ["/usr/local/bin/stalwart"] +CMD ["--config", "/etc/stalwart/config.json"] # ***************** # Runtime image for musl targets # ***************** FROM --platform=$TARGETPLATFORM alpine AS musl -WORKDIR /opt/stalwart -RUN apk add --update --no-cache ca-certificates tzdata && rm -rf /var/cache/apk/* -COPY --from=builder /app/artifact/stalwart /usr/local/bin -COPY ./resources/docker/entrypoint.sh /usr/local/bin/entrypoint.sh -RUN chmod -R 755 /usr/local/bin -CMD ["/usr/local/bin/stalwart"] -VOLUME [ "/opt/stalwart" ] +RUN apk add --update --no-cache ca-certificates curl tzdata libcap && \ + rm -rf /var/cache/apk/* && \ + addgroup -S -g 2000 stalwart && \ + adduser -S -D -H -u 2000 -G stalwart -s /sbin/nologin stalwart && \ + mkdir -p /etc/stalwart /var/lib/stalwart && \ + chown stalwart:stalwart /etc/stalwart /var/lib/stalwart +COPY --from=builder --chmod=0755 /app/artifact/stalwart /usr/local/bin/stalwart +RUN setcap 'cap_net_bind_service=+ep' /usr/local/bin/stalwart +USER stalwart +WORKDIR /var/lib/stalwart +VOLUME ["/etc/stalwart", "/var/lib/stalwart"] EXPOSE 443 25 110 587 465 143 993 995 4190 8080 -ENTRYPOINT ["/bin/sh", "/usr/local/bin/entrypoint.sh"] +ENV STALWART_HEALTHCHECK_URL=https://127.0.0.1:443/healthz/live +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD curl -fsSk "$STALWART_HEALTHCHECK_URL" || curl -fsS http://127.0.0.1:8080/healthz/live || exit 1 +ENTRYPOINT ["/usr/local/bin/stalwart"] +CMD ["--config", "/etc/stalwart/config.json"] diff --git a/resources/docker/Dockerfile.fdb b/Dockerfile.fdb similarity index 55% rename from resources/docker/Dockerfile.fdb rename to Dockerfile.fdb index a5aa20b6..66b93f51 100644 --- a/resources/docker/Dockerfile.fdb +++ b/Dockerfile.fdb @@ -41,12 +41,26 @@ RUN cargo build -p stalwart --no-default-features --features "foundationdb s3 re FROM debian:trixie-slim AS runtime -COPY --from=builder /app/target/release/stalwart /usr/local/bin/stalwart -RUN apt-get update -y && apt-get install -yq --no-install-recommends ca-certificates curl adduser -RUN curl -LO https://github.com/apple/foundationdb/releases/download/7.3.69/foundationdb-clients_7.3.69-1_amd64.deb && \ - dpkg -i foundationdb-clients_7.3.69-1_amd64.deb -RUN useradd stalwart -s /sbin/nologin -M -RUN mkdir -p /opt/stalwart -RUN chown stalwart:stalwart /opt/stalwart +COPY --from=builder --chmod=0755 /app/target/release/stalwart /usr/local/bin/stalwart +RUN export DEBIAN_FRONTEND=noninteractive && \ + apt-get update && \ + apt-get install -yq --no-install-recommends ca-certificates curl libcap2-bin && \ + curl -LO https://github.com/apple/foundationdb/releases/download/7.3.69/foundationdb-clients_7.3.69-1_amd64.deb && \ + dpkg -i foundationdb-clients_7.3.69-1_amd64.deb && \ + rm -f foundationdb-clients_7.3.69-1_amd64.deb && \ + rm -rf /var/lib/apt/lists/* && \ + groupadd -r -g 2000 stalwart && \ + useradd -r -u 2000 -g 2000 -s /usr/sbin/nologin -M stalwart && \ + mkdir -p /etc/stalwart /var/lib/stalwart && \ + chown stalwart:stalwart /etc/stalwart /var/lib/stalwart && \ + setcap 'cap_net_bind_service=+ep' /usr/local/bin/stalwart -ENTRYPOINT ["/usr/local/bin/stalwart", "--config", "/opt/stalwart/etc/config.json"] +USER stalwart +WORKDIR /var/lib/stalwart +VOLUME ["/etc/stalwart", "/var/lib/stalwart"] +EXPOSE 443 25 110 587 465 143 993 995 4190 8080 +ENV STALWART_HEALTHCHECK_URL=https://127.0.0.1:443/healthz/live +HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ + CMD curl -fsSk "$STALWART_HEALTHCHECK_URL" || curl -fsS http://127.0.0.1:8080/healthz/live || exit 1 +ENTRYPOINT ["/usr/local/bin/stalwart"] +CMD ["--config", "/etc/stalwart/config.json"] diff --git a/crates/common/src/config/network.rs b/crates/common/src/config/network.rs index 23c4ce4d..413e05d8 100644 --- a/crates/common/src/config/network.rs +++ b/crates/common/src/config/network.rs @@ -208,10 +208,6 @@ impl Network { if hostname != http_host { http_host = hostname.to_string(); } - pacc.authentication.as_mut().unwrap().oauth_public = OAuthPublic { - issuer: format!("https://{hostname}/",), - } - .into(); pacc.protocols.jmap = HttpServer { url: format!("https://{hostname}/jmap/session",), } diff --git a/crates/common/src/manager/defaults.rs b/crates/common/src/manager/defaults.rs index 6e27256c..9e575365 100644 --- a/crates/common/src/manager/defaults.rs +++ b/crates/common/src/manager/defaults.rs @@ -51,9 +51,10 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> { let is_bootstrap_mode = bp.registry.is_bootstrap_mode(); #[cfg(not(feature = "test_mode"))] - if !is_recovery_mode || is_bootstrap_mode { - if bp.registry.count_object(ObjectType::Application).await? == 0 { - bp.registry + if (!is_recovery_mode || is_bootstrap_mode) + && bp.registry.count_object(ObjectType::Application).await? == 0 + { + bp.registry .write(RegistryWrite::insert( &Application { auto_update_frequency: Duration::from_millis(30 * 24 * 60 * 60 * 1000), @@ -71,7 +72,6 @@ async fn insert_safe_defaults(bp: &mut Bootstrap) -> trc::Result<()> { .into(), )) .await?; - } } if is_bootstrap_mode { diff --git a/crates/common/src/network/dns/records.rs b/crates/common/src/network/dns/records.rs index 1b672726..87942695 100644 --- a/crates/common/src/network/dns/records.rs +++ b/crates/common/src/network/dns/records.rs @@ -4,7 +4,7 @@ * SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL */ -use crate::{Server, network::dkim::generate_dkim_dns_record}; +use crate::{Server, config::network::Pacc, network::dkim::generate_dkim_dns_record}; use ahash::{AHashMap, AHashSet}; use base64::{Engine, engine::general_purpose}; use dns_update::{ @@ -388,13 +388,30 @@ impl Server { .and_then(|directory| { directory .oidc_discovery_document() - .map(|doc| doc.url.to_string()) + .map(|doc| self.core.network.info.pacc.build(&doc.url)) + }) + .unwrap_or_else(|| { + self.core + .network + .info + .pacc + .build(&self.core.network.http.url_https) }) - .unwrap_or_else(|| self.core.network.http.url_https.clone()) }) } } +impl Pacc { + pub fn build(&self, endpoint: &str) -> String { + let mut response = + String::with_capacity(self.prefix.len() + self.suffix.len() + endpoint.len()); + response.push_str(&self.prefix); + response.push_str(endpoint); + response.push_str(&self.suffix); + response + } +} + #[inline(always)] #[allow(unused)] fn provider_domain(url: &Url) -> Option<&str> { diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index 608425c9..d94c5185 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -38,6 +38,7 @@ registry = { path = "../registry" } http_proto = { path = "../http-proto" } migration = { path = "../migration" } tokio = { version = "1.47", features = ["full"] } +rustls = { version = "0.23.5", default-features = false, features = ["std", "aws_lc_rs", "tls12"] } [target.'cfg(not(target_env = "msvc"))'.dependencies] jemallocator = "0.5.0" diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index 3c6bb3cd..3c77bea3 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -32,6 +32,11 @@ static GLOBAL: Jemalloc = Jemalloc; #[tokio::main] async fn main() -> std::io::Result<()> { + // Install AWS-LC-RS as the default Rustls crypto provider + rustls::crypto::aws_lc_rs::default_provider() + .install_default() + .expect("failed to install aws-lc-rs as the default rustls crypto provider"); + // Load config and apply macros let mut init = Box::pin(BootManager::init()).await; diff --git a/resources/docker/download.sh b/resources/docker/download.sh deleted file mode 100644 index 69324c11..00000000 --- a/resources/docker/download.sh +++ /dev/null @@ -1,600 +0,0 @@ -#!/usr/bin/env sh -# shellcheck shell=dash - -# Stalwart install script -- based on the rustup installation script. - -set -e -set -u - -readonly BASE_URL="https://github.com/stalwartlabs/stalwart/releases/latest/download" - -main() { - downloader --check - need_cmd uname - need_cmd mktemp - need_cmd chmod - need_cmd mkdir - need_cmd rm - need_cmd rmdir - need_cmd tar - - # Make sure we are running as root - if [ "$(id -u)" -ne 0 ] ; then - err "❌ Install failed: This program needs to run as root." - fi - - # Detect OS - local _os="unknown" - local _uname="$(uname)" - _account="stalwart" - if [ "${_uname}" = "Linux" ]; then - _os="linux" - elif [ "${_uname}" = "Darwin" ]; then - _os="macos" - _account="_stalwart" - fi - - # Default component setting - local _component="stalwart" - local _dir="/usr/local/bin" - - # Detect platform architecture - get_architecture || return 1 - local _arch="$RETVAL" - assert_nz "$_arch" "arch" - - # Download latest binary - say "⏳ Downloading ${_component} for ${_arch}..." - local _file="${_dir}/stalwart.tar.gz" - local _url="${BASE_URL}/${_component}-${_arch}.tar.gz" - ensure downloader "$_url" "$_file" "$_arch" - ensure tar zxvf "$_file" -C "$_dir" - ignore chmod +x "$_dir/stalwart" - ignore rm "$_file" - - return 0 -} - -get_architecture() { - local _ostype _cputype _bitness _arch _clibtype - _ostype="$(uname -s)" - _cputype="$(uname -m)" - _clibtype="gnu" - - if [ "$_ostype" = Linux ]; then - if [ "$(uname -o)" = Android ]; then - _ostype=Android - fi - if ldd --version 2>&1 | grep -q 'musl'; then - _clibtype="musl" - fi - fi - - if [ "$_ostype" = Darwin ] && [ "$_cputype" = i386 ]; then - # Darwin `uname -m` lies - if sysctl hw.optional.x86_64 | grep -q ': 1'; then - _cputype=x86_64 - fi - fi - - if [ "$_ostype" = SunOS ]; then - # Both Solaris and illumos presently announce as "SunOS" in "uname -s" - # so use "uname -o" to disambiguate. We use the full path to the - # system uname in case the user has coreutils uname first in PATH, - # which has historically sometimes printed the wrong value here. - if [ "$(/usr/bin/uname -o)" = illumos ]; then - _ostype=illumos - fi - - # illumos systems have multi-arch userlands, and "uname -m" reports the - # machine hardware name; e.g., "i86pc" on both 32- and 64-bit x86 - # systems. Check for the native (widest) instruction set on the - # running kernel: - if [ "$_cputype" = i86pc ]; then - _cputype="$(isainfo -n)" - fi - fi - - case "$_ostype" in - - Android) - _ostype=linux-android - ;; - - Linux) - check_proc - _ostype=unknown-linux-$_clibtype - _bitness=$(get_bitness) - ;; - - FreeBSD) - _ostype=unknown-freebsd - ;; - - NetBSD) - _ostype=unknown-netbsd - ;; - - DragonFly) - _ostype=unknown-dragonfly - ;; - - Darwin) - _ostype=apple-darwin - ;; - - illumos) - _ostype=unknown-illumos - ;; - - MINGW* | MSYS* | CYGWIN* | Windows_NT) - _ostype=pc-windows-gnu - ;; - - *) - err "unrecognized OS type: $_ostype" - ;; - - esac - - case "$_cputype" in - - i386 | i486 | i686 | i786 | x86) - _cputype=i686 - ;; - - xscale | arm) - _cputype=arm - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - fi - ;; - - armv6l) - _cputype=arm - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - - armv7l | armv8l) - _cputype=armv7 - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - - aarch64 | arm64) - _cputype=aarch64 - ;; - - x86_64 | x86-64 | x64 | amd64) - _cputype=x86_64 - ;; - - mips) - _cputype=$(get_endianness mips '' el) - ;; - - mips64) - if [ "$_bitness" -eq 64 ]; then - # only n64 ABI is supported for now - _ostype="${_ostype}abi64" - _cputype=$(get_endianness mips64 '' el) - fi - ;; - - ppc) - _cputype=powerpc - ;; - - ppc64) - _cputype=powerpc64 - ;; - - ppc64le) - _cputype=powerpc64le - ;; - - s390x) - _cputype=s390x - ;; - riscv64) - _cputype=riscv64gc - ;; - *) - err "unknown CPU type: $_cputype" - - esac - - # Detect 64-bit linux with 32-bit userland - if [ "${_ostype}" = unknown-linux-gnu ] && [ "${_bitness}" -eq 32 ]; then - case $_cputype in - x86_64) - if [ -n "${RUSTUP_CPUTYPE:-}" ]; then - _cputype="$RUSTUP_CPUTYPE" - else { - # 32-bit executable for amd64 = x32 - if is_host_amd64_elf; then { - echo "This host is running an x32 userland; as it stands, x32 support is poor," 1>&2 - echo "and there isn't a native toolchain -- you will have to install" 1>&2 - echo "multiarch compatibility with i686 and/or amd64, then select one" 1>&2 - echo "by re-running this script with the RUSTUP_CPUTYPE environment variable" 1>&2 - echo "set to i686 or x86_64, respectively." 1>&2 - echo 1>&2 - echo "You will be able to add an x32 target after installation by running" 1>&2 - echo " rustup target add x86_64-unknown-linux-gnux32" 1>&2 - exit 1 - }; else - _cputype=i686 - fi - }; fi - ;; - mips64) - _cputype=$(get_endianness mips '' el) - ;; - powerpc64) - _cputype=powerpc - ;; - aarch64) - _cputype=armv7 - if [ "$_ostype" = "linux-android" ]; then - _ostype=linux-androideabi - else - _ostype="${_ostype}eabihf" - fi - ;; - riscv64gc) - err "riscv64 with 32-bit userland unsupported" - ;; - esac - fi - - # Detect armv7 but without the CPU features Rust needs in that build, - # and fall back to arm. - # See https://github.com/rust-lang/rustup.rs/issues/587. - if [ "$_ostype" = "unknown-linux-gnueabihf" ] && [ "$_cputype" = armv7 ]; then - if ensure grep '^Features' /proc/cpuinfo | grep -q -v neon; then - # At least one processor does not have NEON. - _cputype=arm - fi - fi - - _arch="${_cputype}-${_ostype}" - - RETVAL="$_arch" -} - -check_proc() { - # Check for /proc by looking for the /proc/self/exe link - # This is only run on Linux - if ! test -L /proc/self/exe ; then - err "fatal: Unable to find /proc/self/exe. Is /proc mounted? Installation cannot proceed without /proc." - fi -} - -get_bitness() { - need_cmd head - # Architecture detection without dependencies beyond coreutils. - # ELF files start out "\x7fELF", and the following byte is - # 0x01 for 32-bit and - # 0x02 for 64-bit. - # The printf builtin on some shells like dash only supports octal - # escape sequences, so we use those. - local _current_exe_head - _current_exe_head=$(head -c 5 /proc/self/exe ) - if [ "$_current_exe_head" = "$(printf '\177ELF\001')" ]; then - echo 32 - elif [ "$_current_exe_head" = "$(printf '\177ELF\002')" ]; then - echo 64 - else - err "unknown platform bitness" - fi -} - -is_host_amd64_elf() { - need_cmd head - need_cmd tail - # ELF e_machine detection without dependencies beyond coreutils. - # Two-byte field at offset 0x12 indicates the CPU, - # but we're interested in it being 0x3E to indicate amd64, or not that. - local _current_exe_machine - _current_exe_machine=$(head -c 19 /proc/self/exe | tail -c 1) - [ "$_current_exe_machine" = "$(printf '\076')" ] -} - -get_endianness() { - local cputype=$1 - local suffix_eb=$2 - local suffix_el=$3 - - # detect endianness without od/hexdump, like get_bitness() does. - need_cmd head - need_cmd tail - - local _current_exe_endianness - _current_exe_endianness="$(head -c 6 /proc/self/exe | tail -c 1)" - if [ "$_current_exe_endianness" = "$(printf '\001')" ]; then - echo "${cputype}${suffix_el}" - elif [ "$_current_exe_endianness" = "$(printf '\002')" ]; then - echo "${cputype}${suffix_eb}" - else - err "unknown platform endianness" - fi -} - -say() { - printf '%s\n' "$1" -} - -err() { - say "$1" >&2 - exit 1 -} - -need_cmd() { - if ! check_cmd "$1"; then - err "need '$1' (command not found)" - fi -} - -check_cmd() { - command -v "$1" > /dev/null 2>&1 -} - -assert_nz() { - if [ -z "$1" ]; then err "assert_nz $2"; fi -} - -# Run a command that should never fail. If the command fails execution -# will immediately terminate with an error showing the failing -# command. -ensure() { - if ! "$@"; then err "command failed: $*"; fi -} - -# This wraps curl or wget. Try curl first, if not installed, -# use wget instead. -downloader() { - local _dld - local _ciphersuites - local _err - local _status - local _retry - if check_cmd curl; then - _dld=curl - elif check_cmd wget; then - _dld=wget - else - _dld='curl or wget' # to be used in error message of need_cmd - fi - - if [ "$1" = --check ]; then - need_cmd "$_dld" - elif [ "$_dld" = curl ]; then - check_curl_for_retry_support - _retry="$RETVAL" - get_ciphersuites_for_curl - _ciphersuites="$RETVAL" - if [ -n "$_ciphersuites" ]; then - _err=$(curl $_retry --proto '=https' --tlsv1.2 --ciphers "$_ciphersuites" --silent --show-error --fail --location "$1" --output "$2" 2>&1) - _status=$? - else - echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" - if ! check_help_for "$3" curl --proto --tlsv1.2; then - echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" - _err=$(curl $_retry --silent --show-error --fail --location "$1" --output "$2" 2>&1) - _status=$? - else - _err=$(curl $_retry --proto '=https' --tlsv1.2 --silent --show-error --fail --location "$1" --output "$2" 2>&1) - _status=$? - fi - fi - if [ -n "$_err" ]; then - if echo "$_err" | grep -q 404; then - err "❌ Binary for platform '$3' not found, this platform may be unsupported." - else - echo "$_err" >&2 - fi - fi - return $_status - elif [ "$_dld" = wget ]; then - if [ "$(wget -V 2>&1|head -2|tail -1|cut -f1 -d" ")" = "BusyBox" ]; then - echo "Warning: using the BusyBox version of wget. Not enforcing strong cipher suites for TLS or TLS v1.2, this is potentially less secure" - _err=$(wget "$1" -O "$2" 2>&1) - _status=$? - else - get_ciphersuites_for_wget - _ciphersuites="$RETVAL" - if [ -n "$_ciphersuites" ]; then - _err=$(wget --https-only --secure-protocol=TLSv1_2 --ciphers "$_ciphersuites" "$1" -O "$2" 2>&1) - _status=$? - else - echo "Warning: Not enforcing strong cipher suites for TLS, this is potentially less secure" - if ! check_help_for "$3" wget --https-only --secure-protocol; then - echo "Warning: Not enforcing TLS v1.2, this is potentially less secure" - _err=$(wget "$1" -O "$2" 2>&1) - _status=$? - else - _err=$(wget --https-only --secure-protocol=TLSv1_2 "$1" -O "$2" 2>&1) - _status=$? - fi - fi - fi - if [ -n "$_err" ]; then - if echo "$_err" | grep -q ' 404 Not Found'; then - err "❌ Binary for platform '$3' not found, this platform may be unsupported." - else - echo "$_err" >&2 - fi - fi - return $_status - else - err "Unknown downloader" # should not reach here - fi -} - -# Check if curl supports the --retry flag, then pass it to the curl invocation. -check_curl_for_retry_support() { - local _retry_supported="" - # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. - if check_help_for "notspecified" "curl" "--retry"; then - _retry_supported="--retry 3" - fi - - RETVAL="$_retry_supported" - -} - -check_help_for() { - local _arch - local _cmd - local _arg - _arch="$1" - shift - _cmd="$1" - shift - - local _category - if "$_cmd" --help | grep -q 'For all options use the manual or "--help all".'; then - _category="all" - else - _category="" - fi - - case "$_arch" in - - *darwin*) - if check_cmd sw_vers; then - case $(sw_vers -productVersion) in - 10.*) - # If we're running on macOS, older than 10.13, then we always - # fail to find these options to force fallback - if [ "$(sw_vers -productVersion | cut -d. -f2)" -lt 13 ]; then - # Older than 10.13 - echo "Warning: Detected macOS platform older than 10.13" - return 1 - fi - ;; - 11.*) - # We assume Big Sur will be OK for now - ;; - *) - # Unknown product version, warn and continue - echo "Warning: Detected unknown macOS major version: $(sw_vers -productVersion)" - echo "Warning TLS capabilities detection may fail" - ;; - esac - fi - ;; - - esac - - for _arg in "$@"; do - if ! "$_cmd" --help $_category | grep -q -- "$_arg"; then - return 1 - fi - done - - true # not strictly needed -} - -# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites -# if support by local tools is detected. Detection currently supports these curl backends: -# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. -get_ciphersuites_for_curl() { - if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then - # user specified custom cipher suites, assume they know what they're doing - RETVAL="$RUSTUP_TLS_CIPHERSUITES" - return - fi - - local _openssl_syntax="no" - local _gnutls_syntax="no" - local _backend_supported="yes" - if curl -V | grep -q ' OpenSSL/'; then - _openssl_syntax="yes" - elif curl -V | grep -iq ' LibreSSL/'; then - _openssl_syntax="yes" - elif curl -V | grep -iq ' BoringSSL/'; then - _openssl_syntax="yes" - elif curl -V | grep -iq ' GnuTLS/'; then - _gnutls_syntax="yes" - else - _backend_supported="no" - fi - - local _args_supported="no" - if [ "$_backend_supported" = "yes" ]; then - # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. - if check_help_for "notspecified" "curl" "--tlsv1.2" "--ciphers" "--proto"; then - _args_supported="yes" - fi - fi - - local _cs="" - if [ "$_args_supported" = "yes" ]; then - if [ "$_openssl_syntax" = "yes" ]; then - _cs=$(get_strong_ciphersuites_for "openssl") - elif [ "$_gnutls_syntax" = "yes" ]; then - _cs=$(get_strong_ciphersuites_for "gnutls") - fi - fi - - RETVAL="$_cs" -} - -# Return cipher suite string specified by user, otherwise return strong TLS 1.2-1.3 cipher suites -# if support by local tools is detected. Detection currently supports these wget backends: -# GnuTLS and OpenSSL (possibly also LibreSSL and BoringSSL). Return value can be empty. -get_ciphersuites_for_wget() { - if [ -n "${RUSTUP_TLS_CIPHERSUITES-}" ]; then - # user specified custom cipher suites, assume they know what they're doing - RETVAL="$RUSTUP_TLS_CIPHERSUITES" - return - fi - - local _cs="" - if wget -V | grep -q '\-DHAVE_LIBSSL'; then - # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. - if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then - _cs=$(get_strong_ciphersuites_for "openssl") - fi - elif wget -V | grep -q '\-DHAVE_LIBGNUTLS'; then - # "unspecified" is for arch, allows for possibility old OS using macports, homebrew, etc. - if check_help_for "notspecified" "wget" "TLSv1_2" "--ciphers" "--https-only" "--secure-protocol"; then - _cs=$(get_strong_ciphersuites_for "gnutls") - fi - fi - - RETVAL="$_cs" -} - -# Return strong TLS 1.2-1.3 cipher suites in OpenSSL or GnuTLS syntax. TLS 1.2 -# excludes non-ECDHE and non-AEAD cipher suites. DHE is excluded due to bad -# DH params often found on servers (see RFC 7919). Sequence matches or is -# similar to Firefox 68 ESR with weak cipher suites disabled via about:config. -# $1 must be openssl or gnutls. -get_strong_ciphersuites_for() { - if [ "$1" = "openssl" ]; then - # OpenSSL is forgiving of unknown values, no problems with TLS 1.3 values on versions that don't support it yet. - echo "TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384" - elif [ "$1" = "gnutls" ]; then - # GnuTLS isn't forgiving of unknown values, so this may require a GnuTLS version that supports TLS 1.3 even if wget doesn't. - # Begin with SECURE128 (and higher) then remove/add to build cipher suites. Produces same 9 cipher suites as OpenSSL but in slightly different order. - echo "SECURE128:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1:-VERS-DTLS-ALL:-CIPHER-ALL:-MAC-ALL:-KX-ALL:+AEAD:+ECDHE-ECDSA:+ECDHE-RSA:+AES-128-GCM:+CHACHA20-POLY1305:+AES-256-GCM" - fi -} - -# This is just for indicating that commands' results are being -# intentionally ignored. Usually, because it's being executed -# as part of error handling. -ignore() { - "$@" -} - -main "$@" || exit 1 diff --git a/resources/docker/entrypoint.sh b/resources/docker/entrypoint.sh deleted file mode 100644 index a845de1d..00000000 --- a/resources/docker/entrypoint.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/usr/bin/env sh -# shellcheck shell=dash - -exec /usr/local/bin/stalwart --config /opt/stalwart/etc/config.json