Steps to Secure and harden a Linux Server

Removing Unnecessary Software Packages (RPMs)

A very important step in securing a Linux system is to determine the primary function or role of the Linux server. A detailed knowledge is necessary about what is on one’s own Linux system. It is very critical to look at the default list of software packages and remove unneeded packages or packages that don’t comply with security policy. If that’s done that then there will have less packages to update and to maintain when security alerts and patches are to be installed. Also, it is a good practice not to have development packages, desktop software packages (e.g. X Server) etc. installed on production servers. Other packages like FTP and Telnet daemons should not be installed as well unless there is a justified business reason for it (SSH/SCP/SFTP should be used instead).
One of the first action items should be to create a Linux image that only contains RPMs needed by the applications, and needed for maintenance and troubleshooting purposes. A good approach is to start with a minimum list of RPMs and then add packages as needed. It may be time-consuming but worth the efforts.  To get a list of all installed RPMs  the following command is used:

rpm -qa


To know more about a particular RPM, run: rpm -qi <package_name>


To check for and report potential conflicts and dependencies for deleting a RPM, run: rpm -e –test <package_name>


Patching Linux Systems

Building an infrastructure for patch management is another very important step to proactively secure Linux production environments. It is recommended to have a written security policy and procedure to handle Linux security updates and issues. For example, a security policy should detail the timeframe for assessment, testing, and rollout of patches. Network related security vulnerabilities should get the highest priority and should be addressed immediately within a short period. For example, a security procedure should detail the process for assessment, testing, and rollout of patches. The assessment phase should occur within a testing lab, and initial rollout should occur on development systems first.  A separate security log should detail what Linux security notices have been received, when patches have been researched and assessed, when patches have been applied etc.


Detecting Listening Network Ports

One of the most important tasks is to detect and close network ports that are not needed. To get a list of listening network ports (TCP and UDP sockets), you can run the following command:

# netstat -tulp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name
tcp        0      0 *:auth                      *:*                         LISTEN      2328/xinetd
tcp        0      0 localhost.localdomain:smtp  *:*                         LISTEN      2360/sendmail: acce
tcp        0      0 *:ssh                       *:*                         LISTEN      2317/sshd



From the output you can see that xinetdsendmail, and sshd are listening. (On all newer Red Hat Linux distributions sendmail is configured to listen for local connections only.)Sendmail should not listen for incoming network connections unless the server is a mail or relay server. Running a port scan from another server will confirm that (make sure that you have permissions to probe a machine):

