Listing email accounts hosted on a Webmin / Virtualmin server

Tags:

Here's a little script that I found handy to scan Postfix's virtual address table, compare the domains with the ones actually hosted by the system, and tell me what's really going on.

This works great for servers setup with Webmin and Virtualmin, or with plain postfix installs.

See the comments about how the script determines who "we" really are.


#!/usr/bin/perl

# show_postfix_domains.pl
# Looks at /etc/postfix/virtual and tells us which of those emails
# are _actually_ hosted by this system, based on whether DNS lookups of
# the domains seem to point to "us"... where "us" is defined as any
# of the IP addresses on any of localhost's interfaces.
# Naturally, this will fail if your system is behind a gateway/firewall,
# because we have no way of probing that gadget to see how connections
# are routed from "The Internet" to us.

# Copyright (c) 2012, William Lindley bill -at- saltriversystems -dot- com
# 2012-06-06

# This script is free software, you may distribute it and/or modify it
# under the same terms as Perl itself.

use Net::DNS;
use Socket qw/inet_aton/;

use IO::Socket;
use IO::Interface qw(:flags);

my $s = IO::Socket::INET->new(Proto => 'udp');
my @interfaces = $s->if_list;
my %local_interfaces;

for my $if (@interfaces) {
    my $flags = $s->if_flags($if);

    if ( ( $flags & IFF_RUNNING ) && 
     !( $flags & IFF_LOOPBACK ) &&
     !( $flags & IFF_NOARP )) {
    $local_interfaces{$if}{address} = $s->if_addr($if);
    $local_interfaces{$s->if_addr($if)}{interface} = $if;
    }
}

#########

my $r = Net::DNS::Resolver->new;

open VIRTUAL, '<', '/etc/postfix/virtual';

my %domains_hosted;

while (<VIRTUAL>) {
    chomp;
    s/#.*$//;  # Remove after comment
    my ($address, $alias) = split;
    if ($address) {
    if ($alias !~ /@/) { # Only for local addresses (not forwarded)
        my ($name, $domain) = ($address =~ /^([^@]+)@(.+)$/);
        next unless $name;
#        print "[$name]@[$domain] -> [$alias]n";
        $domains_hosted{$domain}{hosted} = 1;
        $domains_hosted{$domain}{address}{$name}++;
    }
    }
}

use Data::Dumper;

foreach my $domain (keys %domains_hosted) {

# Liberally borrowed from David Landgren (grinder)'s code at
# http://www.perlmonks.org/?node_id=297667
    my %res;
    my $rr = $r->query( $domain, 'MX' );
    if ($rr) {
    for my $mx( $rr->answer ) {
            if( $mx->type eq 'CNAME' ) {
                my $a_rr = $r->query( $mx->cname, 'A' );
                if( !$a_rr ) {
                    push @{$res{-1}}, { ip => $mx->cname, forw => $r->
                        errorstring, back => 'CNAME' };
                } else {
                    $_->type eq "A"
                        and push @{$res{-1}}, { ip => $mx->cname, forw => $_->address, back => 'CNAME' }
            for( $a_rr->answer );
                }
                next;
            }

            next unless $mx->type eq 'MX';

            my $a_rr = $r->query( $mx->exchange, 'A' );

            if( !$a_rr ) {
                push @{$res{$mx->preference ? $mx->preference : 0}}, {
                    ip   => $mx->exchange,
                    forw => $r->errorstring,
                    back => $r->errorstring,
                };
                next;
            }

            my @a;
            for my $a( $a_rr->answer ) {
                next unless $a->type eq "A";

                my $ptr_rr = $r->query( join( '.', reverse( split /./ , $a->address )) . '.in-addr.arpa', 'PTR' );
        if ($local_interfaces{$a->address}{interface}) {
            $domains_hosted{$domain}{local}++;
        }
                if( !$ptr_rr ) {
                    push @{$res{$mx->preference}}, {
                        ip => $a->address,
                        forw => $mx->exchange,
                        back => $r->errorstring,
                    };
                } else {
                    foreach ( $ptr_rr->answer ) {
            if ( $_->type eq 'PTR' ) {
                push @{$res{$mx->preference}}, {
                ip => $a->address,
                forw => lc $mx->exchange,
                back => lc $_->ptrdname,
                };
            }
            }
                }
            }

        }
    }
    $domains_hosted{$domain}{mx} = %res;
}

