Reverse two lines in a hosts_options rule and the client you meant to trust gets denied along with everyone else. This builds a small, testable TCP Wrapper policy that allows a trusted client, denies an unwanted one, and records useful diagnostics without touching a service configuration. The examples use hosts_options(5) from Debian's libwrap0 package, version 7.6.q-33, installed on the reference machine.
Allow about fifteen minutes. You need root access, a service that actually calls the libwrap library, and a second host to test from. TCP Wrapper rules do not protect every listening socket automatically: a daemon must use libwrap, or be launched through a wrapper such as tcpd. The package is old, so confirm the service still supports it before relying on these files.
Read the installed contract before editing a live access list. This is an ordinary, read-only command:
$ man 5 hosts_options
$ dpkg-query -W -f='${Package} ${Version}\n' libwrap0
libwrap0 7.6.q-33
Each extended rule has this shape:
daemon_list : client_list : option : option ...
The first two fields use the patterns documented by hosts_access(5). The later fields are processed left to right, and a colon inside an option must be escaped with a backslash. The option language only matters once the daemon actually reaches libwrap, so identify the daemon name the library receives rather than guessing from its systemd unit name.
Checkpoint: find the service's documentation or startup command and confirm it links to libwrap or runs under tcpd. If it does not, stop here: editing /etc/hosts.allow or /etc/hosts.deny will not create a firewall rule for an unrelated daemon.
These files can lock out remote administration. Before changing them, open a local root shell or keep an existing console session available. Copy only the files that exist:
# install -o root -g root -m 0600 /dev/null /tmp/hosts.allow.backup
# cp -p /etc/hosts.allow /tmp/hosts.allow.backup 2>/dev/null || true
# install -o root -g root -m 0600 /dev/null /tmp/hosts.deny.backup
# cp -p /etc/hosts.deny /tmp/hosts.deny.backup 2>/dev/null || true
The install commands create empty temporary files first; the following copies replace them only when the source exists. Treat these as short-lived recovery material, remove them after testing, and note that none of this touches the live access files.
Use a real daemon name and a client address you control. Replace sshd and 198.51.100.25 below with values from your environment; that address range is reserved for documentation and will never match a real client:
# editor /etc/hosts.allow
sshd : 198.51.100.25 : allow
allow grants the service and must be the final option in that rule. A hostname may be used, but an address avoids a dependency on forward and reverse DNS while you are testing, and you can list several clients separated by whitespace or commas.
For a policy that keeps both outcomes in one file, add a catch-all deny after the specific allow:
sshd : 198.51.100.25 : allow
sshd : ALL : deny
Rules are examined in order and the first match ends the search, so reversing these two lines denies the trusted client too. It is the single most common mistake in a short policy.
The underlying access language checks /etc/hosts.allow first, then /etc/hosts.deny. A matching allow grants access; otherwise a matching deny refuses it; if neither matches, access is granted. A missing file behaves like an empty one, and that default is permissive.
If you prefer the traditional split layout, leave the specific rule in /etc/hosts.allow and put the fallback in /etc/hosts.deny:
/etc/hosts.allow:
sshd : 198.51.100.25 : allow
/etc/hosts.deny:
sshd : ALL : deny
Do not combine a catch-all deny in hosts.deny with an assumed allow in some other service's configuration; the daemon name and client pattern must match this library's own lookup. And remember: an unmatched request is allowed when there is no deny rule at all.
Options are ordered, so a logging option can precede the final decision:
sshd : 198.51.100.25 : severity notice : allow
sshd : ALL : severity notice : deny
severity changes the syslog severity for the event. An optional facility can be included, for example severity mail.info, though older syslog implementations may not support facility names here. Look for the resulting entry in the host's syslog or journal, depending on its logging setup:
# journalctl --since '10 minutes ago' | grep -iE 'tcp|wrapper|sshd'
Do not treat an empty search as proof the rule was ignored: the daemon may log elsewhere, use a different process name, or not call libwrap at all. Check the service's own logs and test from the second host before concluding anything.
First test the explicitly allowed client and confirm the service behaves normally. Then test from a client that should be denied. Record the actual result and the relevant log entry: TCP Wrapper does not provide a universal validation command in this package.
Keep the existing local session open until both tests pass. If you lose remote access, use the console or an already authenticated root session and restore the last known-good files:
# cp -p /tmp/hosts.allow.backup /etc/hosts.allow
# cp -p /tmp/hosts.deny.backup /etc/hosts.deny
Remove the temporary backups only once recovery is no longer needed:
# rm -f /tmp/hosts.allow.backup /tmp/hosts.deny.backup
This rollback changes access policy immediately for new wrapper checks. It normally does not require a service restart, because the files are consulted as connections are checked, but an already established connection may not be re-evaluated.
aclexec runs a shell command and uses its true or false exit status as the access decision. spawn runs a child command, and twist replaces the current process with a command connected to the client. None of these are logging shortcuts.
For example, this records the client address with an absolute path and keeps the command asynchronous:
sshd : ALL : spawn /usr/bin/logger -t tcp-wrapper "connection from %a" &
aclexec and spawn are connected to the null device.%a, are replaced with underscores before execution.twist expects stream I/O and is the wrong tool there.The simpler allow, deny and severity rules are usually enough. Add command hooks only once you have a rollback path and a separate test service: a syntax error is reported to syslog, later options are ignored, and service access is denied, so a typo can become an outage.
tcpd.hosts.allow or hosts.deny.