20
SMALL SYSTEM SERVICES

image

Even a server with a very narrowly defined role, such as a dedicated web server, needs a variety of small “helper” services to handle basic administrative issues. In this chapter, we’ll discuss some of those services, such as time synchronization, sending mail, DHCP services, scheduling tasks, and so on. We’ll start by securing your remote connections to your FreeBSD server with SSH.

Secure Shell

One 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 surrounded by vicious guard dogs mentored by a megalomaniacal weasel named Ivan, if you have network access to the machine, you can control it.

For many years, telnet(1) was the standard way to access a remote server. 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 can’t protect your username and password. Intruders place illicit packet sniffers anywhere they can; I’ve seen them on small local networks and global enterprise networks, in law firms handling sensitive government work, on home PCs, and on internet backbones. The only defense against a packet sniffer is to handle your authentication credentials and data in such a way that a packet sniffer can’t make sense of 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. 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 utterly outweigh them. SSH also has many features that telnet doesn’t have, such as the ability to tunnel arbitrary protocols through the encrypted session. 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 see only 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 or service sshd subcommands 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 unencrypted protocols we look at, sshd is difficult to test by hand. One thing you can do is confirm that sshd is running by using nc(1) to connect to the SSH TCP port.

# nc localhost 22
SSH-2.0-OpenSSH_7.2 FreeBSD-20160310

We connect to port 22, and get an SSH banner back. We can see that the daemon listening on this port calls itself SSH version 2, implemented in OpenSSH 7.2, on FreeBSD, version 20160310. You can get all this information from a simple nc(1) connection, 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-C to leave nc(1) 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. The initializing sshd process creates three pairs of keys: an RSA key, an ECDSA key, and an ED25519 key.

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 verify that the server he’s connecting to is really the server he thinks it is. (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 can’t 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_rsa_key.pub
2048 SHA256:tEcBfgXctTfaaEF9d5QK3oYUwr5Tb/cuIr3MNxV4wwE root@bert (RSA)

The first number, 2048, shows the number of bits in the key. 2048 is standard for an RSA key in 2018, but as computing power increases, I expect this number to increase. The string starting with tEcB and ending with wwE 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. If a human needs to verify the fingerprint, try a web page or a paper list. If your SSH clients support SSHFP records and your DNS zones support DNSSEC, you can use DNS instead. Use this key to confirm your server’s identity the first time you connect, or use one of the other key distribution methods.

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 book obsolete before it hits the shelves. 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 or service sshd restart.

VersionAddendum FreeBSD-20170902

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, if changing VersionAddendum to DrunkenBadgerSoftware amuses you, proceed.)

Port 22

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 (for example, port 443 in addition to port 22), you can include multiple Port entries on separate lines:

Port 22
Port 443

Changing the port isn’t useful as a security measure. It can be useful to reduce log chatter. I freely admit to having a small SSH server that listens on a variety of popular TCP ports specifically to bypass useless network security devices. But it doesn’t make SSH any more secure.

ListenAddress 0.0.0.0

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 203.0.113.8

If 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. See Chapter 21 for more information on logging.

LoginGraceTime 2m

This controls how long a user has to log in after getting connected. If an incoming user connects but doesn’t successfully log in within this time window, sshd drops the connection.

PermitRootLogin no

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.

AllowTcpForwarding yes

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.

X11Forwarding yes

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.

Banner /some/path

The banner is a message that’s displayed before authentication occurs. The most common use for this option is to display legal warnings. The default is not to 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 isn’t listed can’t SSH in.

For example, the AllowGroups option lets you restrict SSH access to users in specified groups defined in /etc/group (see Chapter 9). If this option is set and a user isn’t in any of the allowed groups, he can’t log in. Separate multiple groups with spaces:

AllowGroups wheel webmaster dnsadmin

If 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 can’t 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 can’t 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 Bert is a member of the wheel group. Here’s a snippet of sshd_config :

DenyUsers: bert
AllowGroups: wheel

Bert can’t SSH into this machine because DenyUsers is checked before AllowGroups.

SSH Clients

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 it’s 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 noncommercial purposes and has excellent terminal emulation. Microsoft is integrating a fork of OpenSSH into Windows, but it’s still in beta as I write this.

This is a FreeBSD book, so 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).

To connect to another host with SSH, type ssh hostname. In response, you’ll see something like this:

# ssh mwl.io
The authenticity of host 'mwl.io (203.0.113.221)' can't be established.
ECDSA key fingerprint is SHA256:ZxOWglg4oqcZKHOLv5tfqPlAwDW6UGVbiTvjfAjMc4E.
No matching host key fingerprint found in DNS.
Are you sure you want to continue connecting (yes/no)? yes

Your client immediately retrieves the public key from the host 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 doesn’t have the host key in its list of known hosts, 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 isn’t 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 err 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 mwl.io, giving the remote copy a different name. I’d run:

# scp bookbackup.tgz [email protected]: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 [email protected]:

scp(1) also lets you copy files from a remote system to your local system:

# scp [email protected]: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 [email protected]: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 mwl.io:

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 [email protected]
Connecting to bewilderbeast...
Password:
sftp> ls

The sftp(1) client 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) doesn’t require a choice between ASCII and binary transfers; it just transfers the file as is.

With SSH, scp, and sftp, you can completely eliminate cleartext passwords from your network.

While SSH is the most common sysadmin tool, we’ve just brushed its surface. Time you spend mastering SSH will pay itself back several fold. You can find several good tutorials online and a few decent books, including my own SSH Mastery (Tilted Windmill Press, 2018).

Email

