forked from mirrors/protonmail-bridge-docker
Change docker user and some minor improvements
This commit is contained in:
14
Dockerfile
14
Dockerfile
@@ -11,7 +11,7 @@ COPY build.sh /build/
|
|||||||
COPY http_rest_frontend /build/http_rest_frontend
|
COPY http_rest_frontend /build/http_rest_frontend
|
||||||
RUN bash build.sh
|
RUN bash build.sh
|
||||||
|
|
||||||
FROM ubuntu:bionic
|
FROM krallin/ubuntu-tini:bionic
|
||||||
LABEL maintainer="Xiaonan Shen <s@sxn.dev>"
|
LABEL maintainer="Xiaonan Shen <s@sxn.dev>"
|
||||||
|
|
||||||
EXPOSE 25/tcp
|
EXPOSE 25/tcp
|
||||||
@@ -19,13 +19,15 @@ EXPOSE 143/tcp
|
|||||||
|
|
||||||
# Install dependencies and protonmail bridge
|
# Install dependencies and protonmail bridge
|
||||||
RUN apt-get update \
|
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/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Copy bash scripts
|
# Copy bash scripts
|
||||||
COPY gpgparams entrypoint.sh /srv/protonmail/
|
COPY gpgparams entrypoint.sh run_protonmail_bridge.sh cli.sh /protonmail/bin/
|
||||||
|
|
||||||
# Copy protonmail
|
# 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"]
|
||||||
|
|||||||
6
cli.sh
6
cli.sh
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
URL_BASE=http://127.0.0.1:1080
|
URL_BASE=http://127.0.0.1:${PROTON_MANAGEMENT_PORT:-1080}
|
||||||
|
|
||||||
print_help() {
|
print_help() {
|
||||||
echo "Available commands:"
|
echo "Available commands:"
|
||||||
@@ -24,12 +24,14 @@ account_login() {
|
|||||||
echo
|
echo
|
||||||
read -p "2FA Code (leave empty if not set): " TWO_FACTOR
|
read -p "2FA Code (leave empty if not set): " TWO_FACTOR
|
||||||
read -p "Mailbox Password (leave empty if not set): " MAILBOX_PASSWORD
|
read -p "Mailbox Password (leave empty if not set): " MAILBOX_PASSWORD
|
||||||
|
read -p "Address Mode (combined / split): " ADDRESS_MODE
|
||||||
|
|
||||||
curl ${URL_BASE}/accounts -XPUT \
|
curl ${URL_BASE}/accounts -XPUT \
|
||||||
--data-urlencode "username=${USERNAME}" \
|
--data-urlencode "username=${USERNAME}" \
|
||||||
--data-urlencode "password=${PASSWORD}" \
|
--data-urlencode "password=${PASSWORD}" \
|
||||||
--data-urlencode "two-factor=${TWO_FACTOR}" \
|
--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() {
|
account_delete() {
|
||||||
|
|||||||
41
entrypoint.sh
Normal file → Executable file
41
entrypoint.sh
Normal file → Executable file
@@ -2,36 +2,15 @@
|
|||||||
|
|
||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Generate gpg keys
|
echo "Welcome to ProtonMail Bridge Docker 2.0. This version comes with plently of"
|
||||||
if [ ! -f ${HOME}/.gnupg ]; then
|
echo "improvements. This version is not compatible with old configs and data volumes."
|
||||||
echo "Generateing gpg keys..."
|
echo "If you are coming from the old version, please clean up your volumes and set up"
|
||||||
# set GNUPGHOME as a workaround for
|
echo "everything from scratch. For more information about the new version, check"
|
||||||
#
|
echo "https://github.com/shenxn/protonmail-bridge-docker"
|
||||||
# gpg-agent[106]: error binding socket to '/root/.gnupg/S.gpg-agent': File name too long
|
echo
|
||||||
#
|
|
||||||
# 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
|
|
||||||
|
|
||||||
# Initialize pass
|
groupadd -g ${PROTON_GID:-1001} proton
|
||||||
if [ ! -f ${HOME}/.password-store/.gpg-id ]; then
|
useradd -g proton -u ${PROTON_UID:-1001} -m proton
|
||||||
echo "Initializing pass"
|
chown proton:proton -R /protonmail/data
|
||||||
pass init pass-key
|
|
||||||
fi
|
|
||||||
|
|
||||||
# socat will make the conn appear to come from 127.0.0.1
|
exec gosu proton:proton run_protonmail_bridge.sh "$@"
|
||||||
# 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 $@
|
|
||||||
|
|||||||
@@ -6,11 +6,13 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
"github.com/ProtonMail/proton-bridge/internal/config/settings"
|
||||||
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
"github.com/ProtonMail/proton-bridge/internal/frontend/types"
|
||||||
"github.com/julienschmidt/httprouter"
|
"github.com/julienschmidt/httprouter"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *frontendCLI) loginAccount(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
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")
|
password := r.FormValue("password")
|
||||||
twoFactor := r.FormValue("two-factor")
|
twoFactor := r.FormValue("two-factor")
|
||||||
mailboxPassword := r.FormValue("mailbox-password")
|
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))
|
client, auth, err := f.bridge.Login(username, []byte(password))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.processAPIError(err)
|
f.processAPIError(err)
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf("Server error: %s", err.Error()), http.StatusUnauthorized)
|
||||||
fmt.Fprintln(w, "Server error:", err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if auth.HasTwoFactor() {
|
if auth.HasTwoFactor() {
|
||||||
if twoFactor == "" {
|
if twoFactor == "" {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
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
|
return
|
||||||
}
|
}
|
||||||
err = client.Auth2FA(context.Background(), twoFactor)
|
err = client.Auth2FA(context.Background(), twoFactor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.processAPIError(err)
|
f.processAPIError(err)
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf("Server error: %s", err.Error()), http.StatusUnauthorized)
|
||||||
fmt.Fprintln(w, "Server error:", err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if auth.HasMailboxPassword() {
|
if auth.HasMailboxPassword() {
|
||||||
if mailboxPassword == "" {
|
if mailboxPassword == "" {
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
http.Error(w, "Two password mode enabled but a mailbox password was not provided.", http.StatusUnauthorized)
|
||||||
fmt.Fprintln(w, "Two password mode enabled but a mailbox password was not provided.")
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} 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))
|
user, err := f.bridge.FinishLogin(client, auth, []byte(mailboxPassword))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.processAPIError(err)
|
f.processAPIError(err)
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
http.Error(w, fmt.Sprintf("Server error: %s", err.Error()), http.StatusUnauthorized)
|
||||||
fmt.Fprintln(w, "Server error:", err.Error())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "Account %s was added successfully.\n", user.Username())
|
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)
|
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) {
|
if f.settings.GetBool(settings.SMTPSSLKey) {
|
||||||
smtpSecurity = "SSL"
|
smtpSecurity = "SSL"
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, "IMAP port: %d\nIMAP security: %s\nSMTP port: %d\nSMTP security: %s\nUsername: %s\nPassword: %s\n",
|
fmt.Fprintf(w, "IMAP port: %s\nIMAP security: %s\nSMTP port: %s\nSMTP security: %s\nUsername: %s\nPassword: %s\n",
|
||||||
143,
|
os.Getenv("PROTON_IMAP_PORT"),
|
||||||
"STARTTLS",
|
"STARTTLS",
|
||||||
25,
|
os.Getenv("PROTON_SMTP_PORT"),
|
||||||
smtpSecurity,
|
smtpSecurity,
|
||||||
address,
|
address,
|
||||||
user.GetBridgePassword(),
|
user.GetBridgePassword(),
|
||||||
|
|||||||
@@ -146,7 +146,11 @@ func (f *frontendCLI) watchEvents() {
|
|||||||
|
|
||||||
func (f *frontendCLI) Loop() error {
|
func (f *frontendCLI) Loop() error {
|
||||||
f.loginWithEnv()
|
f.loginWithEnv()
|
||||||
http.ListenAndServe(":1080", f)
|
managementPort := os.Getenv("PROTON_MANAGEMENT_PORT")
|
||||||
|
if managementPort == "" {
|
||||||
|
managementPort = "1080"
|
||||||
|
}
|
||||||
|
http.ListenAndServe(":"+managementPort, f)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
70
run_protonmail_bridge.sh
Executable file
70
run_protonmail_bridge.sh
Executable file
@@ -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 <<EOF > ${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 "$@"
|
||||||
Reference in New Issue
Block a user