Running dovecot from inetd: overcoming weird-ass bug
Warning: this page is not the whole story. Developments are ongoing - see
Debian bug report #377154.
See also here. Use this info at your own risk!
NOW it is fixed! Patch applied to tcp-wrappers and incorporated
into Debian... the "bug" was in the documentation; both the dovecot
wiki and generic tcp-wrappers documentation are broken, and the only
way to get the desired behaviour is to patch tcp-wrappers as described.
Skip the waffle
I have just been struggling like a bastard to get a dovecot IMAP server
(currently at version 0.99.14-1sarge0)
to run from inetd. I want to run it from inetd so that I can take advantage of tcpwrappers
and control access through /etc/hosts.allow
and /etc/hosts.deny
.
The control available through
dovecot's native configuration is too coarse: your only options are to allow access from only
one IP, or allow access from every IP.
According to the dovecot wiki this ought
to be a simple process, simply add the following lines to /etc/inetd.conf
(edited
to reflect the file locations in Debian):
imap stream tcp nowait root /usr/sbin/tcpd /usr/lib/dovecot/imap-login
imaps stream tcp nowait root /usr/sbin/tcpd /usr/lib/dovecot/imap-login --ssl
Unfortunately this doesn't work. I can telnet to port 143, telnet connects OK, and ps axf
on the server
shows that /usr/lib/dovecot/imap-login
has been started by inetd. I then see a line
like localhost imap-login[2820]: refused connect from 192.168.1.4 (192.168.1.4)
appear
in /var/log/syslog
, and the telnet session dies with Connection closed by foreign
host.
Fuck knows why. /etc/hosts.{allow,deny}
are set
to allow connections from 192.168.1.4, and they work as expected where other services are concerned.
On further experimentation, I found that replacing /usr/lib/dovecot/imap-login
with /usr/lib/dovecot/imap
in /etc/inetd.conf
did allow me to log
in. But since this bypasses the login management part of the process, the IMAP session
is logged in automatically, without needing to issue a login
command - and it's
logged in as root, no matter what. This is not exactly the desired behaviour.
My next experiment was to build the dovecot packages from source, having edited the code for the
login process to spew out all sorts of "It got to here!" markers. I installed the hacked package
and to my amazement none of the markers made an appearance. Apparently not even the first line
of main()
was being executed.
Well, this was bloody daft. I decided to try a real sledgehammer of an experiment. I deleted
the /usr/lib/dovecot/imap-login
binary entirely. It didn't make a blind bugger of
difference. I still got exactly the same response on the client telnet session, and
ps axf
on the server still showed that
/usr/lib/dovecot/imap-login
had been started by inetd. Even though the fucking file
didn't exist any more. How the flying fuck can that happen?
Well, my guess is that there's a bug - or maybe it's a feature - in tcpd/inetd that the writer of
the dovecot wiki page didn't know about, that makes it behave in a really weird fashion when
the name of the executable has a - in it. (Wrong - see below.)
Unfortunately, it's not possible to simply rename
the executable to something without a - in it. For one thing, the program logic depends on
the executable name starting with imap-
; more importantly, renaming the executable
would not result in the desired behaviour when upgrading the package.
Fortunately, there does exist a reasonably robust workaround. This is to create a directory
/usr/local/lib/dovecot
and then create a symlink named imap
in it
to /usr/lib/dovecot/imap-login
:
ln -s /usr/lib/dovecot/imap-login /usr/local/lib/dovecot/imap
and edit the entries in /etc/inetd.conf
accordingly:
imap stream tcp nowait root /usr/sbin/tcpd /usr/local/lib/dovecot/imap
imaps stream tcp nowait root /usr/sbin/tcpd /usr/local/lib/dovecot/imap --ssl
And hey presto, it now all works as it should do. Well, nearly. There is one small problem. We are
now no longer the IMAP servers who say "Ni!"... er, hang on... ah, yes. The imap
entry
in /etc/hosts.{allow,deny}
sets the access control for
BOTH imap and imap-ssl services. The imaps
entry is ignored. Which is very strange.
See also Debian bug report #377154.
Perhaps the author of this wishlist
bug report has been having a similar problem?
Furthermore, I don't think the bug is necessarily confined to Debian. My Google
searches in search of a solution revealed very little, but I did find a few
people asking "how do you run dovecot from inetd? PLEASE how do you run dovecot
from inetd?", not getting a working answer, and eventually giving up and using
other methods of access control, such as iptables. (Which is fair enough, but
I prefer the belt-and-braces approach of having tcp-wrappers as a second line
of defence behind iptables.) These people seemed to be using a variety of
different distributions. However, all my machines are running Debian, so I can't
confirm this suspicion without a lot more hassle than I'm prepared to
go through :-)
Update: I've been poking through the source
code of tcpwrappers, and I've figured out what's going on, I think...
hosts_access.c
matches the entries in /etc/hosts.{allow,deny}
against
the name of the daemon running the service. This daemon name is
derived in tcpd.c
by truncating argv[0]
at the final /
.
In the case of dovecot, the daemon responding to the incoming imap
(or pop3) connection is named imap-login
(or pop3-login
).
Therefore, unless the entries in /etc/hosts.{allow,deny}
use the
daemon name imap-login
instead of the obvious imap
or imap2
that
one might deduce from reading /etc/services
, they will not match the
daemon name provided by tcpd.c
.
My symlink workaround works because it effectively renames the daemon
executable to imap
and therefore it does match the entries in
/etc/hosts.{allow,deny}
. However, as I later found, it is still not a
complete solution as it does not produce the required result where
IMAP-over-SSL is concerned. dovecot uses the same daemon to handle
both IMAP and IMAP-over-SSL connections, passing it the --ssl
parameter in the case of an IMAP-over-SSL connection. Since the daemon
name is the same in both cases, permissions for both services are set
by the entry for the plain IMAP service. It is not possible to, for
instance, set more restrictive permissions for IMAP and less
restrictive ones for IMAP-over-SSL.
Similar considerations apply to dovecot's secure and insecure pop3
services, or indeed to any package which uses the same daemon to
provide more than one service according to a command-line parameter or
the port of the incoming connection or whatever.
It is not possible to extend my symlink workaround to cater properly
for IMAP-over-SSL by creating another symlink named imaps
without
modifying dovecot, because dovecot works out what service to provide
based on the stem of the daemon name, and only recognises imap
and
pop3
. The workaround also completely fails to address the problem of
what happens if any other packages behave similarly to dovecot. IIRC
the courier-imap daemon has a not dissimilar problem.
Now, there is much generic Linux documentation on the net that says
that the daemon names in /etc/hosts.{allow,deny}
should correspond to
those in /etc/services
. Certainly the dovecot wiki entry referred to above
seems to be written on that assumption, and there are many other
documents written similarly. However, none of the Debian-specific
documentation links the daemon names in /etc/hosts.{allow,deny}
and
/etc/services
, and grepping the tcp-wrappers source code reveals no
references to /etc/services
, or to getservbyport()
or its relations.
But there is nothing in the Debian changelogs to indicate that support
for /etc/services
has been removed from the Debian package for any
reason - no indication as to why the Debian package doesn't behave as
the generic, non-Debian-specific information suggests it might.
Indeed, it seems
that the upstream tcp-wrappers has never supported /etc/services
, and all this
generic documentation is simply continuing to promote a widespread
fallacy.
Fixed!
Since I couldn't find any reason for it not to be there, and if it was
there it would fix the problem, I decided to add support for
/etc/services
to tcp-wrappers. I also added support for matching
against port numbers as opposed to service names. I submitted my patch
to the developer, but he reckoned the /etc/services
support might break other packages and recommended I stripped it down
to just the port-number matching, which I did. The patch was accepted
and incorporated into tcp-wrappers from version 7.6.dbs-10. The 7.6.dbs-11
version is here: match_port_7.6.dbs-11.gz
Debian users of course can simply install a suitably recent version of
tcpd. Users of lesser distributions :-) will need to edit the
paths in the patch to conform to their particular build tree. It only
hacks one source file and one man page, so it won't be too hard to
figure out what to do :-)
With this patch applied the appropriate entries in /etc/inetd.conf
are:
imap stream tcp nowait root /usr/sbin/tcpd /usr/lib/dovecot/imap-login
imaps stream tcp nowait root /usr/sbin/tcpd /usr/lib/dovecot/imap-login --ssl
with the entries in /etc/hosts.{allow,deny}
along the
lines of this excerpt from /etc/hosts.allow
:
# imap
143: 127.0.0.1, 192.168.1.
# imaps
993: ALL
This allows dovecot to run from inetd in the manner that the broken
documentation suggests, with access limited according to the entries in
/etc/hosts.{allow,deny}
, separate control over imap and
imaps connections, and with the login process not running as root.
Back to sarge page
Back to Pigeon's Nest
Be kind to pigeons