This page has information on tools that can be used to secure a system against attackers coming at it from the network.


General documentation
Firewall security
See the separate page on this wiki

Closing ports


The obvious first step to secure a system is to shut down any unneeded services. If a port is not open, an attacker cannot exploit it. I suggest this procedure:

  • Identify open ports using a local tool such as netstat
  • Close unneeded ports
  • Double-check that things work as intended by using an external port scanner
  • Proceed to tighten security with firewall rules and/or a tool like fail2ban

Identify open ports

Open ports can be identified using netstat. Here is the output from pelargir after its reincarnation as a Dedicated Server:

root@pelargir:~# netstat -lnptu
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0*               LISTEN      6160/postgres
tcp        0      0  *               LISTEN      49598/exim4
tcp        0      0   *               LISTEN      36937/slapd
tcp        0      0 *               LISTEN      439/rpc.statd
tcp        0      0   *               LISTEN      419/rpcbind
tcp        0      0    *               LISTEN      484/sshd
tcp6       0      0 ::1:5432                :::*                    LISTEN      6160/postgres
tcp6       0      0 ::1:25                  :::*                    LISTEN      49598/exim4
tcp6       0      0 :::443                  :::*                    LISTEN      39875/apache2
tcp6       0      0 :::389                  :::*                    LISTEN      36937/slapd
tcp6       0      0 :::42438                :::*                    LISTEN      439/rpc.statd
tcp6       0      0 :::111                  :::*                    LISTEN      419/rpcbind
tcp6       0      0 :::80                   :::*                    LISTEN      39875/apache2
tcp6       0      0 :::22                   :::*                    LISTEN      484/sshd
udp        0      0 *                           439/rpc.statd
udp        0      0  *                           419/rpcbind
udp        0      0 *                           439/rpc.statd
udp        0      0   *                           419/rpcbind
udp        0      0*                           17205/ntpd
udp        0      0 *                           17205/ntpd
udp        0      0   *                           17205/ntpd
udp6       0      0 :::1005                 :::*                                419/rpcbind
udp6       0      0 :::50893                :::*                                439/rpc.statd
udp6       0      0 :::111                  :::*                                419/rpcbind
udp6       0      0 ::1:123                 :::*                                17205/ntpd
udp6       0      0 fe80::42a8:f0ff:fe7:123 :::*                                17205/ntpd
udp6       0      0 :::123                  :::*                                17205/ntpd


  • Those services listening on (IPv4) and ::1 (IPv6) can be ignored
    • postgres
    • exim4
  • Those listening on (IPv4) and :: (IPv6) are also listening on the outgoing interface
    • slapd
    • rpc.statd
    • rpcbind
    • sshd
    • apache2
    • ntpd
  • OK are
    • sshd
    • apache2
  • Not OK are
    • slapd
    • rpc.statd
    • rpcbind
    • ntpd

Close unneeded ports


  • I need OpenLDAP, but it doesn't need to listen on
  • In /etc/default/slapd modify the entry for SLAPD_SERVICES
    • Old: SLAPD_SERVICES="ldap:/// ldapi:///"
    • New: SLAPD_SERVICES="ldapi:///"
  • slapd now only listens on the internal Unix domain socket

rpcbind and rpc.statd

  • First we need to find out why this package is installed at all
root@pelargir:~# which rpcbind
root@pelargir:~# dpkg -S /sbin/rpcbind
rpcbind: /sbin/rpcbind
root@pelargir:~# aptitude why rpcbind
i   nfs-common Depends rpcbind
  • Since I don't need to mount NFS I removed the nfs-common package, and was thus also able to remove the rpcbind


  • After much try & error I had succeeded in preventing ntpd from listening on the public interface (with an "interface ignore" statement in /etc/ntp.conf) but this caused ntpd from functioning correctly
  • In the end I had to give up and let ntpd listen
  • For more details see the NTP page on this wiki

Other ports that were open in older versions of Debian:

  • 79/finger: Can be closed by removing the package fingerd
  • 111/portmapper: Can be closed by removing the package portmap. NFS used to require the portmapper, not sure if this still is the case today.
  • 113/ident: Can be closed by removing the package midentd

