From 1c64b99201e443370e92b8a649d1cf46eb0bc25d Mon Sep 17 00:00:00 2001 From: Xiaonan Shen Date: Sat, 28 May 2022 17:28:20 +0800 Subject: [PATCH] Change docker user and some minor improvements --- Dockerfile | 14 +++--- cli.sh | 6 ++- entrypoint.sh | 41 +++++------------ http_rest_frontend/cli/accounts.go | 40 ++++++++++++----- http_rest_frontend/cli/frontend.go | 6 ++- run_protonmail_bridge.sh | 70 ++++++++++++++++++++++++++++++ 6 files changed, 125 insertions(+), 52 deletions(-) mode change 100644 => 100755 entrypoint.sh create mode 100755 run_protonmail_bridge.sh diff --git a/Dockerfile b/Dockerfile index ad0f379..0ac3123 100644 --- a/Dockerfile +++ b/Dockerfile @@ -11,7 +11,7 @@ COPY build.sh /build/ COPY http_rest_frontend /build/http_rest_frontend RUN bash build.sh -FROM ubuntu:bionic +FROM krallin/ubuntu-tini:bionic LABEL maintainer="Xiaonan Shen " EXPOSE 25/tcp @@ -19,13 +19,15 @@ EXPOSE 143/tcp # Install dependencies and protonmail bridge RUN apt-get update \ - && apt-get install -y --no-install-recommends socat pass libsecret-1-0 ca-certificates dbus-x11 \ + && apt-get install -y --no-install-recommends socat pass libsecret-1-0 ca-certificates curl gosu \ && rm -rf /var/lib/apt/lists/* # Copy bash scripts -COPY gpgparams entrypoint.sh /srv/protonmail/ - +COPY gpgparams entrypoint.sh run_protonmail_bridge.sh cli.sh /protonmail/bin/ # Copy protonmail -COPY --from=build /build/proton-bridge/proton-bridge /srv/protonmail/ +COPY --from=build /build/proton-bridge/proton-bridge /protonmail/bin +ENV PATH "/protonmail/bin:${PATH}" -ENTRYPOINT ["bash", "/srv/protonmail/entrypoint.sh"] +VOLUME [ "/protonmail/data" ] + +ENTRYPOINT ["/usr/local/bin/tini", "--", "/protonmail/bin/entrypoint.sh"] diff --git a/cli.sh b/cli.sh index 38a0789..9c30de4 100755 --- a/cli.sh +++ b/cli.sh @@ -2,7 +2,7 @@ set -e -URL_BASE=http://127.0.0.1:1080 +URL_BASE=http://127.0.0.1:${PROTON_MANAGEMENT_PORT:-1080} print_help() { echo "Available commands:" @@ -24,12 +24,14 @@ account_login() { echo read -p "2FA Code (leave empty if not set): " TWO_FACTOR read -p "Mailbox Password (leave empty if not set): " MAILBOX_PASSWORD + read -p "Address Mode (combined / split): " ADDRESS_MODE curl ${URL_BASE}/accounts -XPUT \ --data-urlencode "username=${USERNAME}" \ --data-urlencode "password=${PASSWORD}" \ --data-urlencode "two-factor=${TWO_FACTOR}" \ - --data-urlencode "mailbox-password=${MAILBOX_PASSWORD}" + --data-urlencode "mailbox-password=${MAILBOX_PASSWORD}" \ + --data-urlencode "address-mode=${ADDRESS_MODE}" } account_delete() { diff --git a/entrypoint.sh b/entrypoint.sh old mode 100644 new mode 100755 index e25dd56..f6f725b --- a/entrypoint.sh +++ b/entrypoint.sh @@ -2,36 +2,15 @@ set -e -# Generate gpg keys -if [ ! -f ${HOME}/.gnupg ]; then - echo "Generateing gpg keys..." - # set GNUPGHOME as a workaround for - # - # gpg-agent[106]: error binding socket to '/root/.gnupg/S.gpg-agent': File name too long - # - # when using docker volume mount - # - # ref: https://dev.gnupg.org/T2964 - # - export GNUPGHOME=/tmp/gnupg - mkdir ${GNUPGHOME} - chmod 700 ${GNUPGHOME} - gpg --generate-key --batch /srv/protonmail/gpgparams - pkill gpg-agent - mv ${GNUPGHOME} ${HOME}/.gnupg - export GNUPGHOME="" -fi +echo "Welcome to ProtonMail Bridge Docker 2.0. This version comes with plently of" +echo "improvements. This version is not compatible with old configs and data volumes." +echo "If you are coming from the old version, please clean up your volumes and set up" +echo "everything from scratch. For more information about the new version, check" +echo "https://github.com/shenxn/protonmail-bridge-docker" +echo -# Initialize pass -if [ ! -f ${HOME}/.password-store/.gpg-id ]; then - echo "Initializing pass" - pass init pass-key -fi +groupadd -g ${PROTON_GID:-1001} proton +useradd -g proton -u ${PROTON_UID:-1001} -m proton +chown proton:proton -R /protonmail/data -# socat will make the conn appear to come from 127.0.0.1 -# ProtonMail Bridge currently expects that. -# It also allows us to bind to the real ports :) -socat TCP-LISTEN:25,fork TCP:127.0.0.1:1025 & -socat TCP-LISTEN:143,fork TCP:127.0.0.1:1143 & - -/srv/protonmail/proton-bridge --cli $@ +exec gosu proton:proton run_protonmail_bridge.sh "$@" diff --git a/http_rest_frontend/cli/accounts.go b/http_rest_frontend/cli/accounts.go index 500323d..c0ef5a7 100644 --- a/http_rest_frontend/cli/accounts.go +++ b/http_rest_frontend/cli/accounts.go @@ -6,11 +6,13 @@ import ( "fmt" "io" "net/http" + "os" "strings" "github.com/ProtonMail/proton-bridge/internal/config/settings" "github.com/ProtonMail/proton-bridge/internal/frontend/types" "github.com/julienschmidt/httprouter" + "github.com/sirupsen/logrus" ) func (f *frontendCLI) loginAccount(w http.ResponseWriter, r *http.Request, _ httprouter.Params) { @@ -23,33 +25,38 @@ func (f *frontendCLI) loginAccount(w http.ResponseWriter, r *http.Request, _ htt password := r.FormValue("password") twoFactor := r.FormValue("two-factor") mailboxPassword := r.FormValue("mailbox-password") + addressMode := r.FormValue("address-mode") + if addressMode == "" { + addressMode = "combined" + } + if addressMode != "combined" && addressMode != "split" { + http.Error(w, fmt.Sprintf("%s is not a valid address mode. Choose from 'combined' and 'split'."), http.StatusBadRequest) + return + } client, auth, err := f.bridge.Login(username, []byte(password)) if err != nil { f.processAPIError(err) - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprintln(w, "Server error:", err.Error()) + http.Error(w, fmt.Sprintf("Server error: %s", err.Error()), http.StatusUnauthorized) return } if auth.HasTwoFactor() { if twoFactor == "" { w.WriteHeader(http.StatusUnauthorized) - fmt.Fprintln(w, "2FA enabled for the account but a 2FA code was not provided.") + http.Error(w, "2FA enabled for the account but a 2FA code was not provided.", http.StatusUnauthorized) return } err = client.Auth2FA(context.Background(), twoFactor) if err != nil { f.processAPIError(err) - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprintln(w, "Server error:", err.Error()) + http.Error(w, fmt.Sprintf("Server error: %s", err.Error()), http.StatusUnauthorized) return } } if auth.HasMailboxPassword() { if mailboxPassword == "" { - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprintln(w, "Two password mode enabled but a mailbox password was not provided.") + http.Error(w, "Two password mode enabled but a mailbox password was not provided.", http.StatusUnauthorized) return } } else { @@ -58,11 +65,20 @@ func (f *frontendCLI) loginAccount(w http.ResponseWriter, r *http.Request, _ htt user, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword)) if err != nil { f.processAPIError(err) - w.WriteHeader(http.StatusUnauthorized) - fmt.Fprintln(w, "Server error:", err.Error()) + http.Error(w, fmt.Sprintf("Server error: %s", err.Error()), http.StatusUnauthorized) return } fmt.Fprintf(w, "Account %s was added successfully.\n", user.Username()) + + if addressMode == "split" { + err = user.SwitchAddressMode() + if err != nil { + logrus.Errorf("Failed to switch address mode of %s to split: %s", user.Username(), err.Error()) + http.Error(w, "Failed to switch address mode to split", http.StatusInternalServerError) + return + } + } + f.printAccountInfo(w, user) } @@ -135,10 +151,10 @@ func (f *frontendCLI) printAccountAddressInfo(w io.Writer, user types.User, addr if f.settings.GetBool(settings.SMTPSSLKey) { smtpSecurity = "SSL" } - fmt.Fprintf(w, "IMAP port: %d\nIMAP security: %s\nSMTP port: %d\nSMTP security: %s\nUsername: %s\nPassword: %s\n", - 143, + fmt.Fprintf(w, "IMAP port: %s\nIMAP security: %s\nSMTP port: %s\nSMTP security: %s\nUsername: %s\nPassword: %s\n", + os.Getenv("PROTON_IMAP_PORT"), "STARTTLS", - 25, + os.Getenv("PROTON_SMTP_PORT"), smtpSecurity, address, user.GetBridgePassword(), diff --git a/http_rest_frontend/cli/frontend.go b/http_rest_frontend/cli/frontend.go index 224de01..53e5662 100644 --- a/http_rest_frontend/cli/frontend.go +++ b/http_rest_frontend/cli/frontend.go @@ -146,7 +146,11 @@ func (f *frontendCLI) watchEvents() { func (f *frontendCLI) Loop() error { f.loginWithEnv() - http.ListenAndServe(":1080", f) + managementPort := os.Getenv("PROTON_MANAGEMENT_PORT") + if managementPort == "" { + managementPort = "1080" + } + http.ListenAndServe(":"+managementPort, f) return nil } diff --git a/run_protonmail_bridge.sh b/run_protonmail_bridge.sh new file mode 100755 index 0000000..ec1f281 --- /dev/null +++ b/run_protonmail_bridge.sh @@ -0,0 +1,70 @@ +#!/bin/bash + +set -e + +# Generate gpg keys +GNUPG_PATH=/protonmail/data/gnupg +export GNUPGHOME=${GNUPG_PATH} +if [ ! -f ${GNUPG_PATH} ]; then + echo "Generateing gpg keys..." + # set GNUPGHOME to a temp directory as a workaround for + # + # gpg-agent[106]: error binding socket to '/root/.gnupg/S.gpg-agent': File name too long + # + # when using docker volume mount + # + # ref: https://dev.gnupg.org/T2964 + # + export GNUPGHOME=/tmp/gnupg + mkdir ${GNUPGHOME} + chmod 700 ${GNUPGHOME} + gpg --generate-key --batch /protonmail/bin/gpgparams + pkill gpg-agent + mv ${GNUPGHOME} ${GNUPG_PATH} + export GNUPGHOME=${GNUPG_PATH} +fi + +# Initialize pass +PASSWORD_STORE=/protonmail/data/password-store +if [ ! -f ${PASSWORD_STORE} ]; then + echo "Initializing pass..." + pass init pass-key + # Move password store to /protonmail/data + mv ${HOME}/.password-store ${PASSWORD_STORE} +fi +# Link the password store back to ~/.password-store +# There is no easy way to change the path used by pass +ln -s ${PASSWORD_STORE} ${HOME}/.password-store + +# Link config and cache folders to /protonmail/data +PROTON_CONFIG_PATH=/protonmail/data/config +PROTON_CACHE_PATH=/protonmail/data/cache +mkdir -p ${PROTON_CONFIG_PATH} +mkdir -p ${HOME}/.config +ln -s ${PROTON_CONFIG_PATH} ${HOME}/.config/protonmail +mkdir -p ${PROTON_CACHE_PATH} +mkdir ${HOME}/.cache +ln -s ${PROTON_CACHE_PATH} ${HOME}/.cache/protonmail + +# Generateing perfs.json +mkdir -p ${PROTON_CONFIG_PATH}/bridge +if [ ${PROTON_SMTP_SECURITY:-STARTTLS} == "SSL" ]; then + PROTON_SSL_SMTP="true" +else + PROTON_SSL_SMTP="false" +fi +cat < ${PROTON_CONFIG_PATH}/bridge/prefs.json +{ + "allow_proxy": "${PROTON_ALLOW_PROXY:-true}", + "autoupdate": "false", + "user_ssl_smtp": "${PROTON_SSL_SMTP}" +} +EOF + +# socat will make the conn appear to come from 127.0.0.1 +# ProtonMail Bridge currently expects that. +# It also allows us to bind to the real ports :) +socat TCP-LISTEN:${PROTON_SMTP_PORT:=25},fork TCP:127.0.0.1:1025 & +socat TCP-LISTEN:${PROTON_IMAP_PORT:=143},fork TCP:127.0.0.1:1143 & + +exec proton-bridge --cli "$@"