CLOVER🍀

That was when it all began.

Ubuntu Linux 18.04 LTSにstunnelをインストールする

これは、なにをしたくて書いたもの?

SSL/TLSのトンネリングツールである、stunnelをちょっと試してみたいなぁと思いまして。

非SSL/TLSなクライアント、サーバー間の通信を、SSL/TLSに対応させる手段としては知っておいてもよいかな、と。

stunnel

最初に書きましたが、SSL/TLSのトンネリングツールです。

stunnel: Home

SSL/TLSに対応していないクラアントやサーバーの間にプロキシ的に配置することで、間の通信を暗号化することができます。

機能詳解は、こちら。

stunnel: Features

クロスプラットフォームで動作し、各プラットフォームに特化した機能もあります。

ドキュメントやExampleはこちら。

stunnel: Documentation

stunnel: Unix Config

stunnel: Windows Config

設定内容は、Exampleを見るとある程度想像できそうな感じですね。

その他の参考情報。

4.8. stunnel の使用 Red Hat Enterprise Linux 7 | Red Hat Customer Portal

@IT:Security Tips > SSL非対応メーラのSSL化

環境

今回の環境は、こちら。Ubuntu Linux 18.04 LTSです。

$ uname -srvmpio
Linux 4.15.0-96-generic #97-Ubuntu SMP Wed Apr 1 03:25:46 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux


$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 18.04.4 LTS
Release:    18.04
Codename:   bionic

インストール

では、stunnelをインストールしてみましょう。

Ubuntu Linuxの場合は、aptでインストールすることができます。パッケージ名は「stunnel4」です。

$ sudo apt install stunnel4

インストールされるコマンドは「stunnel4」とそのエイリアス、そして「stunnel3」です。

$ ll /usr/bin/stunnel*
lrwxrwxrwx 1 root root      8 Apr 19  2018 /usr/bin/stunnel -> stunnel4*
-rwxr-xr-x 1 root root   2788 Apr 19  2018 /usr/bin/stunnel3*
-rwxr-xr-x 1 root root 175888 Apr 19  2018 /usr/bin/stunnel4*

「stunnel4」のバージョンを見ると、4という名前の割には5.44がインストールされたようです。最新版ではありませんが、今回はこれで
良いでしょう。

$ stunnel4 -version
stunnel 5.44 on x86_64-pc-linux-gnu platform
Compiled with OpenSSL 1.1.0g  2 Nov 2017
Running  with OpenSSL 1.1.1  11 Sep 2018
Update OpenSSL shared libraries or rebuild stunnel
Threading:PTHREAD Sockets:POLL,IPv6,SYSTEMD TLS:ENGINE,FIPS,OCSP,PSK,SNI Auth:LIBWRAP
 
Global options:
pid                    = /var/run/stunnel4.pid
RNDbytes               = 64
RNDfile                = /dev/urandom
RNDoverwrite           = yes
 
Service-level options:
ciphers                = FIPS (with "fips = yes")
ciphers                = HIGH:!DH:!aNULL:!SSLv2 (with "fips = no")
curve                  = prime256v1
debug                  = daemon.notice
logId                  = sequential
options                = NO_SSLv2
options                = NO_SSLv3
sessionCacheSize       = 1000
sessionCacheTimeout    = 300 seconds
stack                  = 65536 bytes
TIMEOUTbusy            = 300 seconds
TIMEOUTclose           = 60 seconds
TIMEOUTconnect         = 10 seconds
TIMEOUTidle            = 43200 seconds
verify                 = none

/etc/stunnel/READMEを見ると、ENABLEDを設定してね、サンプルがあるよ、と書かれています。

Stunnel 4 configuration files.

Files found under the /etc/stunnel directory that end with .conf are
used by the stunnel4 service as configuration files, and each will be
used to start a daemon process setting up a tunnel with the given
configuration. Note that this directory is initially empty, as the
settings you may want for your tunnels are completely system dependent.

In order to have the tunnels start up automatically on system boot you
must *also* set ENABLED to 1 in /etc/default/stunnel4