If you don't want to uninstall packages, another way how to close some ports / disable some services is by editing /etc/inetd.conf.


Currently I'm using fail2ban to provide automated security for my box. See the separate page on this wiki.

Using iptables for network security

Note: I no longer use the iptables rules in this section, they may or may not work as presented here.

About iptables

For basics about how iptables is working, see the iptables chapter on the NetworkConfiguration page.

It is possible to use special iptables modules for extended functionality. The modules are referenced by name with -m <module> or --match <module>. After -m has been specified, additional command line options become available.

About the recent module

The recent module is capable of temporarily storing IP addresses in tables, and to query those tables if they contain a certain IP address. This feature can be used to give iptables a memory of past behaviour of network clients, and to react to behaviour that becomes abusive over time.

Protection against SSH brute force attack

In a nutshell

This chapter presents 3 iptables rules that effectively block a network client's connection attempts on port 22 (the SSH port), if that client has made more than 5 connection attempts during the last 20 seconds.

Offenses are logged to the system log, where you can examine the problem at leisure. For instance, I have written a small script that parses the system log files and produces a table digest with details about offending IP addresses:

Rule 1

iptables -A INPUT --source ! -p tcp --dport 22 -m state --state ESTABLISHED -m recent --set --name SSH -j ACCEPT

Dissemination of the rule:

  • store all addresses that are the source for a connect to the ssh port (--dport 22) in the table "SSH"
    • the "SSH" table is internal to the "recent" module; it is not an iptables table that can be used with -t
  • only match successful TCP/IP connections (--state ESTABLISHED), so that packets with forged source address cannot lead to problems (e.g. someone sends packets whose source address = my DNS server)
  • it is basically very simple to forge the source address of a single packet; if we would react to an initiating SYN packet someone could easily provoke us into blocking the wrong source address
  • an established TCP/IP connection means that our server has already sent a SYN/ACK packet in response to the initiating SYN packet; the server is now getting another answering ACK packet, and this ACK packet is matched by the above rule
  • if someone wants to provoke us into blocking a wrong source address, that party must therefore send us an acceptable ACK packet; to make the ACK packet acceptable, the bad guy must have information from our server's SYN/ACK packet, and that is quite difficult - the bad guy must be able to manipulate routing so that our server's SYN/ACK packet is not sent to the SYN packet's forged source address, but to one of the bad guy's hosts where he can extract the necessary information to forge the ACK packet
  • I can only imagine one case where this is possible: the bad guy must have control of my ISP's network, e.g. because the bad guy is the ISP itself

Rules 2 + 3

Rules 2+3 are now used to evaluate the content of the "SSH" table:

iptables -A INPUT --source ! -p tcp --dport 22 -m recent --rcheck --seconds 20 --hitcount 6 --rttl --name SSH -j LOG --log-prefix "SSH_brute_force"
iptables -A INPUT --source ! -p tcp --dport 22 -m recent --update --seconds 20 --hitcount 6 --rttl --name SSH -j DROP

Dissemination of the rules:

  • ignore all packets (-j DROP) from addresses that have initiated 5 or more connects in the last 20 seconds to the ssh port
  • the key option is --update: it checks if the address is already present in the "SSH" table, together with the extended search operators --seconds, --hitcount and --rttl
  • if the check returns false, the entire rule evaluates to false, i.e. the rule's target is not executed and the entry in the "SSH" table is not updated
    • in other words: before the -j DROP target is executed for the first time, the source address must have established 5 TCP/IP connections to port 22 in the last 20 seconds
  • if the check returns true, the target is executed (i.e. the packet is dropped) and the hit count is increased by 1
    • in other words: as soon as the threshold has been exceeded every additional packet is dropped, i.e. on the 6th connection attempt the initiating SYN packet is already dropped
  • in order to get "unlocked", the bad guy has to remain quiet for 20 seconds (maybe less, the important thing is that in the last 20 seconds not more than 5 hits have occurred for the same TTL), or he needs to come in with packets that have a different TTL (usually this means a different IP route, but of course our hypothetical bad guy may easily manipulate his IP packets to contain arbitrary TTL values)
  • the -j LOG rule uses --rcheck instead of --update in order not to increase the hit count unnecessarily
  • it would be possible to use -j REJECT instead of -j DROP; this is more network-friendly, but it also lets the bad guy know that his packets were rejected

