Recently I decided to look into moving away from GMail and start using a different mail service and landed on ProtonMail. My one gripe is their Android App is not great and has some nagging issues. So I wanted to see if I can use a much more well-known mail client for Android, K-9 Mail.
The main issue with this is that ProtonMail doesn’t have native IMAP support. But, they do have a ProtonMail Bridge which simulates IMAP. They say ThunderBird is offically supported, which is a fairly robust Email client, so I was optimistic that I could get K-9 to work.
TL;DR Note: If you want to skip over my trys and only want to know how to get it working, you can skip to my solution.
My Setup
I run a linux based server that is on my home network. It is headless (no monitor) and runs a handful of docker microservices. Most of them do not matter for this write-up, but one that does play a a fairly large role is my Wireguard VPN container, which my phone is 100% always connected to. This allows my to connect directly to my server from my phone using it’s internal IP address and not needing to expose it to the internet. I highly suggest that you do not expose this to the internet.
My phone is Android 11 and has a kernal with wireguard built-in. This allows me to be connected to Wireguard all the time doesn’t effect battery that much. K-9 mail was installed through f-droid.
I have ProntonMail Plus, which is needed in order to use the ProtonMail Bridge.
Attempt 1 - Official ProtonMail Bridge (deb)
Install
First thing I did was download and install the .deb
from ProntonMail’s website. My linux server is normally headless, so I decided to pull out an extra monitor I had around, temporarily setup a desktop and set up my account through the gui and was given a popup similar to this:
Looking like the bridge was up and running, I launched K-9 Mail and started adding the account.
K-9 Mail
I used the all the settings that ProtonMail gave me (username, password and port) but for IP address I used the internal address for my server instead of 127.0.0.1
. I also set the Security
to STARTTLS
.
Issues
K-9 fails to connect. After investigating I found that the ProtonMail Bridge only listens for requests from 127.0.0.1
i.e. the same computer it’s running on.
Alright, I know how to get arround that
Attempt 2 - Proxy to Official ProtonMail Bridge (deb)
Socat Proxy
I can use socat
to proxy a different port that will accept connections from all IP addresses and proxy that to the ProtonBridge.
socat TCP-LISTEN:25,fork TCP:127.0.0.1:1025 &
socat TCP-LISTEN:143,fork TCP:127.0.0.1:1143 &
So now SERVER_IP:25
-> 127.0.0.1:1025
and SERVER_IP:143
-> 127.0.0.1:1143
.
I updated the ports in my K-9 settings and it connected to the server!
Issues
It grabbed my folders/labels from ProntonMail correctly and I was able to send emails successfully, which was good. But there was no mail showing up in my inbox or any folder. I tried go through the K-9 settings to see if I was missing something but it all looked okay.
I started going through the Android logcat to see if there was any errors thing and noticed there was an error message that K-9 was handling, unsupported search query
.
I wasn’t sure if this was from K-9 or from ProtonMail. Luckily both are open source so I could search and I found that it was from ProntMail
ProtonMail/proton-bridge/internal/imap/mailbox_messages.go#L316
// SearchMessages searches messages. The returned list must contain UIDs if
// uid is set to true, or sequence numbers otherwise.
func (im *imapMailbox) SearchMessages(isUID bool, criteria *imap.SearchCriteria) (ids []uint32, err error) { //nolint[gocyclo]
// Called from go-imap in goroutines - we need to handle panics for each function.
defer im.panicHandler.HandlePanic()
if criteria.Not != nil || criteria.Or != nil {
return nil, errors.New("unsupported search query")
}
if criteria.Body != nil || criteria.Text != nil {
log.Warn("Body and Text criteria not applied")
}
So either K-9 is passing criteria.Not
or criteria.Or
when searching for messages. It turns out it’s criteria.Not
.
Now I can either edit the K-9 source or ProtonMail Bridge source and try again. I decided to ProtonMail Bridge source route.
Attempt 3 - Compile ProtonMail Bridge Myself
As I was looking around, I found this great docker setup by GitHub user shenxn
. I wish I would have found this before trying the initial setup with a gui, as this one uses the cli to login and has some good documentation of how to set it up.
They also have an example of how to build the ProtonMail Bridge from source and deploy it as a docker and funny enough was doing the same proxying I was doing.
My Repo
Using that repo as a reference, I decided to make my own repo for a docker image that I can apply some patches to fix stuff as I see fit.
The way that it works is
- I get the source code from https://github.com/ProtonMail/proton-bridge
- I apply the patches from the
./patches
folder in my repo (currently just ignorecriteria.Not
for K-9) - Build the source
- Build the docker image
Then when the docker image is spun up, it creates some socat
proxies and starts the protonmail-bridge.
With that setup, I tore down my previous attempts (uninstalled the bridge, took down the socat
proxies) just to avoid conflicts.
Solution
Setting up the docker is a two step process, first you need to do initialize to get your credentials
docker run --rm -it -v /path/to/protonmail:/root ghcr.io/tchilderhose/protonmail-bridge-docker init
and follow the steps in the readme from the repo
Wait for the bridge to startup, use
login
command and follow the instructions to add your account into the bridge. Then useinfo
to see the configuration information (username and password). After that, use exit toexit
the bridge. You may needCTRL+C
to exit the docker entirely.
This will place a bunch of files needed for the bridge in the /path/to/protonmail
folder (feel free to change this beforehand).
Then you can spin up the actual bridge for use. I use docker-compose
so I just added
protonmail:
image: ghcr.io/tchilderhose/protonmail-bridge-docker
container_name: protonmail
restart: unless-stopped
ports:
- "1025:25/tcp"
- "1143:143/tcp"
volumes:
- /path/to/protonmail:/root
With that running I updated my K-9 settings with those port numbers (1025, 1143) and it worked! All my emails started showing up and it all seemed to be working.
Summary
The final connection path: (Again, I highly suggest that you do not expose the docker to the internet and connect to it through a secure VPN.)
K9 Mail
-> Wireguard Tunnel to my Server
-> protonmail-bridge-docker
-> ProtonMail Bridge
-> ProtonMail Servers
Getting this working took a few attempts, but it wasn’t overall too bad. While it’s only been a few days that it’s been setup, I’ve had no issues and have been really happy with it. There is a lot of good settings and documentation to play around with in K-9 to get it setup to my likeing.
The one thing that is missing is sending via an alias. This isn’t something I’ve needed to do yet, snor do I think I will really do, but it is something to consider.
ProtonMail keep saying a new mobile app will be rolling out by years end, so if/when that drops, I will need to reevaluate if I will switch to that or keep this setup. But currently I am really liking it so will probably be sticking with it for awhile.