それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

ubuntuのaptで入れたsslhで細かい設定を行う。

ubuntu の apt で入れた sslh が微妙だった件。

ubuntu の apt で入れた sslh にいくつか問題があった。どのようにめんどくさいかというと、設定の追加がほぼ不可能なのだ。

/etc/init.d/sslh/ をベースにしていて、 init.dが/etc/default/sslhsourceして起動するが、init.d は systemd 管理下に置かれたので、 sourceをせずにsystemdに変数を設定として文字列を拾われる。

このため/etc/default/sslh/bin/shを使うsource前提にならず、systemd経由の起動されるため変数展開が反映されない。このため起動設定をbin/shで記述ができなくなってる

さらに、sslhは forking で起動するのに、foreground が前提でtype simple に固定されている。そのためpkillで一時的に止めることもかなわない。ユニットの編集は必須である。

さらに、inetd を基本線にしているのか、設定をsystemdに書くのか、init.dでやるのかdefaultsに書くのかと判断に迷う。恐ろしく設定がめんどくさい。

apt で導入される systemdユニットを捨てて書き直すか、自分でカスタマイズするしかない。

systemd ユニットを上書きする

 sudo systemctl edit sslh.service

次のように上書きした。

# /etc/systemd/system/sslh.service.d/override.conf
## 2023-03-17 /etc/defaultsに変数が使えないので代替案
[Service]
ExecStartPre=/usr/bin/bash /etc/sslh/sslh-start-pre.sh
ExecStartPre=/usr/bin/bash /etc/sslh/transparent-iptables.sh
KillMode=control-group
PIDFile=/run/sslh/sslh.pid
KillMode=control-group
KillSignal=SIGTERM
Type=simple

起動前にシェルスクリプトを実行して /etc/default/sslhを動的に書き換えることにした。

/etc/sslh/sslh-start-pre.sh

シェルスクリプトで動的に書き換えるために、テンプレートを使い変数を展開し、/etc/defaults を書き換えるようにした。

#!/usr/bin/env bash
#DAEMON_OPTS="--user sslh --listen <change-me>:443 --ssh 127.0.0.1:22 --ssl 127.0.0.1:443 --pidfile /var/run/sslh/sslh.pid"


ADDR=$( ip -o  addr show | grep eth | grep -oP '192.168.[0-9]{1,3}.[0-9]{1,3}(?=/24)' )
OPTS="--user sslh --listen $ADDR:443 --ssh 127.0.0.1:22 --tls 127.0.0.1:443 --pidfile /run/sslh/sslh.pid"
OPTS="-F/etc/sslh/sslh.conf --listen $ADDR:443"


DAEMON_OPTS=$OPTS

echo "# Default options for sslh initscript
# sourced by /etc/init.d/sslh

# binary to use: forked (sslh) or single-thread (sslh-select) version
# systemd users: don't forget to modify /lib/systemd/system/sslh.service
DAEMON=/usr/sbin/sslh


# auto generate by takuya
DAEMON_OPTS=$OPTS


" > /etc/default/sslh

リッスンするIPアドレスとポートをDHCPの配布状況に応じて書き換える。

/etc/sslhに設置した設定ファイルを使うようにする。

透過プロキシ用iptables

#!/usr/bin/env bash
## sslhでtransparent を使うには、必要な設定
##

IFACE=$( ip  addr show | grep eth | grep -P '192.168.2.[0-9]{1,3}(?=/24)' | grep -oP 'eth[0-9]' )

function add(){
  sysctl -w net.ipv4.conf.default.route_localnet=1
  sysctl -w net.ipv4.conf.all.route_localnet=1

  # DROP martian packets as they would have been if route_localnet was zero
  # Note: packets not leaving the server aren't affected by this, thus sslh will still work
  iptables -t raw -A PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
  iptables -t mangle -A POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP

  # Mark all connections made by ssl for special treatment (here sslh is run as user "sslh")
  iptables -t nat -A OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f

  # Outgoing packets that should go to sslh instead have to be rerouted, so mark them accordingly (copying over the connection mark)
  iptables -t mangle -A OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f

  # Configure routing for those marked packets
  ip rule add fwmark 0x1 lookup 100
  ip route add local 0.0.0.0/0 dev lo table 100
}

