「無理せんと、リバースプロキシ(nginx)使えば?」

ここのところ、Websocket通信のセキュア化のコーディングで、かなり嵌ってしまっている私に、師匠のSさんから、

GoのサーバーでWSSやHTTPSのプロトコルを直接扱うことをせず、かわりに、リバースプロキシサーバーでHTTPSやWSSプロトコルを扱い、GoのサーバーではHTTPやWSプロトコルを扱うように構成を変更する、というのはいかがでしょうか?

とのご提言を頂きました。

あ、そりゃそうだ ―― と思いました。私は、Websocket通信のアタックさえ回避できれば、なんでも良いわけでして、何がなんでもwssをやりたい訳ではありません。

インターネットの中でセキュアが担保できれば、内部システムなんぞスカスカでも構いません(そもそも、システム内部に侵入されたら、その時点で"システムとしては、"The END"です)。

とりあえずPC(Windows10 Box)の中でnginx(Webサーバ)を立てようと思いました。

いや、驚くほど簡単。「インストールから実行まで10秒を切ったWebサーバ」というのは生まれて始めてかもしれません。

http://nginx.org/en/download.html から、Stable versionをダウンロードして、

解凍して、"nginx.exe"を叩くだけ。(私の場合、ダウンロードの場所は、C:\の直下にしておきました)

これで、ブラウザから、http://localhost と入力して稼動確認

ちなみに、トップページは、htmlフォルダの中にあるので、適当に変更すれば、普通のWebサーバにもなります。

それはさておき。

今回の場合、クライアントからnginxまでのセキュアが確保できれば足り、その後ろは、普通のwebsocketの通信ができれば良い、とします(golangでwebsocketのセキュア通信って、もう気が狂いそうになるほど面倒)。

Sさんからは、nginx.confを送付して貰ったのですが、まあ、当然ですが、一発では動きませんでしたので、先ず、セキュア通信なしのリバースプロキシを動かしてみます。

nginxでポート8000で受けて、それを、エコーサーバ(8080)に転送するだけのものですが、NginxのリバースプロキシでWebソケットを通す際の設定 とSさんから送って頂いた設定ファイルを参考にさせて頂いて、以下のようにconf/nginx.confの内容を書き換えました。

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    map $http_upgrade $connection_upgrade { 
        default upgrade;    
        ''      close;
    } 

    server {
        listen 8000;
        server_name localhost;

        location / {
            proxy_pass   http://localhost:8080;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Frame-Options SAMEORIGIN;

            proxy_read_timeout 86400;

        }
    }
}

ちなみにエコーサーバは、以下のものをそのまま使っています。

でもって、こんな感じでちゃんと動いています。もちろんlocalhost:8080でも動きます。

ーーーー

では、ここから、ngnixまでのセキュア化の検討を開始します。

ここでしばしスタック中。TLSがちゃんと動いているのか不安になったので、普通のWebでできるか、以下のようなnginx.confを使って、index.htmlで試してみました。

worker_processes  1;

events {
    worker_connections  1024;
}

http {
    server {
        listen       443;
        server_name  localhost;

        ssl                  on;
        ssl_certificate      algo.crt; # https://wp.kobore.net/2020/09/post-1124/で作ったもの
        ssl_certificate_key  algo.key; # 同上

        location / {
            root html;
            index index.html index.htm;
        }
    }
}  

https://localhost/ でも、http://localhost/ でも動いていますので、algo.crt, algo.keyについては(多分)大丈夫だと思われます。

では、元の問題に戻りましょう。(4時間格闘)

どうやら、外向けをセキュアにするには、nginx.confを3箇所だけ変更追加すれば良い、と記載されていたので、以下のようなnginx.confに改造した “#change"と、"#add"のところだけ。

#user  nobody;
worker_processes  1;

#error_log  logs/error.log;
#error_log  logs/error.log  notice;
#error_log  logs/error.log  info;
#pid        logs/nginx.pid;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    #log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
    #                  '$status $body_bytes_sent "$http_referer" '
    #                  '"$http_user_agent" "$http_x_forwarded_for"';

    #access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    map $http_upgrade $connection_upgrade { 
        default upgrade;    
        ''      close;
    } 

    server {
        #listen 8000;
        listen 443 ssl;  # change

        server_name localhost;

        ssl_certificate algo.crt; #add
        ssl_certificate_key algo.key; #add



        location / {
            proxy_pass   http://localhost:8080;

            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
            proxy_set_header Host $http_host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
            proxy_set_header X-Frame-Options SAMEORIGIN;

            proxy_read_timeout 86400;

        }
    }
}

ところが、https://localhost を実施しても、サーバ側から、以下のエラーがでてきて、どうにもならない。

upgrade:websocket: the client is not using the websocket protocol: 'websocket’ token not found in 'Upgrade’ header

Visual Studio Codeにかけて、サーバでトレースしても読めないし(htmlの部分だから無理ない)、正直もうどうして良いか分からなくなってきたとき、「あ、chromoのディベロッパーツールを使えばいいか」と気が付きました。

そんでもって、

これで気がつきました。ここって"ws:"ではなくて、"wss:"にしないとアカンのじゃないかな、と。

で、ここを修正して、ECHOサーバを立ち上げ直したら、無事稼動することが確認できました。

まあ、まだまだ課題山積ですが、とりあえず、家(システム)の外はセキュアにする目処が立ちましたので、wssの通信プログラムで苦労するのは「やめる方向」で決定しました。

nginxをリバースプロキシにして、そこまではガチガチにしておき、家の中はユルユルに作るで、進めていくことにします。

以上

2020/09,江端さんの技術メモ

Posted by ebata