如何配置 iptables 以与 tcpcrypt 一起工作?

如何配置 iptables 以与 tcpcrypt 一起工作?

背景

操作系统:在 VirtualBox 上运行的 Ubuntu 16.04 x64

我是一名对 Ubuntu/Linux 了解不多的开发人员,被分配到一个项目,目标是利用tcpcrypt与某些端点通信时。

tcpcrypt 附带一个 shell 脚本,该脚本在 iptables 中设置所需的条目,以将数据包路由到 tcpcrypt 进行加密/解密。执行此脚本后,iptables 如下所示:

筛选

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

纳特

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

曼格尔

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

通过这些条目,每一个数据包被放入一个队列,由 tcpcrypt 挑选进行加密/解密。

更新

这是 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

问题

我应该如何修改iptables当前(参见上文)条目以实现以下限制:

  1. 只有具有特定目的地的数据包才应排队进行 tcpcrypt。
  2. 所有其他数据包应该不是排队等待 tcpcrypt 并且必须自由旅行。

我尝试过

A) 我尝试将所需的 IP 地址添加到OUTPUT链中的 tcp 目标,如下所示:

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)我尝试将源和目标参数添加到 NAT 规则中:

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

但所有数据包无论目标地址如何,都会被发送到 tcpcrypt。

答案1

找到了一个解决方案,一个基于方法B)

对于表链上的传入数据包natPREROUTING我尝试进行如下过滤

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

其中-s选项和 IP 值位于后面-p tcp。然后我将其更改为

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

通过这种改变和相应的修改OUTPUTnat,我们实现了预期的结果。

有趣的是,上述任何命令的输出iptables -t nat --line-numbers -L -nv看起来都完全相同。不过,只有第二个命令根据我的限制产生了结果。

这里是修改版本,iptables.sh允许指定一个或多个IP来限制tcpcrypt。

FILTER_IP下文。

#!/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

相关内容