In order to examine the current content of the "SSH" table:

cat /proc/net/ipt_recent/SSH

Note on table size: A certain table of the recent module is limited to 100 entries by default. In order to raise this limit the kernel module must be inserted into the kernel using a special parameter:

modprobe ipt_recent ip_list_tot=<maximum>

Protecting the FTP service

The best way, of course, to protect the FTP service is to disable it completely. If you want to keep the service alive (for whatever insane reasons you have), you will soon notice that brute-force attacks are mounted against you. You can protect yourself by using fail2ban or a similar tool, but first and foremost you should take care to disable FTP for all users except those that really need the service.

This can be done by editing this file:


All users that are listed in this file are not allowed to log in. Typically root and other special system users are already listed in this file, but you may want to double-check to be sure.

Note: Anonymous FTP access is possible only if the user ftp exists in /etc/passwd. This user may or may not exist on your system - double-check to make sure it isn't there.

Persistent attackers can be taken care of by the following iptables rule:

iptables -I INPUT -s -p tcp --dport 21 -j DROP

Using portsentry for protection against portscans etc.


portsentry monitors the network interfaces of a machine and takes steps if it detects a port scan in progress. Basically you tell portsentry which ports on a machine are unused, and how it should react if it detects activities on these unused ports.

Note: I no longer use portsentry because the lower port ranges are now effectively blocked by my ADSL router. Before I switched to ADSL, I was using a cable modem which left these ports unprotected. I keep the following section should I ever decide to start using portsentry again.

Identify ports in use

The first step is to identify which ports are in use. You can do this either by invoking netstat --listening, by using one of the many port scanning tools available on the web (e.g., look for "Shields UP!"), or by executing a command line port scanning program such as nmap:

pelargir:~# nmap

