Introduction

If you require remote access to your computer and you enable Secure Shell (SSH) connections, you must accept that you will automatically attract hackers who will try to break your defenses and take command of your machine. Although there’s no guarantee that your machine won’t be “0wn3d” by a “h4x0r,” a few simple solutions can help reinforce your SSH door and make life a bit more difficult for anybody trying to break in. This article considers three such techniques:

Frequently used acronyms API : Application programming interface

: Application programming interface DNS : Domain Name System

: Domain Name System IETF : Internet Engineering Task Force

: Internet Engineering Task Force LDAP : Lightweight Directory Access Protocol

: Lightweight Directory Access Protocol RFC : Request for comments

: Request for comments TCP : Transmission Control Protocol

: Transmission Control Protocol UDP: User Datagram Protocol

Changing SSH’s standard port to an unusual value and reinforcing SSH configuration so that simple-minded attacks just bounce back. Defining a restricted list of users who are allowed to log in. Completely hiding the fact that you even allow SSH access and requiring a special “knock” sequence to be recognized as a possible user.

To apply these techniques, you need to access the root account. Also, you’ll probably have to install some packages, and you’ll need to configure your firewall and your router—if you have one—to open and close specific ports and forward them to your machine.

Reinforcing the door

The concept “security through obscurity” is well known—and well derided—because doing things in an obscure way, hoping that no one will get wise to your method, is just asking for problems. However, in some contexts, a bit of obscurity can help. Although simple measures cannot stop determined hackers, at least you can be better defended against “script kiddies”, whose scripts usually aren’t that thorough.

Everybody knows that the standard port for SSH connections is 22. So, the first step you should take to make your machine more secure is simply to change the port to another unused and nonstandard port number—say, 22960. Numbers above 1024 are usually safe, but check the references to avoid possible problems. This change simply means that you have to use this command line to connect to your computer:

ssh ‑p 22960 your.machine.url Show more Show more icon

