導入:インターネットに接しないネットワークとは

サーバーサイドのシステム運用を扱っていると、ネットワーク的にインターネットと切り離されたセキュアなネットワークに設置したサーバーを構築することが良くあります。代表的なものがDB(データベース)サーバーなどです。

DBにはシステム上で最もセキュアなデータが格納されますので、外部からの攻撃を受ける可能性の高いインターネットに接したネットワークには設置しません。システムで必要なデータをDBから取り出す場合、インターネットから直接DBにsqlクエリを発行するということは基本的に推奨されず、インターネットからのアクセスが許可されたWebサーバー/アプリケーションサーバーが代行して必要なデータ取得のリクエストを受付け、安全性の高いLAN内でDB上のデータのやり取りを行うというデザインパターンが王道的なインフラ設計と思われます。(インターネットから直接DBにアクセスするNoSQL系DBを使ったパターンもありますが、このケースでは重要なデータはDBに格納しないでしょう。)

インターネットとの通信ができないサーバー構築・運用時の課題

こうしたセキュアなネットワーク上に設置されたインターネットとの通信ができないサーバーを構築する際にまず課題となるのが、yumaptなどを利用した各種パッケージの導入・更新が行えないことです。MySQL DBサーバーを構築する予定だったとして、yum経由での肝心のMySQLのインストールすら行うことができません。

また、運用していく際にcronなどで時刻起動のスクリプトを実行しようとしても、ntpによる時刻同期が行われませんので気づいたら定時実行スクリプトの開始時刻が想定より微妙にズレてしまっていた・・・。なんてことも考えられます。

セキュリティ上の理由もあり、そもそもLAN内のサーバーからの通信さえ行うことができれば良いという想定ではあったので、外部からセキュア(プライベート)ネットワーク方向への通信は一切認めないにしても、せめて、内部から外部(プライベートネットワークからインターネット)方向の通信は限定的に行いたいものです。

ということで、インターネットに接続できないネットワークに設置したサーバーで、インターネット接続が必要な運用コマンドを実行する方法についてご説明したいと思います。

プライベートネットワークからインターネットに接続するための考え方

まず、今回インターネット疎通の無いプライベートネットワークからインターネットへの通信を行うために、前提としてインターネットに接続しているパブリックネットワーク設置のサーバーを踏み台サーバーとして利用できる必要があります。

DBサーバーなどから直接インターネットに接続するのではなく、インターネットに接したサーバーをプロキシとして挟んだ上で、インターネットとの通信を行うという考え方です。具体的な手法については大きく以下の2つの方法が挙げられます。

HTTPプロキシ

インターネットに接した踏み台サーバーに、SquidなどのHTTPプロキシ環境を構築します。HTTP通信を行う大抵のプロダクトの場合はHTTPプロキシを経由した通信を行うためのオプションが用意されていますので、それらのオプションを利用してインターネットとの通信を行います。

たとえば、curlコマンドの場合は以下のようにしてプロキシサーバーを経由して目的のサイトから情報を取得します。

$ curl https://www.mokutekino-site.com -x http://proxy.jibunno-server.com:8080 

SSHダイナミックポートフォワーディング(SOCKSプロキシ)

プライベートネットワーク設置サーバーからHTTPプロキシ経由で通信する場合、インターネット接続可能なネットワーク上でHTTPプロキシサーバーを構築しておく必要があり、ひと手間かかってしまいます。そこでお手軽にプロキシ環境を利用する方法としてSSHポートフォワード(ダイナミックポートフォワーディング)を利用した方法があります。

SSHダイナミックポートフォワーディングの詳細説明は割愛しますが、SSHコマンドを利用してSOCKSプロキシ環境を用意することができます。サーバー運用ではSSH環境は当然用意されているのが一般的なので、インターネット接続可能なネットワーク上のサーバーであればどのサーバーでも利用することができ、導入の敷居が非常に低いのが嬉しい点です。

SSHポートフォワーディングによるSOCKSプロキシ環境の準備

細かい点は「SSH ポートフォワーディング」「SSH ダイナミックポートフォワード」などで検索して頂ければと思いますが、実際の実行手順のみご紹介します。プライベートネットワーク設置サーバー側で準備を行います。

プライベートネットワーク設置サーバー側で、自身のlocalhost:1080をSSHダイナミックポートフォワードによってSOCKSプロキシ化します。このプライベートサーバーローカルのSOCKSプロキシを経由した通信はSSH接続先であるパブリックサーバーからの通信として扱われますので、プライベートサーバーからインターネットへの通信が可能になります。

ssh -v -D 1080 user@public-server

インターネットに接続していないサーバーで各種運用コマンドを利用する

初期構築時のパッケージ類のインストールで必要となるcurlyum、時刻同期に使うntpについて、踏み台サーバー経由の実行方法をご説明します。

curl

curlコマンドをSOCKSプロキシ経由で利用するには--proxyオプションにSSHダイナミックフォワードで受付ホスト・ポートとして設定した宛先を指定します。先の例の場合localhost:1080になります。

$ curl http://ifconfig.io --proxy 'socks5h://localhost:1080'

SOCKSプロキシを利用するためのURLスキームにはsocks5socks5hを設定できますが、socks5はcurlのリクエスト先の名前解決をローカル側(プライベートネットワーク設置サーバー)で行い、socks5hはSOCKSプロキシ側(パブリックネットワーク設置サーバー)で行うという違いがあります。今回のケースではプロキシ側で名前解決を行うsocks5hが良さそうです。

yum

yumコマンドをSOCKSプロキシ経由で利用するには、以下のように環境変数ALL_PROXYにプロキシを指定します。下記の例のようにyumコマンドの前に環境変数を指定すると、コマンドの実行時のみに限定して環境変数の値を利用できるので便利です。

$ ALL_PROXY=socks5h://localhost:1080 yum install awscli

ALL_PROXY以外にも、HTTP_PROXYHTTPS_PROXYFTP_PROXYなど、より対象のプロトコルを絞って指定を行うこともできるようです。

ntp

インターネット接続できない環境ではntpによる時刻同期ができません。代替案としてNICTが提供しているhttpの時刻同期サービスを利用して時刻補正を行うという方法があります。ntpでは時刻同期問い合わせのリクエストとレスポンスの時間も考慮した時刻同期を行っっているので、それと比較するとこの方法では若干の誤差が生まれる可能性はありますが、実用上のほとんどの用途では問題にならないと思われます。

# ntp代替の日次時刻補正
* 12 * * * date --set @"$(curl -Ss https://ntp-a1.nict.go.jp/cgi-bin/jst --proxy 'socks5h://localhost:1080' | sed -n 4p | cut -d. -f1)"

さいごに

セキュリティは最優先の重要事項ですが、それと引き換えに運用上の利便性が犠牲になることもあります。なるべく手間をかけずに工夫をしながら、セキュリティと運用効率を両立できるようにしていきたいですね。本記事が参考になればと思います!

Pocket