lugh.ch

as nerdy as needed.

Lesser known ProtonMail tricks


As you could learn from the "sunsetting" blog post, I am exploring the depths of email as a service (a whole new concept to me) in the form of ProtonMail. During this surveying period, I found some limits that need some non-evident fiddling to overcome.

Filters that work on email headers

Sometimes you want to label or move emails depending on presence, absence or comparison of a specific message header. This is not possible in the ProtonMail filter wizard, as this one only works on subject, sender, recipient or attachments. Custom Sieve filters to the rescue! You simply need to ensure to manually create labels or folders referenced in Sieve filters.

Mark spam as read

This removes the "unread" count in the spam folder.

require ["include", "environment", "variables", "relational", "comparator-i;ascii-numeric", "spamtest", "imap4flags"];

# Mark all spam as read
if allof (environment :matches "vnd.proton.spam-threshold" "*",
spamtest :value "ge" :comparator "i;ascii-numeric" "${1}")
{
    addflag "\\Seen";
    return;
}

Label potentially spammy messages

Useful to heighten your senses to detect baddies. This is for mails that did not cross the spam threshold.

require ["include", "environment", "variables", "relational", "comparator-i;ascii-numeric", "spamtest"];
require "fileinto";

# Generated: Do not run this script on spam messages
if allof (environment :matches "vnd.proton.spam-threshold" "*",
spamtest :value "ge" :comparator "i;ascii-numeric" "${1}")
{
    return;
}

if header :value "gt" :comparator "i;ascii-numeric" "X-Pm-Spamscore" "2" {
  fileinto "spammy";
}

Another option is to rely on the spamtest extension. It's just that you have no insight there. With my above solution, you can adjust labelling by aligning to the X-Pm-Spamscore message header.

fileinto works for folders and labels

fileinto "<foo>" references either a folder or a label. So if you created a folder, it will move it there. If you created a label, the email will get that label applied.

Label mails coming from SimpleLogin

require "fileinto";

# Label all mails forwarded by SimpleLogin
# WARNING: if you attach PGP public keys automatically (see settings),
# the "hidden" original mail address will be leaked on reply
if header :is "X-Simplelogin-Type" "Forward" {
    fileinto "simplelogin";
}

Move emails collected by a catch-all address to a folder

As these are very likely to be spam anyways, you might want to have them out of your way.

require "fileinto";

# Label all external mails
if header :is "Delivered-To" "<catchall>@<example.org>" {
    fileinto "harvester";
}

Label emails received from non-ProtonMail senders

This adds a label to email received from the outsideā„¢.

require ["include", "environment", "variables", "relational", "comparator-i;ascii-numeric", "spamtest"];
require "fileinto";

# Generated: Do not run this script on spam messages
if allof (environment :matches "vnd.proton.spam-threshold" "*",
spamtest :value "ge" :comparator "i;ascii-numeric" "${1}")
{
    return;
}

# Label all external mails
if header :is "X-Pm-Origin" "external" {
    fileinto "external";
}

Label emails received without transport encryption

Today this is already a sign of poor craftsmanship and increased risk of spam or scam.

require ["include", "environment", "variables", "relational", "comparator-i;ascii-numeric", "spamtest"];
require "fileinto";

# Generated: Do not run this script on spam messages
if allof (environment :matches "vnd.proton.spam-threshold" "*",
spamtest :value "ge" :comparator "i;ascii-numeric" "${1}")
{
    return;
}

# Label all mails that have been sent without transport encryption (TLS)
if header :is "x-pm-transfer-encryption" "none" {
    fileinto "unencrypted";
}

MTA-STS with a custom domain

On a paid plan, you can use your own domain to send/receive mail. People have already written down everything worth knowing about MTA-STS. But I need to document my own set-up somewhere and eventually somebody can profit from this.

Web part (mta-sts.txt)

Create a text file reachable at https://mta-sts.example.org/.well-known/mta-sts.txt and adjust mode and max_age to your needs:

version: STSv1
mode: testing
mx: mail.protonmail.ch
mx: mailsec.protonmail.ch
max_age: 604800

See RFC 8461 for details on the supported modes and max_age configuration.

MTA-STS requirements

MTA-STS policies are obviously fetched via HTTPS only so ensure you have a valid certificate. Additionally:

  • return the text file as text/plain
  • ensure to not cache it
  • respond with HTTP 200 (redirects are not followed)

More details in https://datatracker.ietf.org/doc/html/rfc8461#section-3.3

DNS records

  1. Depending on your DNS/web server setup: create the necessary DNS record to support serving the above text file to MTAs
  2. Create an additional DNS TXT record like this (the id value needs to uniquely identify a version of the policy, so use a method to represent a point in time (e.g. UNIX timestamp or ISO 8601 dates)

    _mta-sts.example.org.   120 IN  TXT "v=STSv1; id=1696855385"
    
  3. Whenever you make textual changes in mta-sts.txt, update the id value of the TXT record afterwards


Similar posts