Running an email server has become vastly more complicated in the last few years. Coping with the spam, viruses, and random crud that arrives on a mail server requires a specialized skill set, and the amount of that crud balloons every year. Think carefully before you deploy a mail server. Every host needs some sort of mail client, however. FreeBSD includes two software suites that can be used for managing local mail and forwarding mail to the mail server: Sendmail and the Dragonfly Mail Agent.

Sendmail is the great-granddaddy of mail programs. It can be a server, a client, a filter, and an arbitrary mail spindler. If you want to exchange mail with sites so isolated that they communicate once a day over UUCP over a dialup line, and also exchange mail with the latest commercial mail servers, Sendmail is a solid choice. For most of us, though, the Swiss Army Car Crusher of Email is overkill.

The Dragonfly Mail Agent (DMA) comes from Dragonfly BSD. It’s a very minimal mail client that can deliver mail on the local host or forward it to a mail server. It’s exactly what your average host needs to forward daily status mails to the minion tasked with reading them, to send reports from your application to the application administrator, and to forward all those annoying reports your WordPress security plugin wants you to read.

We’ll spend some time with DMA. Before we can go there, though, let’s talk about how FreeBSD copes with the world’s multiplicity of mail servers.

mailwrapper(8)

For decades, Sendmail was the only mail server available for Unix-like systems. As such, huge amounts of software expects every server to have /usr/sbin/sendmail and expects it to behave exactly like Sendmail. What makes matters worse, Sendmail behaves differently when called by different names. The program mailq(1) is a hard link to sendmail(8), but as it has a different name, it behaves differently. So do newaliases(1), send-mail(8), hoststat(8), and purgestat(8).1

As clients expect to find Sendmail, any replacement mail server must precisely emulate Sendmail, down to this multiname behavior. Using a different mail server isn’t as easy as erasing the Sendmail binaries and replacing them with something else. But people try.

As a result, sysadmins exploring unfamiliar Unix systems might have no idea what /usr/sbin/sendmail really is! If someone previously installed several different mail servers in an effort to find something less ghastly than Sendmail, you’ll have to resort to detective work and dogged persistence to identify your so-called sendmail(8).

FreeBSD does an end-run around all this confusion by using a separate mailwrapper(8) program. The mail wrapper directs requests for Sendmail to the preferred mail server, installed elsewhere.

Configure mailwrapper(8) in /etc/mail/mailer.conf. This file contains a list of program names, along with the paths to the actual programs to be called. Here’s the default mailer.conf directing everything to good old sendmail(8):

sendmail        /usr/libexec/sendmail/sendmail
send-mail       /usr/libexec/sendmail/sendmail
mailq           /usr/libexec/sendmail/sendmail
newaliases      /usr/libexec/sendmail/sendmail
hoststat        /usr/libexec/sendmail/sendmail
purgestat       /usr/libexec/sendmail/sendmail

Each of these six “programs” in the left column is a name that other programs might use for Sendmail. The right column gives the path to the program that should be called instead. Here, we see that Sendmail is installed as /usr/libexec/sendmail/sendmail. If you use an alternative mailer, you must edit mailer.conf to point to the proper path to the mailer programs. Most alternative mailers use separate programs for each of these functions because the cost of disk space has plunged since Sendmail’s birth. When you install an alternative mailer from a package or port, the post-install message usually provides instructions on exactly how to update mailer.conf for your installation. Follow those instructions if you want the new mail server to work. If you install a different mail server without using a package, you need to edit mailer.conf yourself.

The Dragonfly Mail Agent

The Dragonfly Mail Agent (DMA) can deliver mail locally and send mail to another server. It can’t receive mail over the network. Where most mail servers bind to TCP port 25 on the local host, dma(8) does not. It delivers mail only for programs that can call /usr/sbin/sendmail or one of its counterparts.

Before activating DMA, configure it in /etc/dma/dma.conf. This file contains variables you can uncomment and set to a specific value. While DMA has several configurable settings, you should leave most of them at the default.

Smart Host

A smart host is the actual mail server, the host this client should relay mail through. Use the hostname or IP address.

SMARTHOST=mail.mwl.io

TCP Port

If your mail administrator is a madman that runs the smart host’s email on a nonstandard port, or if you’re trying to evade your ISP’s block port 25 outbound, set the TCP port here:

PORT 2025

If you don’t set a smart host but do set a port, you’ll break mail delivery.

False Hostname and Usernames

You might want your server to claim to be a different host when it sends mail. Maybe your cloud provider has given this system a hostname composed of random digits and numbers, but you want it to send mail as www.example.com. Use the MAILNAME to set a fake hostname.

MAILNAME www.mwl.io

If you give MAILNAME the full path to a file, dma(8) will use the first line of that file as the hostname.

Some mail servers very strictly inspect relayed mail and reject inadequately forged messages. For those hosts, you’ll need to use the MASQUERADE option. Masquerading gives you a couple different options for changing messages. If you use an entire email address, all mail sent via dma(8) is rewritten so it comes from that address. If you use a username with an @ sign, such as bert@, all email appears to be coming from that user at the host. A hostname on its own leaves the sending username untouched but changes the hostname.

MASQUERADE [email protected]

Any messages sent from this host appear to be from Bert. Any replies will go to him. All is as it should be.

Disable Local Delivery

Some hosts should never receive mail. No account on the host should ever get mail, not even from other local accounts. Totally disable local mail delivery by uncommenting the NULLCLIENT option.

Secure Transport

Over the decades, the email protocol has had a whole bunch of different security measures wedged into it. Your mail server might use any or all of them. Speak to your email administrator about what your smart host requires and supports.

Enable TLS (or SSL, if your mail server is notably awful) by uncommenting the SECURETRANSFER option. You don’t need to set this to a value; its mere presence turns on TLS. If your mail server needs STARTTLS, also uncomment that option. If you want to send mail even if TLS negotiation fails, also uncomment OPPORTUNISTIC_TLS.