# nmap -sTU <remote_host>
Starting nmap 3.70 ( ) at 2004-12-10 22:51 CST
Interesting ports on jupitor (
(The 3131 ports scanned but not shown below are in state: closed)
22/tcp  open          ssh
113/tcp open          auth
Nmap run completed -- 1 IP address (1 host up) scanned in 221.669 seconds

Note that the above nmap command can take a while. If UDP port scan is removed (without the option “-U“), then nmap will finish the port scan immediately. If run on the local machine it will also complete very fast. Also note that nmap might not show all listening network sockets if a firewall is being used to block ports.
From the output above you can see that the xinetd daemon is listening on port auth (port 113) for IDENT. Also sendmail is not listening for remote incoming network connections, (it is revealed from the output)


Another method to list all of the TCP and UDP sockets to which programs are listening is lsof:

# lsof -i -n | egrep 'COMMAND|LISTEN|UDP'
sshd      2317 root    3u  IPv6   6579       TCP *:ssh (LISTEN)
xinetd    2328 root    5u  IPv4   6698       TCP *:auth (LISTEN)
sendmail  2360 root    3u  IPv4   6729       TCP (LISTEN)

Closing Network Ports and Disabling Runlevel System Services

One of the most important tasks is to remove any network services from the system startup process that are not needed. To list all services which are started at bootup the following command is used:  chkconfig –list |grep on

There will be  quite a lot of services enabled on the system. But many runlevel services (Stand-Alone Services) are not network related services like kudzu which is responsible for detecting and configuring new and/or changed hardware on your system. This service is only run during the boot process. Ensure not to disable runlevel services that are needed by the system to run smoothly.
Here are examples of Red Hat Runlevel System Services which may or may not want to enable:






























needed if you want to use the mouse at the console
important for detecting new hardware
important for syslog services
needed only if there are NFS shares that should be mounted at boot time
important for starting network interfaces (e.g. eth0, eth1, bonding,...)
used for the system entropy pool
needed if the at(1) service is used instead of cron
Advanced Power Management (APM) daemon is used for laptops and some desktops
needed if ISDN is being used
needed if Netfilter (iptables) Firewall is being used
needed if ip6tables Firewall is being used
not needed on servers - needed for laptops
important for distributing interrupts across all CPUs
needed if Sendmail is used - Procmail should be used which is more secure
needed if automounter is used - production applications should not be dependent on automounter
important for logins via SSH
needed if e.g. NFS is being used
needed if NFS shares are mounted
needed if server runs the NFS server
needed only if software RAID is being used
important for running cron jobs
needed if xinetd services are being used, see /etc/xinetd.d/ for list of services
needed if CUPS is used for the printing system
needed if server should connect to RHN to check for software updates etc.
needed to reset system statistics logs
needed only if Linux Audit Subsystem (LAuS) should run for collecting system call audit records
needed only if kernel process accounting information is needed
important for monitoring disk problems if hard disks support SMART technology
important if kernel oops data and memory dumps should be sent to a Netdump      server for server crashes

The start/stop scripts of all runlevel services can be found in the /etc/init.d directory. For example, if you don’t know what the atd service does, go to /etc/init.d and open the file atd. And in the script look for lines that start programs. In the atd script the “daemon/usr/sbin/atd” line starts the binary atd. Now having the name of the program that is started by this service, you can check the online pages of atd by running man atd. This will help you to find out more about a system service.
To permanently disable e.g. the runlevel service nfs, run: chkconfig nfs off

To immediately disable the runlevel service nfs, run: /etc/init.d/nfs stop


Closing Network Ports and Disabling Xinetd Services

The xinetd daemon is a replacement for inetd, the internet services daemon. It monitors the ports for all network services configured in/etc/xinetd.d, and starts the services in response to incoming connections. To check if xinetd is enabled and running, execute: # chkconfig –list xinetd

xinetd          0:off   1:off   2:off   3:on    4:on    5:on    6:off
# /etc/init.d/xinetd status
xinetd (pid 2619) is running...

If xinetd is active, it is important to check which Unix services are active and controlled by xinetd. The following command shows all services configured in /etc/xinetd.d and wheter xinetd monitors the ports for these services:

# chkconfig --list | awk '/xinetd based services/,/""/'
xinetd based services:
        krb5-telnet:    off
        rsync:  off
        eklogin:        off
        gssftp: off
        klogin: off
        chargen-udp:    off
        kshell: off
        auth:   on
        chargen:        off
        daytime-udp:    off
        daytime:        off
        echo-udp:       off
        echo:   off
        services:       off
        time:   off
        time-udp:       off
        cups-lpd:       off

To get a list of only active services for which xinetd monitors the ports, you could run:

# chkconfig --list | awk '/xinetd based services/,/""/' | grep -v off
xinetd based services:
        auth:   on

The above example shows that  the telnet-server RPM is not installed on the system. If the Telnet Server package telnet-server would be installed, it would show up on the list whether it’s active or not. Here is an example how to disable a service. Assuming the telnet service is active, run the following commands to disable it and to see how the telnet service entries are being updated:

# chkconfig --list telnet
telnet          on
# cat /etc/xinetd.d/telnet | grep disable
disable = no
# chkconfig telnet off
# chkconfig --list telnet
telnet          off
# cat /etc/xinetd.d/telnet | grep disable
disable = yes


For the telnet service it would be better to remove the package from the system since SSH should be used instead:  # rpm -e telnet-server

It is important to investigate all active xinetd services and to disable them if they are not needed. Here is an example how to find out what a service does. Assuming you don’t know what the auth service does which is list as active in the list above, run the following commands:

# grep "  server" /etc/xinetd.d/auth
       server          = /usr/sbin/in.authd
        server_args     = -t60 --xerror --os -E
#  man in.auth
No manual entry for in.auth
# rpm -qf /usr/sbin/in.authd
# rpm -qi authd-1.4.1-1.rhel3 | awk '/Description/,/""/'
authd is a small and fast RFC 1413 ident protocol daemon
with both xinetd server and interactive modes that
supports IPv6 and IPv4 as well as the more popular features
of pidentd.
# rpm -ql authd-1.4.1-1.rhel3

This example shows what can be done if there exists no online manuals for the binary in.authd that is started by xinetd. The steps above should be helpful for finding out more about services.
The auth service (aka IDENT, see RFC 1413) allows remote daemons to query information about users establishing TCP connections on the local server. In a trusted environment it helps a server to identify who is trying to use it. For example, it can provide vital information for troubleshooting and who has done what. IDENT requests are needed by some applications like IRC. However, IDENT can be a security risk. To disable the auth service, run the following command:  # chkconfig auth off
The xinetd daemon is quite flexible and has many features. Here are just a few functionalities of Xinetd:

– Access control for TCP, UDP, and RPC services

– Access limitations based on time

– Provides mechanisms to prevent DoS attacks


Reviewing Inittab and Boot Scripts

The inittab file /etc/inittab also describes which processes are started at bootup and during normal operation. For example, Oracle uses it to start cluster services at bootup. Therefore, it is recommended to ensure that all entries in /etc/inittab are legitimate in your environment. At least remove the CTRL-ALT-DELETE trap entry to prevent accidental reboots: # sed -i ‘s/ca::ctrlaltdel:/#ca::ctrlaltdel:/g’

The default runlevel should be set to 3 since in my opinion X11 (X Windows System) should not be running on a production server. In fact, it shouldn’t even be installed.

# grep ‘:initdefault’ /etc/inittab


And depending on your environment you might want to comment out the UPS entries as well. To have changes in /etc/inittab become effective immediately, you can run:

# init q

The /etc/rc.local script is used for commands or startup scripts which are pertinent only to a specific server. (/etc/rc.local is a link to/etc/rc.d/rc.local).
Ensure that all startup scripts in /etc/rc.d/rc.local are legitimate.


Restricting System Access from Servers and Networks

Usually a firewall is used to protect a server from other servers and networks. However, in some cases you may also want to protect a server within a network by using a TCP Wrapper.  The Xinetd super server that comes with most Linux distributions includes a built-in TCP wrapper. It can be used to explicitly define network services to accept incoming connections from specified servers and networks. The TCP wrappers implements access control through the use of two files, /etc/hosts.allow and /etc/hosts.deny. Note that the hosts.allow file takes precedence over the hosts.deny file. And you may want to change the permissions on the two configuration files since they are both world readable. A recommended security-strategy is to block all incoming requests by default, but allow specific hosts or networks to connect. This is the strategy I will describe here.

To deny everything by default, add the following line to /etc/hosts.deny:

    ALL: ALL
To accept incoming SSH connections from e.g. nodes rac1cluster, rac2cluster and rac3cluster, add the following line to /etc/hosts.allow:
   sshd: rac1cluster rac2cluster rac3cluster

To accept incoming SSH connections from all servers from a specific network, add the name of the subnet to /etc/hosts.allow. For example:

    sshd: rac1cluster rac2cluster rac3cluster

To accept incoming portmap connections from IP address and subnet 192.168.5, add the following line to /etc/hosts.allow:

    portmap: 192.168.5.

To accept connections from all servers on subnet but not from server, you could add the following line to /etc/hosts.allow:


Here are other examples that show some features of TCP wrapper:

If you just want to restrict ssh connections without configuring or using /etc/hosts.deny, you can add the following entries to/etc/hosts.allow:

    sshd: rac1cluster rac2cluster rac3cluster
    sshd: ALL: DENY

Here is an example how an additional program can be spawned in e.g. the /etc/hosts.allow file:

    sshd: ALL : spawn echo "Login from %c to %s" | mail -s "Login Info for %s" log@loghost

The TCP wrapper is quite flexible. And xinetd provides its own set of host-based and time-based access control functions. You can even tellxinetd to limit the rate of incoming connections. I recommend reading various documentations about the Xinetd super daemon on the Internet.


Securing SSH

Many network services like telnetrlogin, and rsh are vulnerable to eavesdropping which is one of several reasons why SSH should be used instead. Red Hat’s default configuration for SSH meets the security requirements for many environments. However, there are a few parameters in /etc/ssh/sshd_config that you may want to change on RHEL and other Linux systems.
It’s prudent to disable direct root logins at the SSH level as well.

PermitRootLogin no

Also ensure to have privilege separation enabled where the daemon is split into two parts. With privilege separation a small part of the code runs as root and the rest of the code runs in a chroot jail environment. Since SSH protocol version 1 is not as secure you may want to limit the protocol to version 2 only:

Protocol 2

You may also want to prevent SSH from setting up TCP port and X11 forwarding if you don’t need it:

AllowTcpForwarding no
X11Forwarding no

Ensure the StrictModes directive is enabled which checks file permissions and ownerships of some important files in the user’s home directory like ~/.ssh~/.ssh/authorized_keys etc. If any checks fail, the user won’t be able to login.

StrictModes yes

Ensure that all host-based authentications are disabled. These methods should be avoided as primary authentication.

IgnoreRhosts yes
HostbasedAuthentication no
RhostsRSAAuthentication no

Disable sftp if it’s not needed:

#Subsystem      sftp    /usr/lib/misc/sftp-server

After changing any directives make sure to restart the sshd daemon:

/etc/init.d/sshd restart

Securing Postfix

Postfix is a replacement for Sendmail which has several security advantages over Sendmail. Postfix consists of several small programs that perform their own small task. And almost all programs run in a chroot jail. These are just a few examples why Postfix is recommended over Sendmail. Linux servers that are not dedicated mail or relay servers should not accept external emails. However, it is important for production servers to send local emails to a relay server. Before you continue on a Red Hat system, make sure Postfix is activated using the following command:

# alternatives --set mta /usr/sbin/sendmail.postfix

The following parameters in /etc/postfix/ should be set to ensure that Postfix accepts only local emails for delivery:

  mydestination = $myhostname, localhost.$mydomain, localhost
  inet_interfaces = localhost


The parameter mydestination lists all domains to receive emails for. The parameter inet_interfaces specifies the network to liston on.
Once Postfix configured, restart the mail system with the following command:

# /etc/init.d/postfix restart

To verify whether Postfix is still listening for incoming network request, you can run one of the following commands from another node:

# nmap -sT -p 25 <remode_node>

# telnet <remote_node> 25

Don’t run these commands on the local host since Postfix is supposed to accept connections from the local node.


Securing Sendmail

Sendmail or Postfix is usually required for local mail delivery. Note that it is recommended to use Postfix over Sendmail for various security reasons.
On newer Linux systems Sendmail is configured to run in the background for local mail delivery and not to accept incoming network connections. If your server is not a mail or relay server, then it is important that Sendmail is not accepting incoming network connections from any host other than the local server.
The default configuration file on RedHat does not allow Sendmail to accept incoming network connections. The following setting in /etc/mail/ tells Sendmail not to accept incoming network connections from servers other than the local node:

  O DaemonPortOptions=Port=smtp,Addr=, Name=MTA

If that’s not the case on your system, you can change it by setting or uncommenting the DAEMON_OPTIONS parameter in the/etc/mail/ file. Uncomment the DAEMON_OPTIONS line in /etc/mail/ to read:

DAEMON_OPTIONS(`Port=smtp,Addr=, Name=MTA')dnl


Then run:

# mv /etc/mail/ /etc/mail/
# m4 /etc/mail/ > /etc/mail/
# /etc/init.d/sendmail restart

To verify whether Sendmail is still listening for incoming network request, you can run one of the following commands from another node (make sure that you have permissions to probe a machine):

# nmap -sT -p 25 <remode_node>
# telnet <remote_node> 25

Don’t run these commands on the local host since Sendmail is supposed to accept connections from the local node.


Securing NFS


NFS (Network File System) allows servers to share files over a network. But like all network services using NFS involves risks. Here are some basic rules:

  • NFS should not be enabled if not needed.
  • If you must use NFS, use TCP wrapper to restrict remote access.
  • Make sure you export to only those machines that you really need to.
  • Use fully qualified domain names to diminish spoofing attempts.
  • Export only directories you need to export.
  • Export read-only wherever possible.
  • Use NFS over TCP.

If there is no shared directories to export, ensure that the NFS service is NOT enabled and running:  # service nfs status

rpc.mountd is stopped
nfsd is stopped
rpc.rquotad is stopped
# chkconfig --list nfs
nfs             0:off   1:off   2:off   3:off   4:off   5:off   6:off

There is no need for the portmap service as well which is used by NFS (the portmap daemon registers rpc-based services for services like NFS, NIS, etc.):

# service portmap status
portmap is stopped
# chkconfig --list portmap
portmap         0:off   1:off   2:off   3:off   4:off   5:off   6:off

Enabling and Starting NFS Server

If you must use NFS, it can be activated using the following commands on Red Hat Linux:

chkconfig portmap on
chkconfig nfs on
service portmap start
service nfs start

The “portmap” service starts the portmap daemon.  Depending on the Linux OS distribution and version, the “nfs” service starts therpc.rquotadnfsdlockdrpciodrpc.mountd and rpc.idmapd daemons on Red Hat Fedora Core 3 which is the OS that Red Hat Advanced Server 4 is based on. On Red Hat Advanced Server 3, the NFS service starts rpc.mountdnfsd and rpc.rquotad.


To probe the portmapper for all registered NFS related RPC programs, run rpcinfo. On Red Hat Advanced Server 3, the output is:  # rpcinfo -p <server>

   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100011    1   udp    607  rquotad
    100011    2   udp    607  rquotad
    100011    1   tcp    610  rquotad
    100011    2   tcp    610  rquotad
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    2   tcp   2049  nfs
    100003    3   tcp   2049  nfs
    100005    1   udp    623  mountd
    100005    1   tcp    626  mountd
    100005    2   udp    623  mountd
    100005    2   tcp    626  mountd
    100005    3   udp    623  mountd
    100005    3   tcp    626  mountd

Restricting Incoming NFS Requests
The portmap program and some of the NFS programs include a built-in TCP wrapper. To verify if a program includes a TCP wrapper, you can run the following commands:

# strings /sbin/portmap | egrep "hosts.deny|hosts.allow|libwrap"
# strings /usr/sbin/rpc.rquotad | egrep "hosts.deny|hosts.allow|libwrap"
# ldd /usr/sbin/rpc.rquotad | grep libwrap => /usr/lib/ (0x00874000)

If hosts.deny and hosts.allow are displayed, or if libwrap is displayed, then the program includes a built-in TCP wrapper. If none of these strings are displayed, then adding the program name to /etc/hosts.deny and /etc/hosts.allow will most probably have no effect.

To block all incoming requests by default, add the following line to /etc/hosts.deny if you have not done so yet:


Verify from a remote server that portmapper does not list any registered RPC programs:

# rpcinfo -p <server>
No remote programs registered.

To allow NFS mounts from trusted servers and networks only, you have to configure the servers and networks for portmap and various NFS related programs in /etc/hosts.allow. To find out which NFS programs are invoked when you start the NFS server, you can check the/etc/init.d/nfs service script. Then run the “strings <program> | egrep "hosts.deny|hosts.allow|libwrap" command.

On Red Hat Advanced Server 3, the /usr/sbin/rpc.rquotad program includes a built-in TCP wrapper. On Red Hat Fedora Core 3,/usr/sbin/rpc.mountd now also includes a built-in TCP wrapper.  To allow NFS requests from e.g. servers,, and from the network, the configuration in /etc/hosts.allow would look like as follows:



Make sure to test NFS access thoroughly when using TCP wrappers. If a TCP wrapper has been added to another NFS related program, please drop me a note. For portmapper you can now test access from trusted servers or networks using the rpcinfo command:

# rpcinfo -p <server>
   program vers proto   port
    100000    2   tcp    111  portmapper
    100000    2   udp    111  portmapper
    100011    1   udp    607  rquotad
    100011    2   udp    607  rquotad
    100011    1   tcp    610  rquotad
    100011    2   tcp    610  rquotad
    100003    2   udp   2049  nfs
    100003    3   udp   2049  nfs
    100003    2   tcp   2049  nfs
    100003    3   tcp   2049  nfs
    100005    1   udp    623  mountd
    100005    1   tcp    626  mountd
    100005    2   udp    623  mountd
    100005    2   tcp    626  mountd
    100005    3   udp    623  mountd
    100005    3   tcp    626  mountd

If you run it from an “untrusted” server or network, you should get the following output:

# rpcinfo -p <server>
No remote programs registered.

Exporting NFS File Systems

To allow a client access to a filesystem or directory, the /etc/exports serves as the access control list. To give the network “” read-only access to /pub, the entries in /etc/exports would look like as follows:

/pub      *,sync)

It is very important NOT to give write access to NFS clients if not absolutely needed! Entries in /etc/exports are exported read-only (“ro” option) by default.

To allow servers rac1pub, rac2pub and rac3pub read-write access to the /data/OracleArch directory, the entries in /etc/exports would look like as follows:



Note that options MUST NOT be separated from hostnames or networks with whitespace(s).
And use fully qualified domain names to diminish spoofing attempts.

All entries in /etc/exports are exported with the root_squash option (‘root squashing’) by default. This means that a root user on a client machine does not have root privileges (root access) to root-owned files on exported NFS filesystems/directories. It is not recommended to turn ‘root squashing” off using the no_root_squash option!

After you’ve made all your entries in /etc/exports, you can export all filesystems/directories using the following command:

# exportfs -a

To unexport all shared filesystems/directories, run:

# exportfs -ua

To see all shared filesystems/directories, run:

# showmount -e localhost
Export list for localhost:
/pub             *

Using NFS over TCP

If you need NFS, it is recommended to use NFS over TCP since NFS over UDP is not very secure. All 2.4 and 2.6 kernels support NFS over TCP on the client side. Server support for TCP appears in later 2.4 kernels, and in all 2.6 kernels. To verify whether your server supports NFS over TCP, use the wire-test command (/usr/sbin/wire-test is part of the am-utils package). If your server supports NFS over TCP, the output looks like this:

# wire-test localhost
Network 1: wire="" (netnumber=172.16.1).
Network 2: wire="" (netnumber=172.16.1).
My IP address is 0xac100101.
NFS Version and protocol tests to host "localhost"...
        testing vers=2, proto="udp" -> found version 2.
        testing vers=3, proto="udp" -> found version 3.
        testing vers=2, proto="tcp" -> found version 2.
        testing vers=3, proto="tcp" -> found version 3.

If the server does not support NFS over TCP, the output will look like this:

# wire-test localhost
Network 1: wire="" (netnumber=172.16.1).
Network 2: wire="" (netnumber=172.16.1).
My IP address is 0xac100101.
NFS Version and protocol tests to host "localhost"...
        testing vers=2, proto="udp" -> found version 2.
        testing vers=3, proto="udp" -> found version 3.
        testing vers=2, proto="tcp" -> failed!
        testing vers=3, proto="tcp" -> failed!

To mount a shared directory using NFS over TCP, use the “proto=tcp” mount option:

# mount -o proto=tcp <nfs_server_name>:/pub /usr/local/pub


Make sure the target directory, in this example /usr/local/pub, exists on the client.

Verify the NFS over TCP mount using the mount command:

# mount
nfsserver:/pub on /usr/local/pub type nfs (rw,proto=tcp,addr=

To have the shared directory mounted on the client at boot time, use the /etc/fstab file.
For the above example, the /etc/fstab entry could look like this:

nfsserver:/pub  /usr/local/pub      nfs   rsize=8192,wsize=8192,timeo=14,intr,tcp  0 0


Copying Files Using SSH Without Providing Login Prompts

The following example is a suggestion you may want to use in some cases. It shows how files can be copied over the network using ssh without providing an interactive login prompt.  SSH allows you to do a forced command using the “command” option. When you use this option you can disable scp and enforce every passed ssh command to be ignored. On the server side where you want to retrieve the file from, add the following entry to the beginning of the SSH key in the ~/.ssh/authorized_keys2 file:

command="/bin/cat ~/<file_name>" ssh-dss AAABB33Nza...OpenSSH key


To copy now the file from the remote server, you can run the following command:

ssh <user>@<server> > <local_file>

Since /bin/cat is executed on the server side, the output has to be redirected to the local file. Another approach is to replace /bin/cat with your own script that checks the passed SSH commands by reading the environment variable


if [[ $SSH_ORIGINAL_COMMAND = "File1" ||
        $SSH_ORIGINAL_COMMAND = "File2" ]]
      /bin/cat $SSH_ORIGINAL_COMMAND
      echo "Invalid file name!"
      exit 1


So you replace /bin/cat with the script name in ~/.ssh/authorized_keys2, and run the following command to copy “File1″:

ssh <user>@<server> File1 > <local_file>

To copy “File 2″, run:

ssh <user>@<server> File2 > <local_file>

Every other passed parameter will return an error.


Kernel Tunable Security Parameters

The following list shows tunable kernel parameters you can use to secure your Linux server against attacks. For each tunable kernel parameters I will show the entry that needs to be added to the /etc/sysctl.conf configuration file to make the change permanent after reboots. To activate the configured kernel parameters immediately at runtime, use:

# sysctl -p

Enable TCP SYN Cookie Protection

A “SYN Attack” is a denial of service attack that consumes all the resources on a machine. Any server that is connected to a network is potentially subject to this attack.

To enable TCP SYN Cookie Protection, edit the /etc/sysctl.conf file and add the following line:  net.ipv4.tcp_syncookies = 1
Disable IP Source Routing

Source Routing is used to specify a path or route through the network from source to destination. This feature can be used by network people for diagnosing problems. However, if an intruder was able to send a source routed packet into the network, then he could intercept the replies and your server might not know that it’s not communicating with a trusted server. To enable Source Route Verification, edit the/etc/sysctl.conf file and add the following line:  net.ipv4.conf.all.accept_source_route = 0
Disable ICMP Redirect Acceptance

ICMP redirects are used by routers to tell the server that there is a better path to other networks than the one chosen by the server. However, an intruder could potentially use ICMP redirect packets to alter the hosts’s routing table by causing traffic to use a path you didn’t intend. To disable ICMP Redirect Acceptance, edit the /etc/sysctl.conf file and add the following line:  net.ipv4.conf.all.accept_redirects = 0
Enable IP Spoofing Protection

IP spoofing is a technique where an intruder sends out packets which claim to be from another host by manipulating the source address. IP spoofing is very often used for denial of service attacks. To enable IP Spoofing Protection, turn on Source Address Verification. Edit the/etc/sysctl.conf file and add the following line: net.ipv4.conf.all.rp_filter = 1
Enable Ignoring to ICMP Requests

If you want or need Linux to ignore ping requests, edit the /etc/sysctl.conf file and add the following line: net.ipv4.icmp_echo_ignore_all = 1

This cannot be done in many environments.


Enable Ignoring Broadcasts Request

If you want or need Linux to ignore broadcast requests, edit the /etc/sysctl.conf file and add the following line:net.ipv4.icmp_echo_ignore_broadcasts = 1
Enable Bad Error Message Protection

To alert you about bad error messages in the network, edit the /etc/sysctl.conf file and add the following line:net.ipv4.icmp_ignore_bogus_error_responses = 1
Enable Logging of Spoofed Packets, Source Routed Packets, Redirect Packets

To turn on logging for Spoofed Packets, Source Routed Packets, and Redirect Packets, edit the /etc/sysctl.conf file and add the following line:

net.ipv4.conf.all.log_martians = 1


Checking File Permissions and Ownership

Default umask

The umask (user file-creation mode mask) command is a shell built-in command which determines the default file permissions for newly created files. This can be overwritten by system calls but many programs and utilities make use of umask.

By default, Red Hat sets umask to 022 or 002 which is fine. If the name of the user account and the group account is the same and the UID is 100 or larger, then umask is set to 002, otherwise it’s set to 022, see /etc/bashrc for bash shells.

$ id
uid=509(test) gid=510(test) groups=100(users),510(test) context=user_u:system_r:unconfined_t
$ umask
# id
uid=0(root) gid=0(root)
groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel) context=root:system_r:unconfined_t
# umask

Here is an example how umask works:

$ umask 000
$ touch file1
$ ls -l file1
-rw-rw-rw-  1 oracle oinstall 0 Dec 26 19:24 file1
$ umask 002
$ touch file2
$ ls -l file2
-rw-rw-r--  1 oracle oinstall 0 Dec 26 19:24 file2
$ umask 022
$ touch file3
$ ls -l file3
-rw-r--r--  1 oracle oinstall 0 Dec 26 19:25 file3

For the bash shell you can find the setting of umask in /etc/bashrc. The /etc/bashrc file is for system-wide aliases and functions and is invoked by ~/.bashrc.


When the SUID (set user ID) or SGID (set group ID) bits are set on an executable, it executes with the UID or GID of the owner of the executable rather than that of the person executing it. This means that e.g. all executables that have the SUID bit set and are owned by root are executed with the UID of root. A good example is the passwd command that allows ordinary users to update the password field in the /etc/shadow file which is owned by root. But SUID/SGID bits can be misused when the SUID/SGID executable has a security hole. Therefore, you might want to search the entire system for SUID/SGID executables and document it. For example, ensure that code developers don’t set SUID/SGID bits on their programs if it’s not an absolute requirement. Very often you can use workarounds like removing just the executable bit for world/others. However, a better approach is to change the design of the software if possible.

To search the entire system for SUID or SGID files, you can run the following command:

find / -path /proc -prune -o -type f -perm +6000 -ls

The -prune option in this example is used to skip the /proc filesystem.

World-Writable Files

World-writable files are a security risk since it allows anyone to modify them. Additionally, world-writable directories allow anyone to add or delete files.
To locate world-writable files and directories, you can use the following command:

find / -path /proc -prune -o -perm -2 ! -type l -ls

The “! -type l” parameter skips all symbolic links since symbolic links are always world-writable. However, this is not a problem as long as the target of the link is not world-writable, which is checked by the above find command.
World-Writable directories with sticky bit such as the /tmp directory do not allow anyone except the owner of a file to delete or modify it in this directory. The sticky bit makes files stick to the user who created it and it prevents other users from deleting and renaming the files. Therefore, depending on the purpose of the directory world-writable directories with sticky are usually not an issue. An example is the /tmp directory:

$ ls -ld /tmp
drwxrwxrwt 18 root root 16384 Dec 23 22:20 /tmp

The “t” mode, which denotes the sticky bit, allows files to be deleted and renamed only if the user is the owner of this file or the owner of the directory.

Unowned Files

Files not owned by any user or group might not necessarily be a security problem in itself. However, unowned files could pose a security problem in the future. For example, if a new user is created and the new users happens to get the same UID as the unowned files have, then this new user will automatically become the owner of these files. To locate files not owned by any user or group, use the following command:

find / -path /proc -prune -o -nouser -o -nogroup


Checking Accounts

Checking for Unlocked Accounts

It is important that all system and vendor accounts that are not used for logins are locked. To get a list of unlocked accounts on your system, you can check for accounts that do NOT have an encrypted password string starting with “!” or “*” in the /etc/shadow file. If you lock an account using passwd -l, it will put a ‘!!’ in front of the encrypted password, effectively disabling the password. If you lock an account usingusermod -L, it will put a ‘!’ in front of the encrypted password. Many system and shared accounts are usually locked by default by having a ‘*’ or ‘!!’ in the password field which renders the encrypted password into an invalid string.

Hence, to get a list of all unlocked (encryptable) accounts, run:

# egrep -v '.*:\*|:\!' /etc/shadow | awk -F: '{print $1}'

Also make sure all accounts have a ‘x’ in the password field in /etc/passwd. The following command lists all accounts that do not have a ‘x’ in the password field:

# grep -v ':x:' /etc/passwd

A ‘x’ in the password fields means that the password has been shadowed, i.e. the encrypted password has to be looked up in the /etc/shadowfile. If the password field in /etc/passwd is empty, then the system will not lookup the shadow file and it will not prompt the user for a password at the login prompt.
Checking for Unused Accounts

All system or vendor accounts that are not being used by users, applications, by the system or by daemons should be removed from the system. You can use the following command to find out if there are any files owned by a specific account:

# find / -path /proc -prune -o -user <account> -ls

The -prune option in this example is used to skip the /proc filesystem.
If sure that an account can be deleted, you can remove the account using the following command:  # userdel -r <account>

Without the “-r” option userdel will not delete the user’s home directory and mail spool (/var/spool/mail/<user>). Note that many system accounts have no home directory.

Single User Mode Password for root

Some admins suggest adding the following line to the /etc/inittab file to ensure that a root password is required for Single User Mode logons:



This works but can be circumvented easily! At the GRUB or LILO prompt you can tell the boot loader to alternate the init program by using the boot params “init=/bin/bash“. This will place you at a root shell prompt without a password.

Enabling Password Aging

In general I do not recommend that the system enforces password expiration for system and shared accounts. This could lead to outages if an application’s account expires:

# su oracle -c id
You are required to change your password immediately (password aged)
Changing password for test
(current) UNIX password:

Rather a corporate policy should govern password changes for system and shared accounts. But for individual user accounts the system should expire the passwords automatically. The following example shows how password expiration can be setup for individual user accounts.
The following files and parameters in the table are used when a new account is created with the useradd command. These settings are recorded for each user account in the /etc/shadow file. Therefore, make sure to configure the following parameters before you create any user accounts using the useradd command:

/etc/login.defs PASS_MAX_DAYS 60 Maximum number of days a password is valid.
/etc/login.defs PASS_MIN_DAYS 7 Minimum number of days before a user can change the password since the last change.
/etc/login.defs PASS_MIN_LEN n/a This parameter does not work. It is superseded by the PAM module “pam_cracklib”. See Enforcing Stronger Passwords for more information.
/etc/login.defs PASS_WARN_AGE 7 Number of days when the password change reminder starts.
/etc/default/useradd INACTIVE 14 Number of days after password expiration that account is disabled.
/etc/default/useradd EXPIRE   Account expiration date in the format YYYY-MM-DD.

Ensure that the above parameters are changed in the /etc/login.defs and /etc/default/useradd files. When a user account is created using the useradd command, the parameters listed in the above table are recorded in the /etc/shadow file in the following fields:


To create a new user account you can execute the following command:

useradd -c "Test User" -g users test

The -g option specifies the primary group for this account:

# id test
uid=509(test) gid=100(users) groups=100(users)

The settings in /etc/login.defs and /etc/default/useradd are recorded for the test user in the /etc/shadow file as follows:

# grep test /etc/shadow

You can change the password aging any time using the chage command.

To disable password aging for system and shared accounts, you can run the following chage command:

# chage -M 99999 <system_account_name>

To get password expiration information:

# chage -l <system_account_name>

For example:

# chage -l test
Minimum:        7
Maximum:        60
Warning:        7
Inactive:       14
Last Change:            Jan 11, 2005
Password Expires:       Mar 12, 2005
Password Inactive:      Mar 26, 2005
Account Expires:        Never



Locking User Accounts After Too Many Login Failures

I do not recommend that the system automatically locks system and shared accounts after too many failed login or su attempts. This could lead to outages if the application’s account gets locked due to too many login failures like in this example:

# su oracle -c id
su: incorrect password

This could be an easy target for a denial of service attack. In the following example I will show how to lock only individual user accounts after too many failed su or login attempts.

Add the following two lines highlighted in blue to the /etc/pam.d/system-auth file as shown below:

auth        required      /lib/security/$ISA/
auth        required      /lib/security/$ISA/ onerr=fail no_magic_root
auth        sufficient    /lib/security/$ISA/ likeauth nullok
auth        required      /lib/security/$ISA/
account     required      /lib/security/$ISA/
account     required      /lib/security/$ISA/ per_user deny=5 no_magic_root reset
account     sufficient    /lib/security/$ISA/ uid < 100 quiet
account     required      /lib/security/$ISA/
password    requisite     /lib/security/$ISA/ retry=3
password    sufficient    /lib/security/$ISA/ nullok use_authtok md5 shadow
password    required      /lib/security/$ISA/
session     required      /lib/security/$ISA/
session     required      /lib/security/$ISA/

The first added line counts failed login and failed su attempts for each user. The default location for attempted accesses is recorded in/var/log/faillog.

The second added line specifies to lock accounts automatically after 5 failed login or su attempts (deny=5). The counter will be reset to 0 (reset) on successful entry if deny=n was not exceeded. But you don’t want system or shared accounts to be locked after too many login failures (denial of service attack). To exempt system and shared accounts from the deny=n parameter, I added the per_user parameter to the module. The per_user parameter instructs the module NOT to use the deny=n limit for accounts where the maximum number of login failures is set explicitly. For example:

# faillog -u oracle -m -1
# faillog -u oracle
Username   Failures  Maximum  Latest
oracle            0       -1  Fri Dec 10 23:57:55 -0600 2005 on unknown


The faillog command with the option “-m -1” has the effect of not placing a limit on the number of failed logins. To instruct the module to activate the deny=n limit for this account again, run:

# faillog -u oracle -m 0

By default, the maximum number of login failures for each account is set to 0 which instructs pam_tally to use the deny=n parameter. Thefaillog manual page on my Red Hat system says that selecting maximum number of login failures of 0 will deactivate deny=n for the account. The PAM documentation, however, says that per_user will only work if the .fail_max field contains a non-zero value. After testing both values setting it to -1 worked. Maybe because it’s read as a high unsigned value? .

To see failed login attempts, run:  # faillog

To unlock an account after too many login failures, run:  # faillog -u <user> -r

Make sure to test these changes thoroughly on your system using e.g. ssh and su, and make sure root does not get locked! To lock/unlock accounts manually, you can run one of the following commands:

# passwd -l <user>
# usermod -L <user>
# passwd -u <user>
# usermod -U <user>

Since the /var/log/faillog is owned by root and only root can write to the /var/log/faillog file, xscreensaver and vlock won’t work correctly. Each time xscreensaver or vlock is executed as a non-root user, you won’t be able to do an unlock since these programs can’t write to /var/log/faillog. I don’t have a good solution for that. I can only think of setting the SUID bits on these programs.


Restricting Direct Login Access for System and Shared Accounts

On an audited production system it is very important to know who switched to which system or shared account. Therefore it is prudent to restrict direct logins for all system and shared account where more than one individual knows the password. All users should do a direct login using their own account and then switch to the system or shared account. However, there are situations where you have to allow direct logins for system or shared accounts. For example, within an Oracle RAC cluster you have to enable direct ssh logins for oracle. But in such an environment you have to protect the whole cluster as a single entity against incoming ssh connection, i.e. direct oracle logins should not work if you come from a node that is not part of the cluster. In the following example I will show how to achieve this goal as well. Usually all system and shared accounts have one thing in common, that is they are not in the “users” group. The following example assumes that all individual user accounts are in the “users” group but system and shared accounts like root and oracle are not. If you want to go a step further, a good solution would be to implement a new ‘logingroup’ users group which would require users to be given explicit access. In this example I will show how to restrict direct logins for:

- SSH                   (/etc/pam.d/sshd)
- Console Login         (/etc/pam.d/login)
- Graphical Gnome Login (/etc/pam.d/gdm
- or for all logins     (/etc/pam.d/system-auth)

To accomplish this goal I will add the pam_access module to the PAM configuration files listed above. This module provides logdaemon-style login access control based on login names, host names, IP addresses, etc. The PAM module type that has to be used in the configuration files isaccount. This module type does the authorization, i.e. is the user allowed to login (e.g. time, day)? Don’t confuse the PAM module type accountwith auth which does the authentication, for example checking the password. And the control flag I will use is required. It specifies that Success is required, Failure means that it will still call the remaining modules, but the result is already determined.  For SSH Logins add thepam_access module to /etc/pam.d/sshd as follows:

auth       required service=system-auth
auth       required
account    required
account    required service=system-auth
password   required service=system-auth
session    required service=system-auth


For Console Logins add the pam_access module to /etc/pam.d/login as follows:

auth       required
auth       required service=system-auth
auth       required
account    required
account    required service=system-auth
password   required service=system-auth
session    required close
session    required service=system-auth
session    optional
session    required multiple open

For Graphical Gnome Logins add the pam_access module to /etc/pam.d/gdm as follows:

auth       required
auth       required service=system-auth
auth       required
account    required
account    required service=system-auth
password   required service=system-auth
session    required service=system-auth
session    optional

Now add the following line to the /etc/security/access.conf configuration file:


The /etc/security/access.conf configuration file is read by the pam_access module. This entry specifies that no users are accepted except users that are in the “users” group. Since the pam_access module has been configured for “Authorization” (account) in the above PAM configuration files, it denies direct logins for all accounts except the ones that are in the “users” group.  Now on some systems like Oracle RAC clusters you have to enable direct ssh logins for oracle within the cluster. On such systems you can enable direct ssh logins for oracle within the cluster by adding/changing the following lines in /etc/security/access.conf:

 -:ALL EXCEPT users oracle:ALL

-:oracle:ALL EXCEPT


The first line has been edited to include the oracle account which will allow general direct logins. However, the second line specifies that direct logins for oracle are only allowed from Oracle RAC nodes (rac1cluster, rac2cluster, and rac3cluster) that are part of the cluster.
NOTE: In RHEL4 pam_access is already configured for crond:

# grep pam_access /etc/pam.d/*
/etc/pam.d/crond:account    required accessfile=/etc/security/access-cron.conf

This means that the above entries in /etc/security/access.conf will stop cron from working. Note that it is very prudent to always check whether pam_access is configured for any other service on the system!  To ensure that all users on the system can still run cron jobs you can add the following argument to pam_access in /etc/pam.d/crond:

account    required accessfile=/etc/security/access-cron.conf

This ensures that the /etc/security/access.conf configuration file is not invoked by crond. Since pam_cracklib does not grant permissions if the configuration file does not exist, execute the following command to create an empty file:

# touch /etc/security/access-cron.conf

Now verify that cron jobs can be launched by any user on the system.

NOTE:  The above example will only work if there exists no “users” account in the /etc/passwd file on the system, which is usually the case. Otherwise you have to either delete the “users” account or you have to designate or create another group name.

Restricting su Access to System and Shared Accounts


Users usually don’t share the passwords of their own accounts but are less hesitant to share it for shared accounts. This chapter helps to mitigate this problem.
The following example shows how to restrict su access to the rootoracle, and postgres account to a specific set of users.

NOTE: The documentation about the pam_wheel module included in many Linux distributions is wrong. For instance, in Red Hat Advanced Server 2.1 the pam_wheel module does not only restrict people from su-ing to the root account like it used to be. It restricts people from su-ing to any account.
Example for Restricting su Access to root, oracle, and postgres Accounts

Create a new group for each set of users that are allowed to su to the rootoracle, and postgres account:

# groupadd rootmembers
# groupadd oraclemembers
# groupadd postgresmembers

Add all users who are allowed to su to the rootoracle, and postgres account to the new member groups created above.The following requirement will be configured:

  • Only admin1 should be able to su to rootoracle, and postgres.
  • Only oracledba1 should be able to su to oracle.
  • Only postgresdba1 should be able to su to postgres.


No one else on the system should be able to su to any account.

# usermod -G rootmembers adminuser1
# usermod -G oraclemembers oracleuser1
# usermod -G postgresmembers postgresuser1

As you probably noted, I did not add adminuser1 to the other member groups. Instead, I will show how to give people in the rootmembersgroup automatically su access to the oracle and postgres account without adding them to the oraclemembers and postgresmembersgroups. I consider root admins an exception. They should not be added to all member groups on the system. Next add the three authentication lines highlighted in blue to the /etc/pam.d/su file as shown below:

auth       sufficient   /lib/security/$ISA/
auth       required     /lib/security/$ISA/ service=system-auth
auth       sufficient   /lib/security/$ISA/ service=su-root-members
auth       sufficient   /lib/security/$ISA/ service=su-other-members
auth       required     /lib/security/$ISA/
account    required     /lib/security/$ISA/ service=system-auth
password   required     /lib/security/$ISA/ service=system-auth
session    required     /lib/security/$ISA/ close
session    required     /lib/security/$ISA/ service=system-auth
session    required     /lib/security/$ISA/ open multiple
session    optional     /lib/security/$ISA/


These additional authentication lines specify that nobody should be able to su to any account unless at least one of the PAM services, su-root-members or su-other-members, returns Success. The control flag sufficient means that a Success will bypass the remaining authentication modules and overall Success is returned for the authentication part. Failure means that the failed authentication PAM service is ignored. If both authentication PAM services fail, then the last authentication module pam_deny is invoked which will deny all requests for any available authentication module. This will cause the authentication part to fail for the su command. Next the new authentication PAM service configuration files /etc/pam.d/su-root-members and /etc/pam.d/su-other-members need to be created. The file /etc/pam.d/su-root-members referenced in /etc/pam.d/su should read like:

auth       required     /lib/security/ use_uid group=rootmembers
auth       required     /lib/security/ item=user sense=allow onerr=fail file=/etc/security/su-rootmembers-access

The file /etc/security/ su-rootmembers-access referenced in /etc/pam.d/su-root-members should read like:


The control flag required which is specified for both modules means that both modules have to return Success. Otherwise this PAM service will return Failure to the “su” PAM service configured in /etc/pam.d/su. The first line returns Success only if the user is in the rootmembersgroups. The second line allows only access (sense=allow) to those users specified in /etc/security/rootusername, which is root, oracle, and postgres – these are the only users that will be accepted as a user argument to su. The item=user argument instructs pam_listfile that the entries in /etc/security/rootusername are usernames. If an error occurs, such as an unreadable configuration file, access is denied (onerr=fail).

NOTE: Once su access to root is working for users in the root members, I recommend to avoid making any changes to the /etc/pam.d/su-root-members file in the future. Making a mistake in this file could revoke access to root for all users on the system. That’s the reason why I created two PAM service files, /etc/pam.d/su-root-members for people in the root members group, and /etc/pam.d/su-other-members(see below) for all other member groups since you will most probably add more member groups to this file in the future.


Share your thoughts with us!!!

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s