published on in Featured Geekyness fcgiwrap FreeBSD Mailman Nginx

mailman with nginx on freebsd

I like Nginx a lot.

Not because I’m some sort of hipster sysadmin either.

I like it because it is small, fast, and as a FreeBSD port, it compiles and updates quickly.

What I also like is the separation of services and processes. For example, if I want to run a PHP script, I don’t load “mod_php” like you would with Apache. Instead, you have a PHP processor, like php-fpm, running (on localhost, or, another server that only processes PHP scripts).

This is not about PHP though, this is about Mailman :)

Most of the “freebsd + mailman + nginx” search results did not turn up specific instructions on running Mailman with Nginx, on FreeBSD. So, here is what I did:

Software Installation

FreeBSD’s ports tree has all of the required packages:

  • mail/mailman
  • www/nginx
  • www/fcgiwrap

To install those:

    $ sudo portinstall mail/mailman www/nginx www/fcgiwrap

Or, if you want pacakges:

$ sudo pkg_add -r mailman nginx fcgiwrap

I recommend building and configuring ports, it doesn’t take long, and you will know what options you are compiling in.

All three, Mailman, Nginx and fcgiwrap need to run as services, so you’ll need to enable them either in rc.conf, or, with the rcNG structure in /etc/rc.conf.d/

I prefer the rcNG style of enabling and configuring services, so this is how I enable all three:

$ sudo echo mailman_enable=YES > /etc/rc.conf.d/mailman; \
    echo nginx_enable=YES > /etc/rc.conf.d/nginx; \
    echo fcgiwrap_enable=YES > /etc/rc.conf.d/fcgiwrap

Additionally, I explicitly declared fcgiwrap to run as the www user:

$ sudo echo 'fcgiwrap_user="www"' >> /etc/rc.conf.d/fcgiwrap

so this is what my /etc/rc.conf.d/fcgiwrap file looks like:

fcgiwrap_enable=YES
fcgiwrap_user="www"

Configuration

Mailman

On FreeBSD, the mailman port installs itself in /usr/local/mailman

The only thing I changed was the mm_cfg.py script to reflect the FQDN of the Mailman server.

fcgiwrap

The Fast CGI wrapper daemon did not require any additional work. Just note that the default rc script on FreeBSD creates a socket in /var/run/fcgiwrap/fcgiwrap.sock which is used by the nginx server.

Nginx

Nginx needs to point to the correct location to run the CGI scripts, as well as source the fcgiwrap configuration.

www/nginx installs fastcgi_params-dist in /usr/local/etc/nginx. Rename that:

$ sudo cp /usr/local/etc/nginx/fastcgi-params-dist /usr/local/etc/fastcgi-params

Now, edit /usr/local/etc/nginx/nginx.conf for mailman:

root /usr/local/mailman/cgi-bin;
    location = / {
    rewrite ^ /mailman/listinfo permanent;
}

location / {
    rewrite ^ /mailman$uri;
}
    
location ~ ^/mailman(/[^/]*)(/.*)?$ {
    fastcgi_split_path_info (^/mailman/[^/]*)(.*)$;
    include fastcgi_params;
    fastcgi_param GATEWAY_INTERFACE CGI/1.1;
    fastcgi_param SCRIPT_FILENAME $document_root$1;
    fastcgi_param PATH_INFO $fastcgi_path_info;
    fastcgi_param PATH_TRANSLATED $document_root$2;
    fastcgi_pass  unix:/var/run/fcgiwrap/fcgiwrap.sock;
}
     
location /images/mailman {
    alias /usr/local/mailman/icons;
}
    	
location /icons {
    alias /usr/local/mailman/icons;
}
     
location /pipermail {
    alias /usr/local/mailman/archives/public;
    autoindex on;
}
    

This is inside one of the “server { }” stanza’s, I have mine in a SSL Aware block:

server {
    listen       443;
    server_name  lists.example.com;
    
    ssl                  on;
    ssl_certificate      /var/puppet/ssl/certs/lists.example.com.pem;
    ssl_certificate_key  /var/puppet/ssl/private_keys/lists.example.com.pem;

    ssl_session_timeout  5m;
    
    ssl_protocols  SSLv2 SSLv3 TLSv1;
    ssl_ciphers  HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers   on;
    
    root /usr/local/mailman/cgi-bin;
    
    location = / {
        rewrite ^ /mailman/listinfo permanent;
    }
     
    location / {
        rewrite ^ /mailman$uri;
    }
    
   location ~ ^/mailman(/[^/]*)(/.*)?$ {
      fastcgi_split_path_info (^/mailman/[^/]*)(.*)$;
      include fastcgi_params;
      fastcgi_param GATEWAY_INTERFACE CGI/1.1;
      fastcgi_param SCRIPT_FILENAME $document_root$1;
      fastcgi_param PATH_INFO $fastcgi_path_info;
      fastcgi_param PATH_TRANSLATED $document_root$2;
      fastcgi_pass  unix:/var/run/fcgiwrap/fcgiwrap.sock;
   }
     
   location /images/mailman {
      alias /usr/local/mailman/icons;
   }
    	
   location /icons {
      alias /usr/local/mailman/icons;
   }
     
   location /pipermail {
    alias /usr/local/mailman/archives/public;
    autoindex on;
   }
}    

I used Puppet as our internal Root CA, so deploying SSL based services is INCREDIBLY simple :)

Wrapping Up

You can now start the fcgiwrap daemon, nginx and mailman, and you are off and running:

    
    $ sudo /usr/local/etc/rc.d/fcgiwrap start
    $ sudo /usr/local/etc/rc.d/mailman start
    $ sudo /usr/local/etc/rc.d/nginx start

Point your browser to you list server, and you should see either your existing lists, or the option to create a new ones:

todo

Our lists, blurred because I forgot my glasses

One last thing, and this is because it drove me crazy.

The Mailman interface will show you the lists for the hostname that the browser requests…

Does that makes sense? Okay, if you point your web browser to: https://lists.example.com/, Mailman will only show you the lists that have been created from lists.example.com.

If you use the IP address: https://10.10.10.10/, you will only see lists for 10.10.10.10, even if that resolves to lists.example.com.

I did not know this, and I was testing a replacement server for lists, and I called it lists-2, so I never saw the mailing lists through the web interface (even though bin/list_lists showed everything for lists.example.com).

Finally, I edited my /etc/hosts file on my workstation to point lists.example.com to the IP address of lists-2.example.com, and it all worked.