# This could be greatly expanded by doing more with the data herein:
# print Dumper(%domains_hosted);

print "These email accounts are actually hosted here:n";

foreach my $domain (sort keys %domains_hosted) {
    next unless $domains_hosted{$domain}{local};
    print $domain . "n";
    foreach my $email (sort keys %{$domains_hosted{$domain}{address}}) {
    print "   ${email}@${domain}n";
    }
}

1;

How to renew the SSL certificate for dovecot, CentOS, Webmin

Tags:

As root: First make a backup of the existing key and certificate file

# cd /etc/pki/dovecot
# cp -a certs/dovecot.pem certs/dovecot.pem.old
# cp -a private/dovecot.pem private/dovecot.pem.old

Create the new SSL certificate for two years:

# openssl genrsa -out private/dovecot.pem 1024
openssl req -new -x509 -key private/dovecot.pem -out certs/dovecot.pem -days 730

Restart Dovecot:

# /etc/init.d/dovecot restart

To see the start and end dates for the certificate:

# openssl x509 -dates -in certs/dovecot.pem

Controlling backscatter spam in Mailman

Tags:

By default, Mailman -- which is installed by default in Virtualmin as your mailing list manager, exhibits some nasty behavior, being open to sending "backscatter" spam. This means that the Bad Guys send fraudulent messages "from" the email address they actually want to send spam to; Mailman rejects those messages, basically sending a bounce message to the victim.

The mechanism the spammers use is left over from the days before web interfaces. Nowadays, your subscribers interact with Mailman almost exclusively via its HTTP interface. Other than actual postings from subscribed members, and messages to the list owner, there is little or no reason to keep the vestigial email aliases.

To disable the vulnerable aliases:

With Virtualmin, go into the Webmin interface, under Servers; Postfix Mail Server; and click on the Aliases icon. You should see, for example, defined among the email addresses:

yourlist-example.com    Program /usr/lib/mailman/mail/mailman post yourlist
<strong>yourlist-admin-example.com      Program /usr/lib/mailman/mail/mailman admin yourlist</strong>
yourlist-bounces-example.com    Program /usr/lib/mailman/mail/mailman bounces yourlist
<strong>yourlist-confirm-example.com    Program /usr/lib/mailman/mail/mailman confirm yourlist
yourlist-join-example.com       Program /usr/lib/mailman/mail/mailman join yourlist
yourlist-leave-example.com      Program /usr/lib/mailman/mail/mailman leave yourlist
</strong>yourlist-owner-example.com     Program /usr/lib/mailman/mail/mailman owner yourlist
<strong>yourlist-request-example.com    Program /usr/lib/mailman/mail/mailman request yourlist
yourlist-subscribe-example.com  Program /usr/lib/mailman/mail/mailman subscribe yourlist
yourlist-unsubscribe-example.com        Program /usr/lib/mailman/mail/mailman unsubscribe yourlist</strong>

for "yourlist@example.com" ... I recommend you disable these aliases shown in bold above:

-admin-, -confirm-, -join-, -leave-, -request-, -subscribe-, -unsubscribe-.

You also need to edit /etc/postfix/virtual -- or in Webmin, open "Servers" and click on "Postfix Mail Server" then click on the "Virtual Domains" icon:

and delete the virtual mappings for all the addresses you just removed.

You will want to do this for each mailing list on your system.

Installing webmin on Centos 5.x

Tags:

For CentOS 5.1 -- First we install some dependencies. (This is already present on CentOS 5.2 and CentOS 5.3.)

