Upgrade to Pro — share decks privately, control downloads, hide ads and more …

The Perdition and NGINX IMAP Proxies

The Perdition and NGINX IMAP Proxies

Held at the UKUUG Spring 2010 Conference, in Manchester.UK

Jan-Piet Mens

March 16, 2012

More Decks by Jan-Piet Mens

Other Decks in Technology


  1. The Perdition and NGINX IMAP Proxies UKUUG Spring 2010 Conference

    Manchester, UK March 2010 Jan-Piet Mens mens.de
  2. Overview IMAP servers: one, two, .... lots How an IMAP

    proxy works Perdition NGINX Summary.
  3. Single server User configures IMAP server name in MUA MUA

    connects to IMAP server Expansion means Larger server Multiple servers Distribute accounts IMAP server client
  4. Multiple servers More than one server? Users "know" server names

    Non-transparent to users User "freddy" server1? User "paula" server3? Use DNS CNAMES to associate users to servers client IMAP server a-f.example.net IMAP server g-m.example.net IMAP server n-z.example.net
  5. How a proxy works Client connects to proxy (in) Proxy

    retrieves username 01 login [email protected] secret Proxy looks up username in "map" Optional: authentication Optional: translate username Map returns address of back-end IMAP server Proxy hands off connection to IMAP server (out) and authenticates with target server lookup client IMAP server IMAP server IMAP server IMAP proxy map
  6. Pros and Cons Advantages Transparent to users: imap.example.net Replace/maintain back-end

    servers at will Migrate users from server1 to serverN Synthetic usernames on IMAP servers Different ways of lookup up users (DB, LDAP, etc.) Consolidate heterogenous brands of IMAP servers Integrate monitoring to provide access to available IMAP servers only Central logging Disadvantages Additional code Possible single point of failure Load-balance / heartbeat
  7. IMAP Capabilities Different servers have different capabilities http://www.iana.org/assignments/imap4-capabilities Clients typically

    query them before login List capabilities of your IMAP server $ openssl s_client -connect imap.gmail.com:993 * OK ready for requests 01 capability * CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA XLIST CHILDREN XYZZY 01 OK Thats all she wrote! Adapt proxy’s CAPABILITY to common denominator client IMAP proxy Lotus Domino Dovecot Cyrus CAPABILITY
  8. Testing IMAP connections Telnet / SSL $ telnet imap.example.net imap

    $ openssl s_client -connect imap.example.net:993 Watch your IMAP connection with IMAP Proxy Server http://www.aboutmyip.com/AboutMyXApp/ImapProxy.jsp $ java -jar IMAPProxy.jar
  9. Perdition Created by Simon Horman POP3 and IMAP4 One process

    per connection Can bridge between SSL/TLS and plain back-ends Perdition can authenticate user locally if compiled with PAM support Supported lookups: CDB, GDBM, BDB, MySQL, PostgresSQL, LDAP, NIS, regex Custom C function Username replacement No support for CRAM-MD5
  10. Perdition configuration /etc/perdition/perdition.conf bind_address connection_logging log_facility mail log_passwd fail

    imap_capability IMAP4REV1 UIDPLUS IDLE map_library /lib/libperditionXXX.so map_library_opt "this or that" username_from_database ssl_mode tls_listen,ssl_listen ssl_ca_chain_file /etc/perdition/chain.pem ssl_cert_file /etc/perdition/imap.crt ssl_key_file /etc/perdition/imap.key ssl_cert_verify_depth 0 ssl_listen_ciphers "ALL:!ADH:!NULL:+HIGH: ...
  11. Perdition map: POSIX regexp Options map_library libperditiondb_posix_regex.so.0 map_library_opt /etc/popmap.re Data

    First match wins $ cat popmap.re ^john: [email protected]:143 ^[a-m]: server2.example.net (.*)@(.*): $1_$2@localhost
  12. Perdition map: MySQL, PostgresSQL Options map_library libperditiondb_mysql.so.0 map_library_opt dbhost:dbport:dbname:dbtable:dbuser:\ dbpwd:dbservercol:dbusercol:dbportcol

    Data SELECT * FROM users; +------+------------------+------+ | user | servername | port | +------+------------------+------+ | sue | localhost | NULL | | jane | [email protected] | 143 | +------+------------------+------+
  13. Perdition map: LDAP Options map_library libperditiondb_ldap.so.0 map_library_opt 3:ldap://localhost/o=example.net?\ username,mailhost,port?one?(uid=%s) Data

    Attributes returned from search new username (optional), server, port (optional) perdition.schema has a structural objectclass dn: uid=jane, o=example.net objectClass: uidObject objectClass: perditionPopmap uid: jane username: s009 mailhost: example.org port: 143 Can use mailHost from inetLocalMailRecipient
  14. Perdition map: custom C Options map_library libperditiondb_XXYY.so.0 map_library_opt "this or

    that" Data Look up user in map int dbserver_get(const char *key, \ const char *options_str, char **str_return, \ int *len_return) Initialize / Terminate int dbserver_init(char *options_str) int dbserver_fini(void) Make gcc -shared $(L).c -L/usr/lib -o $(L).so
  15. Perdition map: custom C (2) Example int dbserver_get(const char *key,

    const char *opts, char **sret, int *lret) { int status = -2; // not found if (key && *key && !strcmp(key, "jp")) { char *server = ""; // [user@]host[:port] *lret = strlen(server) + 1; if ((*sret = calloc(1, *lret)) == NULL) status = -3; // other error else { status = 0; // OK strcpy(*sret, server); } } return (status); }
  16. Case study: Perdition with Lotus Domino Domino cluster contains n

    hosts User has mail on >= 2 hosts Custom Perdition database map Retrieves username Finds user’s back-end IMAP servers (cldbdir.nsf) Attempts TCP connect Fails over to backup server Returns first live server to Perdition Perdition connects to that IMAP server LDAP socket() client Perdition Domino Domino Domino
  17. NGINX (Engine X) Fast HTTP server and reverse proxy, and

    IMAP/POP3 and SMTP proxy created by Igor Sysoev for rambler.ru Fixed process pool and non-blocking code Powers WordPress, Github, Ohloh, SourceForge, ... Doesn’t require special database or LDAP schema POST to a URL to do authentication/authorization via HTTP or UNIX socket Authorization / authentication Apache, NGINX (HTTP), Lighttpd, ..., thttpd NGINX Web server is built-in FastCGI Embedded Perl
  18. NGINX IMAP/POP3 proxy Client connects to NGINX NGINX passes authorization

    request to URL via POST or UNIX socket Auth back-end looks up user (optionally password, etc.) Auth back-end determines target server/port Auth back-end passes data back to NGINX NGINX proxies connection to user’s IMAP/POP3 server:port (optionally using new credentials) NGINX IMAP proxy IMAP IMAP IMAP HTTP process IMAP client headers
  19. NGINX authorization NGINX passes authorization info in HTTP headers HTTP_AUTH_PROTOCOL:

    imap HTTP_HOST: nano.mens.de HTTP_CLIENT_IP: HTTP_AUTH_METHOD: plain HTTP_AUTH_USER: [email protected] HTTP_AUTH_PASS: seacret HTTP_AUTH_LOGIN_ATTEMPT: 2 HTTP_YY_AUTH: jp-mysecret Authmethod CRAM-MD5 sets salt and HMAC-MD5 hashed password (need cleartext on server to verify and hand back to NGINX) HTTP_AUTH_METHOD = cram-md5 HTTP_AUTH_SALT = <[email protected]> HTTP_AUTH_PASS = 1b0864a115d8ae5f3562e3bc7d05cb59
  20. NGINX authorization Authorization module (e.g. CGI) can Authenticate Redirect to

    back-end server Report failure Should complete fast
  21. NGINX authorization response Good response HTTP/1.0 200 OK Auth-Status: OK

    Auth-Server: Auth-Port: 143 Auth-User: newusername Auth-Pass: newseacret Bad response HTTP/1.0 200 OK Auth-Status: Invalid login or password Auth-Wait: 5 Don’t forget CGI Content-type
  22. NGINX configuration nginx.conf error_log logs/error.log info; events { worker_connections 1024;

    multi_accept on; } mail { auth_http web.example.net:80/nginx/auth.cgi; auth_http_header YY-Auth "jp-mysecret"; imap_auth login plain cram-md5; imap_capabilities "IMAP4rev1" "UIDPLUS" "IDLE"; server { listen *:143; protocol imap; proxy on; proxy_pass_error_message on; } }
  23. Sample NGINX authenticator Perl CGI my $q = new CGI;

    my $username = $q->http(’HTTP_AUTH_USER’); my $password = $q->http(’HTTP_AUTH_PASS’); if ($username eq ’jpm’ && $password eq ’ooh’) { print "Auth-Status: OK\n"; print "Auth-Server:\n"; print "Auth-Port: 143\n"; } elsif ($username eq ’sue’) { ... print "Auth-Server:\n"; } else { print "Auth-Status: Invalid login or password\n"; print "Auth-Wait: 3\n"; } print "Content-type: text/plain\n"; print "\n";
  24. Some numbers Perdition 40,000 users with 2 x Perdition, 9

    Courier IMAP ISP: 10 Perdition, 10 M POP3/IMAP connections/day NGINX fastmail.fm: 10,000 connections on 10% CPU (3.2 GHZ Xeon) (FastMail.fm moved from Perdition to NGINX)
  25. Summary Perdition has built-in database lookups Easier to deploy: no

    coding Perdition’s custom functions allow complex setups NGINX as IMAP/POP3 proxy requires custom code Consider using memcached for caching "expensive" user-lookups Integrate your monitoring system to influence choice of target IMAP server