Security Tips
Warning! This information is more than 10 years old! It might be inaccurate and not applicable anymore! 

Latest version: 0.31 . 8/24/2003
drolez.com home
Bookmark and Share

You want to secure your stock Linux installation while still being able to log in, run a public ftp or http server on the Internet ? That page is for You ! Version Francaise ici !

You will find security tips to improve your overall system security by the use of IPChains, IPTables, and TCP Wrappers. And you will learn some useful and subtle configuration options for Sendmail, Postfix, Bind, NFS and others.

These informations have been verified on Red Hat and Mandrake distributions but should be ok with others.

Feel free to post your comments or tips here !.

Contents

  Is your system secure ? (For Dummies)
  IP Chains (kernel 2.2.x)
  IP Tables (kernel 2.4.x)
  TCP Wrappers and Inetd
  Mail Transport Agents:
      Sendmail
      Exim
      Postfix
      Smail
  DNS, Bind
  Samba
  Apache
  RPC and NFS
  Lpd

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:

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:

# 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 --syn
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 and DNS queries;
udp="517 518 53"

# 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
...
# some unblocking (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

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=/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 all priveleged ports (<1024) but ssh, + squid postgres linuxconf
tcp="1:21 23:1023 3128 5432 10000"
# reject all priveleged ports (<1024) but named, + nfs 
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.

Warning ! If you have name resolution problems, it's because you have rejected too much UDP ports. If you do not use a local caching name server, then random UDP ports above 1024 will be used by the libc for name resolution, therefore you should not close UDP ports above 1024, which is not satisfying for your security. To have total control over the UDP ports used for name resolution, your should install a caching name server (bind 8.x or 9.x), and add the line "query-source address * port 53;" to named.conf, to set the DNS query port to 53. After that, you only have to open the port 53 on your firewall (of course, any other port number would also be fine).

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 in /etc/inetd.conf 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.

Adding the option "query-source address * port 53;", allows to set the UDP port used for DNS queries, so that you only have one well identified UDP port to open on your firewall (the 53, or whatever you like).

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.