A sample configuration file with defaults may be found at
 /usr/share/doc/stunnel4/examples/stunnel.conf-sample

サンプルの内容を見てみます。

$ zcat /usr/share/doc/stunnel4/examples/stunnel.conf-sample.gz
; Sample stunnel configuration file for Unix by Michal Trojnara 2002-2017
; Some options used here may be inadequate for your particular configuration
; This sample file does *not* represent stunnel.conf defaults
; Please consult the manual for detailed description of available options

; **************************************************************************
; * Global options                                                         *
; **************************************************************************

; It is recommended to drop root privileges if stunnel is started by root
;setuid = stunnel4
;setgid = stunnel4

; PID file is created inside the chroot jail (if enabled)
;pid = /var/run/stunnel.pid

; Debugging stuff (may be useful for troubleshooting)
;foreground = yes
;debug = info
;output = /var/log/stunnel.log

; Enable FIPS 140-2 mode if needed for compliance
;fips = yes

; The pkcs11 engine allows for authentication with cryptographic
; keys isolated in a hardware or software token
; MODULE_PATH specifies the path to the pkcs11 module shared library,
; e.g. softhsm2.dll or opensc-pkcs11.so
; Each section using this feature also needs the "engineId = pkcs11" option
;engine = pkcs11
;engineCtrl = MODULE_PATH:/usr/lib/softhsm/libsofthsm2.so
;engineCtrl = PIN:1234

; **************************************************************************
; * Service defaults may also be specified in individual service sections  *
; **************************************************************************

; Enable support for the insecure SSLv3 protocol
;options = -NO_SSLv3

; These options provide additional security at some performance degradation
;options = SINGLE_ECDH_USE
;options = SINGLE_DH_USE

; **************************************************************************
; * Include all configuration file fragments from the specified folder     *
; **************************************************************************

;include = /etc/stunnel/conf.d

; **************************************************************************
; * Service definitions (remove all services for inetd mode)               *
; **************************************************************************

; ***************************************** Example TLS client mode services

; The following examples use /etc/ssl/certs, which is the common location
; of a hashed directory containing trusted CA certificates.  This is not
; a hardcoded path of the stunnel package, as it is not related to the
; stunnel configuration in /etc/stunnel/.

[gmail-pop3]
client = yes
accept = 127.0.0.1:110
connect = pop.gmail.com:995
verifyChain = yes
CApath = @sysconfdir/ssl/certs
checkHost = pop.gmail.com
OCSPaia = yes

[gmail-imap]
client = yes
accept = 127.0.0.1:143
connect = imap.gmail.com:993
verifyChain = yes
CApath = @sysconfdir/ssl/certs
checkHost = imap.gmail.com
OCSPaia = yes

[gmail-smtp]
client = yes
accept = 127.0.0.1:25
connect = smtp.gmail.com:465
verifyChain = yes
CApath = @sysconfdir/ssl/certs
checkHost = smtp.gmail.com
OCSPaia = yes

; Encrypted HTTP proxy authenticated with a client certificate
; located in a cryptographic token
;[example-pkcs11]
;client = yes
;accept = 127.0.0.1:8080
;connect = example.com:8443
;engineId = pkcs11
;cert = pkcs11:token=MyToken;object=MyCert
;key = pkcs11:token=MyToken;object=MyKey

; ***************************************** Example TLS server mode services

;[pop3s]
;accept  = 995
;connect = 110
;cert = /etc/stunnel/stunnel.pem

;[imaps]
;accept  = 993
;connect = 143
;cert = /etc/stunnel/stunnel.pem

;[ssmtp]
;accept  = 465
;connect = 25
;cert = /etc/stunnel/stunnel.pem

; TLS front-end to a web server
;[https]
;accept  = 443
;connect = 80
;cert = /etc/stunnel/stunnel.pem
; "TIMEOUTclose = 0" is a workaround for a design flaw in Microsoft SChannel
; Microsoft implementations do not use TLS close-notify alert and thus they
; are vulnerable to truncation attacks
;TIMEOUTclose = 0

