The Perdition and NGINX IMAP Proxies

Preview:

DESCRIPTION

 

Citation preview

The Perdition and NGINX IMAP Proxies

UKUUG Spring 2010 Conference Manchester, UK

March 2010

Jan-Piet Mens mens.de

Overview

IMAP servers: one, two, .... lots How an IMAP proxy works Perdition NGINX Summary.

Single server

User configures IMAP server name in MUA MUA connects to IMAP server Expansion means Larger server

Multiple servers Distribute accounts

IMAP

serverclient

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

IMAPserver

a-f.example.net

IMAPserver

g-m.example.net

IMAPserver

n-z.example.net

How a proxy works

Client connects to proxy (in) Proxy retrieves username 01 login sue@example.org 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

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

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

IMAPproxy

LotusDomino

Dovecot Cyrus

CAPABILITY

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

IMAP Proxies

Perdition NGINX (Engine X)

Others Courier IMAP/POP3 proxy

Cyrus Murder

Dovecot proxy

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

Perdition configuration

/etc/perdition/perdition.conf bind_address 192.168.1.120 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: ...

Perdition map: GDBM

Options map_library libperditiondb_gdbm.so.0 map_library_opt /etc/my_popmap.gdbm.db

Data $ cat popmap sue:localhost jane:s009@example.org:143 joe@foo.net:j999@imap-1.internal

$ makegdbm popmap.gdbm.db < popmap

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: user2@imap.example.net:143 ^[a-m]: server2.example.net (.*)@(.*): $1_$2@localhost

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 | s009@example.org | 143 | +------+------------------+------+

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

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

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 = "192.168.1.20"; // [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); }

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

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

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)

NGINXIMAPproxy

IMAP

IMAP

IMAPHTTP process

IMAP client

headers

NGINX authorization

NGINX passes authorization info in HTTP headers HTTP_AUTH_PROTOCOL: imap HTTP_HOST: nano.mens.de HTTP_CLIENT_IP: 192.168.1.154 HTTP_AUTH_METHOD: plain HTTP_AUTH_USER: sue@example.net 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 = <1885293406.1268902260@nano.mens.de> HTTP_AUTH_PASS = 1b0864a115d8ae5f3562e3bc7d05cb59

NGINX authorization

Authorization module (e.g. CGI) can Authenticate

Redirect to back-end server

Report failure

Should complete fast

NGINX authorization response

Good response HTTP/1.0 200 OK Auth-Status: OK Auth-Server: 192.168.1.20 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

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; } }

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: 192.168.1.20\n"; print "Auth-Port: 143\n"; } elsif ($username eq ’sue’) { ... print "Auth-Server: 192.168.3.47\n"; } else { print "Auth-Status: Invalid login or password\n"; print "Auth-Wait: 3\n"; }

print "Content-type: text/plain\n"; print "\n";

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)

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

Further reading

Perdition http://www.vergenet.net/linux/perdition/

NGINX http://wiki.nginx.org/Main

Alternative DNS Servers, UIT, 2009, Jan-Piet Mens http://mens.de/:/altdns

Thank you

Questions?

Recommended