OpenBSD PF - User Shell for Authenticating Gateways (authpf) [Contents]



Introduction

The authpf(8) utility is a user shell for authenticating gateways. An authenticating gateway is just like a regular network gateway (also known as a router) except that users must first authenticate themselves to it before their traffic is allowed to pass through. When a user's shell is set to /usr/sbin/authpf and he or she logs in using SSH, authpf will make the necessary changes to the active pf(4) ruleset so that the user's traffic is passed through the filter and/or translated using NAT/redirection. Once the user logs out, or the session is disconnected, authpf will remove any rules loaded for the user and kill any stateful connections the user has open. Because of this, the ability of the user to pass traffic through the gateway only exists while the user keeps the SSH session open.

A user's rules are loaded into a unique anchor point by authpf. The anchor is named by combining the username and the authpf process-id in the username(PID) format. Each user's anchor is stored within the authpf anchor which is in turn anchored to the main ruleset. The fully qualified anchor path then becomes:

main_ruleset/authpf/username(PID)
The rules that authpf loads can be configured on a per-user or global basis.

Example uses of authpf include:

The authpf tool logs the username and IP of each user who authenticates successfully, as well as the start and end times of the login session via syslogd(8). By using this information, an administrator can determine who was logged in when and also make users accountable for their network traffic.

Configuration

The basic steps needed to configure authpf are outlined here. For a complete description of authpf configuration, please refer to the man page.

Enabling authpf

The authpf utility will not run if the /etc/authpf/authpf.conf config file is not present. The file may be empty, but, unless it is present, authpf will exit immediately after a user authenticates successfully.

The following configuration directives can be placed in authpf.conf:

Linking authpf into the Main Ruleset

To link authpf into the main ruleset, use an anchor rule:
anchor "authpf/*"
Wherever the anchor rule is placed within the ruleset is where PF will branch off from the main ruleset to evaluate the authpf rules.

Configuring Loaded Rules

The authpf rules can be loaded from one of these two files: The first file contains rules that are only loaded when the user $USER (which is replaced with the user's username) logs in. The per-user rule configuration is used when a specific user, such as an administrator, requires a set of rules that is different than the default set. The second file contains the default rules which are loaded for any users that don't have their own authpf.rules file. If the user-specific file exists, it will override the default file. At least one of the files must exist or authpf will not run.

Rules have the same syntax as any other PF ruleset, with the exception that authpf allows for the use of two predefined macros:

It's recommended practice to use the $user_ip macro to only permit traffic through the gateway from the authenticated user's computer.

In addition to the $user_ip macro, authpf will make use of the authpf_users table (if it exists) for storing the IP addresses of all authenticated users. Be sure to define the table before using it:

table <authpf_users> persist
pass in on egress proto tcp from <authpf_users> to port smtp
This table should only be used in rules that are meant to apply to all authenticated users.

Access Control Lists

Specific users can be prevented from accessing authpf by creating a file in the /etc/authpf/banned directory that matches the username. The contents of this file will be displayed to the users before authpf disconnects them. This provides a handy way to notify the users of why they're disallowed access and who to contact to have it restored.

Conversely, it's also possible to only grant access to specific users by placing usernames in the /etc/authpf/authpf.allow file. If the file does not exist, or if "*" is entered into it, authpf will permit access to any users who successfully log in via SSH as long as they are not explicitly banned.

If authpf is unable to determine whether a username is allowed or denied, it will print a brief message and then disconnect the user. A file in /etc/authpf/banned always overrides an entry in /etc/authpf/authpf.allow.

Displaying a Login Message

Whenever a user successfully authenticates to authpf, a greeting is printed which indicates that the user is authenticated.
Hello charlie. You are authenticated from host "198.51.100.10"
This message can be supplemented by putting a custom message in /etc/authpf/authpf.message. The contents of this file will be displayed after the default welcome message.

Assigning authpf as a User's Shell

In order for authpf to work, it must be assigned as the user's login shell. When the user successfully authenticates to sshd(8), authpf will be executed as the user's shell. It will then check if the user is allowed to use authpf, load the rules from the appropriate file, etc.

There are a couple ways of assigning authpf as a user's shell:

  1. Manually for each user using chsh(1), vipw(8), useradd(8), usermod(8), etc.
  2. By assigning users to a login class and changing the class's shell option in login.conf(5).

Creating an authpf Login Class

When using authpf on a system that has regular user accounts and authpf user accounts, it can be beneficial to use a separate login class for the authpf users. This allows for certain changes to be made to those accounts on a global basis, and also allows different policies to be placed on regular accounts and authpf accounts. Some examples of what policies can be set:

Login classes are created in the login.conf(5) file. OpenBSD comes with an authpf login class defined as:

authpf:\
    :welcome=/etc/motd.authpf:\
    :shell=/usr/sbin/authpf:\
    :tc=default:
Users are assigned to a login class by editing the class field of the user's passwd(5) database entry. One way to do this is with the chsh(1) command.

Seeing Who is Logged In

Once a user has successfully logged in and authpf has adjusted the PF rules, authpf changes its process title to indicate the username and IP address of the logged in user:
# ps -ax | grep authpf
23664 p0  Is+     0:00.11 -authpf: charlie@192.168.1.3 (authpf)
Here the user charlie is logged in from the machine 192.168.1.3. By sending a SIGTERM signal to the authpf process, the user can be forcefully logged out. Any rules loaded for the user will be removed and any stateful connections the user has open will be killed.
# kill -TERM 23664

Example

The authpf tool is being used on an OpenBSD gateway to authenticate users of a wireless network that is part of a larger campus network. Authenticated users will be permitted to SSH out, browse the web, and access the campus DNS servers, assuming they're not on the banned list.

The /etc/authpf/authpf.rules file contains the following rules:

wifi_if = "wi0"

pass in quick on $wifi_if \
   proto tcp from $user_ip to any port { ssh, http, https }
The administrative user charlie needs to be able to access the campus SMTP and POP3 servers in addition to surfing the web and using SSH. The following rules are set up in /etc/authpf/users/charlie/authpf.rules:
wifi_if = "wi0"
smtp_server = "10.0.1.50"
pop3_server = "10.0.1.51"

pass in quick on $wifi_if \
   proto tcp from $user_ip to $smtp_server port smtp
pass in quick on $wifi_if \
   proto tcp from $user_ip to $pop3_server port pop3
pass in quick on $wifi_if \
   proto tcp from $user_ip to port { ssh, http, https }
The main /etc/pf.conf ruleset is set up as follows:
wifi_if = "wi0"
ext_if  = "fxp0"
dns_servers = "{ 10.0.1.56, 10.0.2.56 }"

table <authpf_users> persist

block drop all

pass out quick on $ext_if \
   inet proto { tcp, udp, icmp } from { $wifi_if:network, $ext_if }

pass in quick on $wifi_if \
   inet proto tcp from $wifi_if:network to $wifi_if port ssh

pass in quick on $wifi_if \
   inet proto { tcp, udp } from <authpf_users> to $dns_servers port domain

anchor "authpf/*" in on $wifi_if
The ruleset is very simple and does the following: The idea behind the main ruleset is to block everything and allow the least amount of traffic through as possible. Traffic is free to flow out on the external interface but is blocked from entering the wireless interface by the default deny policy. Authenticated users' traffic is permitted to pass in on the wireless interface and to then flow through the gateway into the rest of the network. The quick keyword is used throughout so that PF doesn't have to evaluate each named ruleset when a new connection passes through the gateway.