To effect this bit of subterfuge, make a simple change in the /etc/ssh/sshd_config file. Edit it (you must work as root for this), look for the line that reads Port 22 , and change the number to whatever you have chosen (If the line is commented out because it starts with a pound sign [ # ], remember to uncomment it). Save the file, and restart SSH with the command /etc/init.d/sshd restart . You should also open the chosen port in your firewall and close port 22.

But you can do even more. Fiddle with the configuration file so that it includes the lines shown in Listing 1. Note that some of these lines may already exist, but they could be commented out.

Listing 1. Some changes to your SSH configuration file enhance security at little cost Port 22960 LoginGraceTime 30 MaxAuthTries 3 Protocol 2 PermitRootLogin no Show more Show more icon

The LoginGraceTime allows 30 seconds for a login; if the user takes longer than this, he or she won’t be allowed access and will have to relog. MaxAuthTries limits the user to three wrong attempts before the login attempt is denied. The Protocol 2 line avoids using a somewhat weaker protocol. Finally, the last line doesn’t allow anybody to log in as root, making life a bit more difficult for hackers. You could also use the DenyUsers , AllowUsers , DenyGroups , and AllowGroups options for extra restrictions. These changes don’t substantially add to the security of your machine, but a common script that just tries brute force attacks at the standard port 22 will fail harmlessly. In any case, consider this a first step in the right direction. Further along in the article, you’ll use even safer methods to not only change the port number but to completely hide it.

Who can enter?

For most people, PAM is a cooking oil that comes from a can. But in terms of Linux® security, PAM stands for Pluggable Authentication Modules. These modules provide extra authentication rules to harden access to your machine.

Let’s start with a basic question: Why use PAM at all? If every program had to define its own authentication logic, it would be a mess. How could anybody be certain that all applications implemented the same tests and checks? What would you do if extra controls were needed—reprogram everything? In computer science, it’s sometimes said that everything can be solved with an extra layer, and at least for security, that stands true. If a program needs to authenticate a user, it can call the PAM API. The API will take care of running all the checks that you might have specified in the PAM configuration files. This method even allows you to modify the authentication rules on the fly, because all PAM-aware programs start applying the new rules without the need for any changes in their code. If you want to use some biometric checks (such as iris scanners or fingerprint readers) and the manufacturer provides you with a PAM, you’re set. When you include the module call in the configuration files, your device will be available to all your applications.

Configuring PAM

PAM provides four distinct security areas, though it isn’t likely that your applications will require all of them. For example, the passwd command only requires the third group in the list below:

account deals with account limitations. Given that the user is valid, what is he or she allowed to do?

deals with account limitations. Given that the user is valid, what is he or she allowed to do? auth has to do with user identification—for example, through entering a user name and password.

has to do with user identification—for example, through entering a user name and password. password has to do exclusively with password-related functions, such as establishing a new password.

has to do exclusively with password-related functions, such as establishing a new password. session deals with connection management, including logging.

You will have to create a configuration file in the /etc/pam.d directory for each application that will use PAM, and the name of the file will be the same as the application’s name. For example, the configuration file for the login command would be /etc/pam.d/login.

You have to define which modules will be applied, creating a “stack” of actions. PAM runs through all the modules in the appropriate stack and, depending on their results, grants or denies the user’s request. You must also define whether or not checks are mandatory. Finally, an other file provides default rules for any application without specific rules.

optional modules may succeed or fail; PAM returns success or failure depending on whether any module eventually succeeds.

modules may succeed or fail; PAM returns or depending on whether any module eventually succeeds. required modules are mandatory. On failure, PAM returns failure , but only after running the rest of the modules in the same stack.

modules are mandatory. On failure, PAM returns , but only after running the rest of the modules in the same stack. requisite modules also need to succeed. But if they fail, PAM immediately returns failure without running any other modules.

modules also need to succeed. But if they fail, PAM immediately returns without running any other modules. sufficient modules, when they succeed, cause PAM to immediately return success without running any other modules.

The structure of the configuration files is simple. You can include comments, which start with a hash character ( # ); continue long lines by ending them with a backslash ( \ ). Lines have three fields: the area ( account , auth , password , or session ), the control flag ( optional , required , requisite , or sufficient ), the path to the module that will be run, and any possible parameters. Note that the second field can be more complicated; see Resources on the right for more information. Also, you can use include rules, as in auth include common-account , to include rules from other files.

The special /etc/pam.d/other file is a “default” configuration file (see Listing 2) whose rules are automatically applied to applications without specific configuration files of their own. To be safe, give the /etc/pam.d file a quick review and just rename all configuration files that you don’t use (so that the other configuration will be used). If you decide you actually need the application, just rename the configuration file to its original name. The default configuration usually denies all requirements (by using the pam_deny.so module) and warns the administrator (via the pam_warn.so module) so that he or she can fix the situation.

The standard “other” configuration file provides a secure default (which denies everything) for all applications without a configuration file of their own.

Listing 2. The standard “other” configuration file account required pam_deny.so auth required pam_deny.so auth required pam_warn.so password required pam_deny.so password required pam_warn.so session required pam_deny.so Show more Show more icon

If you replace pam_deny.so with pam_unix.so , the standard authentication method (enter a user name and password) will be applied. Likewise, if you do not care about security, use pam_permit.so instead, which happily allows any request!

Some available modules

Although there is no standard list of modules, all distributions include most of the following options. Check the /lib/security or /usr/lib/security directories, where modules reside. For 64-bit operating systems, substitute lib64 for lib . If you need more information, you can try man the.name.of.the.module , but do not attempt to execute it directly; PAMs are not executable binaries.

pam_access allows or denies access according to file /etc/security/access.conf. You’ll use this module later to decide which users are allowed to log in.

allows or denies access according to file /etc/security/access.conf. You’ll use this module later to decide which users are allowed to log in. pam_cracklib and pam_pwcheck check possible new passwords for strength.

and check possible new passwords for strength. pam_deny and pam_permit are basic and always deny or allow access.

and are basic and always deny or allow access. pam_echo shows the content of a given file to the user.

shows the content of a given file to the user. pam_lastlog shows the user the date and time of his or her last login.

shows the user the date and time of his or her last login. pam_ldap.so lets users authenticate against an LDAP server to provide centralized authentication across a network.

lets users authenticate against an LDAP server to provide centralized authentication across a network. pam_limits module lets you specify system resource limits, as defined in the file /etc/security/limits.conf.

module lets you specify system resource limits, as defined in the file /etc/security/limits.conf. pam_listfile provides yet another way to allow or deny services based on the contents of a file.

provides yet another way to allow or deny services based on the contents of a file. pam_mail checks whether the user has pending mail.

checks whether the user has pending mail. pam_motd displays the “message of the day” file to the user.

displays the “message of the day” file to the user. pam_nologin blocks all logins should a file named /etc/nologin exist.

blocks all logins should a file named /etc/nologin exist. pam_rootok allows access to the root user without further checks. This module is usually included in /etc/pam.d/su; the required line is auth sufficient pam_rootok.so . Root can act as any other user without having to provide a password.

allows access to the root user without further checks. This module is usually included in /etc/pam.d/su; the required line is . Root can act as any other user without having to provide a password. pam_succeed_if checks for specific desired account characteristics, such as being a member of a particular group.

checks for specific desired account characteristics, such as being a member of a particular group. pam_time can restrict access to a service, as specified by the rules in /etc/security/time.conf.

can restrict access to a service, as specified by the rules in /etc/security/time.conf. pam_unix (or pam_unix2 ) provides classical UNIX® style authentication per the /etc/passwd and /etc/shadow files.

(or ) provides classical UNIX® style authentication per the /etc/passwd and /etc/shadow files. pam_userdb provides authentication against a Berkeley database.

provides authentication against a Berkeley database. pam_warn logs information in the system logs.

logs information in the system logs. pam_wheel only provides root access to members of group wheel; the required line is auth required pam_wheel.so .

Check the resources on the right section for information on more modules and even on writing your own modules. Now, turn to using PAM so that you can decide who can log in to your machine.

Limiting access with PAM

Now, let’s use PAM to restrict who is going to be allowed to connect to your server. You must edit the /etc/pam.d/sshd file so that it reads like Listing 3.

Listing 3. Adding pam_access.so to the sshd PAM file #%PAM‑1.0 account include common‑account account required pam_access.so auth include common‑auth auth required pam_nologin.so password include common‑password session include common‑session Show more Show more icon

Adding pam_access.so to the sshd PAM file lets you easily define who can use SSH to connect to your machine. The pam_access.so module implements security controls based on the /etc/security/access.conf file, as shown in Listing 4.

Listing 4. With pam_access.so, you can define who can or cannot use SSH + : ALL : 192.168.1. + : jack : ALL + : jill : ALL ‑ : ALL : ALL Show more Show more icon

The first line enables everybody ( ALL ) to log in from within the internal network. The next two lines allow users jack and jill to access the server from any location. The last line is a “catch-all” and denies everybody else access from every other address. Another way of enabling several users access is to use pam_listfile.so , creating a list of approved users (for example, /etc/ssh_users). Add the following line to the /etc/pam.d/sshd file:

auth required pam_listfile.so item=user sense=allow file=/etc/ssh_users onerr=fail Show more Show more icon

You’re not done yet. You have to modify the /etc/ssh/sshd_config file more so that it uses PAM. Add a UsePAM yes line to the file, restart the sshd daemon, and that’s all!

Is there a door at all?

Even if you have applied the methods in both previous sections, you must assume that hackers will try to get through any open doors in your system, no matter your precautions. Changing the SSH port number is just a small bother to a knowledgeable would-be intruder, and the limitations placed on users also help (provided no user falls prey to a hacking or social engineering attack that reveals his or her password). However, the mere fact that there’s a door to your system is enough to tempt hackers.

The last scheme for adding security to your machine is the boldest one. You will close the open port making your machine virtually impregnable to any attack. Open it only to a user who can provide a “secret knock”, that opens the required port so that he or she can enter his password and gain access to the machine.

This technique, called port knocking, is appropriate for users who require access to servers that aren’t available to the public. The server can keep all of its ports closed until the user has provided a secret knock sequence (a sequence is easy to implement and requires modest resources).

When the secret ports are open, the usual security mechanisms (such as passwords or certificates) will apply. All services that require the secret ports will function correctly with an extra security layer provided at the firewall level.

The point of this method is to close all ports and monitor external attempts to make connection. Whenever a predefined and specific sequence of attempts is recognized (called a knock sequence), you can execute actions like opening a port so the outsider can get in. The knock sequence can be as complex as you wish—from a simple list (such as trying TCP port 7000; UDP port 7100; TCP port 7200) to a collection of use-once-only sequences. (In cryptography terms, this concept is similar to “one-time pads”, the most secure encryption method known.) The outsider must know the port number and the password to use SSH, as well as the knock sequence required to open that port and enable the password. Without the sequence, connection attempts just mutely fail.

Why is this a good safety scheme? There are 65,535 possible ports (see resources on the right side for more information). Even if you consider the ports already assigned, you’re still left with over 60,000 available ports. If you settle on a sequence just four “knocks” long, a hacker trying to guess it by brute force would have to test about 13 million million million possible sequences (that’s 13 followed by 18 zeros). It should be obvious that such an attack isn’t likely to work! Of course, never assume that brute force or blind luck are the only possible ways to guess the correct sequence. That’s the reason you’re not using a single security method; instead, you have a series of security layers making life more complex for hackers.

You have to install the knockd knocking daemon; it monitors the knock sequences and acts on detecting a valid one. If you wish, you can build it from source, but as this package is available in most (if not all) distributions. You’ll do better using your package-management tool. For example, in OpenSUSE, you could install it with Yast2 or by executing sudo zypper install knockd . In Ubuntu, you could similarly use sudo apt-get install knockd and in Debian use sudo aptitude install knockd . Just search for knockd with your distribution’s software-installation tool, and you’ll be set.

After installing the package, you must edit the /etc/knockd.conf file to specify your port-knocking rules, and then you’ll have to start your daemon running. You must know how your firewall works to do the required setup. For example, in OpenSUSE, you could use something like the setup shown in Listing 5.

Listing 5. A sample configuration file designed for the OpenSUSE firewall [opencloseSSH] sequence= 7000,8000,9000 tcpflags= syn seq_timeout= 15 cmd_timeout= 30 start_command= /usr/sbin/iptables ‑s %IP% ‑I input_ext 1 ‑p tcp ‑‑dport 22960 ‑j ACCEPT stop_command= /usr/sbin/iptables ‑s %IP% ‑D input_ext ‑p tcp ‑‑dport 22960 ‑j ACCEPT Show more Show more icon

The above sample enables SSH access after successive knocks on ports 7000, 8000, and 9000, respectively.

Before starting knockd , close port 22960 and try to log in remotely. The attempt should fail, as shown below in Listing 6.

Listing 6. If you close SSH access and don’t start the knock daemon, login attempts merely fail. > ssh the.url.for.your.site ‑p 22960 ‑o ConnectTimeout=15 ssh: connect to host the.url.for.your.site port 22960: Connection timed out Show more Show more icon

Now try that again after starting the port-knocking daemon by using sudo /etc/init.d/knockd start or sudo knockd -d ; the commands are equivalent. The port-opening sequence requires knocking at ports 7000, 8000, and 9000. You have 15 seconds to accomplish this sequence. The port opens after recognition, and you have 30 seconds to log in. Otherwise, the port will close again.

To verify this process, go back to your remote machine and log in. This time provide the required knocks, as shown in Listing 7. Note that the knock command is usually installed when you get knockd . Otherwise, just use your distribution’s package-management tool and look for it.

Listing 7. A successful login after having provided the required sequence of knocks > knock the.url.for.your.site 7000 > knock the.url.for.your.site 8000 > knock the.url.for.your.site 9000 > ssh the.url.for.your.site ‑p 22960 ‑o ConnectTimeout=10 Password: Show more Show more icon

If you provided a wrong sequence of knocks (or no knocks at all), you would receive a “Connection timed out” message, and the SSH port would remain completely closed with no sign of its existence.

If you are behind a router If your server is connected to the Internet through a router, you’ll have to change its configuration a bit. Details vary among different routers and firewalls, but in general terms, you should: Open and forward the knock ports to your machine so that knockd will be able to recognize and process them.

will be able to recognize and process them. Forward port 22960 (the one you’re using for SSH connections) to port 22960 on your machine.

Configure your machine firewall so that it rejects connections to port 22960 and the knock ports. Although the router will have some ports open, all access to them will lead to your machine’s firewall. Access will be blocked unless, obviously, a correct port knock sequence is detected.

Knock configuration

The /etc/knockd.conf file has a general options section, options , and a section for each knock sequence you want to use. You can write options in uppercase, lowercase, or mixed case.

As standard, knockd monitors the eth0 interface. To work with a different interface—for example, eth1—you can include a Interface=eth1 line. Note that you just include the device name not the full path to it.

monitors the eth0 interface. To work with a different interface—for example, eth1—you can include a line. Note that you just include the device name not the full path to it. If you want to enable logging, you can opt for the standard Linux log files by including a useSyslog line, or you can use a specific file of your own by including LogFile=/the/full/path/to/your/file . However, be aware that logging is a weakness; should a hacker get his or her hands on a log, that intruder would have the port-knocking sequences in the clear.

line, or you can use a specific file of your own by including . However, be aware that logging is a weakness; should a hacker get his or her hands on a log, that intruder would have the port-knocking sequences in the clear. If you want to be able to check whether knockd is still running, include PidFile=/the/full/path/to/a/PID/file . The process ID (PID) of the daemon will be stored in the file. You should have a cron task that periodically checks whether knockd is still alive and restarts it, if needed. Note that in the case of a crash, your system will be safe; all ports will be closed and unaccessible. However, until the daemon starts running again, you won’t be able to log in.

You can have knockd listen for several sequences and react in different ways to each one. In the earlier example, you had knockd open the SSH port; you could just as easily have enabled the HTTP port so a web server is accessible or run a specific process. You will have a section in the configuration file for each sequence.

Use sequence to define the sequence of knocks, as in 7000,8000,9000 . By default, knocks use TCP, but you can add UDP to mix it up a bit more, as in 7000,8000:udp,9000 .

to define the sequence of knocks, as in . By default, knocks use TCP, but you can add UDP to mix it up a bit more, as in . As an alternative to having single fixed sequences, you can specify a file with “one-time sequences” that, after they are used, are erased and cannot be used again. To specify such sequences, use:

one_time_sequences=/the/full/path/to/a/sequences/file Show more Show more icon

Use any text editor to create the file; it should have a sequence (in the format shown above) in each line. You should have a copy of this file on your remote machine to remember how to log in.

You can specify which incoming TCP packets to scan and discard those that don’t match the flags ACK , FIN , PSH , RST , SYN , or URG . Over an SSH connection, you should use TCPFlags=SYN .

, , , , , or . Over an SSH connection, you should use . You can define a maximum time for completing a sequence with Seq_Timeout=seconds.to.wait . If the complete sequence isn’t entered in this time, it won’t be recognized and access won’t be granted.

. If the complete sequence isn’t entered in this time, it won’t be recognized and access won’t be granted. Similarly, you can set a maximum time for the user to execute a second command after the sequence was recognized with Cmd_Timeout=seconds.to.wait . If the user who provided the knock sequence doesn’t act quickly (logging in, for example), the port will close again.

. If the user who provided the knock sequence doesn’t act quickly (logging in, for example), the port will close again. A most important parameter is Start_command=some.command.to.execute , which specifies what command or script is to be executed after a successful knock sequence is recognized. If you need to refer to the knocker’s IP address (for example, to allow a connection from his or her machine to yours), you can use %IP% . It is then replaced by the correct value at run time. In the example above, you wrote: /usr/sbin/iptables ‑s %IP% ‑I input_ext 1 ‑p tcp ‑‑dport 22960 ‑j ACCEPT Show more Show more icon iptables opens port 22960 for the user at the IP address from where the knock sequence came.

, which specifies what command or script is to be executed after a successful knock sequence is recognized. If you need to refer to the knocker’s IP address (for example, to allow a connection from his or her machine to yours), you can use . It is then replaced by the correct value at run time. In the example above, you wrote: opens port 22960 for the user at the IP address from where the knock sequence came. The other important parameter is Stop_command=some.command.to.execute ; it is executed after the Cmd_timeout time has elapsed.

In this case, because you just wanted to open or close port 22960, a single command sufficed. If your needs are more complicated, you can invoke a script and do whatever you want—even if it doesn’t involve opening ports at all. You could trigger any action, such as running a process or performing a backup. Of course, learning what command to use can be tricky. For example, because I was running OpenSUSE and it provides its own firewall front end, I had to examine the output of iptables -l to learn what command I should provide to open or close port 22960.

As to knockd itself, there are few options to consider:

-c : Lets you specify a configuration file other than the default, /etc/knockd.conf

: Lets you specify a configuration file other than the default, /etc/knockd.conf -d : Makes knockd run as a background daemon, which is the standard way to run it

: Makes run as a background daemon, which is the standard way to run it -D : Provides output debugging messages

: Provides output debugging messages -h : Provides help on syntax and options

: Provides help on syntax and options -i : Allows you to change the interface to watch from the default eth0 value

: Allows you to change the interface to watch from the default value -l : Enables DNS lookup for log entries—a bad practice, given that it forces your machine to use DNS traffic, which means dropping stealthiness

: Enables DNS lookup for log entries—a bad practice, given that it forces your machine to use DNS traffic, which means dropping stealthiness -v : Provides more verbose messages and explanations

: Provides more verbose messages and explanations -V : Displays the version number of the program

Finally, to produce the knock sequence itself, you could use many methods, but programming knock is the simplest way to go about it.

You just:

knock the.url.for.your.site 7000 Show more Show more icon

for knocking TCP port 7000 and either:

knock the.url.for.your.site ‑u 8000 Show more Show more icon

or

knock the.url.for.your.site 8000:udp Show more Show more icon

for knocking UDP port 8000. The -h parameter provides some help for this command.

Conclusion

You’ve seen three ways of hardening SSH access to your machine: modification of sshd configuration parameters, selecting which users can log in by means of PAM, and use of port-knocking sequences to hide the existence of SSH access. Although I mentioned that there is no way to secure fully any machine, adding these three layers will make your server more than just a little safer.