nginxだけで、HTTPSとsshを同時に443番ポートで待ち受ける

はじめに

どんなにひどいネットワーク環境であっても、 せめて22番ポートくらいは通るようにしておいて欲しいものだが、 そういう訳にもいかない。 通るのは80と443のみである。

sshでログインする先のサーバーが、httpdを実行していなければ、80番ポートでも443番ポート でも良いので、sshdが待ち受けるように設定してしまえば、そんな環境からでも sshでログインして作業できるのだが、httpdを実行しているサーバーにログイン したいニーズは存在する。 そのサーバーがHTTPとHTTPSで配信しているファイルを変更したいからsshでログイン したいのであって、httpdを止める訳にはいかないのである。

そのサーバーのhttpdには、nginx-1.19.6を利用している。 HTTPSとsshを同時に待ち受けさせるには、sslhを使うのが一般的な解ではないかと思う。 pkgsrcでも、net/sslhとして収録されている。

しかし、nginxほどの拡張性のあるウェブサーバーであれば、それ自身でそれくらいの 多重化はできるのではないかと考えた。 検索してみると、nginxの公式blogに、 Running SSL and Non-SSL Protocols over the Same Port with NGINX 1.15.2 という記事があるのに気付いた。 $ssl_preread_protocolと言うのを使うと、HTTPSとsshを同時に待ち受けて それぞれ適切な方にリダイレクトさせることが可能だという。

実際に試してみた。

nginxの再インストール

pkgsrcを使ってnginx-1.19.6をインストールしている。 これまでは、標準のビルドオプションに加えて、HTTP/2のサポートを有効化していた。 これに加えて、$ssl_preread_protocolを有効にするには、 まずは/etc/mk.confに以下のように書いておく。

# vi /etc/mk.conf
(snip)
PKG_OPTIONS.nginx=http2 stream-ssl-preread
(snip)

この上で、nginx-1.19.6をインストールし直す。 以下のように実行すれば良い。/usr/pkg/etc/nginxディレクトリー内の 設定ファイルの変更は維持される。

# cd /usr/pkgsrc/www/nginx-devel
# make update

これで、nginxバイナリーは準備できた。 次に設定ファイルを変更していく。 方針は以下のようである。

  1. 443番ポートでHTTPSとsshを待ち受ける。
  2. 443番ポートに接続されたログを取得する。

これを実現するために、/usr/pkg/etc/nginx/nginx.confは以下のように 設定した。コメントを入れつつnginx.conf全体を掲載しておく。

$ cat /usr/pkg/etc/nginx/nginx.conf
user   nginx  nginx;
worker_processes  1;

events {
    # After increasing this value You probably should increase limit
    # of file descriptors (for example in start_precmd in startup script)
    worker_connections  1024;
}

# Share 443 port with TLS and ssh
# https://www.nginx.com/blog/running-non-ssl-protocols-over-ssl-port-nginx-1-15-2/
stream {
	upstream ssh {
		server localhost:22; # sshdはlocalhostの22番ポートで待機しているので、それにリダイレクトする。
	}

	upstream webtls {
		server localhost:8443; # httpdはHTTPSをlocalhostの8443番ポートで待機しているので、それにリダイレクトする。
	}

	map $ssl_preread_protocol $upstream {
		default webtls; # nginx blogではHTTPS側でTLSのClientHelloを指定していたが、
				# どのようなClientHelloが来るか分からないので、既定値としてはHTTPSにしている。
		"" ssh; # ClientHelloがない場合にはsshとして扱う。
	}

	log_format stream_log  '$remote_addr - [$time_local] '
		'$status'; # 最小限の接続ログを出力する。
	access_log /var/log/nginx/access_stream.log stream_log; # ログの出力先を設定する。

	# 実際に443番ポートの多重化を設定している。
	server {
		listen 443;

		proxy_pass $upstream;
		ssl_preread on;
	}
}

http {
    include       /usr/pkg/etc/nginx/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  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;

    server {
        listen       80;
        server_name  www.example.org;

        #charset koi8-r;

        #access_log  /var/log/nginx/host.access.log  main;

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

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   share/examples/nginx/html;
        }

	location ~ ^/~(.+?)(/.*)?$ {
		alias /home/$1/public_html$2;
	}

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        /usr/pkg/etc/nginx/fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }


    # another virtual host using mix of IP-, name-, and port-based configuration
    #
    #server {
    #    listen       8000;
    #    listen       somename:8080;
    #    server_name  somename  alias  another.alias;

    #    location / {
    #        root   share/examples/nginx/html;
    #        index  index.html index.htm;
    #    }
    #}


    # HTTPS server
    #
    server {
        listen       8443 http2 ssl; # 8443番ポートで待機させる。
        server_name  www.example.org;

        ssl_certificate      www.example.org.cer;
        ssl_certificate_key  www.example.org.key;

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

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

	location /pub {
		alias /usr/htdocs/pub;
		autoindex on;
		location ~*.diff {
			add_header Content-Type text/plain;
		}
	}

	location ~ ^/~(.+?)(/.*)?$ {
		alias /home/$1/public_html$2;
	}
    }

}

以下のように443番ポート経由でsshログインできるか確認する。 同時にHTTPSでもページを表示できるか確認する。

# /etc/rc.d/nginx restart
$ ssh www.example.org -p 443
$ w3m https://www.example.org/

0 件のコメント:

コメントを投稿

「星宝転生ジュエルセイバー」の素材を利用する場合について考えてみた

「星宝転生ジュエルセイバー」というスマートフォン向けのゲームがあって、そのサービス終了時に 利用規約 にあるような条件で、 二次利用ができることになっている。 とは言っても、 jewel-s.jp というドメインは、既に元の所有者である株式会社ブリリアントサービスの...