Starting Nmap 4.11 ( ) at 2007-01-06 01:31 CET
Interesting ports on (
Not shown: 1666 closed ports
21/tcp   open  ftp
22/tcp   open  ssh
25/tcp   open  smtp
80/tcp   open  http
111/tcp  open  rpcbind
139/tcp  open  netbios-ssn
143/tcp  open  imap
389/tcp  open  ldap
443/tcp  open  https
445/tcp  open  microsoft-ds
873/tcp  open  rsync
901/tcp  open  samba-swat
993/tcp  open  imaps
8000/tcp open  http-alt

Nmap finished: 1 IP address (1 host up) scanned in 0.179 seconds

Disabling ports that are not needed

It might be a good idea to think about if you really need all those open ports. See the section Closing ports further up for details.


The actual configuration of portsentry is done by modifying one or more of the following files. For each file I give a list of the changes I made to the defaults.


  • RESOLVE_HOST="1" (enable DNS name resolution)
  • KILL_ROUTE="/sbin/iptables -I INPUT -s $TARGET$ -j DROP" (use iptables instead of route for blocking attackers)
  • commented the KILL_HOSTS_DENY entry
  • ADVANCED_EXCLUDE_TCP="20,21,22,25,80,389,443,515,548"
  • ADVANCED_EXCLUDE_UDP="22,67,68,389,548"
  • BLOCK_UDP="1"
  • BLOCK_TCP="1"

Note: I could not find out what ports 515 (printer), 548 (afpovertcp), 67 (bootps) and 68 (bootpc) were, so I left them in the exclude lists for the moment.


  • TCP_MODE="atcp"
  • UDP_MODE="audp"


  • (
  • (internal network cable ISP)
  • (internal network cable ISP)

Let the daemon loose

After (re-)starting the daemon to let the modified configuration take effect, portsentry begins placing more and more hosts on iptables black list. In order to see that list:

iptables -t filter -L -n

To remove a host from the black list:

iptables -t filter -D INPUT -s <host> -j DROP

Password handling

Traditional UNIX accounts

Traditionally, UNIX accounts are kept in system files such as /etc/passwd. Password handling can be varied, though, as detailed in the following short sections.

Shadow passwords

The term "shadow passwords" means that passwords (or better: their hashes) are stored in the protected system file /etc/shadow, instead of in the system file /etc/passwd which can be read by anyone.

Shadow passwords should always be enabled! If not enabled, you can turn it on by saying

shadowconfig on

Password hashes

Traditionally, each user's password is encrypted by the crypt algorithm into a hash. That hash is placed into either /etc/shadow, or /etc/passwd if shadow passwords are disabled (which is not a good idea, see above).

Because more modern hashing algorithms provide better protection against hash collisions than crypt, it is desirable to configure the system to use such a modern algorithm for generating password hashes. The hashing algorithm used in a given password in /etc/shadow is recognizable by its $algorithm$ prefix. Supported algorithms are listed in the crypt(3) man page. For instance, $1$ represents for MD5, $6$ represents SHA-512.

A modern hashing algorithm is usually enabled by default on a Debian system. If you ever need to change the algorithm you need to edit the system's PAM configuration. For instance, the following configuration uses SHA-512:

root@pelargir:~# grep "^password" /etc/pam.d/common.password
password	[success=2 default=ignore] obscure sha512

See the PAM wiki page for more information on PAM. I have not investigated how to switch between crypt and other hashing algorithms on a system that does not use PAM.

Accounts stored in LDAP

Instead of storing account information in system files (traditional approach), it is possible to manage accounts within an LDAP directory. In this case, the above stuff about shadow passwords and password hashing algorithms does not apply.

See the separate wiki pages on PAM, NSS and OpenLDAP for details about this.



The system log files contain such a huge amount of information that it is unpractical - if not outright impossible - to monitor all files for the occasional blip that might indicate a break-in attempt or some other irregularity.

The logcheck tool automatically performs this task for us. Run periodically by cron, logcheck scans those log files that it was configured for and checks if they contain stuff that looks "suspicious". What exactly is (or is not) considered suspicious can be defined through an elaborate set of regular expressions. If suspicious content is discovered, the system administrator is notified by email.


Debian packages



All configuration files are stored in

  • The main configuration file is /etc/logcheck/logcheck.conf
  • The file that lists the log files to be examined is /etc/logcheck/logcheck.logfiles
  • The rules that determine whether or not a log file entry is considered "suspicious" are stored in various sub-directories (e.g. /etc/logcheck/violations.d)

In the main configuration file logcheck.conf I usually set the following things:

  • Date format
  • Recipient of warning emails
  • Report level = server
  • Enable support for cracking.ignore.d

How does it work?

In a first step, all log files to be examined are collected in one big text file. At this point, logcheck remembers up to which position it has processed every log file so that in a subsequent run it will not parse the same log file content a second time.

Depending on the report level, different run-parts directories in /etc/logcheck are now evaluated:

  • cracking.d and cracking.ignore.d are always evaluated (although cracking.ignore.d can be disabled in the main configuration file)
  • violations.d and violations.ignore.d are always evaluated
  • The report level determines which kind of the remaining ignore.d directories are evaluated
  • The more loose the report level, the more ignore.d directories are evaluated
  • For instance, report level "server" causes these directories to be evaluated

The entire report generated by logcheck is mailed with a subject that corresponds to the most critical message found. For instance, if the cracking.d patterns have yielded a result, the subject will be "Security Alerts", even if at a later stage logcheck finds messages that are less critical.

The report can have three types of messages:

  • Type/level 1: cracking.d patterns minus cracking.ignore.d patterns
  • Type/level 2: violations.d patterns minus violations.ignore.d patterns minus cracking.d patterns
  • Type/level 3: All messages minus ignore.d.* patterns minus violations.d patterns minus cracking.d patterns

Positive matching of patterns vs. ignore patterns

  • A message that was positively matched on levels 1 or 2 is always ignored on a lower level; in these cases, the ignore patterns of the lower levels are not relevant
  • A message for which an ignore pattern exists on levels 1 or 2, but which was not positively matched, may still be matched on a lower level; in other words, ignore patterns of a higher level are not relevant for a lower level

How patterns are processed

Lines in a pattern file that begin with "#" are treated as comments. All other lines are egrep regular expressions. This means that, for instance, parantheses need to be escaped ("\(foobar\)").

The way how patterns are processed for cracking.d and violations.d is not obvious. A basic overview has been given in the previous chapter, the following list provides more details.

  • Files in cracking.d (or violations.d) are processed one after the other; each file is supposed to "belong" to a package
  • The patterns in a package file are used to find "suspicious" messages, i.e. to find positive matches
  • If the patterns for a package file "foo" yield one or more positive matches, these matches are now cleaned as follows
  • If there is a file with the same name (e.g. "foo") in the cracking.ignore.d (or violations.ignore.d) folder, the ignore patterns in that file are applied
  • Also applied are the patterns in an ignore file with the same name and with a prefix "logcheck-" (e.g. "logcheck-foo")
  • Also applied are the patterns in an ignore file named "local"
  • Also applied are the patterns in all ignore files with a prefix "local-"

The list of files consulted for ignore patterns is even longer (at least it was when I last checked the script code of logcheck), but I am not interested in enumerating all these esoteric uses. The most important conclusion from the above list is:

cracking.ignore.d/local and violations.ignore.d/local contain ignore patterns that are applied for all packages.

In addition, it should be noted that if the ignore pattern file ignore.server.d/local exists, its name has no special meaning. This is because package names are only relevant for cracking and violations.


logcheck can be executed manually at any time, just like that without specifying any parameters. To get a hint about what logcheck actually does, run it in debug mode

logcheck -d

The best way to use logcheck is to run it periodically and automatically through the cron service. When you install the Debian package, this is already enabled by default. The cron script is stored in


It will execute logcheck every day at 2am, and at reboot time.



After you have taken all kinds of measures to make your system invulnerable to attacks, at some point you will start wondering whether your actions are successful. Is your machine, in effect, secure?

Enter Nessus.

Nessus is a security scanner that you can use to test your system for vulnerabilities. You select a number of more or less destructive tests, then let Nessus execute them in an attempt to break into (or break down) your system.

A useful three-part introduction article about Nessus can be found here:


On my Mac, I use fink to install Nessus.


Create a user with the command


The tool is interactive and queries you for the following information

  • User name
  • Password
  • Ruleset that specifies what the user can or cannot do; you will probably enter nothing here (just hit Ctrl+D)

On my Mac, the user data is stored in


Next you need to create a certificate that is going to be used to encrypt the traffic between the Nessus client and server. The following command accomplishes this:


On my Mac, this creates/updates the following files and directories:


Finally, you should update your installed set of Nessus plugins. You should do this before each scan because plugins are often updated, or new plugins become available, to test vulnerabilities that were newly discovered. A plugin update can be done with

nessus-update-plugins -v

Doing a test

Start the daemon (use -D if you want the daemon to detach itself from the console and run in the background):


Start the client:


Connect to the daemon by entering network details, user name and password.

On the "Plugins" tab, select the plugins you want to run. By default, all "dangerous" plugins are disabled (i.e. plugins that have the potential of crashing the machine).

On the "Scan Options" tab, select "Nmap (NASL wrapper)" to be used as a port scanner. If you want you can disable the "safe checks" option, which should be enabled by default.

Finally, on the "Target" tab you enter the host you want to scan, check the and click the "Start the scan" button at the bottom of the client window.

Audit of on 28.05.2007


  • Dangerous plugins = off
  • Safe checks = on

Revealed weaknesses that I fixed:

  • On TCP ports 6969 and 9696 an instance of nngssrv (No Name Go Server) was listening; I had the thing installed a few months ago when I was evaluating turn-based Go servers, but then forgot to kill the process when I was done

Revealed weaknesses that I need to consider for the future:

  • Web server supports TRACE and/or TRACK methods which can be used for cross-site-scripting attacks
  • LDAP server on port 389 may allow - if improperly configured - the retrieval of information by an anonymous user through NULL BASE queries
  • Web server offers 4 strong and 2 weak SSLv2 ciphers; the weak ciphers - if chosen by a client to communicate with the server - may not be strong enough to resist brute force attack

Nessus also pointed out other potential weaknesses (e.g. web clients can browse the /doc directory, thus revealing what kind of programs are installed on the machine) that I did not fix because I thought they were not severe enough to harm my small private server.