transparency — network transparency and the related aspects
The Kernun firewall is able to transparently grab clients' connections and hand them over to its proxies, as well as to pretend to servers that connections come from clients' real IP addresses instead of the firewall's IP address. This ability is implemented in the transparency support that was added to the FreeBSD kernel.
Transparency for clients is realized using special sockets called transparent listening sockets. Unlike a regular listening socket, a transparent listening socket is able to accept transparent connections, i.e., it accepts a connection even if the client is not connecting explicitly to the firewall, but directly to some server's IP address.
A transparent listening connection can be configured
either to accept connections that arrive to any
interface, or it can be limited to a particular
interface. The former case is configured in the Kernun
configuration by specifying a special IP address,
0.0.0.0
. The
latter one can be configured either by specifying the
name of the interface, or by specifying its IP address
(which is only used to
determine the particular interface). See listen-on(5) for the proper syntax.
Transparent listening sockets can be identified in a
running system by
sockstat(1). They are
distinguished from regular sockets by the special syntax
in the LOCAL ADDRESS
field of the
sockstat(1):
{
.
If present, the iface
|*}>>:port
iface
value
denotes the interface the transparent listening socket is
limited to. Otherwise (denoted by *
),
the socket listens on all network interfaces.
In the TCP protocol, accepting connection by
accept(2) returns a new
socket that is used for communication with the client.
Packets sent via this socket are automatically assigned
the real destination (the server's) IP address as the
source address, and all packets sent within this
connection from the client to the server would come to
this socket. These sockets are indicated by syntax
>>
in the addr
:port
LOCAL
ADDRESS
field of the
sockstat(1). Here, the
addr
denotes the IP address of the
server (i.e., the IP address the client thinks to be
connected to).
In the typical scenario there would be one transparent
listening socket for each interface the proxy listens on
(FD
5 in the following example).
This socket is shared by the proxy's parent process and by
all of its child processes.
In addition, there would be two sockets for each
connection established via the proxy: one for the
connection from the client to the proxy
(FD
11), and another for the connection
from the proxy to the server (FD
12).
The situation is shown in the following example
(10.1.1.1
stands for the client IP address, 10.3.3.3
stands for
the firewall external address and 10.4.4.4
stands for
the server IP address):
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS kernun tcp-proxy 26802 5 tcp4 vr0>>:22 *:* kernun tcp-proxy 26801 5 tcp4 vr0>>:22 *:* kernun tcp-proxy 26800 5 tcp4 vr0>>:22 *:* kernun tcp-proxy 26800 11 tcp4 >>10.4.4.4:22 10.1.1.1:36528 kernun tcp-proxy 26800 12 tcp4 10.3.3.3:62175 10.4.4.4:22 kernun tcp-proxy 26799 5 tcp4 vr0>>:22 *:* kernun tcp-proxy 26798 5 tcp4 vr0>>:22 *:* kernun tcp-proxy 26797 5 tcp4 vr0>>:22 *:*
Connections can be considered in ACLs according to their
transparency. This is done using a keyword to the to
configuration item. If the key transparent
is present
in this item, only transparent connections get matched. Similarly,
with keyword non-transparent
present, only
non-transparent connections get matched.
This feature allows servers to see real clients' addresses upon receiving connections instead of the firewall's address (which is the standard behavior for proxies). It can be specified in an ACL section using either of the following syntax constructions:
source-address client; source-address [5.5.5.5];
This feature can be regarded as transparency for servers.
source-address
might be used either in
transparent
or non-transparent
mode.
For example, if a client of 10.1.1.1
wants to
connect through proxy at 10.2.2.2
(either
transparently or non-transparently) to server 4.4.4.4
,
the server sees normally the connection as coming from
firewall's external address, e.g. 3.3.3.3
. Taking
advantage of source-address client
,
the server sees the connection as if it were coming from
10.1.1.1
. In
that case, the
sockstat(1) gives
the following output:
USER COMMAND PID FD PROTO LOCAL ADDRESS FOREIGN ADDRESS ... kernun tcp-proxy 23008 11 tcp4 >>10.4.4.4:22 10.1.1.1:36528 kernun tcp-proxy 23008 12 tcp4 10.1.1.1:62175 10.4.4.4:22 ...
Unlike in the first example,
the connection from the firewall to the server
(FD
=12) shows the LOCAL
ADDRESS
to be 10.1.1.1
(i.e., the
client's IP address).
For a given TCP/UDP port there can be more than one type of application listening side-by-side: transparent proxies, non-transparent proxies or system daemons (such as ssh daemon sshd). However, they must not be in mutual conflict.
Conflicts are detected by the /verify command of cml(8). Two applications that listen on the same port (or with their listen port ranges overlapping) are in conflict, if any of the following cases occurs:
Both listen in the non-transparent mode on the same IP address
Both listen in the non-transparent mode on the
wildcard IP address 0.0.0.0
Both listen transparently on the same interface (either if the interface name was given directly or if it was deduced from the IP address)
Both listen transparently without interface restriction
Note that the conflicts check is only performed for the components configured within the cml configuration file. It is not performed for the components that are configured out of it.
When a packet arrives, the most specific socket is chosen according to the following precedence order (from the most specific to the most generic):
Non transparent, single IP address, single port
Non transparent, wildcard
address (0.0.0.0
),
single port
Transparent with interface restriction, single port
Transparent without interface restriction, single port
Non transparent, single IP address, port range
Non transparent, wildcard
address (0.0.0.0
),
port range
Transparent with interface restriction, port range
Transparent without interface restriction, port range
It is therefore possible to provide several services for
the same port, as long as they do not collide. For
example, the SSH daemon might be available for an
administrative connection to the firewall on all
interfaces (SSH daemon only makes sense in the
non-transparent mode), while tcp-proxy
might be configured in the transparent mode for proxying the
ssh traffic through the server for the internal
interface. In that case, packets with the destination
address equal to any of the firewall's IP addresses
would end up in the SSH daemon, while packets with the
destination in the external network would be processed by
the tcp-proxy. See the examples section.
When transparency is enabled (sysctl
net.inet.ip.transparency=1
), every packet is
considered to be potentially local (i.e., destinated for
some firewall's process) and is therefore delivered
into the local IP stack. One of the consequences of this fact
is that the packet is not eventually ip-forwarded (even if
sysctl net.inet.ip.forwarding=1
and it
would have been forwarded, if the standard FreeBSD kernel
had been used).
Under certain circumstances it might be desirable to
bypass transparency. Kernun firewall uses the packet
filter (pf(4),
packet-filter(5)) for this
purpose. When the packet has the pf tag
NOTRANSP
set, the kernel handles it the same
way the regular FreeBSD kernel would. The actual tag that
is used for this purpose can be changed using sysctl
net.inet.ip.no_transp_tag
.
NOTRANSP is the default value.
The following rule can be used to tag all the
traffic from client of 10.1.1.1
:
pass in on vr0 from 10.1.1.1 to any tag NOTRANSP
Note that all the traffic that is
required not to undergo the transparency must be tagged
NOTRANSP
. Especially, for bidirectional communication,
packets for both directions must be tagged so (see the
latter example in this manual page).
Kernun: acl(5), ftp-proxy.cfg(5), listen-on(5), access-control(7), dns-proxy(8), ftp-proxy(8), kat(8), smtp-proxy(8), tcp-proxy(8)
FreeBSD: ioctl(2), pf(4), pf.conf(5), pfctl(8)