These three options all require the previous options. You can use SECURETRANSFER on its own, STARTTLS and SECURETRANSFER together, or all three. STARTTLS and SECURETRANSFER without their preceding options don’t work.

If you need a local TLS certificate, set it with the CERTFILE option.

CERTFILE /etc/ssl/host.crt

These options should let you connect to just about any smart host.

Username and Password

Some smart hosts require clients authenticate with a username and password. Put authentication credentials in the file /etc/dma/auth.conf. Each entry needs the format:

user|host:password

Suppose my smart host is mail.mwl.io. The username is www1, and the password is BatteryHorseStapleCorrect. My auth.conf would contain:

www1|mail.mwl.io:BatteryHorseStapleCorrect

DMA will use this to log into your host.

If you want to use a username and password over an unencrypted connection, you must set the INSECURE variable. Sending unencrypted authentication information over the network is a bad idea, but many mail servers are full of bad ideas.

Enabling DMA

Using DMA requires shutting down any existing Sendmail processes and enabling dma(8) in mailer.conf.

Sendmail runs as a daemon even when it only handles local delivery. Shut down Sendmail with service(8) or the /etc/rc.d/sendmail script.

# service sendmail stop

Make sure it never starts again.

# sysrc sendmail_enable=NONE

Now, go to /etc/mail/mailer.conf and point every mail program to dma(8).

sendmail        /usr/libexec/dma
send-mail       /usr/libexec/dma
mailq           /usr/libexec/dma
newaliases      /usr/libexec/dma
rmail           /usr/libexec/dma

DMA has no persistent daemon, so it doesn’t need a startup script.

Congratulations, you now have a small, simple, effective client mail agent.

The Aliases File and DMA

The /etc/mail/aliases file contains redirections for email sent to specific accounts or usernames. Even mail clients and mail agents like DMA use the aliases file. Adding an entry to the aliases file is a good way to locally redirect email.

While the aliases file has a whole bunch of features, DMA can exercise only a few of them. Features like redirecting email to an arbitrary file don’t work. We’ll discuss the basic functions.

Open up the aliases file and look around. Each line starts with an alias name or address, followed by a colon and a list of real users to send the email to. We’ll illustrate how aliases work by example.

Forwarding Email from One User to Another

Someone should always read email sent to the root account. Rather than having that someone log onto every server to read the messages, forward all of root’s email to another email address.

root: [email protected]

I’ve assigned Bert the job of reading all the mail from all the machines.2

Many email addresses don’t have accounts associated with them. For example, the required postmaster address often doesn’t have an account. You can use an alias to forward this to a real account.

postmaster: root

So, postmaster forwards to root, which forwards to Bert. Bert gets all the email for these two addresses.

The default aliases file contains a variety of standard addresses for internet services, as well as aliases for all of the default FreeBSD service accounts. They all go to root by default. By defining a real address as a destination for your root email, you’ll automatically get all system administration email.

Aliased Mailing Lists

You can list multiple users to create small mailing lists. This doesn’t scale for dynamic lists, but it’s sufficient for quick and dirty lists.

escalate: [email protected], [email protected], [email protected]

The moment you find yourself creating an aliased mailing list is the moment you need to start considering which mailing list solution you’re going to deploy. You’ll need it sooner than you think.

Network Time

If a database starts entering dates three hours behind, or if emails arrive dated tomorrow, you’ll hear about it pretty quickly. Time is important.3 You have two tools to manage system time: tzsetup(8) to control the time zone and ntpd(8) to adjust the clock. Start by setting your time zone manually, and then use network time protocol.

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 the default of UTC (Universal Time Clock, previously known as Greenwich Mean Time, currently known as Coordinated Universal Time, soon to be known by Yet Another Name) on their systems, while others use their own local time. Enter tzsetup, follow the geographic prompts, and choose the appropriate time zone for your location. If you know your time zone’s official name, you can set it at the command prompt without going through the prompts.

# tzsetup America/Detroit

The tzsetup(8) program copies the relevant time zone file from /usr/share/zoneinfo to /etc/localtime. This is a binary file, and you can’t edit with your average text editor. If the characteristics of your time zone change—for example, the day Daylight Saving Time begins changes—you must upgrade FreeBSD to get the new time zone files and then rerun tzsetup(8) to correctly reconfigure time.

Users can use the TZ environment variable to set their personal time zone.

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 timekeeping device. If you really need this sort of accuracy, then what you really need is your own atomic clock. A USB radio clock such as that found on an inexpensive GPS might look very nice, but USB turns out to be a lousy medium for transferring timing data. Go price a dedicated non-USB GPS receiver, and then choose a Tier 1 NTP server instead.

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 http://www.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 following examples, but look up the proper servers for your country and use those instead when setting up your own time service.

Configuring ntpd(8)

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/ntpd.conf. Here’s a sample that uses Canadian time servers:

server 1.ca.pool.ntp.org
server 2.ca.pool.ntp.org
server 3.ca.pool.ntp.org

This 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 won’t 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 whether one server is wrong when it has only 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 doesn’t make sense against the time offered by the other two servers. (Think of this as a “tyranny of the majority”; the one guy whose opinion differs from the rest doesn’t get any voice at all.)

ntpd(8) at Boot Time

To have ntpd perform a one-time clock synchronization at boot and then continually adjust the clock afterward, set the following in /etc/rc.conf:

ntpd_enable="YES"
ntpd_sync_on_start="YES"

Ntpd will force correct time immediately on boot and then gently keep the clock synchronized.

Instant Time Correction

