Warning! This information is more than 10 years old! It might be inaccurate and not applicable anymore!
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 !.
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
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.
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
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).
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 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, 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 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 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.
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'.
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
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.
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 !
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.