Skip to content

Instantly share code, notes, and snippets.

@gwire
Last active June 7, 2025 18:40
Show Gist options
  • Save gwire/47a9073a9aa55c1696dae5bdf99a1216 to your computer and use it in GitHub Desktop.
Save gwire/47a9073a9aa55c1696dae5bdf99a1216 to your computer and use it in GitHub Desktop.
Using Nginx to proxy IMAPS

Using Nginx to proxy IMAPS

Nginx can be used to proxy several mail protocols. This can be useful in a variety of scenarios: load balancing, providing up-to-date TLS for servers that don’t support it, graceful server migration, or enabling user-specific storage policies.

Unfortunately, what’s not clear from the documentation is that nginx (at least at 1.18) can’t proxy over encrypted connections - which makes this unworkable except in the case of servers on the local network.

However, you can instead put nginx in front of stunnels to enable this.

For this example we'll configure nginx to route IMAP requests for user bob to oldmail.example.com and requests for alice to newmail.example.com.

This assumes a server running Ububtu 22.04 with IPv6 enabled.

Nginx authentication server

The first tricky aspect of the nginx configuration is the nginx auth server, but if you think of it as a username-based router, rather than authenticator, it makes more sense. You don't have to check passwords, you don't even have to check for a valid user.

We'll set up a simple auth server on http://[::1]:8081/auth_imap

  • Add the file /etc/nginx/sitate-available/mailauth.conf

    # /etc/nginx/sites-available/mailauth.conf
    
    map $http_auth_user $authstatus {
      "alice" OK;
      "bob" OK;
      default "[AUTHENTICATIONFAILED] Authentication failed.";
    }
    map $http_auth_user $authport {
      "bob" 8998;
      default 8997;
    }
    
    server {
      listen [::1]:8081;
      location /auth_imap {
        add_header Auth-Status $authstatus;
        add_header Auth-Server ::1;
        add_header Auth-Port $authport;
        return 204;
      }
      location / {
        return 404;
      }
    }
  • Enable the site and reload nginx

    ln -s /etc/nginx/sites-available/mailauth.conf /etc/nginx/sites-enabled/

Add the mail proxy config

  • Add a configuration file /etc/nginx/mail.conf

    # /etc/nginx/mail.conf
    mail {
      server_name mail.example.com;
      ssl_certificate /etc/letsencrypt/live/mail.example.com/fullchain.pem;
      ssl_certificate_key /etc/letsencrypt/live/mail.example.com/privkey.pem;
    
      server {
        listen [::]:993 ssl;
        listen 993 ssl;
        ssl_protocols TLSv1.2 TLSv1.3;
        ssl_ciphers AES128-SHA:AES256-SHA:RC4-SHA:DES-CBC3-SHA:RC4-MD5;
        ssl_session_timeout 10m;
    
        protocol imap;
        imap_auth plain;
    
        proxy_pass_error_message on;
        auth_http http://[::1]:8081/auth_imap;
        auth_http_header User-Agent "nginx_mail_imap";
      }
    }
  • Edit /etc/nginx/nginx.conf to include the following (near the bottom)

    include /etc/nginx/mail.conf;
  • Reload nginx

    systemctl reload nginx

Set up stunnel

  • Install stunnel

    apt install stunnel
  • Add the config files into /etc/stunnel/

    # /etc/stunnel/imaps-8997.conf 
    client = yes
    foreground = yes
    connect = newmail.example.com:imaps
    
    # /etc/stunnel/imaps-8998.conf 
    client = yes
    foreground = yes
    connect = oldmail.example.com:imaps
    
  • Add systemd socket files for the ports

    # /etc/systemd/system/imaps-8997.socket
    [Unit]
    Description=Stunnel socket for IMAPS proxy 8997
    
    [Socket]
    ListenStream=[::1]:8997
    Accept=yes
    NoDelay=yes
    
    [Install]
    WantedBy=sockets.target
    # /etc/systemd/system/imaps-8998.socket
    [Unit]
    Description=Stunnel socket for IMAPS proxy 8998
    
    [Socket]
    ListenStream=[::1]:8998
    Accept=yes
    NoDelay=yes
    
    [Install]
    WantedBy=sockets.target
  • Add the corresponding service files

    # /etc/systemd/system/[email protected]
    [Unit]
    Description=Stunnel socket for IMAPS proxy 8997
    
    [Service]
    Type=simple
    StandardInput=socket
    StandardOutput=socket
    StandardError=journal
    ProtectHome=yes
    ProtectSystem=strict
    PrivateTmp=yes
    ExecStart=/usr/bin/stunnel4 /etc/stunnel/imaps-8997.conf
    
    [Install]
    WantedBy=multi-user.target
    # /etc/systemd/system/[email protected]
    [Unit]
    Description=Stunnel socket for IMAPS proxy 8998
    
    [Service]
    Type=simple
    StandardInput=socket
    StandardOutput=socket
    StandardError=journal
    ProtectHome=yes
    ProtectSystem=strict
    PrivateTmp=yes
    ExecStart=/usr/bin/stunnel4 /etc/stunnel/imaps-8998.conf
    
    [Install]
    WantedBy=multi-user.target
  • Enable the sockets

    systemctl enable imaps-8997.socket --now
    systemctl enable imaps-8998.socket --now

Test

  • Use nc to check the tunnels are working

    $ nc ::1 8997
    * OK [CAPABILITY IMAP4...
    
  • Generate some good and bad login strings using base64

    $ echo -en "\0bob\0goodpass" | openssl base64
    AGJvYgBnb29kcGFzcw==
    $ echo -en "\0bob\0badpass" | openssl base64
    AGJvYgBiYWRwYXNz
    
  • Use s_client for interactive sessions

    $ openssl s_client -connect mail.example.com:993
    [...]
    * OK IMAP4 ready
    A AUTHENTICATE PLAIN AGJvYgBnb29kcGFzcw==
    A OK [CAPABILITY ...]
    A LOGOUT
    * BYE Logging out
    A OK LOGOUT completed
    closed
    
    $ openssl s_client -connect mail.example.com:993
    [...]
    * OK IMAP4 ready
    A AUTHENTICATE PLAIN AGJvYgBiYWRwYXNz
    A NO [AUTHENTICATIONFAILED] Authentication failed.
    closed
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment