Security
for linux servers tips
| Is
your system secure ? (For Dummies) |
To
know if your system is secure you first have to
get the list of potential network security holes.
Any open Tcp or Udp port, can be used to attack
your system, and is a potential security hole.
To see which ports are open locally, run 'netstat
-a'. Example:
Active Internet connections (including servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 1 0 localhost:12653 localhost:www CLOSE_WAIT
tcp 1 0 localhost:12652 localhost:www CLOSE_WAIT
tcp 1 0 sone.cf.fr:12642 chaines.voila.fr:www CLOSE_WAIT
tcp 81 0 sone.cf.fr:11709 graft.XCF.Berkeley.:ftp CLOSE_WAIT
tcp 0 0 sone.cf.fr:1023 graft.XCF.Berkeley.o:22 ESTABLISHED
tcp 0 0 *:6000 *:* LISTEN
tcp 0 0 *:1024 *:* LISTEN
tcp 0 0 *:22 *:* LISTEN
tcp 0 0 *:www *:* LISTEN
tcp 0 0 *:printer *:* LISTEN
tcp 0 0 *:login *:* LISTEN
tcp 0 0 *:ftp *:* LISTEN
udp 0 0 *:177 *:*
udp 0 0 *:syslog *:*
raw 0 0 *:1 *:*
Active UNIX domain sockets (including servers)
....
....
|
Above, you can see the list of open connections
(Those with 'State=ESTABLISHED'), and Tcp servers
listening for incoming connections (State=LISTEN).
Some Udp servers are listening too but you won't
see the LISTEN state. The 'Local Address' row
of LISTENing servers is the most important one,
because it gives important informations:
- on
which network interface the server listens
(IP address or '*' before : )
- on
which port it listens (number or word after
: ), which indicate what kind of service is
run
If
you run 'netstat -nap' you can even know which
programs are listening to these ports (and 'fuser
-n tcp [port]' can also be used to identify
servers open ports).
On
the example we have login,www,ftp,printer,syslog
services running and also some others like ssh
(port 22), X11 (port 6000). (read also the '/etc/services'
file for name<->number translations).
If
the network interface is '*', then the concerned
service will be likely seen from the external
network, so anyone can TRY to connect to that
service. This is BAD and you should try to find
if the server has an access control list (ACL)
feature to secure things (to set some ACL see
next chapters).
However,
if the network interface is an IP address, that
means that the server is listening only for
incoming connection on the interface which has
that IP address. For example if we only have
'localhost:ftp', then the ftp server is only
bound to the localhost interface (e.g. loopback),
and won't be seen outside your Ethernet or you
Internet PPP connection. THIS Is Great, because
you can run services on your system which will
never be seen from the outside (internet) !
Note
that any other interface name than 'localhost',
is bad, because it probably means that the service
can be seen outside your computer. So then,
carefully check your ACLs !
Now
that you have seen which services are running
on your system, You probably want to know how
it looks when seen from the outside. For that
you need to log into a remote system and use
Nmap, THE stealth port scanner (get it here).
For example, run 'nmap your.ip.address', and
you will get the list of all open services seen
by anyone on the outside network (Internet maybe).
Now
that you have collected information about the
services running on your system, it's time to
close the holes and shut down the servers/services
you don't need.
|
| IP
Chains |
One
of the best ways to close the holes (open network
ports) is to use the Linux 2.2 firewall mechanism:
IP Chains. (man ipchains). 2.4.x kernels series
use iptables which will be covered in the next
section.
Below you'll find a typical
IPChains script to add to your /etc/rc.d/rc.local
file, that will:
- set
up the IP Masquerading to protect other hosts
on your local network.
- disable
incoming pings and log them.(If someone 'pings'
you he won't get any reply but he will be
logged.)
- disable
ip spoofing.(and log attempts)
- block
access to the services you do not want for
requests coming from any ppp interface (ppp+)
to your network (10.0.0.0/24)
- and
(bonus) try to get better delays and throughput.
# Generic IPChains script, accept all by default
# your ipchains executable
$IPC=/sbin/ipchains
# put your local network address below
mynet=10.0.0.0/24
# the interface you want to secure
# here, any PPP interface if you're connected to the internet through a ppp modem link.
iface=ppp+
# reject dns, X11 server, lpd
tcp="53 6000:6010 515"
# reject dns, xdmcp, nfS;
udp="53 177 2049"
# delete previous rules
$IPC -F input
$IPC -F forward
$IPC -F output
# ip masquerading rules (only useful for computers connecting to the internet
# through your system)
$IPC -N user_msq
$IPC -F user_msq
$IPC -A user_msq -s 0/0 -d 0/0 -j MASQ
$IPC -A forward -s $mynet -d 0/0 -i $iface -j user_msq
/sbin/modprobe ip_masq_ftp
# disable ping reply and log incoming pings, so you'll get in /var/log/messages
# IP addresses of little Hackers trying to check if your host is up.
$IPC -A input -l -i $iface -p icmp -s 0/0 echo-request -j DENY
# improve throughput (0x08) and delays (0x10)
$IPC -A output -p tcp -d 0/0 telnet -t 0x01 0x10
$IPC -A output -p tcp -d 0/0 ssh -t 0x01 0x10
$IPC -A output -p tcp -d 0/0 ftp -t 0x01 0x10
$IPC -A output -p tcp -s 0/0 ftp-data -t 0x01 0x08
# disable ip spoofing (and log)
$IPC -A input -i $iface -s $mynet -l -j DENY
# more blocking
for p in $tcp ; do
$IPC -A input -p tcp -i $iface -s 0/0 -d 0/0 $p -j REJECT
done
for p in $udp ; do
$IPC -A input -p udp -i $iface -s 0/0 -d 0/0 $p -j REJECT
done
|
Of
course, you can reject more ports if you want
by adding more numbers in tcp and udp lists.
Above,
we have set the default policy to ACCEPT IP
packets and then REJECT some of them. We could
have done the contrary. This may be more secure
but could also lead to many programs not working
anymore (as you might block more ports than
needed). Here is the other way to do it:
# Generic IPChains script, reject all by default
# your ipchains executable
$IPC=/sbin/ipchains
# put your local network address below
mynet=10.0.0.0/24
# the interface you want to secure
# here, any PPP interface if you're connected to the internet through a ppp modem link.
iface=ppp+
# accept only web and ssh
tcp="80 22"
# accept talk ntalk;
udp="517 518"
# the default policy is to reject
$IPC -P input reject
# delete previous rules
$IPC -F input
$IPC -F forward
$IPC -F output
# ip masquerading rules (only useful for computers connecting to the internet
# through your system)
...
... same rules as above
...
# disable ping reply and log incoming pings, so you'll get in /var/log/messages
# IP addresses of little Hackers trying to check if your host is up.
...
# improve throughput (0x08) and delays (0x10)
...
... same rules as above
...
# more blocking (we ACCEPT instead of REJECTing)
for p in $tcp ; do
$IPC -A input -p tcp -i $iface -s 0/0 -d 0/0 $p -j ACCEPT
done
for p in $udp ; do
$IPC -A input -p udp -i $iface -s 0/0 -d 0/0 $p -j ACCEPT
done
|
You
should take care of not rejecting 1024 ,1025
tcp or upd ports or you'll get name server problems.
|
| IP
Tables |
This
next generation filtering tool is one of the new
features included in 2.4.x Linux kernels. Although,
you can still use ipchains with 2.4 but you should
forget it because, iptables is faster, more flexible,
extensible and can much more things for you, such
as: source NAT, destination NAT, avoid various
denial of service (DoS) attacks, and much more.
But, right now we will only
see how to translate our generic ipchains script
to an iptables one. Here is the script:
# Generic IPTables script, accept all by default
# path to your iptables executable
IPT=/usr/sbin/iptables
# put your local network address below
mynet=10.0.0.0/24
# the interface you want to secure here
# ppp+ = any PPP interface if you're connected to the internet through a ppp modem link.
iface=ppp+
# our reject lists
# reject everything but ssh, + squid postgres apache linuxconf
tcp="1:21 23:1023 3128 5432 8080 10000"
# reject everyting + nfs but named
udp="1:52 54:1023 2049"
# delete previous rules
$IPT -F INPUT
$IPT -F OUTPUT
$IPT -F FORWARD
$IPT -F POSTROUTING -t nat
$IPT -F PREROUTING -t nat
$IPT -F OUTPUT -t nat
# IP masquerading rules
$IPT -t nat -A POSTROUTING -s 10.0.0.0/24 -o $iface -d 0/0 -j MASQUERADE
# disable ping reply and log incoming pings, so you'll get in /var/log/messages
# IP addresses of little Hackers trying to check if your host is up.
$IPT -A INPUT -i $iface -p icmp -s 0.0.0.0/0 --icmp-type echo-request -j LOG
$IPT -A INPUT -i $iface -p icmp -s 0.0.0.0/0 --icmp-type echo-request -j DROP
# improve throughput (0x08) and delays (0x10)
$IPT -t mangle -A OUTPUT -p tcp -d 0.0.0.0/0 --dport telnet -j TOS --set-tos 0x10
$IPT -t mangle -A OUTPUT -p tcp -d 0.0.0.0/0 --dport ssh -j TOS --set-tos 0x10
$IPT -t mangle -A OUTPUT -p tcp -d 0.0.0.0/0 --dport ftp -j TOS --set-tos 0x10
$IPT -t mangle -A OUTPUT -p tcp -d 0.0.0.0/0 --dport ftp-data -j TOS --set-tos 0x08
# disable ip spoofing (and log)
$IPT -A INPUT -i $iface -s $mynet -j LOG
$IPT -A INPUT -i $iface -s $mynet -j DROP
# more blocking
for p in $tcp ; do
$IPT -A INPUT -p tcp -i $iface -s 0/0 -d 0/0 --dport $p -j REJECT --reject-with tcp-reset
done
for p in $udp ; do
$IPT -A INPUT -p udp -i $iface -s 0/0 -d 0/0 --dport $p -j REJECT
done
|
The
new option '--reject-with tcp-reset' allows
the firewall to be transparent to port scanners.
(If you want to do this with ipchains, you need
the 'Return-RST' daemon). As you can see, a
basic IPTables script is not very different
from an IPChains one. If you go beyond basics,
benefits of using iptables over ipchains are
great.
|
| TCP
Wrappers and Inetd |
One
of the most important things is securing Inetd
by configuring TCP Wrappers. Indeed, a stock installation
will enable a lot of services (spawned by Inetd)
which you will mostly never use. Remember that
The more enabled services you have, the most potential
security holes you will get.
So edit /etc/inetd.conf and remove every line
that is not useful. In particular: echo, discard,
daytime, chargen, telnet, gopher, shell, login
(and use SSH instead), exec, *talk, pop-*, imap,
uucp, *finger, netstat, time, linuxconf. You should
leave the 'auth' service, in order to be able
to get some informations in the case your system
has been used to issue attacks: It enables a server
to know which user is behind a socket which helps
to finger culprits. (or remove it if you want
to become a Cr4CkeR ;-)
Then if you Really NEED some services like ftp,
smtp, nntp, imap, pop-3, you should take care
that they are run by TCP Wrappers (/usr/sbin/tcpd)
and that the latter is properly configured. For
example, if ftp is run through TCP Wrappers, you
will see a line like
ftp stream tcp nowait root /usr/sbin/tcpd in.ftpd -l -a
To restrict the access to a service, you need
to edit /etc/hosts.deny and /etc/hosts.allow files.
A good starting point is to add the following
line to /etc/hosts.deny to deny every access to
Inetd's services:
ALL: ALL
You can also also run a script when an access
is denied:
ALL: ALL: (/usr/sbin/safe_finger -l @%h -s %d-%h | mail root) &
That line will try a finger on
attacker's host and send the report to root. Or
you can try to use the attacker's ident service
(rfc931) to log his login as well as his IP:
ALL: ALL: rfc931
Then in /etc/hosts.allow, you can grant access
only to your hosts on your local domain with:
ALL: LOCAL 10.0.0.
You can also add per service policy line:
ftp: 10.0.0.1
will allow the ftp server only for the host 192.168.1.1
Or even
ftp: linus@10.0.0.1
to match only one user.
For more information about the syntax of these
files do 'man hosts_acces'. And use ssh , openssh
or other to replace rcp, rlogin, telnet, ftp...
|
| Sendmail |
Sendmail
has been one of the most bugged software and was
often used to compromise systems.
The
first security rule when using Sendmail is to
always upgrade to the latest version (This is
also true for other software).
The
second one is to tweak the infamous /etc/sendmail.cf
file:
- if you use sendmail only to send email without
going through your ISP mail gateway, then add
'O DaemonPortOptions=Addr=127.0.0.1'. With this,
sendmail will be bound only to the loopback
interface, so it won't be seen from the Internet
! (netstat will show '127.0.0.1:smtp' and not
'*:smtp'). You can also put your ethernet IP
number instead of 127.0.0.1 but, this time you
could not send mail locally but other systems
on your network could use your sendmail, while
hackers on the internet will not see it !
- Add 'O PrivacyOptions=goaway' to disable the
VRFY smtp command which can be used to disclose
information about the accounts your server.
The
third one is to put ip addresses that can use
sendmail to relay in the /etc/mail/ip_allow
file.
If
your sendmail must be seen from anywhere and
that you are not afraid of loading your CPU,
you can run it from Inetd (with 'sendmail -bs')
, so that you can use TCPWrappers ACLs. In that
case do not forget to run 'sendmail -q30m'
in order to flush the queue periodically.
Finally,
here is a little tip about the way to solve
a common problem If you use Netscape Communicator
for browsing and sending mail: Your email will
be used for BOTH ftp login and email. So, anyone
on the WEB, can get your email address:
- They simply have to put a fake ftp link in
their Web page
- and once you go to the page, Communicator
connects to the ftp site (to fetch an image
for example)
- and your email is logged by the ftp server
and ready to be used by spammers !
To
avoid this, use mail address masquerading. You
can either set up this in Linuxconf or tweak
'sendmail.cf'. If you add the following lines
to 'sendmail.cf':
# Masquerading rules
S1
Rwarez<@mail.net> $@ me < @ mail.net>
Rwarez<@mail.net.> $@ me < @ mail.net.>
|
AND then set your email in communicator to 'warez@mail.net',
so :
- if you connect to a ftp site, you will send
'warez@mail.net' and your real email won't be
disclosed.
- but if you send a mail, sendmail will translate
'warez@mail.net' back to your real email 'me@mail.net'.
So, all in all, you will get much less Spam in
your mailbox.
Finally, do not neglect the
option to run Sendmail in a chroot prison (,
or to run it without the rights of the 'root'
user. Most servers (http, ftp,...) usually provide
these last two options, to maximize security,
and to protect them from some 'not yet discovered
bugs'.
|
| Exim |
Exim,
my favorite MTA, and the default MTA chosen by
the Debian project has a lot of security related
options. For instance, you can restrict which
hosts can connect to the mta with the 'host_reject'
option:
host_reject = !1.1.1 : *.com
With
the line above you'll reject every host in the
.com domain, or not coming from the 1.1.1. network.
As Exim is very flexible, if you want to get
more dynamic access control you can use LDAP
or MySQL queries:
host_reject = ${lookup ldap {user="cn=manager,o=mysite,c=com" pass=secret
ldap:///o=mysite,c=com?host?(cn=foo)} {$value}fail}
or
host_reject = ${lookup mysql{select host from acl where id='ph10'}{$value}fail}
In
a similar way, with 'host_accept_relay' you
can control which hosts can use your MTA as
a mail relay to arbitrary domains.
And
if you want to bind Exim to a specific network
interface, you just have to use the 'local_interfaces'
option as shown below:
local_interfaces = 127.0.0.1 : ::::1 # IPv6 address
We've seen only a tiny part of Exim's features,
and now, I hope that you'll throw yourself into
Exim's documentation, to discover its power (moreover,
for everyone). |
| Postfix |
Postfix
has an 'inet_interfaces' option similar to sendmail's
"DaemonPortOptions=Addr" but what is cool if that
you can add multiple interfaces here ! (If anyone
has other security tips for Postfix, let me know)
|
| Smail |
Smail
has an 'listen_name' option similar to sendmail's
"DaemonPortOptions=Addr".
Also, with the 'smtp_remote_allow'
option you can set up simple host based access
control.
|
| DNS,
Bind |
If
one looks back over past events, like Sendmail,
Bind has an eventful past. So, always upgrade
to the latest version.
Fortunately,
you can easily add ACLs in '/etc/named.conf'.
In the example below you can see the setup of
an ACL named 'internal' which contains the specification
of our local network. You can add as many 'acl'
sections as you want. Then in the 'allow-query'
sub-section of 'options', you can add your global
ACLs . The 'allow-query' option can also be
local to a zone of you DNS.
acl internal {
10.0.0.0/24;
127.0.0.0/24;
};
options {
allow-query {
internal;
};
};
In the same way you should restrict zone transfers
with the 'allow-transfer' directive in a zone
definition, as your network topology can be a
valuable information to a hacker which wants to
break in.
As most other servers, you can
also run Bind in a chroot prison (-t command
line option), and/or an other user than the
default 'root' (-u command line option), in
order to minimize damages in case of a 'break-in'.
|
| Samba |
ACLs
are here set by the 'hosts allow' parameter. It
can be set either in the '[global]' section of
'smb.conf', or in any share section. As you can
see below, the syntax is trivial:
hosts allow = 10.0.0., 127. , myhost.mydomain
or
hosts allow = 10.0.0. EXCEPT 10.0.0.200
and even,
hosts allow = 10.0.0.0/255.255.255.0
You can also play with the 'host
deny' parameter which has the inverse effect.
Of course, Samba provides support for binding
the servers on only one interface with the 'bind
interface only' parameter which can be set in
the global section.
For example, the following configuration will
make smbd and nmbd listen only on one interface.
bind interfaces only = True
interfaces = 192.168.0.0/255.255.255.0
|
| Apache |
Access
control in Apache is very powerful and flexible.
Below,
you can see how to protect a single page (add
this to the global 'httpd.conf' file). Of course,
you can protect entire directories by replacing
'<Files' by '<Directory'. Note that the
authorization realm (words after AuthName) should
be unique. To create the password file (/home/bill/apache.htpasswd
in this example), use the 'htpasswd' utility.
<Directory /home/bill/public_html>
<Files thesecret.file>
AuthName "Bill secret stuff"
AuthType Basic
AuthUserFile /home/bill/apache.htpasswd
Require valid-user
</Files>
</Directory>
Below, is a more interesting configuration example.
It shows how to get unrestricted access to a file
when it is viewed from the local network (10.0.0.0/255.0.0.0),
but for external accesses, a password is required:
<Files foo.html>
Order Deny,Allow
Deny from All
Allow from 10.0.0.0/255.0.0.0
AuthName "Insiders Only"
AuthType Basic
AuthUserFile /home/httpd/etc/htpasswd
Require valid-user
Satisfy Any
</Files>
Instead of an entire network (10.0.0.0/255.0.0.0),
you can also deny a single host by setting 'Allow
from 10.2.3.4' or 'Allow from foo.com', although
the later form is slower because of the required
reverse DNS lookup.
In fact, Apache security features
and logging options are so powerful, that a
complete description is beyond the scope of
that page. Refer to the Apache manual for more
information.
|
| RPC
and NFS |
Latest
version of RPC (which is used by NFS) rely on
the same files used by Inetd for access control.
So, in order to make your NFS a little more secure,
you can add this to '/etc/hosts.allow'
portmap: 127.0.0.1 10.0.0.2 10.0.0.1 255.255.255.255 0.0.0.0
And this, to '/etc/hosts.deny':
portmap: ALL: (/usr/sbin/safe_finger -l @%h | mail root) &
Another problem: Programs using RPCs, often randomly
choose their server port, then properly configuring
your firewall can be tricky. For example 'NFS'
is most of the time bound to port 2049 but 'mountd'
will choose an unsed port after 1024. Fortunately,
you can force 'mountd' port: add a '-P 909' option
on rpc.mount launch script. Filtering mountd's
UDP port is now trivial ! |
| Lpd |
The
standard line printer daemon allows you to set
up basic ACLs by adding authorized hosts to the
'/etc/hosts.lpd' file. I still do not know if
Lprng, the new generation line printer daemon,
has more network security options. Anyway, it
should be more robust. If you can, remove lpd
and switch to lprng. |
|