6

Background

OS: Ubuntu 16.04 x64 running on VirtualBox

I am a developer with minimal Ubuntu/Linux knowledge and have been assigned to a project where the goal is to utilize tcpcrypt when communicating with certain endpoints.

tcpcrypt comes with a shell script which sets required entries in iptables to route packets to tcpcrypt for encrpt/decrypt. After execution of this script iptables looks like:

filter

Chain INPUT (policy ACCEPT 4 packets, 552 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 ACCEPT     tcp  --  lo     any     anywhere             anywhere             tcp dpt:65530 tos match0x22/0xff
    0     0 NFQUEUE    tcp  --  any    any    !localhost            anywhere             tcp dpt:65530 flags:FIN,SYN,RST,PSH,ACK,URG/SYN NFQUEUE num 666
    0     0 NFQUEUE    tcp  --  any    any     anywhere             anywhere             multiport sports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s tcp flags:FIN,SYN,RST,PSH,ACK,URG/SYN,ACK NFQUEUE num 666

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 4 packets, 536 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 NFQUEUE    tcp  --  any    any     anywhere             anywhere             multiport dports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s tos match0x04/0xff owner UID match tcpcryptd NFQUEUE num 666
    0     0 NFQUEUE    tcp  --  any    any     anywhere             anywhere             tcp spt:65530 flags:FIN,SYN,RST,PSH,ACK,URG/SYN,ACK NFQUEUE num 666

nat

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         
REDIRECT   tcp  --                                            multiport dports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s redir ports 65530

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         
ACCEPT     tcp  --  anywhere             anywhere             multiport dports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s owner UID match tcpcryptd
REDIRECT   tcp  --  anywhere             anywhere             multiport dports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s redir ports 65530

mangle

Chain PREROUTING (policy ACCEPT)
target     prot opt source               destination         

Chain INPUT (policy ACCEPT)
target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination         

Chain POSTROUTING (policy ACCEPT)
target     prot opt source               destination         
TOS        all  --  anywhere             anywhere             tos match0x04/0xff TOS and 0x00

With these entries, every packet is put on a queue where tcpcrypt picks for enc/dec.

UPDATE

This is the script for iptables:

#!/bin/sh
#DAEMON_USER DIVERT_PORT ONLY_PORTS OMIT_PORTS

# determine which operation is requested (Append or Delete)
if [ "$1" = "start" -o -z "$1" ]; then
    # during startup, bail early if any of these commands fails
    set -e
    OP="-A"
elif [ "$1" = "stop" -o "$1" = "-f" ] ; then
    OP="-D"
else
    echo "Expected \"start\" or \"stop\" as first argument" >&2
    exit 1
fi

# determine which ports should be tcpcrypt-enabled
if [ -z "$ONLY_PORTS" -a -z "$OMIT_PORTS" ] ; then
    echo "Expected either OMIT_PORTS or ONLY_PORTS environment variables to be set" >&2
    exit 1
fi
if [ -n "$ONLY_PORTS" -a -n "$OMIT_PORTS" ] ; then
    echo "Expected only one of OMIT_PORTS or ONLY_PORTS environment variables to be set" >&2
    exit 1
fi
if [ -n "$OMIT_PORTS" ] ; then
    PORT_TEST=!
    PORTS="$OMIT_PORTS"
fi
if [ -n "$ONLY_PORTS" ] ; then
    PORT_TEST=
    PORTS="$ONLY_PORTS"
fi

# more necessary configuration
if [ -z "$DAEMON_USER" ] ; then
    echo "Expected DAEMON_USER environment variable to be set" >&2
    exit 1
fi
if [ -z "$DIVERT_PORT" ] ; then
    echo "Expected DIVERT_PORT environment variable to be set" >&2
    exit 1
fi

# some shorthand to make rules more concise
from_enabled_port="-m multiport $PORT_TEST --source-ports $PORTS"
to_enabled_port="-m multiport $PORT_TEST --destination-ports $PORTS"
NFQUEUE="NFQUEUE --queue-num $DIVERT_PORT"
CRYPT_PORT="65530"
REDIRECT="REDIRECT --to-port $CRYPT_PORT"
INJECT_TOS="0x22"
HANDSHAKE_TOS="0x04"

filter="$ECHO iptables -t filter $OP"

# Injection from daemon: Accept
$filter INPUT -i lo -p tcp --dport $CRYPT_PORT \
          -m tos --tos $INJECT_TOS \
  -j ACCEPT

# SYN redirected to daemon:
#   Queue for daemon to initiate proxy connection with original destination
$filter INPUT -p tcp --dport $CRYPT_PORT --tcp-flags ALL SYN \
  -j $NFQUEUE

# SYN+ACK on proxy connection:
#   Queue for daemon to complete original handshake
$filter INPUT -p tcp $from_enabled_port --tcp-flags ALL SYN,ACK \
  -j $NFQUEUE

# Handshake packet of proxy connection from daemon:
#   Queue for daemon to set tcp options via DIVERT_MODIFY
$filter OUTPUT -p tcp $to_enabled_port \
           -m tos --tos $HANDSHAKE_TOS \
           -m owner --uid-owner $DAEMON_USER \
  -j $NFQUEUE

# SYN+ACK on redirected connection:
#   Queue for daemon to delay handshake until proxy connection succeeds
$filter OUTPUT -p tcp --sport $CRYPT_PORT --tcp-flags ALL SYN,ACK \
  -j $NFQUEUE


nat="$ECHO iptables -t nat $OP"

# Inbound connection for enabled ports:
#   Redirect to daemon (at localhost:$CRYPT_PORT) for encryption
#
# (The nat module will now translate addresses in both directions,
#  for the lifetime of this connection.)
$nat PREROUTING -p tcp $to_enabled_port \
  -j $REDIRECT


# Proxy connection from daemon to enabled port: Accept
$nat OUTPUT -p tcp $to_enabled_port \
        -m owner --uid-owner $DAEMON_USER \
  -j ACCEPT

# Outbound connections to enabled ports on remote hosts:
#   Redirect to daemon (at localhost port $CRYPT_PORT) for encryption
#
# (The nat module will now translate addresses in both directions,
#  for the lifetime of this connection.)
$nat OUTPUT \! -o lo -p tcp $to_enabled_port \
  -j $REDIRECT


mangle="$ECHO iptables -t mangle $OP"

# Packets leaving the machine with bookkeeping mark: Remove mark
$mangle POSTROUTING -m tos --tos $HANDSHAKE_TOS \
  -j TOS --set-tos 0x00

Question

How should I modify iptables with current(see above) entries to achieve the following restrictions:

  1. Only packets with a certain destination should be queued for tcpcrypt.
  2. All other packets should not be queued for tcpcrypt and must travel freely.

What I have tried

A) I tried adding desired IP address to tcp destination in OUTPUT chain which looks like:

Chain OUTPUT (policy ACCEPT 4 packets, 536 bytes)
 pkts bytes target     prot opt in     out     source               destination         
    0     0 NFQUEUE    tcp  --  any    any     anywhere             XXX.XXX.XXX.XXX      multiport dports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s tos match0x04/0xff owner UID match tcpcryptd NFQUEUE num 666
    0     0 NFQUEUE    tcp  --  any    any     anywhere             XXX.XXX.XXX.XXX      tcp spt:65530 flags:FIN,SYN,RST,PSH,ACK,URG/SYN,ACK NFQUEUE num 666

B) I tried adding source and destination parameters to NAT rules:

target     prot opt source               destination         
REDIRECT   tcp  --  XXX.XXX.XXX.XXX       XXX.XXX.XXX.XXX       multiport dports  !ssh,261,https,nntps,614,ldaps,684,695,ftps-data,ftps,telnets:pop3s redir ports 65530

But still all packets regardless of destination address, are sent to tcpcrypt.

4
  • is that the complete output of iptables --list -n -v?
    – JayEye
    Feb 22, 2017 at 0:16
  • Hmm, your option A might have worked with a little tweak of doing the same for the filter INPUT, NAT PREROUTING and OUTPUT and MANGLE POSTROUTING, if the script hadn't re-written the whole iptables. Actually you have it all there, just replace the destination from anywhere to desired ip addresses. The problem is that you'll have to write the default settings for other traffic. You could flush the tables and follow help.ubuntu.com/community/IptablesHowTo and of course write the crypto rules afterwards, just save the current settings somewhere so you'll know what to write and where.
    – user633551
    Feb 22, 2017 at 0:50
  • May I just add that once you flush the tables, you won't have access to internet so you'll need to disable ufw. Also you might need to change the source instead of the destination in some of the rules...such as filter INPUT for example. You'll see what works and what does not...
    – user633551
    Feb 22, 2017 at 1:10
  • @JayEye , It's the output of iptables -t TABLE -L -v for each of filter, nat and mangle tables. Itried your suggested -n, with this option all of source/destination values are displayed as 0.0.0.0/0
    – raidensan
    Feb 22, 2017 at 7:39

1 Answer 1

0

Found a solution, a working one based on method B):

For incoming packets on nat table's PREROUTING chain, I was trying to filter like below

$nat PREROUTING -p tcp -s XXX.XXX.XXX.XXX $to_enabled_port \
    -j $REDIRECT

where -s option and IP value is after -p tcp. Then I changed it to

$nat PREROUTING -s XXX.XXX.XXX.XXX -p tcp $to_enabled_port \
    -j $REDIRECT

With this change and modifying OUTPUT chain of nat accordingly, desired results were achieved.

Interestingly, output of iptables -t nat --line-numbers -L -nv looks exactly the same for any of above commands. Still, only the second one yield results according to my restrictions.