ntpd(8) is great at keeping the system clock accurate over time, but it adjusts the local clock only gradually. If your time is off by hours or days (which isn’t 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.976809s

This 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 forward or backward.

If you have really good hardware with an excellent oscillator, 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.

Redistributing Time

While ntpd doesn’t 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 that of the time-server donors. It can also lead to very slight (subsecond) variances in time on your own network.

Reliable time servers aren’t virtual machines. Tier 1 NTP servers are all run on real hardware specifically to avoid the clock jittering virtual machines can suffer.

I recommend setting up three authoritative time servers for your network. Have these servers synchronize their clock with the global NTP pool. Configure each server on your network to point to these servers for NTP updates. That way, every clock on your network will be perfectly synchronized. You won’t have to trawl through NTP logs to try to determine whether a particular server in the global time server pool has somehow messed up your system clock. It’s 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.

Name Service Switching

Any Unix-like system performs innumerable checks of many different name services. We’ve already talked about the Domain Name System that maps hostnames to IP addresses (see Chapter 7), but there’s also a password entry lookup service, a TCP/IP port number and name lookup service, an IP protocol name and number lookup service, and so on. You can use /etc/nsswitch.conf to configure how your FreeBSD system makes these queries and what information sources it uses through nsswitch (name service switching).

Each name service has an nsswitch.conf entry including the type of the service and the information sources it uses. We previously saw an example of name service switching in Chapter 8. Remember this entry for host lookups?

hosts: files dns

This means, “Look for IP addresses in the local files first, and then query DNS.” The other information sources work similarly. FreeBSD, like most other Unix-like operating systems, supports name service switching for the information sources listed in Table 20-1.

Table 20-1: Lookups Supporting Name Service Switching

Lookup

Function

groups

Group membership checks (/etc/group)

hosts

Hostname and IP checks (DNS and /etc/hosts)

networks

Network entries (/etc/networks)

passwd

Password entries (/etc/passwd)

shells

Checks for valid shells (/etc/shells)

services

TCP and UDP services (/etc/services)

rpc

Remote procedure calls (/etc/rpc)

proto

TCP/IP network protocols (/etc/protocols)

Most of these you don’t want to muck with, unless you like breaking system functionality. If you have a Kerberos or an NIS domain, for example, you might want to have your FreeBSD box attach to them for user and group information—but if you don’t, reconfiguring the password lookups would make your system slow at best or entirely stop working at worst!

For each name service, you must specify one or more sources of information. Many of these name services are very simple and default to having a single authoritative source of information—a file. Others, such as the host’s name service, are more complicated and have multiple sources. A few are very complicated simply because of the vast array of information available and the many possible ways to get that information. As this book doesn’t cover Kerberos, NIS, or any other enterprise-level user management systems, we won’t cover changing password, group, and shell information sources. If you’re in such an environment, read nsswitch.conf(5) for details.

Most common services have specific valid information sources. Files are the standard text files containing information for the service. For example, network protocols are traditionally stored in /etc/protocols, network services in /etc/services, and passwords in /etc/passwd and friends. A source of dns means that the information is available on a DNS server, as is typical for the hosts service responsible for mapping hostnames to IP addresses. The password service often uses compat, which grants compatibility with /etc/passwd and NIS but could also use files. You might add information sources to the system—for example, enabling LDAP authentication adds the ldap information source.

List each desired information source in the order you want them to be tried. Our hosts entry tells the name service lookup to try the local file first and then query the DNS server.

hosts: files dns

If you deploy a central authentication scheme like LDAP, you’ll need to add an appropriate entry to tell the host to look up passwords and groups in LDAP. The important question is, should hosts use their local password file and then fall back to LDAP or start with LDAP and fall back to the password file?

passwd: ldap files

Here, we start with LDAP but fall back to the password file if LDAP isn’t available.

inetd

The inetd(8) daemon handles incoming network connections for less frequently used network services. Most systems don’t have a steady stream of incoming FTP requests, so why have the FTP daemon running all the time? Instead, inetd listens to the network for incoming FTP requests. When an FTP request arrives, inetd(8) starts the FTP server and hands off the request. Other common programs that rely on inetd are telnet, tftp, and POP3.

Inetd also handles functions so small and rarely used that they’re easier to implement within inetd, rather than route them through a separate program. This includes discard (which dumps any data received into the black hole of /dev/null), chargen (which pours out a stream of characters), and other functions. These days, most of these services are not only not required but often considered harmful. The chargen service, for example, is mostly useful for denial-of-service attacks.

/etc/inetd.conf

Take a look at /etc/inetd.conf. Most daemons have separate IPv4 and IPv6 configurations, but if you’re not running IPv6, you can ignore the IPv6 entries. Let’s look at one entry, the FTP server configuration.

ftp    stream     tcp     nowait     root      /usr/libexec/ftpd     ftpd -l

The first field is the service name , which must match a name in /etc/services. inetd performs a service name lookup to identify which TCP port it should listen to. If you want to change the TCP/IP port your FTP server runs on, change the port for FTP in /etc/services. (You could also change the first field to match the service that runs on the desired port, but I find that this makes the entry slightly confusing.)

The socket type dictates what sort of connection this is. All TCP connections are of type stream, while UDP connections are of type dgram. While you might find other possible values, if you’re considering using them, either you’re reading the documentation for a piece of software that tells you what to use, or you’re just wrong.

The protocol is the layer 4 network protocol, either tcp (IPv4 TCP), udp (IPv4 UDP), tcp6 (IPv6 TCP), or udp6 (IPv6 UDP). If your server accepts both IPv4 and IPv6 connections, use the entries tcp46 or udp46.

The next field indicates whether inetd should wait for the server program to close the connection or just start the program and go away . As a general rule, TCP daemons use nowait while UDP daemons need wait. (There are exceptions to this, but they’re rare.) inetd(8) starts a new instance of the network daemon for each incoming request. If a service uses nowait, you can control the maximum number of connections inetd accepts per second by adding a slash and a number directly after nowait, like this: nowait/5. One way intruders (usually script kiddies) try to knock servers off the internet is by opening more requests for a service than the server can handle. By rate-limiting incoming connections, you can stop this. On the other hand, this means that your intruder can stop other people from using the service at all. Choose your poison carefully!

We then have the user that the server daemon runs as. The FTP server ftpd(8) runs as root, as it must service requests for many system users, but other servers run as dedicated users.

The sixth field is the full path to the server program inetd runs when a connection request arrives . Services integrated with inetd(8) appear as internal.

The last field gives the command to start the external program, including any desired command line arguments .

Configuring inetd Servers

While /etc/inetd.conf seems to use a lot of information, adding a program is actually pretty simple. The easiest way to learn about inetd(8) is to implement a simple service with it. For example, let’s implement a Quote of the Day (qotd) service. When you connect to the qotd port, the server sends back a random quote and disconnects. FreeBSD includes a random quote generator, fortune(1), in its games collection. This random quote generator is all we need to implement an inetd-based network program. We must specify a port number, a network protocol, a user, a path, and a command line.

port number

The /etc/services file lists qotd on port 17.

network protocol

The qotd service requires that you connect to a network port and get something back, so it needs to run over TCP. Remember, UDP is connectionless—a reply isn’t required. We must specify tcp in our inetd configuration, which means that we must specify nowait in the fourth field.

user

Best practice says to create an unprivileged user to run the qotd service, as discussed in Chapter 19. For this example, we’ll just use the general unprivileged user nobody, but if you were implementing this in production, you’d want to create an unprivileged user qotd.

path

Find fortune at /usr/bin/fortune.

Running the Command

fortune(6) doesn’t require any command line arguments, but you can add them if you like.4 On FreeBSD 11, believers in Murphy’s Law can use fortune murphy, while Star Trek fans can get quotes with fortune startrek. (The latter correctly includes only the One True Star Trek, not any of the wannabe followups.) Those interested in education could use fortune freebsd-tips. FreeBSD 12 removes many of the fortune databases, sadly.

Sample inetd.conf Configuration

Putting this all together, the entry for qotd in /etc/inetd.conf looks like this:

qotd    stream    tcp    nowait    nobody    /usr/bin/fortune    fortune

You might think this example trivial, but providing other services out of inetd(8) is no more difficult.

Starting inetd(8)

First, enable inetd(8) at boot by adding the following entry to /etc/rc.conf:

inetd_enable=YES

With this set, start inetd by hand with /etc/rc.d/inetd start. Now that inetd is running, telnet to port 17 to test our new service:

   # telnet localhost 17
Trying 127.0.0.1...
   Connected to localhost.
   Escape character is '^]'.
It is difficult to produce a television documentary that is both
   incisive and probing when every twelve minutes one is interrupted by
   twelve dancing rabbits singing about toilet paper.
                   -- Rod Serling
   Connection closed by foreign host.

It works! We have the usual TCP/IP connection information and our random fortune . (As an added bonus, you also know why I don’t write for television.)

Changing inetd’s Behavior

inetd behaves differently depending on the flags you set for it. The default flags turn on TCP wrappers, as configured in /etc/hosts.allow (see Chapter 19). Table 20-2 lists some of the useful flags.

Table 20-2: inetd(8) Flags

Flag

Description

-l

Log every successful connection.

-c

Set the maximum number of connections per second that can be made to any service. By default, there’s no limit. Note that “unlimited” isn’t the same as “infinite”—your hardware only handles so many connections.

-C

Set the number of times one IP address can connect to a single service in one minute. This connection rate is unlimited by default, but using this can be useful against people trying to monopolize your bandwidth or resources.

-R

Set the maximum number of times any one service can be started in one minute. The default is 256. If you use -R 0, you allow an unlimited number of connections to any one service.

-a

Set the IP address inetd(8) attaches to. By default, inetd listens on all IP addresses attached to the system.

-w

Use TCP wrappers for programs started by inetd(8), as per hosts.allow (see Chapter 19).

-W

Use TCP wrappers for services integrated with inetd(8), as per hosts.allow (see Chapter 19).

As an extreme example, if you want to use TCP wrappers, allow only two connections per second from any single host, allow an unlimited number of service invocations per minute, and listen only on the IP address 203.0.113.2, then you’d set the following in /etc/rc.conf:

inetd_flags="-Ww -c 2 -R 0 -a 203.0.113.2"

With inetd(8), almost anything can be a network service.

DHCP

Dynamic Host Configuration Protocol (DHCP) is the standard method for handing out IP addresses to client computers. While DHCP services aren’t integrated with FreeBSD out of the box, they’re commonly required to implement such services as diskless workstations. We’ll cover the basics of DHCP configuration here so you can set up your own network.

These days, every firewall and embedded device has a DHCP server. Why would you need a separate DHCP server? Most of the embedded DHCP servers lack functions needed to run diskless clients, such as network-booted servers and VoIP phones. When they do support such functions, those DHCP servers are often difficult to manage. Services are meant to run on actual servers. We’ll cover enough of DHCP to let you configure your own network clients, including diskless hosts.

FreeBSD packages include several DHCP servers. The two I like are OpenBSD’s dhcpd and ISC DHCP server. The ISC DHCP server is an industry standard and supports every feature you could possibly want. For small deployments, I recommend OpenBSD’s dhcpd. The OpenBSD folks took ISC DHCP, ripped out all the rarely used features, and made a smaller, simpler server. The configuration file is still one-way compatible; you can run an OpenBSD dhcpd configuration on ISC’s DHCP server without trouble. (The reverse is also true if you’re not using any of the features OpenBSD ripped out of the server.) If you want to run diskless FreeBSD clients, or if you need LDAP integration, switching to the more complex ISC server is fairly straightforward. You can install only one of the two servers.

The package for either server includes dhcpd(8), the configuration file /usr/local/etc/dhcpd.conf, and extensive man pages.

How DHCP Works

DHCP can be terribly complicated in a large network where we are relaying DHCP requests between offices, but it’s rather simple on a local Ethernet. Each DHCP client sends a broadcast across the local Ethernet asking for someone—anyone—to provide network configuration information. If your DHCP server is on that local Ethernet, it answers directly. If your DHCP server is on another network segment, the router for that network segment needs to know which IP address to forward the DHCP request to. The DHCP server then loans configuration information to the client and tracks which clients have been assigned which IP addresses. A configuration issued to a client is called a lease. Like the lease you pay on a home or auto, DHCP leases expire and must be renewed occasionally.

The client can request certain features—for example, Microsoft clients ask for the IP address of the WINS server, while diskless systems ask where to find a kernel. You can set all these options as necessary.

Each client is uniquely identified by the MAC address of the network card used to connect to the network. ISC dhcpd tracks MAC and IP addresses, as well as leases, in the file /var/db/dhcpd.leases. In this file, you can identify which hosts have which IP addresses. If a host disappears from the network for a time and returns, dhcpd(8) reissues the same IP to that client if that IP is still available.

Configuring dhcpd(8)

The file /usr/local/etc/dhcpd.conf contains all the configuration for dhcpd. While ISC dhcpd(8) can and does fill entire books on its own, we’ll focus on the functions needed for a basic small office as well as those used in the examples later in this book. The default dhcpd.conf is well commented and includes still more examples, while dhcpd.conf(5) is painfully exhaustive. We’re going to assume that you’re running a single DHCP server on your network, and that your server should answer all requests for DHCP services. (It’s entirely possible to cluster dhcpd for fault tolerance, but that’s beyond our scope here.)

Global Settings

Start your dhcpd.conf with a few general rules for client configuration. These rules apply to all DHCP clients unless specifically overridden.

option domain-name "mwl.io";
option domain-name-servers 198.51.100.2, 198.51.100.3;
option subnet-mask 255.255.255.0;
default-lease-time 600;
max-lease-time 7200;

Each DHCP client registers its hostname with the DHCP server, but the client must learn the local domain name from the server. (It’s also possible for the DHCP server to set the client’s hostname.) Set this with the domain-name option . You can give your DHCP clients any domain name you like; they don’t need to share the server’s domain name. You can include multiple domains if you separate them with spaces, but not all operating systems will recognize additional domain names.

Every TCP/IP client needs a DNS server or two. Specify them with the option domain-name-servers . Separate multiple DNS servers with commas.

It’s a good idea to set a default subnet mask . Individual networks can override this, but a global default is useful.

The normal duration of a lease is given (in seconds) by the default-lease-time option . After the lease time runs out, the client requests a new DHCP lease from the DHCP server. DHCP servers commonly default to a small number of minutes, but if your network is fairly stable you can extend this to hours or a couple days. If the client can’t reach the DHCP server, it continues to use the old lease for a number of minutes equal to the maximum life of the lease, specified with max-lease-time . You can think of the maximum lease time as “if my DHCP server fails, this is how long I have to replace it before the phone starts ringing.” Give yourself time to fix the issue.5

Now define subnets.

Subnet Settings

Each subnet on your network needs a subnet statement to identify configuration information for DHCP clients on that subnet. For example, here’s a network statement for a single small office network:

subnet 198.51.100.0 netmask 255.255.255.0 {
   range 198.51.100.50 198.51.100.99;
   option routers 198.51.100.1;
  }

Each subnet declaration starts by identifying the network number and netmask of the subnet. Here, we have a subnet using the IP network number 198.51.100.0 with the netmask 255.255.255.0, or the IP addresses 198.51.100.1 through 198.51.100.255. The information that follows in braces all pertains to hosts on that particular subnet.

The range keyword identifies the IP addresses that dhcpd(8) may issue to clients. In this example, we have 50 IP addresses available for clients. If 51 DHCP clients connect before any leases expire, the last host won’t get an address.

Define a default route with the routers option . Note that you can’t define additional routes with dhcpd(8); instead, your local network router needs to have the proper routes to reach the destination. If you have multiple gateways on your local network, your gateway transmits an ICMP redirect to the DHCP client to give it an updated route. (If you have no idea what this means, that’s all right. When you need it, you’ll abruptly comprehend what I’m talking about, and if you never need it, you’ve just wasted the two seconds it took to read this aside.)

If you have multiple subnets, create multiple subnet statements. Some of those subnets might need settings different than the global defaults, such as a netmask or DNS servers. If so, use those same keywords to define those values for that subnet.

Dhcpd lets you set anything from the subnet mask, boot servers, and even WINS servers for antediluvian Windows clients. We’ll use some of these less common settings to manage diskless clients in Chapter 23. See dhcpd.conf(5) for an exhaustive list.

Managing dhcpd(8)

Dhcpd defaults to listening to all network interfaces to catch DHCP request broadcasts. I’ve run many DHCP servers with multiple network cards, however, and usually want dhcpd to listen only to a single interface. Give the desired interface as a command line argument.

sysrc dhcpd_flags="em1"

Now enable dhcpd(8) itself.

sysrc dhcpd_enable=YES

You can now fire up dhcpd with service dhcpd start.

Congratulations, you’re ready to go!

Printing and Print Servers

Printing on Unix-like operating systems is a topic that makes new sysadmins cry and seasoned sysadmins ramble on about the good old days when printers were TTY devices and about the younger generation not knowing how good they have it.6 The most common printing situations are printers directly attached to a computer via a USB port and printers attached to a network print server.

If you have a printer attached directly to your FreeBSD machine, such as by a USB cable, I suggest using the Common Unix Printing System (CUPS). This suite of software manages many popular consumer-grade and commercial printers, from lowly inkjets to web-scale laser printers. I’m not going into any detail about CUPS, as it’s complicated and varies by printer model. Learn more about CUPS at http://www.cups.org/. Many brands of printers have special setup programs in CUPS, such as HP’s hp-setup. If your printer supports a network connection, though, try to avoid CUPS and use network printing instead.

Accessing a remote print server or network printer via the Line Printer Spooler Daemon (LPD) is simple in comparison. LPD takes in PostScript and produces printouts. Most office print servers run LPD. The lpd(8) daemon manages LPD. Most modern networked printers also support LPD and can act as their own print server.

Test for LPD support by connecting to TCP port 515; if you get a connection, the device speaks LPD.

# nc -v color-printer 515
Connection to color-printer 515 port [tcp/printer] succeeded!

This device supports LPD. We can send print jobs to it by configuring /etc/printcap.

/etc/printcap

Every printer your system knows about needs an entry in /etc/printcap, the printer capability database. This file is, by modern standards, in a rather obtuse format and will look very unfamiliar to anyone who hasn’t previously worked with termcap(5). Fortunately, to access a print server you don’t need to understand printcap(5); you just need to use the following template.

To connect to a printer on a print server, you must have the print server’s hostname or IP address and its name for the printer you want to access. Make an entry in /etc/printcap following this template. Pay special attention to the colons and backslashes—they’re absolutely vital.

lp|printername:
        :sh=:
        :rm=printservername:
        :sd=/var/spool/output/lpd/printername:
        :lf=/var/log/lpd-errs:
        :rp=printername:

Our first line shows the printer’s name . If you print from LibreOffice or a graphical web browser, these names will show up as printer options. Each printer can have any number of names, separated by the pipe symbol (|). The default printer on any Unix-like system is called lp, so list that as one of the names for your preferred printer. One other name should be the name used by the print server for your printer (for example, 3rdFloorPrinter). Be warned, Microsoft print servers frequently share one printer under several different names and use different names to handle printing differently. If you find this to be the case on your network, be sure to choose the PostScript name.7

By default, lpd(8) precedes each print job with a page listing the job name, number, host, and other information. Unless you’re in an environment with a single massive shared printer, this is probably a waste of paper. The :sh: entry suppresses this page.

The rm (remote machine) variable provides the hostname of the print server. You must be able to ping this server by the name you give here. If the print server is part of the printer, give the printer’s hostname here.

Each printer requires a unique spool directory , where the local print daemon can store documents in transit to the print server. This directory must be owned by user root and group daemon.

Unlike spool directories, which must be different, printers can share a common log file .

Finally, specify the remote printer name , as the print server identifies it. If you’re connecting directly to a printer, not to a central print server, you can skip this entry—but you must get rid of the trailing slash on the previous line.

Be sure you end /etc/printcap with a new line; don’t just terminate the file immediately after the printer name. Also, note that unlike every other entry in this template, the last line doesn’t require a trailing backslash.

Printers have dozens and dozens of options, from the cost per page to manually setting a string to feed a new sheet of paper. Most of these are obsolete today. If you have an older printer or special needs, though, consult printcap(5) for enough glorious detail to choke on.

Enabling LPD

Set lpd_enable to YES in /etc/rc.conf to have lpd(8) start at boot. Any time you edit /etc/printcap you must restart lpd(8). View the print queue with lpq(1) and watch for any problems in /var/log/lpd-errs.

TFTP

Let’s end our discussion of small network services with perhaps the smallest network service still used, the Trivial File Transfer Protocol (TFTP). TFTP lets you transfer files from machine to machine without any authentication whatsoever. It’s also much less flexible than file copy protocols, such as SCP or FTP. TFTP is still used by makers of embedded devices, such as Cisco, to load system configurations and operating system updates. We cover it here only because diskless clients use TFTP to download their operating system kernel and get their initial configuration information. Run tftpd(8) out of inetd(8) on TCP port 69.

Setting up a tftpd(8) server involves four steps: choosing a root directory for your server, creating files for the server, choosing an owner for your files, and running the server process.

Root Directory

The tftpd(8) daemon defaults to using the directory /tftpboot. This might be suitable if you have only a couple files that you rarely access, but the root partition is best reserved for files that don’t change often. You don’t want a TFTP upload to crash your system by filling the root partition! If you’re running ZFS, create a tftp dataset. On UFS, I usually put my tftpd(8) root directory in /var/tftpboot and add a symlink to /tftpboot:

# mkdir /var/tftpboot
# ln -s /var/tftpboot /tftpboot

Now you can create files for access via TFTP.

tftpd and Files

Users can both read and write files via TFTP. If you want tftpd(8) users to be able to read a file, the file must be world-readable:

# chmod +r /var/tftproot/filename

Similarly, tftpd(8) won’t allow anyone to upload a file unless a file of that name already exists and is world-writable. Remember, programs and regular files have different permissions. A program must have execute permissions in addition to read and write permissions, so you must set permissions differently for programs and files. You can use touch(1) to precreate files that you’ll want to upload via TFTP.

# chmod 666 /var/tftproot/filename
# chmod 777 /var/tftproot/programname

Yes, this means that anyone who knows a file’s name can overwrite the contents of that file. Make vital files read-only.8 This also means you don’t have to worry about someone uploading a big file and filling your hard drive.

File Ownership

Files in a TFTP server should be owned by a user with the least possible privilege. If you run a TFTP server only intermittently, you can use the nobody user. For example, if you need the TFTP server only to perform the occasional embedded device upgrade, let the nobody user own your files and just turn tftpd(8) off when it’s not needed. If you run a permanent TFTP server, however, it’s best to have a dedicated tftp unprivileged user to own the files. The tftp user doesn’t need to own the tftproot directory and, in fact, should have an entirely different home directory. He needs ownership only of the files available to users.

tftpd(8) Configuration

tftpd(8) is configured entirely through command line arguments, and there aren’t many of them. For a full list, read tftpd(8), but here are the most commonly used ones.

If you create a user just to run tftpd(8), specify that user with the -u argument. If you don’t specify a user, tftpd(8) runs as nobody. Create an unprivileged user.

I recommend logging all requests to your TFTP daemon. The -l argument turns on logging. tftpd(8) uses the FTP facility, which you must enable in syslog.conf (see Chapter 21).

Tftpd supports chrooting with the -s flag. This lets you confine tftpd(8) to your selected directory. You don’t want users to TFTP world-readable files such as /etc/passwd, or even /boot/kernel/kernel, just on general principle! Always chroot your tftpd(8) installation.

You can chroot TFTP clients by IP address with the -c argument. In this case, you must create a directory for every client permitted to connect. For example, suppose the only host you want to give TFTP access to is your router, with the IP address of 192.168.1.1. You could create a directory /var/tftproot/192.168.1.1 and use -c. You must also use -s to define the base directory of /var/tftproot. This is a good compromise when you must offer TFTP to only one or two hosts, but you don’t want the world to have access to your TFTP server.

You can choose to allow a client to write new files to your TFTP server. This is a bad idea because it lets remote users fill up your hard disks with arbitrary files. If you must have this functionality, use the -w flag.

For example, suppose you want to log all requests to tftpd, chroot to /var/tftpboot, run the server as the user tftpd, and chroot clients by IP address. The command to run tftpd would look like this:

tftpd -l -u tftpd -c -s /var/tftpboot

Enter this into inetd.conf as described earlier this chapter, restart inetd(8), and you’re in business!

Scheduling Tasks

The FreeBSD job scheduler, cron(8), allows the administrator to have the system run any command on a regular basis. Combined with the system maintenance scheduling system, periodic(8), you can schedule almost anything.

cron(8)

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. Global cron entries can also be placed in /etc/cron.d and will be run as if they were part of /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 run jobs only 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 (#). The /etc/crontab file must end with a new line, or the last line won’t get parsed and run. That’s fine if your last entry is a comment, but not so good if it’s a command.

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 doesn’t inherit any environment variables from anywhere; any environment variables a program needs must be specified in the crontab. For example, here’s the environment from /etc/crontab on a FreeBSD 12 system:

SHELL=/bin/sh
PATH=/etc:/bin:/sbin:/usr/bin:/usr/sbin

Yes, this is extremely minimal! Feel free to add environment variables as needed 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.

Crontab Format

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.

The /etc/crontab file, and files under /etc/cron.d, have one extra column: the user under which to run the command. It goes between the time specification and the command itself. Check out the many examples in /etc/crontab if you like.

Sample Crontabs

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.sh

Asterisks 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 run this job only at minute 55.

To run the same job at 1:55 PM every day, use the following:

55    13    *    *    *    /usr/local/bin/maintenance.sh

Here, 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.sh

This 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.sh

To 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 like this:

55    8-18    *    *    1-5    /usr/local/bin/maintenance.sh

To specify multiple exact times, separate them with commas:

55    8,10,12,14,16    *    *    *    /usr/local/bin/maintenance.sh

More interestingly, you can specify fractions of time, or steps. For example, to run a program every 5 minutes, use:

*/5    *    *    *    *    /usr/local/bin/maintenance.sh

You can combine ranges with steps. To run the program every 5 minutes, but 1 minute after the previous example, use this:

1-56/5    *    *    *    *    /usr/local/bin/maintenance.sh

Control 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.sh

If 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.sh

cron 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.sh

Crontabs and cron(8) let you schedule your work any way you like, eliminating the human being from many routine maintenance tasks.

periodic(8)

Some system maintenance jobs should be run only on particular systems, but the way they should be run is identical across all hosts. That’s where periodic(8) comes in.

The periodic(8) command runs system functions on schedule, as cron(8) determines. Periodic checks a directory for a set of scripts to run. FreeBSD includes several directories for periodic tasks: /etc/periodic/daily, /etc/periodic/weekly, /etc/periodic/monthly, and /etc/periodic/security. Depending on which packages you install, you might have corresponding directories in /usr/local/etc/periodic. When cron runs, say, periodic daily, periodic(8) checks each script in each periodic/daily directory to see whether it should be run.

When you have spare time, I recommend perusing the periodic(8) scripts. You might find disabled maintenance scripts useful for your environment.

Which scripts should be run? The default settings are listed in /etc/defaults/periodic.conf, but you can override them in /etc/periodic.conf.

Once periodic(8) runs, it mails the results of the scripts to root on the local machine. Forward root’s mail to someone who will actually read it.

Why use periodic(8)? It’s all for system maintenance. /etc/crontab is for configuring your own system administration jobs. Using separate scripts allows the system upgrade process to replace tasks and packages to add and remove them.

All periodic(8) jobs run as root, though. If you have scheduled jobs that should be run by less privileged users, run them from the user’s crontab.

Now that you have a decent understanding of the common small services provided by FreeBSD, let’s go on to performance.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset