SSHOne of Unix’s great strengths is its ease of remote administration. Whether the server is in front of you or in a remote, barricaded laboratory in a subterranean, maximum-security installation, if you have network access to the machine you can control it just as if you were sitting in front of it.
For many years, telnet(1) was the standard way to access a remote server. telnet is nifty. You can use it to connect to an arbitrary TCP port on a system and manually talk to that server across the network. As a remote administration protocol, however, telnet has one crushing problem: Everything sent over most versions of telnet is unencrypted. Anyone with a packet sniffer, attached anywhere along your connection, can steal your username, your password, and any information you view in your telnet session. When you use telnet, the best password-selection scheme in the world cannot protect your username and password. Intruders place illicit packet sniffers anywhere they can on small local networks, in law firms handling sensitive government work, on home PCs, and on Internet backbonesThe only defense against a packet sniffer is to handle your authentication credentials and data in such a way that a packet sniffer cannot capture them.
That’s where SSH, or secure shell, comes in. SSH behaves much like telnet in that it provides a highly configurable terminal window on a remote host. But unlike telnet, SSH encrypts everything you send across the network. SSH ensures not only that your passwords can’t be sniffed, but also that the commands you enter and their output are encrypted as well. While telnet does have a few minor advantages over SSH in that it requires less CPU time and is simpler to configure, SSH’s security advantages heavily outweigh them. SSH also has many features that telnet does not have, such as the ability to tunnel arbitrary protocols through the encrypted session. Like telnet, SSH runs on every modern variant of Unix and even on Microsoft Windows.
SSH encrypts and authenticates remote connections via public key cryptography. The SSH daemon offers the server’s public key to clients and keeps the private key to itself. The client and server use the cryptographic key to negotiate a cryptographically secure channel between them. Since both public and private keys are necessary to complete this transaction, your data is secure; even if someone captures your SSH traffic, they can only see encrypted garbage.To use SSH, you must run an SSH server on your FreeBSD machine and an SSH client on your workstation.
The SSH Server: sshd(8)
The sshd(8) daemon listens for SSH requests coming in from the network on TCP port 22. To enable sshd at boot, add the following line to /etc/rc.conf:
sshd_enable="YES"Once this is set, you can use the /etc/rc.d/sshd script to start and stop SSH.
Stopping the SSH daemon doesn’t terminate SSH sessions that are already in use; it only prevents the daemon from accepting new connections.Unlike some of the other protocols we look at, sshd is difficult to test by hand. One thing you can do is confirm that sshd is running by using telnet to connect to the SSH TCP port.
# telnet localhost 22 Trying ::1... Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. SSH-2.0-OpenSSH_4.4p1 FreeBSD-20060930
Telnet tries the IPv6(if you just set it) address for localhost, then the IPv4 address , and succeeds . The connection attempt succeeds, and we see that the daemon listening on this port calls itself SSH version 2(The SSH protocol hastwo versions, 1 and 2. Always use version 2), implemented in OpenSSH 4.4p1, on FreeBSD, version 20060930. You can get all this information from a simple telnet command, but it’s the last free information sshd offers.
Unless you’re capable of encrypting packets by hand, on the fly, this is about
as far as you can go. Press CTRL-] to close the connection (and probably CTRL-C to leave telnet and return to the command prompt).
SSH Keys and Fingerprints
The first time you start sshd(8), the program realizes that it has no encryption keys and automatically creates them. If the system is just booting, you will be offered a chance to pound on the keyboard for a while to help enhance system randomness.
The initializing sshd process will create three pairs of keys.
- SSH version 1 uses /etc/ssh/ssh_host_key, /etc/ssh/ssh_host_key.pub, /etc/ssh/ssh_host_rsa_key, and /etc/ssh/ssh_host_rsa_key.pub. SSH version 1, while not really secure, is much less appallingly insecure than telnet.
- SSH version 2 uses the RSA key files as well as the DSA key files /etc/ssh/ssh_host_dsa_key and /etc/ssh/ssh_host_dsa_key.pub.
The key files ending in .pub contain the public keys for each type of key. These are the keys that sshd hands to connecting clients. This gives the connecting user the ability to confirm that the server he is connecting to is really the server he thinks it is. (In the past, intruders have tricked users into logging into bogus machines in order to capture their usernames and passwords.)Take a look at one of these public key files; it’s pretty long. Even when a user is offered the chance to confirm that the server is offering the correct key, it’s so long that even the most paranoid users won’t bother to verify every single character.
Fortunately, SSH allows you to generate a key fingerprint, which is a much shorter representation of a key. You cannot encrypt traffic or negotiate connections with the fingerprint, but the chances of two unrelated keys having the same fingerprint are negligible. To generate a fingerprint for a public key, enter the command ssh-keygen -lf keyfile.pub:
# ssh-keygen -lf /etc/ssh/ssh_host_dsa_key.pub 1024 31:28:4b:6e:aa:23:63:2e:9a:6b:44:00:9f:fd:28:21 /etc/ssh/ssh_host_dsa_key.pubThe first number, 1024, shows the number of bits in the key. 1,024 is standard in 2007, but nowadays is increased to 2,048 as computing power increases. The hexadecimal string starting with 31 and ending with 21 is the fingerprint of the public key. While it’s long, it’s much shorter and much more readable than the actual key. Copy this key fingerprint from the original server to a place where you can access it from your client machines, either on a web page or on a paper list. Use this key to confirm your server’s identity the first time you connect.
Configuring the SSH Daemon
While sshd comes with a perfectly usable configuration, you might want to tweak the settings once you learn all the features sshd(8) offers. The configuration file /etc/ssh/sshd_config lists all the default settings, commented out with a hash mark (#). If you want to change the value for a setting, uncomment the entry and change its value.
We won’t discuss all the available sshd options; that would take a rather large book of its own. Moreover, OpenSSH advances quickly enough to make that text obsolete. Instead, we’ll focus on some of the more common desirable configuration changes people make.
After changing the SSH daemon’s configuration, restart the daemon with
# /etc/rc.d/sshd restart
The VersionAddendum appears in the server name when you connect to sshd’s
TCP port. Some people recommend changing this to disguise the operating system version. Identifying a computer’s operating system is simple enough, however, by using fingerprinting techniques on packets exchanged with the host, so this isn’t generally worth the time. (On the other hand, changing
the VersionAddendum to DrunkenBadgerSoftware because it amuses you might
sshd(8) defaults to listening to TCP port 22. If you want, you can change this to a nonstandard port. If you want sshd to listen to multiple ports (e.g., port 443 in addition to port 22, to bypass a badly configured firewall), you can include multiple Port entries on separate lines:
Port 22Protocol 2
Any modern SSH install only supports version 2 of the SSH protocol by default. SSH version 1 has security problems, and SSH version 2 clients are now free for all widely deployed systems. sshd(8) still supports version 1, however, and you can enable its support here even though it has poor security. List your permitted protocol versions in order of preference, separated by commas.
sshd defaults to listening for incoming requests on all IP addresses on the machine. If you need to restrict the range of addresses to listen on (for example, on a jail server), you can specify it here:
ListenAddress 192.168.33.8If you want sshd to listen on multiple addresses, use multiple ListenAddress lines.
SyslogFacility AUTH and LogLevel INFO
These two settings control how sshd(8) logs connection information.
This controls how long a user has to log in after getting connected. If an incoming user connects but does not successfully log in within this time window, sshd drops the connection.
Do not let people log into your server as root. Instead, they should SSH in as a regular user and become root with su(1).
Allowing direct root logins eliminates any hope you have of identifying who misconfigured your system and allows intruders to cover their tracks much more easily.MaxAuthTries 6
This is the number of times a user may attempt to enter a password during a single connection. After this number of unsuccessful attempts to log in, the user is disconnected.
SSH allows users to forward arbitrary TCP/IP ports to a remote system. If your users have shell access, they can install their own port forwarders, so there’s little reason to disable this.
Unix-like operating systems use the X11 (or X) protocol to display graphical programs. In X, the display is separated from the physical machine. You can run, say, a web browser on one machine and display the results on another.
As X has had a checkered security history, many admins reflexively disable X forwarding. Denying X forwarding over SSH doesn’t disable X forwarding in general, however.
Most users, if denied SSH-based X forwarding, just forward X over unencrypted TCP/IP using either X’s built-in network awareness or a third-party forwarder, which in most circumstances is far worse than allowing X over SSH.If your sshd server has the X libraries and client programs installed, a user can forward X one way or another; it’s best to let SSH handle the forwarding for you. If you don’t have the X software installed, then X11Forwarding has no effect.
This is the number of connection attempts that can occur at the same time. If more users than specified by MaxStartups attempt to SSH to the server simultaneously, sshd(8) refuses some of the connection attempts until other users log in, timeout, or fail to log on enough times to be disconnected.
The banner is a message that is displayed before authentication occurs. The most common use for this option is to display legal warnings. The default is to not use a banner.
Subsystem sftp /usr/libexec/sftp-server
SSH allows you to securely copy files from one system to another with scp(1). While scp works well, it’s not very user-friendly. The sftp server provides an FTP-like interface to file transfer, reducing the amount of time you must spend on user education but still maintaining solid security.
Managing SSH User Access
By default, anyone with a legitimate shell can log into the server. Using the configuration variables AllowGroups, DenyGroups, AllowUsers , and DenyUsers,
sshd(8) lets you define particular users and groups that may or may not access your machine.
When you explicitly list users who may SSH into a machine, any user who is not listed cannot SSH in. For example, the AllowGroups option lets you restrict SSH access to users in specified groups defined in /etc/group. If this option is set and a user is not in any of the allowed groups, he cannot log in. Separate multiple groups with spaces:
AllowGroups wheel webmaster dnsadminIf you don’t want to give a whole group SSH access, you can list individual users with AllowUsers. By using AllowUsers, you disallow SSH access for everyone except the listed users.
The DenyGroups list is the opposite of AllowGroups . Users in the specified system groups cannot log in.
The listed group must be their primary group, meaning it must be listed in /etc/master.passwd and not just /etc/group. This limitation makes DenyGroups less useful than it seems at first; you cannot define a general group called nossh and just add users to it, unless you make it their primary group as well. Explicitly listing allowed groups is a much more useful policy.Finally, the DenyUsers variable lists users who may not log in. You can use this to explicitly forbid certain users who are in a group that is otherwise allowed.
These four different settings make it possible for a user to be in multiple groups simultaneously. For example, one user might be in a group listed in AllowGroups and a group listed in DenyGroups. What then? The SSH daemon checks these values in the order: DenyUsers, AllowUsers, DenyGroups, and
AllowGroups. The first rule that matches wins. For example, suppose I’m a
member of the wheel group. Here’s a snippet of sshd_config:
DenyUsers: mwlucasI cannot SSH into this machine, because DenyUsers is checked before AllowGroups.
Of course, FreeBSD comes with the SSH client, as do most Unix-like operating systems.
If possible, use the included SSH client—it’s part of OpenSSH, developed by a subset of the OpenBSD team, and is not only the most popular implementation but also the best. If you’ve been sentenced to run a Microsoft operating system, I recommend PuTTY, which is free for commercial or non commercial purposes and has excellent terminal emulation.
We’ll focus on FreeBSD’s OpenSSH client. You can configure the client in a variety of ways, but the most common configuration choices available simply disable the functions offered by the server. If you’re really interested in tweaking your client’s behavior, read ssh_config(5).OPEN SSH SECURES EVERYTHINGMany network programs include support for communicating over SSH. Additionally, OpenSSH can create arbitrary tunnels between TCP ports on different machines. Thanks to OpenSSH, you can avoid sending any data unencrypted over your network.
To connect to another host with SSH, type ssh hostname. In response, you’ll see something like this:
# ssh sardines.blackhelicopters.org The authenticity of host 'sardines.blackhelicopters.org (192.168.1.1)' can't be established. DSA key fingerprint is a4:df:7c:7e:0e:27:e5:21:b4:f4:0e:2b:c9:10:5f:ea. Are you sure you want to continue connecting (yes/no)? yes
Your client immediately retrieves the public key from the host(running sshd) you’re connecting to, and checks its own internal list of SSH keys for a matching key for that host. If the key offered by the server matches the key the client has in its list, the client assumes you’re talking to the correct host. If the client does not have the host key in its list of known hosts(i.e. the first time you connect to sshd), it presents the key fingerprint for your approval.
The fingerprint presented by the SSH client should be identical to the fingerprint you generated on your server. If the fingerprint is not identical, you’re connecting to the wrong host and you need to immediately disconnect. If it matches, accept the key and continue. Once you accept the fingerprint, the key is saved under your home directory in .ssh/known_hosts.
If you’re building a new server on your local network for your private use, perhaps you don’t have to manually compare the key fingerprints. You should still copy the key fingerprint, however, since you’ll eventually want to connect from a remote location and will need to verify the key. If many people will connect to a server, it’s generally okay to put the fingerprint on a web page. You must decide how much security you need. I strongly encourage you to error on the side of caution. Accept the host key, and you’ll be allowed to log into the server. While using a private key with a passphrase is preferable to using passwords, a password with SSH is still better than telnet.
Copying Files over SSH
The SSH client is fine for command-line access, but what about moving files from one system to another? SSH includes two tools for moving files across
the network, scp(1) and sftp(1).
scp(1) is “secure copy” and is ideal for moving individual files. scp takes two arguments: first, the file’s current location, then the desired location. The desired location is specified as: "username@hostname:filename". Suppose I want to copy the file bookbackup.tgz from my local system to the remote server bewilderbeast.blackhelicopters.org, giving the remote copy a different name. I would run:
# scp bookbackup.tgz email@example.com:bookbackup-january.tgz
If you want to give the new copy the same name, you can leave off the filename in the second argument:
# scp bookbackup.tgz firstname.lastname@example.org:
scp(1) also lets you copy files from a remote system to your local system:
# scp email@example.com:bookbackup-january.tgz bookbackup.tgz
If you don’t want to change the filename on the local system, you can use a single dot as the destination name:
# scp firstname.lastname@example.org:bookbackup.tgz
Finally, if your username on the remote system is the same as your local username, you can delete the username and the @ sign. For example, to back
up my work I just use:
# scp bookbackup.tgz bewilderbeast.blackhelicopters.org:
While this looks complicated, it’s quite useful for quickly moving individual files around the network.
If you like interactive systems or if you don’t know the precise name of the file you want to grab from a remote server, sftp(1) is your friend. sftp(1) takes a single argument, the username and server name, using scp’s syntax for a remote server:
# sftp mwlucas@bewilderbeast,blackhelicopters.org Connecting to bewilderbeast... Password: sftp> ls
sftp(1) looks much like a standard command-line FTP client; it supports the usual FTP commands such as ls (list), cd (change directory), get (download a file), and put (upload a file).
One important difference is that sftp(1) does not require a choice between ASCII and binary transfers(a common ftp protocol issue ); it just transfers the file as is.
With SSH, scp, and sftp, you can completely eliminate cleartext passwords from your network.
Network Time ProtocolIf a database starts entering dates three hours behind, or if emails arrive with dates from tomorrow, you’ll hear about it pretty quickly. Time is important. You have three tools to manage system time:
- tzsetup(8) controlling the time zone
- ntpdate(8) the network time correction tool , and
- ntpd(8) the continuous time- adjustment program
Setting the Time Zone
Time zone is easy to manage with tzsetup(8), a menu-driven program that makes the appropriate changes on your system for each time zone. Global organizations might use on their systems the default of UTC (Universal Time Clock), while others use their own local time. Enter tzsetup, follow the geographic prompts, and choose the appropriate time zone for your location.
Network Time Protocol
Network Time Protocol (NTP), is a method to synchronize time across a network. You can make your local computer’s clock match the atomic clock at your government’s research lab or the time on your main server.
Computers that offer time synchronization are called time servers and are roughly lumped into two groups, Tier 1 and Tier 2.
Tier 1 NTP servers are directly connected to a highly accurate time-keeping device. If you really need this sort of accuracy, then what you really need is your own atomic clock. If the time lag caused by the speed of light is acceptable, a USB radio clock such as that found on an inexpensive GPS is
Tier 2 NTP servers feed off the Tier 1 NTP servers, providing time service as a public service. Their service is accurate to within a fraction of a second and is sufficient for almost all non–life sustaining applications. Some digging will even lead you to Tier 3 time servers, which feed off of Tier 2 servers.
The best source of time servers is the list at pool.ntp.org. This group has collected public NTP servers into round-robin DNS pools, allowing easy NTP configuration.
These NTP servers are arranged first in a global list, then by continent, and then by country. For example, if you’re in Canada, a brief search on that site leads you to 0.ca.pool.ntp.org, 1.ca.pool.ntp.org, and 2.ca.pool.ntp.org. We’ll use these servers in the examples below, but look up the proper servers for your country and use those instead when setting up your own time service.
ntpd(8) checks the system clock against a list of time servers. It takes a reasonable average of the times provided by the time servers, discarding any servers too far away from the consensus, and gradually adjusts the system time to match the average. This gives the most accurate system time possible, without demanding too much from any one server, and helps keep errant hardware in check. Configure ntp in /etc/ntp.conf. Here’s a sample:
server 1.ca.pool.ntp.orgThis system checks three time servers for updates.
- If you list only one server, ntpd(8) slaves its clock to that one server and shares any time problems that server experiences.
- Using two time servers guarantees that your system will not know what time it is; remember, NTP takes an average of its time servers but throws out any values too far out of range of the others. How can NTP decide if one server is wrong when it only has two values to choose from?
- Using three time servers is optimal; if one server runs amok, ntpd recognizes that the time offered by that server does not make sense against the time offered by the other two servers.
Instant Time Correction
ntpd(8) is great at keeping the system clock accurate over time, but it only adjusts the local clock gradually. If your time is off by hours or days (which is not unlikely at install time or after a long power outage), you probably want to set your clock correctly before letting any time-sensitive applications start.
ntpd(8) includes that functionality as well, with ntpd -q.
To perform a single brute-force correction of your clock, use ntpd -q. This connects to your NTP servers, gets the correct time, sets your system clock, and exits:
# ntpd -q ntpd: time set -76.976809sThis system’s time was off by about 77 seconds, but is now synchronized with the NTP servers.
Do not change the clock arbitrarily on a production system. Time-sensitive software, such as many database-driven applications, has problems if time suddenly moves forwards or backwards.
If you have really good hardware with an excellent clock, using ntpd -q at boot handles all of your time problems.Very few people have that sort of hardware, however. Most of us have to make do with commodity hardware with notoriously poor clocks. The best way to ensure you have accurate time is to run ntpd(8) to gently adjust your clock on an ongoing basis.
ntpd(8) at Boot Time
To have ntpd perform a one-time clock synchronization at boot and then continually adjust the clock afterwards, set the following in /etc/rc.conf :
While ntpd does not use a large amount of network bandwidth,
having every server on your network query the public NTP servers is a waste of network resources—both yours and the time server donors’. It can also lead to very slight (subsecond) variances in time on your own network. I recommend setting up a single authoritative time server for your network. Have this server synchronize its clock with the global NTP pool. Configure each server on your network to point to this server for its NTP updates.That way, every clock on your network will be perfectly synchronized. Any clock errors will occur either everywhere on your network (which tells you that the time server has an issue) or only on one server (which will indicate a problem on that particular machine). You will not have to trawl through NTP logs to try to determine if a particular server in the global time server pool has somehow messed up your system clock.
It is best to enforce this policy via firewall rules at your network border; allowing only your time server to communicate with outside NTP servers eliminates one common source of temporal chaos.
Scheduling Tasks(CRON)The FreeBSD job scheduler, cron(8), allows the administrator to have the system run any command on a regular basis. If you need to back up your database nightly or reload the nameserver four times a day, cron is your friend.
cron(8) configuration files are called crontabs and are managed with crontab(1).
Every user has a separate crontab stored in /var/cron/tabs, and the global crontab file is /etc/crontab.
User Crontabs vs. /etc/crontab
The purpose of /etc/crontab is different from that of individual users’ crontabs.
With /etc/crontab, root may specify which user will run a particular command.For example, in /etc/crontab the sysadmin can say, “Run this job at 10 PM Tuesdays as root, and run this other job at 7 AM as www.”
Other users can only run jobs as themselves. Of course, root can also edit a user’s crontab.
Also, any system user can view /etc/crontab. If you have a scheduled job that you don’t want users to know about, place it in a user crontab.For example, if you have an unprivileged user for your database, use that unprivileged user’s crontab to run database maintenance jobs.
/etc/crontab is considered a FreeBSD system file. Don’t overwrite it when you upgrade!
One way to simplify upgrading /etc/crontab is to set your custom entries at the end of the file, marked off with a few lines of hash marks (#).Finally, while you edit /etc/crontab with a text editor, edit a user crontab with crontab -e.
cron and Environment
crontabs run in a shell, and programs might require environment variables to run correctly. You can also specify environment variables on the command line for each command you run from cron.
cron does not inherit any environment variables from anywhere; any environment programs need must be specified. For example, here’s the environment from /etc/crontab on a FreeBSD 7 system:
SHELL=/bin/shYes, this is extremely minimal! Feel free to add environment variables asneeded to user crontabs, but be conservative when changing /etc/crontab.
If you need a custom environment variable, it’s safest to use a user crontab rather than /etc/crontab, because many of the commands in /etc/crontab are for core system maintenance.
Beneath the environment statements,
- a user crontab is divided into six columns. The first five columns represent the time the command should run, as minute, hour, day of the month, month of the year, and day of the week, in that order.
- An asterisk (*) in any column means every one, while a number means at this exact time.
- Minutes, hours, and days of the week begin with 0, and days of the month and months begin with 1. Also, thanks to an ancient disagreement between AT&T and BSD, Sunday can be represented by either 7 or 0.
- After the time, list the command to be run at that time.
- /etc/crontab has one extra column: the user under which to run the command. It goes between the time specification and the command itself.
Assume that we’re editing the crontab of an unprivileged user to schedule maintenance of a program. As /etc/crontab has column headings at the top, we’ll demonstrate user crontabs here. (To use these examples in /etc/crontab,
just add the user before the command.)
Here, we want to run the program /usr/local/bin/maintenance.sh at 55 minutes after each hour, every single hour:
55 * * * * /usr/local/bin/maintenance.shAsterisks tell cron to run this job every hour, on every day of the month,
every month, and on every weekday. The 55 tells cron to only run this job at
To run the same job at 1:55 PM every day, use the following:
55 13 * * * /usr/local/bin/maintenance.shHere, 13 represents 1:00 PM on the 24-hour clock, and 55 is the number of minutes past that hour.
One common mistake people make when using cron is specifying a large
unit of time but missing the small one. For example, suppose you want to run
the job every day at 8 AM:
* 8 * * * /usr/local/bin/maintenance.shThis is wrong. Yes, the job will run at 8:00 AM. It will also run at 8:01, 8:02, 8:03, and so on, until 9 AM. If your job takes more than one minute to run, you’ll quickly bring your system to its knees. The correct way to specify
8:00 AM, and only 8:00 AM, is this:
0 8 * * * /usr/local/bin/maintenance.shTo specify ranges of time, such as running the program once an hour, every hour, between 8 AM and 6 PM, Monday through Friday, use something
55 8-18 * * 1-5 /usr/local/bin/maintenance.shTo specify multiple exact times, separate them with commas:
55 8,10,12,14,16 * * * /usr/local/bin/maintenance.shMore interestingly, you can specify fractions of time, or steps. For example,
to run a program every five minutes, use:
*/5 * * * * /usr/local/bin/maintenance.shYou can combine ranges with steps. To run the program every five minutes, but one minute after the previous example, use this:
1-56/5 * * * * /usr/local/bin/maintenance.shControl the day a job runs with two fields: the day of the month and the day of the week. If you specify both, the job will run whenever either condition is met.
For example, tell cron to run a job on the 1st and the 15th of every month, plus every Monday, as follows:
55 13 * 1,15 1 /usr/local/bin/maintenance.shIf your job has a nonstandard environment, set the environment on the command line just as you would in the shell. For example, if your program requires a LD_LIBRARY_PATH environment variable, you can set it thus:
55 * * * * LD_LIBRARY_PATH=/usr/local/mylibs ; /usr/local/bin/maintenance.shcron also supports special scheduling, such as “annually” or “daily,” with the @ symbol. Most of these terms are best not used, as they can be ambiguous.
While the machine knows exactly what they mean, humans tend to misunderstand! One useful crontab entry is for “whenever the system boots,” which is @reboot. This lets an unprivileged user run jobs when the system boots. Use the @reboot label instead of the time fields:
@reboot /usr/local/bin/maintenance.shcron(8) lets you schedule your work any way you like, eliminating the human being from many routine maintenance tasks.