; Remote shell protected with PSK-authenticated TLS
; Create "/etc/stunnel/secrets.txt" containing IDENTITY:KEY pairs
;[shell]
;accept = 1337
;exec = /bin/sh
;execArgs = sh -i
;ciphers = PSK
;PSKsecrets = /etc/stunnel/secrets.txt

; Non-standard MySQL-over-TLS encapsulation connecting the Unix socket
;[mysql]
;cert = /etc/stunnel/stunnel.pem
;accept = 3307
;connect = /run/mysqld/mysqld.sock

; vim:ft=dosini

ドキュメントもExampleを見てもよいですし、こちらも参考になりそうですね。

stunnelの設定を行う。

最初にstunnel4サービスの状態を確認しています。

$ sudo systemctl status stunnel4
● stunnel4.service - LSB: Start or stop stunnel 4.x (TLS tunnel for network daemons)
   Loaded: loaded (/etc/init.d/stunnel4; generated)
   Active: active (exited) since Tue 2020-04-07 14:59:47 UTC; 6min ago
     Docs: man:systemd-sysv-generator(8)
    Tasks: 0 (limit: 2317)
   CGroup: /system.slice/stunnel4.service

Apr 07 14:59:47 ubuntu1804.localdomain systemd[1]: Starting LSB: Start or stop stunnel 4.x (TLS tunnel for network daemons)...
Apr 07 14:59:47 ubuntu1804.localdomain stunnel4[1931]: TLS tunnels disabled, see /etc/default/stunnel4
Apr 07 14:59:47 ubuntu1804.localdomain systemd[1]: Started LSB: Start or stop stunnel 4.x (TLS tunnel for network daemons).

よくよく見ると、init.dですね。

   Loaded: loaded (/etc/init.d/stunnel4; generated)

インストール直後は停止しています。

ここで、環境変数の設定を行います。
/etc/default/stunnel4

# /etc/default/stunnel
# Julien LEMOINE <speedblue@debian.org>
# September 2003

# Change to one to enable stunnel automatic startup
ENABLED=0
FILES="/etc/stunnel/*.conf"
OPTIONS=""

# Change to one to enable ppp restart scripts
PPP_RESTART=0

# Change to enable the setting of limits on the stunnel instances
# For example, to set a large limit on file descriptors (to enable
# more simultaneous client connections), set RLIMITS="-n 4096"
# More than one resource limit may be modified at the same time,
# e.g. RLIMITS="-n 4096 -d unlimited"
RLIMITS=""

「ENABLED」を1に変更。

#ENABLED=0
ENABLED=1

起動。「restart」にしましたけど。

$ sudo systemctl restart stunnel4

これで、stunnelが利用できるようになります。

では、設定ファイルを書いていきましょう。Exampleを参考にします。

あと、こちらも参考にしましょう。

stunnel TLS Proxy

非SSL/TLSなクライアントを、SSL/TLSを使用するサーバーに対応させる

お題として、https://www.google.com/へのHTTPSアクセスをstunnelに肩代わりしてもらい、クライアントからはHTTPでアクセスするように
してみましょう。