yum -y install perl-Net-SSLeay

step 1: webmin

Install the system, using the link at http://www.webmin.com/download.html

cd /usr/src
wget <a href="http://superb-east.dl.sourceforge.net/sourceforge/webadmin/webmin-1.450-1.noarch.rpm">http://superb-east.dl.sourceforge.net/sourceforge/webadmin/webmin-1.450-1.noarch.rpm</a>
rpm -i webmin-1.450-1.noarch.rpm

Then you can start in your browser (note: https: ) at:

https://localhost:10000

Webmin permits login from the root user as well as anyone who has sudo privilege. To permit logging into webmin with your username and password, give yourself permission to execute the sudo command by using the visudo command (do not edit the file /etc/sudoers directly) and un-comment the line:

%wheel ALL=(ALL) ALL

step 2: usermin

You might also want usermin... for the latest version, check:

<a href="http://www.webmin.com/udownload.html">http://www.webmin.com/udownload.html</a>

For example, to install:

cd /usr/src
wget http://prdownloads.sourceforge.net/webadmin/usermin-1.360-1.noarch.rpm
rpm -Uvh usermin-1.360-1.noarch.rpm

Then point your browser to

https://localhost:20000

step 3: virtualmin

For managing virtual domains, there are both GPL and commercial version of Virtualmin. For many sites, the key difference will be that the paid version includes the ability to manage spam and email antivirus settings on a per-domain, per-user level, whereas the GPL doesn't.

<a href="http://www.virtualmin.com/">http://www.virtualmin.com/</a>

Download the install script and run it. Note, the installer for Centos 5 misses one permission setting. Users attempting to retrieve POP3 email can't login. If you manually connect to port 110 and login, you will see the error as follows:

$ telnet mailserver 110
Trying 192.168.1.20...
Connected to mailserver.domain.
Escape character is '^]'.
+OK Dovecot ready.
user user.domain
+OK
pass password
+OK Logged in.
-ERR [IN-USE] Couldn't open INBOX: Internal error occurred.
     Refer to server log for more information.

In the file /var/log/maillog you will see:

Feb 23 05:01:02 host dovecot: POP3(user.domain): mkdir(/var/lib/dovecot/index/user.domain/.INBOX) failed: Permission denied
Feb 23 05:01:02 host dovecot: POP3(user.domain): mkdir(/var/lib/dovecot/control/user.domain/.INBOX) failed: Permission denied
Feb 23 05:01:02 host dovecot: POP3(user.domain): mkdir(/var/lib/dovecot/index/user.domain/.INBOX) failed: Permission denied
Feb 23 05:01:02 host dovecot: POP3(user.domain): mkdir(/var/lib/dovecot/control/user.domain/.INBOX) failed: Permission denied
Feb 23 05:01:02 host dovecot: POP3(user.domain): Couldn't open INBOX: Internal error occurred. Refer to server log for more information. [2009-02-23 05:01:02]
Feb 23 05:01:02 host dovecot: POP3(user.domain): Couldn't open INBOX top=0/0, retr=0/0, del=0/0, size=0

To solve this, as root:

chmod 755 /var/lib/dovecot
chmod 777 /var/lib/dovecot/index /var/lib/dovecot/control

step 4: configuring spamassassin

Once Webmin is installed, spamassassin is very easy to turn on -- there is a button that will automatically create the correct entries in /etc/procmailrc ... the one missing link is that, by default, Postfix doesn't use that file. You can either manually add this line in /etc/postfix/main.cf:

mailbox_command = /usr/bin/procmail

or in Webmin, on the Local Delivery page of Postfix, set the parameter:

External command to use instead of mailbox delivery

to be:

/usr/bin/procmail

And that's all there is to getting SpamAssassin running.

step 5: change runlevel to avoid loading X

In your /etc/inittab, change the default runlevel from 5 to 3, so X won't load. That means no graphical login -- and a bunch of saved memory, if you're running a server instead of workstation.