function down(){

  iptables -t raw -D PREROUTING ! -i lo -d 127.0.0.0/8 -j DROP
  iptables -t mangle -D POSTROUTING ! -o lo -s 127.0.0.0/8 -j DROP
  iptables -t nat -D OUTPUT -m owner --uid-owner sslh -p tcp --tcp-flags FIN,SYN,RST,ACK SYN -j CONNMARK --set-xmark 0x01/0x0f
  iptables -t mangle -D OUTPUT ! -o lo -p tcp -m connmark --mark 0x01/0x0f -j CONNMARK --restore-mark --mask 0x0f
  ip rule del fwmark 0x1 lookup 100
  ip route del local 0.0.0.0/0 dev lo table 100

}

function main(){
  case $1 in
    add)
      add;
      ;;
    del)
      down;
      ;;
  esac

}


main $@

透過プロキシのスクリプトを、nftablesで書き直した例

#!/bin/sh

function sslh_transparent_add_nft(){
  ## router ip
  LAN_IP=$(ip a s br-lan | grep -oP '(?<=inet )[\d\.]+(?=/)')
  LAN_NET=$(ip a s br-lan | grep -oP '(?<=inet ).+(?= brd)')

  ## up
    nft add table sslh_tproxy
    nft add chain sslh_tproxy sslh_mark
    nft add  rule sslh_tproxy sslh_mark mark set 0x1 accept
    nft add chain sslh_tproxy prerouting { type filter hook prerouting priority mangle\; }
    nft add  rule sslh_tproxy prerouting meta l4proto tcp socket transparent 1 jump sslh_mark
    nft add chain sslh_tproxy output { type route hook output priority mangle\; }
    nft add  rule sslh_tproxy output oifname "eth0" meta l4proto tcp tcp sport 443 jump sslh_mark
    ## sslh process output rule
  ip rule add fwmark 0x1 lookup 100
  ip route add local 0.0.0.0/0 dev lo table 100
  ## NAT loop back
    nft add chain sslh_tproxy postrouting { type nat hook postrouting priority srcnat\;}
  nft add  rule sslh_tproxy postrouting ip saddr $LAN_NET ip daddr { 192.168.2.5, 192.168.2.21 } tcp dport 443 snat to $LAN_IP

}

function sslh_transparent_del_nft(){

  ## down
  ip rule del fwmark 0x1 lookup 100
  ip route del local 0.0.0.0/0 dev lo table 100
  if nft list tables | grep -wq 'table ip sslh_tproxy' ; then
    nft delete table sslh_tproxy
  fi

}

function sslh_transparent_start(){
  sslh_transparent_del_nft;
  sslh_transparent_add_nft;
}
function sslh_transparent_stop(){
  sslh_transparent_del_nft;
}

function main(){
  #  sslh_transparent_start;
  case $1 in
    "add")
      echo transparent iptables,route added.
      sslh_transparent_start;
      ;;
    "del")
      echo transparent iptables,route removed.
      sslh_transparent_stop;
      ;;
    *)
      echo "usage"
      echo "     " $0 ' del'
      echo "     " $0 ' add'
  esac
}

if [[ $0 =~ 'transparent-sslh.sh' ]] ; then
  # execute する場合
  main $*
fi

/etc/sslh/sslh.conf

リッスンするアドレスとポートは、systemdから起動時のオプションで与えるため設定では省略

systemdはforegroundは常にTrueを想定するのでtrueにした。

foreground: true;
inetd: false;
numeric:false;
transparent: true;
timeout: 2;
user: "sslh";
pidfile: "/var/run/sslh.pid";
chroot: "/etc/sslh";


listen:()

protocols:
(
  { name: "ssh"; service: "ssh"; host: "localhost"; port: "22";
    keepalive: true; fork: true; tfo_ok: true },
  { name: "tls"; host: "localhost"; port: "443";  tfo_ok: true }
);

つながった

curl で動作確認して、疎通を確認した。

これで無事にsslhを使って接続を仕分けられる。

sslhのメリット/ nginxの設定がIP非依存に

現在では、nginxの前段にsslhを入れている。

nginxの設定では、次のように、listenを記述している。

listen 127.0.0.1:443 ssl http2;

このように書けるので、nginxの設定ファイルを使い回せるようになった。環境依存の固定IPアドレスがnginxの設定ファイルから消えて、nginxの設定が環境非依存になりコピペがしやすくクラスタリングも楽になった。CD/CIファイルの記述も減った。

初期接続開始のTCPセッション・TLSセッション時間が極僅かだけ遅くなったが許容範囲だった。

2023-06-27

iptablesで書くと優先度の管理が面倒になったし、openwrtではiptables記述が警告になったので、思い切ってnft(nf_tables)に書き直した。