stunnelは「/etc/stunnel/*.conf」というルールで設定ファイルを読み込むようになっているので、ミニマムとして以下の設定を記述。
※今回は、グローバルな設定はパスします
/etc/stunnel/stunnel.conf

[google-web]
client = yes
accept = 127.0.0.1:80
connect = www.google.com:443
verifyChain = yes
CApath = /etc/ssl/certs
checkHost = www.google.com
OCSPaia = yes

それぞれ、以下の意味になります。

  • client … yesの場合、クライアントに対してSSL/TLS通信を肩代わりする
  • accept … リッスンするアドレス、ポート
  • connect … 接続先のアドレス、ポート
  • verifyChain … ルートCAから始まる証明書チェーンを検証する
  • CApath … verifyChainまたはverifyPeerオプションが使用する証明書が格納されたディレクトリ
  • checkHost … 証明書検証時のホスト名
  • OCSPaia … AIA OCSPレスポンダーを使用する

今回は、実はclient、accept、connectの3つだけで動作するのですが。

acceptは、バインド先のIPアドレスを指定できます。やるかどうかはさておき、「0.0.0.0」で全ネットワークインターフェースにも
バインド可能です。

accept = 127.0.0.1:80
;accept = 0.0.0.0:80

ところで、Exampleなどにある「@sysconfdir」というのは、今回の環境では以下のディレクトリを指しています。

/etc/ssl/certs

では、stunnelを再起動。

$ sudo systemctl restart stunnel4

起動状態を確認。

$ sudo systemctl status stunnel4
● stunnel4.service - LSB: Start or stop stunnel 4.x (TLS tunnel for network daemons)
   Loaded: loaded (/etc/init.d/stunnel4; generated)
   Active: active (running) since Tue 2020-04-07 15:19:17 UTC; 2s ago
     Docs: man:systemd-sysv-generator(8)
  Process: 2624 ExecStop=/etc/init.d/stunnel4 stop (code=exited, status=0/SUCCESS)
  Process: 2646 ExecStart=/etc/init.d/stunnel4 start (code=exited, status=0/SUCCESS)
    Tasks: 2 (limit: 2317)
   CGroup: /system.slice/stunnel4.service
           └─2673 /usr/bin/stunnel4 /etc/stunnel/stunnel.conf

Apr 07 15:19:17 ubuntu1804.localdomain systemd[1]: Starting LSB: Start or stop stunnel 4.x (TLS tunnel for network daemons)...
Apr 07 15:19:17 ubuntu1804.localdomain systemd[1]: Started LSB: Start or stop stunnel 4.x (TLS tunnel for network daemons).
Apr 07 15:19:17 ubuntu1804.localdomain stunnel4[2646]: Starting TLS tunnels: /etc/stunnel/stunnel.conf: started (no pid=pidfile specified!)

telnetで確認してみましょう。

$ curl telnet://localhost:80
GET / HTTP/1.1
Host: www.google.com


HTTP/1.1 200 OK
Date: Wed, 08 Apr 2020 14:51:51 GMT
Expires: -1
Cache-Control: private, max-age=0
Content-Type: text/html; charset=ISO-8859-1
P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
Server: gws
X-XSS-Protection: 0
X-Frame-Options: SAMEORIGIN
Set-Cookie: 1P_JAR=2020-04-08-14; expires=Fri, 08-May-2020 14:51:51 GMT; path=/; domain=.google.com; Secure
Set-Cookie: NID=201=K1ffuoUljb0TF4f0eDIAqPm5Cdym1Do5K_AAlqUi69BSW6rf_QmRQIneW6vIMX03PYX3bEnDCBnN6_nciHcgHfNJBSBAmynzVhNzy2nzCJ2HpNOHMliECZmDmNueQiB_mGFEi7pg6C02nQen3vJMmny4vLxRvNAsvCa9R4CBZpI; expires=Thu, 08-Oct-2020 14:51:51 GMT; path=/; domain=.google.com; HttpOnly
Alt-Svc: quic=":443"; ma=2592000; v="46,43",h3-Q050=":443"; ma=2592000,h3-Q049=":443"; ma=2592000,h3-Q048=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,h3-T050=":443"; ma=2592000
Accept-Ranges: none
Vary: Accept-Encoding
Transfer-Encoding: chunked

64e4
<!doctype html><html itemscope="" itemtype="http://schema.org/WebPage" lang="ja"><head><meta content="&#19990;&#30028;&#20013;&#12398;&#12354;&#12425;&#12422;&#12427;&#24773;&#22577;&#12434;&#26908;&#32034;&#12377;&#12427;&#12383;&#12417;&#12398;&#12484;&#12540;&#12523;&#12434;&#25552;&#20379;&#12375;&#12390;&#12356;&#12414;&#12377;&#12290;&#12373;&#12414;&#12374;&#12414;&#12394;&#26908;&#32034;&#27231;&#33021;&#12434;&#27963;&#29992;&#12375;&#12390;&#12289;&#12362;&#25506;&#12375;&#12398;&#24773;&#22577;&#12434;&#35211;&#12388;&#12369;&#12390;&#12367;&#12384;&#12373;&#12356;&#12290;" name="description"><meta content="noodp" name="robots"><meta content="text/html; charset=UTF-8" http-equiv="Content-Type"><meta content="/logos/doodles/2020/thank-you-emergency-services-workers-6753651837108755-law.gif" itemprop="image"><meta content="&#25937;&#24613;&#38538;&#21729;&#12398;&#12415;&#12394;&#12373;&#12435;&#12289;&#12354;&#12426;&#12364;&#12392;&#12358;&#12290;" property="twitter:title"><meta content="&#25937;&#24613;&#38538;&#21729;&#12398;&#12415;&#12394;&#12373;&#12435;&#12289;&#12354;&#12426;&#12364;&#12392;&#12358;&#12290;#GoogleDoodle" property="twitter:description"><meta content="&#25937;&#24613;&#38538;&#21729;&#12398;&#12415;&#12394;&#12373;&#12435;&#12289;&#12354;&#12426;&#12364;&#12392;&#12358;&#12290;#GoogleDoodle" property="og:description"><meta content="summary_large_image" property="twitter:card"><meta content="@GoogleDoodles" property="twitter:site"><meta content="https://www.google.com/logos/doodles/2020/thank-you-emergency-services-workers-6753651837108755-2xa.gif" property="twitter:image"><meta content="https://www.google.com/logos/doodles/2020/thank-you-emergency-services-workers-6753651837108755-2xa.gif" property="og:image"><meta content="1288" property="og:image:width"><meta content="460" property="og:image:height"><meta content="https://www.google.com/logos/doodles/2020/thank-you-emergency-services-workers-6753651837108755-2xa.gif" property="og:url"><meta content="video.other" property="og:type"><title>Google</title><script nonce="FqdOdIcTeadLpYiSohseeQ==">(function()

〜省略〜

function _F_installCss(c){}
(function(){google.spjs=false;google.snet=true;google.em=[];google.emw=false;google.pdt=0;google.uwp=false;})();(function(){var pmc='{\x22d\x22:{},\x22sb_he\x22:{\x22agen\x22:true,\x22cgen\x22:true,\x22client\x22:\x22heirloom-hp\x22,\x22dh\x22:true,\x22dhqt\x22:true,\x22ds\x22:\x22\x22,\x22ffql\x22:\x22ja\x22,\x22fl\x22:true,\x22host\x22:\x22google.com\x22,\x22isbh\x22:28,\x22jsonp\x22:true,\x22msgs\x22:{\x22cibl\x22:\x22&#26908;&#32034;&#12434;&#12463;&#12522;&#12450;\x22,\x22dym\x22:\x22&#12418;&#12375;&#12363;&#12375;&#12390;:\x22,\x22lcky\x22:\x22I\\u0026#39;m Feeling Lucky\x22,\x22lml\x22:\x22&#35443;&#32048;\x22,\x22oskt\x22:\x22&#20837;&#21147;&#12484;&#12540;&#12523;\x22,\x22psrc\x22:\x22&#12371;&#12398;&#26908;&#32034;&#12461;&#12540;&#12527;&#12540;&#12489;&#12399;\\u003Ca href\x3d\\\x22/history\\\x22\\u003E&#12454;&#12455;&#12502;&#23653;&#27508;\\u003C/a\\u003E&#12363;&#12425;&#21066;&#38500;&#12373;&#12428;&#12414;&#12375;&#12383;\x22,\x22psrl\x22:\x22&#21066;&#38500;\x22,\x22sbit\x22:\x22&#30011;&#20687;&#12391;&#26908;&#32034;\x22,\x22srch\x22:\x22Google &#26908;&#32034;\x22},\x22ovr\x22:{},\x22pq\x22:\x22\x22,\x22refpd\x22:true,\x22refspre\x22:true,\x22rfs\x22:[],\x22sbpl\x22:16,\x22sbpr\x22:16,\x22scd\x22:10,\x22stok\x22:\x224H1gy_x0YYHGa1RJ1NO8X9IIrZs\x22,\x22uhde\x22:false}}';google.pmc=JSON.parse(pmc);})();</script>        </body></html>
0

HTTPでアクセスしましたが、結果が返ってきました。

$ curl telnet://localhost:80
GET / HTTP/1.1
Host: www.google.com

tcpdumpで見ると、ちゃんと443で通信しに行っていることが確認できます。

$ sudo tcpdump -i any -n tcp port 443
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on any, link-type LINUX_SLL (Linux cooked), capture size 262144 bytes
14:54:19.139300 IP 192.168.121.216.38072 > 216.58.221.228.443: Flags [S], seq 1433332297, win 64240, options [mss 1460,sackOK,TS val 4078539507 ecr 0,nop,wscale 7], length 0
14:54:19.198305 IP 216.58.221.228.443 > 192.168.121.216.38072: Flags [S.], seq 929050666, ack 1433332298, win 60192, options [mss 1380,sackOK,TS val 1149326253 ecr 4078539507,nop,wscale 8], length 0
14:54:19.198371 IP 192.168.121.216.38072 > 216.58.221.228.443: Flags [.], ack 1, win 502, options [nop,nop,TS val 4078539566 ecr 1149326253], length 0
14:54:19.198853 IP 192.168.121.216.38072 > 216.58.221.228.443: Flags [P.], seq 1:347, ack 1, win 502, options [nop,nop,TS val 4078539567 ecr 1149326253], length 346
14:54:19.256534 IP 216.58.221.228.443 > 192.168.121.216.38072: Flags [.], ack 347, win 240, options [nop,nop,TS val 1149326312 ecr 4078539567], length 0
14:54:19.283249 IP 216.58.221.228.443 > 192.168.121.216.38072: Flags [P.], seq 1:2633, ack 347, win 240, options [nop,nop,TS val 1149326338 ecr 4078539567], length 2632
14:54:19.286097 IP 192.168.121.216.38072 > 216.58.221.228.443: Flags [.], ack 2633, win 495, options [nop,nop,TS val 4078539651 ecr 1149326338], length 0

ssでも見ておきましょう。

$ sudo ss -tnp | grep 443
ESTAB  0        0            192.168.121.216:38112       216.58.221.228:443      users:(("stunnel4",pid=3116,fd=10)) 

大丈夫そうですね。

非SSL/TLSなサーバーを、SSL/TLSに対応させる

次に、非SSL/TLSなサーバーを、SSL/TLS通信越しに使えるようにしてみましょう。

Pythonで、簡易なHTTPサーバーを立てます。

$ echo 'Hello World!!' > hello.txt
$ python3 -m http.server
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...

確認。

$ curl localhost:8000/hello.txt
Hello World!!

このHTTPサーバーを、stunnelを使ってSSL/TLSに対応させてみます。

証明書が必要になるので、今回は自己署名証明書を作成しましょう。

秘密鍵の作成。

$ openssl genrsa -aes256 -out sample.key 2048

パスワードの解除。

$ openssl rsa -in sample.key -out sample.key

CSRの作成。

$ openssl req -new -key sample.key -out sample.csr

署名。

$ openssl x509 -req -days 365 -in sample.csr -signkey sample.key -out sample.crt

では、このキーを使うように、/etc/stunnel/stunnel.confを修正します。

[python-http]
accept  = 8443
connect = 8000
cert = /path/to/sample.crt
key = /path/to/sample.key

accept、connectの意味は、クライアントとして動作する時と同じですね。今回はポートのみ指定で、バックエンドのPython HTTPサーバーの
前段に8443ポートで構えます。

cert、keyは自己署名証明書、秘密鍵をそれぞれ指定します。

stunnelを再起動。

$ sudo systemctl restart stunnel4

自己署名証明書なので、curlの「--cacert」オプションに指定して実行。

$ curl --cacert sample.crt https://localhost:8443/hello.txt
Hello World!!

OKですね。

これで、stunnelを使ったSSL/TLSトンネリングの初歩的な内容が確認できました、と。