一般的に Jupyter Notebook はローカルの環境にインストールして使うことが多い。 ただ、ローカルの環境は計算資源が乏しい場合もある。 そんなときは IaaS などリモートにあるサーバで Jupyter Notebook を使いたい場面が存在する。 ただ、セキュリティのことを考えると Jupyter Notebook の Web UI をインターネットに晒したくはない。
そこで、今回は SSH Port Forwarding を使って Web UI をインターネットに晒すことなく使う方法について書く。 このやり方ならリモートサーバに SSH でログインしたユーザだけが Jupyter Notebook を使えるようになる。 また、Web UI との通信も SSH 経由になるので HTTP over SSL/TLS (HTTPS) を使わなくても盗聴のリスクを下げられる。
リモートサーバを想定した環境は次の通り。 話を単純にするために環境は Vagrant で作ってある。
vagrant $ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=18.04 DISTRIB_CODENAME=bionic DISTRIB_DESCRIPTION="Ubuntu 18.04.1 LTS" vagrant $ uname -r 4.15.0-29-generic
そこに接続するクライアントの環境は次の通り。
client $ sw_vers ProductName: Mac OS X ProductVersion: 10.13.6 BuildVersion: 17G65 client $ ssh -V OpenSSH_7.6p1, LibreSSL 2.6.2
必要なパッケージをインストールする
ここからは、すでにリモートの Ubuntu マシンに SSH でログインしている前提で話を進める。
まずは必要なパッケージをインストールする。 ログインするたびに Jupyter Notebook を起動するコマンドを入力するのも面倒なので、最終的に Supervisord でデーモン化することにした。
vagrant $ sudo apt-get update vagrant $ sudo apt-get -y install jupyter-notebook supervisor
今回は OS のパッケージ管理システム経由でインストールしてるけど pip を使うとかはお好みで。
まずはサクッと試す
ひとまず手っ取り早く今回やることの本質を示す。
最初にリモートサーバ上で Jupyter Notebook を起動する。
これで TCP/8888
で Jupyter Notebook の Web UI が動く。
vagrant $ jupyter notebook
ターミナルに Web UI のアクセストークンが表示されるのでメモしておこう。
続いて、クライアントの別のターミナルを開いて、改めてリモートサーバに SSH でログインする。
このとき SSH Port Forwarding を使って、リモートサーバの TCP/8888
をローカルホストのポートにマッピングする。
client $ ssh -L 8888:localhost:8888 <username>@<remotehost>
今回は Vagrant の環境を使っているのでこんな感じ。
恒久的に設定を入れたいなら Vagrantfile
を編集する。
client $ vagrant ssh-config > ssh.config client $ ssh -L 8888:localhost:8888 -F ssh.config default
あとは、クライアントのブラウザでローカルホストにマッピングしたポート番号を開くだけ。
client $ open http://localhost:8888
すると、Jupyter Notebook の Web UI でアクセストークンを入力する画面が表示される。 先ほど Jupyter Notebook を起動するときにターミナルに表示されたトークンを入力しよう。
これで、いつもの見慣れた Web UI が表示されるはず。 あとは使うだけ。
以上で、今回やることの本質は示せた。
ただ、上記の操作は毎回やるには結構めんどくさいしセキュリティをあまり考慮していない。 そこで、ここからは運用をできるだけ楽に、そしてセキュアな環境を手に入れるべく手順を記載していく。
以降の手順を試すときは、一旦先ほど起動した Jupyter Notebook は停止しておこう。
アクセス制御をかける
リモートサーバを想定しているので、念のため必要なポート以外はファイアウォールを使って閉じておく。
SSH に使うポートだけを残して、それ以外は全て閉じる。 SSH に使うポート番号を 22 以外にしているときは、適宜読み替える感じで。
vagrant $ sudo ufw allow 22 vagrant $ sudo ufw default DENY vagrant $ yes | sudo ufw enable vagrant $ sudo ufw status Status: active To Action From -- ------ ---- 22 ALLOW Anywhere 22 (v6) ALLOW Anywhere (v6)
ファイアウォールの設定を変更するときはリモートサーバから追い出されないように注意しよう。
Jupyter Notebook を起動するユーザを追加する
若干好みの問題にも近いけど、念のため Jupyter Notebook を起動する専用のユーザを追加しておく。
vagrant $ sudo useradd -m -s $SHELL jupyter
Jupyter Notebook を設定する
ここからは Jupyter Notebook を設定していく。
まずは先ほど作ったユーザにログインする。
vagrant $ sudo su - jupyter
続いて、設定ファイルを生成する。
jupyter $ jupyter notebook --generate-config Writing default config to: /home/jupyter/.jupyter/jupyter_notebook_config.py
Jupyter Notebook の作業ディレクトリを用意する。
jupyter $ mkdir -p /home/$(whoami)/jupyter-working
設定ファイルを編集する。
jupyter $ sed -i.back \ -e "s:^#c.NotebookApp.token = .*$:c.NotebookApp.token = u'':" \ -e "s:^#c.NotebookApp.ip = .*$:c.NotebookApp.ip = 'localhost':" \ -e "s:^#c.NotebookApp.open_browser = .*$:c.NotebookApp.open_browser = False:" \ -e "s:^#c.NotebookApp.notebook_dir = .*$:c.NotebookApp.notebook_dir = '/home/$(whoami)/jupyter-working':" \ /home/$(whoami)/.jupyter/jupyter_notebook_config.py jupyter $ cat ~/.jupyter/jupyter_notebook_config.py | sed -e "/^#/d" -e "/^$/d" c.NotebookApp.ip = 'localhost' c.NotebookApp.notebook_dir = '/home/jupyter/jupyter-working' c.NotebookApp.open_browser = False c.NotebookApp.token = u''
それぞれの設定の内容や意図としては以下のような感じ。
- c.NotebookApp.ip = 'localhost'
- Jupyter Notebook が Listen するアドレスをループバックアドレスにする
- もしファイアウォールがなくてもインターネットからは Jupyter Notebook の WebUI に疎通がなくなる
- c.NotebookApp.notebook_dir = '/home/jupyter/jupyter-working'
- Jupyter Notebook の作業ディレクトリを専用ユーザのディレクトリにする
- 仮に Web UI が不正アクセスを受けたときにも影響範囲を小さくとどめる (気休め程度)
- c.NotebookApp.open_browser = False
- 起動時にブラウザを開く動作を抑制する
- ローカル環境ではないので起動するときにブラウザを起動する必要はない
- c.NotebookApp.token = u''
- Jupyter Notebook の Web UI にビルトインで備わっている認証を使わない
- 認証は SSH によるログインで担保する場合の設定 (心配なときは後述する共通パスワードなどを設定する)
(オプション) Jupyter Notebook の Web UI に共通パスワードをかける
SSH のログイン以外にも認証をかけたいときは、例えばシンプルなものだと共通パスワードが設定できる。
Jupyter Notebook の Web UI に共通パスワードをかけるには jupyter notebook password
コマンドを実行する。
jupyter $ jupyter notebook password Enter password: Verify password: [NotebookPasswordApp] Wrote hashed password to /home/jupyter/.jupyter/jupyter_notebook_config.json
すると、ソルト付きの暗号化されたパスワードが設定ファイルとしてできる。
jupyter $ cat ~/.jupyter/jupyter_notebook_config.json { "NotebookApp": { "password": "sha1:217911554b0b:f2fa9cd9f336951c335bdaa06a6c16eb6286c192" } }
上記のやり方だとハッシュのアルゴリズムが SHA1
固定っぽい。
もし、より頑丈なものが使いたいときは次のように Python のインタプリタ経由で生成する。
jupyter $ python3 Python 3.6.6 (default, Sep 12 2018, 18:26:19) [GCC 8.0.1 20180414 (experimental) [trunk revision 259383]] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from notebook.auth import passwd >>> passwd('jupyter-server-password', algorithm='sha512') 'sha512:d197670d2987:19bb2eedfc6fde56f1a9fc04d403999c3f03a99af368e528f45ee9a68f01a7c5f07e375bd34ec176d1c66a0f2e8ef7615ebcf9e524a23ace5ab6dd5a930398d4'
生成した暗号化済みパスワードを、次のような形で Jupyter Notebook の設定ファイルに入力すれば良い。
c.NotebookApp.password_required = True c.NotebookApp.password = u'sha512:d197670d2987:19bb2eedfc6fde56f1a9fc04d403999c3f03a99af368e528f45ee9a68f01a7c5f07e375bd34ec176d1c66a0f2e8ef7615ebcf9e524a23ace5ab6dd5a930398d4'
上記の共通パスワード方式を含む Jupyter Notebook の認証周りについては以下の公式ドキュメントを参照のこと。
Running a notebook server — Jupyter Notebook 5.7.0 documentation
Jupyter Notebook を Supervisord 経由で起動する
続いては Jupyter Notebook をデーモン化する設定に入る。
一旦、元の管理者権限をもったユーザに戻る。
jupyter $ exit logout
Supervisord の設定ファイルを用意する。
vagrant $ cat << 'EOF' | sudo tee /etc/supervisor/conf.d/jupyter.conf > /dev/null [program:jupyter] command=jupyter notebook user=jupyter stdout_logfile=/var/log/supervisor/jupyter.log redirect_stderr=true autostart=true autorestart=true EOF
Supervisord を起動する。
vagrant $ sudo systemctl enable supervisor
vagrant $ sudo systemctl reload supervisor
ちゃんと Jupyter Notebook が起動しているかを確認する。
vagrant $ ps auxww | grep [j]upyter jupyter 4689 27.0 5.4 183560 55088 ? S 16:31 0:01 /usr/bin/python3 /usr/bin/jupyter-notebook vagrant $ ss -tlnp | grep :8888 LISTEN 0 128 127.0.0.1:8888 0.0.0.0:* LISTEN 0 128 [::1]:8888 [::]:*
もし、上手く立ち上がっていないときはログから原因を調べよう。
vagrant $ sudo tail /var/log/supervisor/supervisord.log vagrant $ sudo tail /var/log/supervisor/jupyter.log
(オプション) ログインシェルを無効化する
もし Jupyter Notebook 専用に作ったユーザをシェル経由で操作するつもりがなければ、ログインシェルを無効化しておく。
vagrant $ sudo usermod -s /usr/sbin/nologin jupyter
こうするとシェル経由でユーザにログインできなくなる。
vagrant $ grep jupyter /etc/passwd jupyter:x:1001:1001::/home/jupyter:/usr/sbin/nologin vagrant $ sudo su - jupyter This account is currently not available.
デーモンプログラムを起動するユーザは、不正アクセスを受けた場合の影響を小さくする意図でこうすることが多い。
SSH Port Forwarding 経由で Jupyter Notebook の Web UI にアクセスする
ここまでで、リモートサーバ上の Jupyter Notebook の設定は終わった。
一旦リモートサーバから SSH でログアウトする。
vagrant $ exit
改めて SSH Port Forwarding を有効にしてリモートサーバにログインする。
このときリモートサーバの TCP/8888
ポートを、ローカルホストのポートにマッピングする。
ユーザ名やホスト名は適宜読み替える。
client $ ssh -L 8888:localhost:8888 <username>@<remotehost>
今回は Vagrant の環境を使っているので、こんな感じで。
client $ vagrant ssh-config > ssh.config client $ ssh -L 8888:localhost:8888 -F ssh.config default
あとは、クライアントのブラウザでローカルホストにマッピングしたポート番号を開く。
client $ open http://localhost:8888
すると、見覚えのある Web UI が表示される。 オプションの共通パスワード認証を使っていないのであれば、いきなりいつもの画面になるはず。
あとは、もしポータビリティとかを考えるのであればお好みで Docker イメージとかにする感じで。
めでたしめでたし。
スマートPythonプログラミング: Pythonのより良い書き方を学ぶ
- 作者: もみじあめ
- 発売日: 2016/03/12
- メディア: Kindle版
- この商品を含むブログ (1件) を見る