Here is the modified version of iptables.sh which allows specifying one or more IPs to restrict tcpcrypt.

See FILTER_IP below.

#!/bin/sh
#DAEMON_USER DIVERT_PORT ONLY_PORTS OMIT_PORTS

# determine which operation is requested (Append or Delete)
if [ "$1" = "start" -o -z "$1" ]; then
    # during startup, bail early if any of these commands fails
    set -e
    OP="-A"
elif [ "$1" = "stop" -o "$1" = "-f" ] ; then
    OP="-D"
else
    echo "Expected \"start\" or \"stop\" as first argument" >&2
    exit 1
fi

# determine which ports should be tcpcrypt-enabled
if [ -z "$ONLY_PORTS" -a -z "$OMIT_PORTS" ] ; then
    echo "Expected either OMIT_PORTS or ONLY_PORTS environment variables to be set" >&2
    exit 1
fi
if [ -n "$ONLY_PORTS" -a -n "$OMIT_PORTS" ] ; then
    echo "Expected only one of OMIT_PORTS or ONLY_PORTS environment variables to be set" >&2
    exit 1
fi
if [ -n "$OMIT_PORTS" ] ; then
    PORT_TEST=!
    PORTS="$OMIT_PORTS"
fi
if [ -n "$ONLY_PORTS" ] ; then
    PORT_TEST=
    PORTS="$ONLY_PORTS"
fi

# more necessary configuration
if [ -z "$DAEMON_USER" ] ; then
    echo "Expected DAEMON_USER environment variable to be set" >&2
    exit 1
fi
if [ -z "$DIVERT_PORT" ] ; then
    echo "Expected DIVERT_PORT environment variable to be set" >&2
    exit 1
fi

# some shorthand to make rules more concise
from_enabled_port="-m multiport $PORT_TEST --source-ports $PORTS"
to_enabled_port="-m multiport $PORT_TEST --destination-ports $PORTS"
NFQUEUE="NFQUEUE --queue-num $DIVERT_PORT"
CRYPT_PORT="65530"
REDIRECT="REDIRECT --to-port $CRYPT_PORT"
INJECT_TOS="0x22"
HANDSHAKE_TOS="0x04"

# You can specify multiple IPs, or a IP range accrding to required format
# For example, restricting tcpcrypt to 192.192.192.192 and 127.127.127.127
# FILTER_IP="192.192.192.192,127.127.127.127"
# See iptables manpage for more info
FILTER_IP="XXX.XXX.XXX.XXX"

filter="$ECHO iptables -t filter $OP"

# Injection from daemon: Accept
$filter INPUT -i lo -p tcp --dport $CRYPT_PORT \
          -m tos --tos $INJECT_TOS \
  -j ACCEPT

# SYN redirected to daemon:
#   Queue for daemon to initiate proxy connection with original destination
$filter INPUT -p tcp --dport $CRYPT_PORT --tcp-flags ALL SYN \
  -j $NFQUEUE

# SYN+ACK on proxy connection:
#   Queue for daemon to complete original handshake
$filter INPUT -p tcp $from_enabled_port --tcp-flags ALL SYN,ACK \
  -j $NFQUEUE

# Handshake packet of proxy connection from daemon:
#   Queue for daemon to set tcp options via DIVERT_MODIFY
$filter OUTPUT -p tcp $to_enabled_port \
           -m tos --tos $HANDSHAKE_TOS \
           -m owner --uid-owner $DAEMON_USER \
  -j $NFQUEUE

# SYN+ACK on redirected connection:
#   Queue for daemon to delay handshake until proxy connection succeeds
$filter OUTPUT -p tcp --sport $CRYPT_PORT --tcp-flags ALL SYN,ACK \
  -j $NFQUEUE


nat="$ECHO iptables -t nat $OP"

# Inbound connection for enabled ports:
#   Redirect to daemon (at localhost:$CRYPT_PORT) for encryption
#
# (The nat module will now translate addresses in both directions,
#  for the lifetime of this connection.)
$nat PREROUTING -s $FILTER_IP -p tcp $to_enabled_port \
  -j $REDIRECT


# Proxy connection from daemon to enabled port: Accept
$nat OUTPUT -p tcp $to_enabled_port \
        -m owner --uid-owner $DAEMON_USER \
  -j ACCEPT

# Outbound connections to enabled ports on remote hosts:
#   Redirect to daemon (at localhost port $CRYPT_PORT) for encryption
#
# (The nat module will now translate addresses in both directions,
#  for the lifetime of this connection.)
$nat OUTPUT -d $FILTER_IP \! -o lo -p tcp $to_enabled_port \
  -j $REDIRECT


mangle="$ECHO iptables -t mangle $OP"

# Packets leaving the machine with bookkeeping mark: Remove mark
$mangle POSTROUTING -m tos --tos $HANDSHAKE_TOS \
  -j TOS --set-tos 0x00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .