Linux中級者への道: ネットワーク編

[Linux初心者あるある - Qiita](https://qiita.com/Yasushi-Shinohara/items/f2da1a6a6f8cca085943)の続編。
2020/04/05現在、[COVID-19](Severe acute respiratory syndrome coronavirus 2)の世界的感染で、都市が封鎖されたり、外出の自粛が要請されたりしている。勤務も可能な限りテレワークが推奨されているので、大学の理系研究室でありそうな環境を念頭に、リモートで研究室外から研究室内のネット環境にアクセスするための必要事項をまとめる。

# インターネットからのイントラネットへのアクセス
イントラネット(例えば研究室内のネットワーク)からインターネット(例えば研究室外のネットワーク)は、通常制限が少なく、自由に出ていくことができる。この時、ゲートウェイサーバーを経由して外に出ていて、インターネットからは、イントラネットの構造は直接には見ることはできない。ゲートウェイの持っているグローバルIPアドレスからのアクセスのように見えている。
これとは逆にインターネットの特定のアドレスから、イントラネットに入る時、どのようなコマンドで、どのような事が出来るのか をここでは説明する。ここでは基本的にsshを使ったアクセスについて触れる。

## イントラネットの踏み台サーバー(Bastion server)にsshでアクセスする
ゲートウェイにグローバルIPアドレス/ホスト名が降られているため、このIPアドレス/ホスト名を頼りに、sshでアクセスをすればよい。アクセスにはIPアドレス/ホスト名が必要なので、イントラネットを管理している人に確認すること。このIPアドレスをwww.xxx.yyy.zzzとし、ホスト名をhost.gateとすると、userがユーザー名の時

```terminal
ssh user@www.xxx.yyy.zzz
ssh user@host.gate
```
でアクセスが出来る(鍵が必要な場合後述)。多分このままではアクセスはできない。<font color='red'>理由は、普通ポート番号は22からずれていて、上のコマンドではそれを指定していないため。ここでは説明の都合上必要なので、実際のログインはもう少し待ってほしい。</font>仮に、ポート番号がデフォルトの22に場合、このコマンドでアクセスできない場合は、そもそも自身がユーザーとして追加されていないか、ユーザー設定時のパスワードが誤っている可能性が高い。

ここで、一応実際に何が起きているのかについて、もう少しだけ説明する。
通常、ゲートウェイは専用機ルーターが兼ねていることが多く、この専用機ルーターには通常ユーザー情報は存在していない。すると、どこにログインしているのか？
その答えは、踏み台サーバー(bastion server)である。踏み台サーバーは、インターネットからイントラネットに入る際に、ルーターに来たアクセスを一手に担うサーバーである。外からイントラネットに入る際は、先ずはこのサーバーにログインすることになる。


### ポート番号をずらす `-p`
通常ゲートウェイ、あるいは踏み台サーバーのsshプロトコルのポート番号はデフォルトの22番からずらされている。その理由は、インターネットに面したこうしたサーバーは常時不特定の計算機からログインが試みられており、22番はsshのデフォルトポートなので有望な攻撃対象になる。そこで、通常インターネットに面している計算機のsshでのアクセスにおいては、ポート番号は22からずらされている。

例えば、ポート番号が31415(円周率の最初の数字を五桁取っただけ)だったとする。そのとき、sshでのアクセスは以下のようにすればよい:

```bash
ssh -p 31415 user@www.xxx.yyy.zzz
ssh -p 31415 user@host.gate
```

### 鍵の指定 `-i`
デフォルトでは、sshはユーザー名と対応するパスワードで、そのログインができる。仮にポート番号が22の場合、よくあるユーザー名、推測されやすいパスワードであれば、容易に悪意のある人間/計算機のログインを許してしまう。こうした侵入を一段と困難にするためにsshの鍵認証の方法が使える。公開鍵をログイン先の所定の位置に置き、ログイン元の計算機に秘密鍵を置いておくことで、この鍵のペアが一致したときにのみ、ログインができるようになる。ここではRSA鍵を念頭に、鍵を使ったサーバーへのアクセス法について記載した。詳しくは「ssh 鍵認証」あたりで調べればわかる。
ログインのためのコマンドは、鍵を`~/.ssh/id_rsa`に置いてあるときに`-i`オプションで以下のように指定すればよい:

```bash
ssh -p 31415 -i ~/.ssh/id_rsa user@www.xxx.yyy.zzz
ssh -p 31415 -i ~/.ssh/id_rsa user@host.gate
```
鍵を生成したときに指定したパスフレーズが聞かれるのでそれを入力する。

## X Window Systemを使いたい `-X`, `-Y`
sshでリモートアクセスをすると、大方のアプリはウィンドウを手元に表示することができる。xtermとか、xpdf、xdviのようないかにもX window systemっぽいものから、gnuplot, firefoxとか大抵のGUIの画面は飛ばせる。(ただし、終了時に妙なデーモン残すアプリも結構あるので注意)解析したいデータがあるサーバーで立ち上げたいアプリ、またアプリが効果で特定のサーバーにしか入っていない場合、アプリのインストールが極めて面倒(OS、ライブラリ等の依存関係などで)で特定の計算機にしか入っていない場合などに有効。
こうした機能を使いたい場合は、`-X`か`-Y`のオプションを付ける。違いはいまいちわからないが、Macを使っている場合、前者は割と短いスパンで接続が切れ、後者の方が安定している。コマンドの例は以下の通り

```bash
ssh -X user@www.xxx.yyy.zzz
ssh -X user@host.gate
ssh -Y user@www.xxx.yyy.zzz
ssh -Y user@host.gate
```

遠隔地にあるサーバー等、中途の通信の速度が遅い場合(携帯電話のネットワークとか)、通信内容を圧縮することで帯域を節約することができる。`man ssh`で見ると、速いネットワークを使う場合はむしろ遅くなる とのこと。

```bash
ssh -XC user@www.xxx.yyy.zzz
ssh -YC user@host.gate
```

## 踏み台サーバーにログイン後、自動的にイントラネット内の他の計算機に移動したい `-t`
イントラネットにおいて、通常踏み台サーバーはそれ用に用意されていて、一般に他の機能を有していない。通常は、踏み台サーバーにログイン後、クラスター計算機や、ファイルサーバー、個人で管理する計算機にログインしてデータを見る という需要が大きいだろう。こうした目的を達成したい場合、一つの方法として、踏み台サーバーにログインしたのちに`ssh -XC user@192.168.xxx.yyy`のような形で次のサーバーにログインする方法がある。しかし、これを毎回行うのは、端的に言って愚かで、自動化できるに違いないと感じると思う。素人が思いつく「こんなことできないだろうか」は必ずと言っていいほど誰かが実践していて、ウェブ上で情報がみつかる。今回のケースではsshに用意されているデフォルトオプションの`-t`でこれが実現できる。
`-t`オプションを指定して、ホスト名の右にログインした端末で実行したい端末上でのコマンドを書く。

```bash
ssh -p 31415 -t user@www.xxx.yyy.zzz ssh user@192.168.xxx.yyy
ssh -p 31415 -t user@host.gate ssh user@mylinux.intra
```
一応`-t`が無くても、ホスト名の右に書いたコマンドは実行されるが、このケースでは`-t`オプションが必須。このコマンドが実行されると、引き続く192.268.xxx.yyy/linux.intraのパスワードが聞かれるはず。

コマンドは別に`-t`オプションを受けた変数というわけではないので、離れていてもいいのだけれど、個人的には、以下のようにくっつけて書いておいた方が好み:

```bash
ssh -p 31415 user@www.xxx.yyy.zzz -t ssh user@192.168.xxx.yyy
ssh -p 31415 user@host.gate -t ssh user@mylinux.intra
```

あと、最終ログイン先からX飛ばしたい場合は`ssh -p 31415 -X user@www.xxx.yyy.zzz -t ssh -X user@192.168.xxx.yyy`としておけばOK。192.168.xxx.yyyで立ち上げたウィンドウが手元に飛んでくる。

### `-t`を使ったコマンドの応用
何となくわかるように、別にとホスト名より右のコマンドはsshである必要はない。踏み台サーバーに限らず、ログインした計算機ですぐに実行したいコマンドがあれば、ここに書いておくと良い。
例えば、スーパーコンピューターのジョブの実行状況や、残りのバジェットを確認したいときは

```bash
ssh -t user@www.xxx.yyy.zzz qstat
ssh -t user@super.computer point
```
等と書いておけば、実行状況や残りポイント数を表示後、すぐにログアウトされる。あと、割と`-t`オプション無くても、いける場合も多い。ただ、この時コマンドに付随しているいくつかのオプション(多分どの設定ファイルが読み込まれいるかに依る)が無視されることがあるので、注意。

## 特定のポートへのアクセスを手元のポートへのアクセスで代替したい `-L`
研究室イントラネットにある特定の計算機へのポートアクセスを、アクセス元の計算機のポートに代替する/変換するポートフォワーディングという機能がある。例としては、以下に述べるjupyter notebookへのリモートアクセスや、sshへのアクセスが便利になる。

### Jupyter notebook `-L 8888:192.168.xxx.yyy:8888`
Jupyter notebookはデフォルトでは、立ち上げた端末上でブラウザが立ち上がる。実はリモートからのアクセスの設定をする(例えば[リモートサーバでjupyter notebookを起動させローカル環境で使う - Qiita](https://qiita.com/syo_cream/items/05553b41277523a131fd)参照)ことで、イントラネットにあるアクセス制限のない計算機からはブラウザから`192.168.xxx.yyy:8888`を入力することでアクセスが出来る。この文字列の意味は、IPアドレス:ポート番号という事で、ポート番号が8888であることを意味している。複数回Jupyter notebookを立ち上げるとポート番号は通常8888, 8889, 8890と一つずつ大きくなっていく。
踏み台サーバーはイントラネットに存在しているので、ここからポートフォワーディングを行うことで、イントラネットの特定計算機のポートをログイン元の特定ポートに飛ばすことができる。例えばイントラネットにある192.168.xxx.yyyの8888をログイン元の8888あるいは18888に飛ばす場合の書き方は以下の通り:

```bash
# -L [ログイン元のポート番号]:[イントラネットの計算機のIP]:[イントラネットの計算機のポート]
ssh -p 31415 user@www.xxx.yyy.zzz -L 8888:192.168.xxx.yyy:8888 -t ssh user@192.168.xxx.yyy
ssh -p 31415 user@www.xxx.yyy.zzz -L 18888:192.168.xxx.yyy:8888 -t ssh user@192.168.xxx.yyy
```
`-t`より右のコマンド`ssh user@192.168.xxx.yyy`はポートフォワーディング自体には必要ないが、Jupyter notebookを立ち上げる際に、普通は一度対象計算機にログインする必要があるので、記載してある。既に8888番でアクセスできるJupyter notebookが立ち上がっているなら、このコマンドは不要。
ログイン元のポート番号は、基本的に任意で好きな数字を用意すればよい。ただ、0-1023は既存のよく使うサービスに予約されているので、指定しないほうが良い。

### ssh/sftpのアクセス `-L 10022:192.168.xxx.yyy:22`
ここまで、述べてきたようにインターネットからは、イントラネットの様子は直接は見ることができない。イントラネットに踏み台サーバーを用意して、そこにゲートウェイ経由でログインすることで、イントラネットの中に入ることになる。一度踏み台サーバーにログインしてしまえば、ポートフォワーディングをすることで、以降もっと容易にイントラネットにアクセスできるようになる。その方法をssh/sftp接続を例にここで説明する。
踏み台サーバー上で、イントラネット内の計算機(192.168.xxx.yyy)の22番ポートをログイン元の10022にマップするようにしておけば、引き続く接続では自分自身の10022をsshのログイン先に指定することで、イントラネットの22番にアクセスしている挙動になる。そのための最初のコマンドは

```bash
ssh -p 31415 user@www.xxx.yyy.zzz -L 10022:192.168.xxx.yyy:22 
```
で、引き続いて以下のコマンドで、イントラネットの192.168.xxx.yyyにssh/sftpでアクセスできる:

```bash
ssh -p 10022 user@localhost
sftp -P 10022 user@localhost
```
ここで、sftpのポート番号の指定は`-P`で、Pが大文字である事に注意。あと、sftpのバージョンによっては`-Port=10022`とすることもある。`man sftp`で要確認。
正直なところ、sshよりも、sftpの方がここでの設定のご利益は大きい。ファイルを所望の計算機にアップロードする場合、計算機がイントラネットにあると、素朴にやろうと思うと「sftpで先ず踏み台サーバーにファイルを置く」→「踏み台サーバーにsshでログイン」→「踏み台サーバーでsftpを実行し、所望の計算機にファイルを移動」という極めてだるい手続きになる。ポートフォワーディングを使うことで、この手間が一気に削減される。
「踏み台サーバーへのsshでログインはするのだから、手間は減っていないではないか」という批判が考えられる。sftpはインタラクティヴなアプリなので、手元とログイン先の両方の環境を見ながらアップロード/ダウンロードできる。一方、ファイルを一回踏み台サーバーに置く という手続きを経ると、例えばアップロード/ダウンロードしたファイルが間違っていた というケースで、すべての手続きを再度やり直すことになる。ポートフォワーディングを使って、直接sftpでつないでいれば、この種の問題は起きえない。


## イントラネットからしかアクセスできないウェブページを手元のブラウザで閲覧したい `-D`
様々なレイヤーで、アクセスの制限がかかっていたり、認証の都合で一般のインターネットからはアクセスできないものの、イントラネットからはアクセスできるというウェブページがある。そこにポートフォワーディングを使ってアクセスする方法。
上で使ったポートフォワーディングは、ローカルフォワーディングというもので、ここではダイナミックフォワーディングを使う。オプションは`-D`でポート番号を指定する:

```bash
ssh -p 31415 -D 10080 user@www.xxx.yyy.zzz
```
このように書くと、ポート番号10080のSOCKSプロキシサーバーになる。ブラウザのプロキシサーバーの設定の項目でSOCKSホストにlocalhostを指定して、ポートに10080を指定することで、踏み台サーバーからブラウザでアクセスしているかのような挙動になる。


## 踏み台サーバー、及び経由のアクセスの設定をconfigファイルにまとめる
以上で、sshのオプションを駆使して、イントラネットにある様々な機能を有効にする手続きについて記載してきた。しかし、こうしたコマンドを毎回打つのはだるい。設定ファイルにまとめることが出来れば、かっこいい。その設定ファイルが`~/.ssh/config`である。これまで記載して機能を反映させたファイル(ぼくのかんがえたさいきょうの".ssh/configファイル)について説明する。

基本的な文法は

```
Host [好きな名前]
    Hostname [IP/ホスト名}
    IdentityFile [鍵のpath]
    Port [ポート番号]
    LocalForward [ログイン元のポート] [イントラネットの計算機のIP]:[イントラネットの計算機のポート]
    DynamicForward [ダイナミックフォワーディングに使うポート番号]
    ForwardX11 [yesにするとX Window Systemを使う] 
    Compression [yesにすると、通信内容を圧縮する]
    ProxyCommand [ログインした後に実行されるコマンド]
```
という感じ。まとめると以下のようになる:

```:~.ssh/config
#
Host Bastion
    HostName 192.168.xxx.zzz
    IdentityFile /home/user/.ssh/id_rsa
Host linux
    HostName 192.168.xxx.yyy
Host Bastionout
    HostName www.xxx.yyy.zzz
    IdentityFile /home/user/.ssh/id_rsa
    Port 31415
Host linuxout
    HostName 192.168.xxx.yyy
    ProxyCommand ssh -XCW %h:%p Bastionout
Host linuxtunnelout
    HostName 192.168.xxx.yyy
    ProxyCommand ssh -XCW %h:%p Bastionout
    LocalForward 10022 localhost:22
    LocalForward 8888 localhost:8888
    LocalForward 8889 localhost:8889
    LocalForward 8890 localhost:8890
    LocalForward 8891 localhost:8891
    DynamicForward 10080
    GatewayPorts yes
Host linuxsecondout
    HostName localhost
    Port 10022
#
HOST *out
    ServerAliveInterval 60
    TCPKeepAlive yes
    Compression yes
HOST *
    User user
    ForwardX11 yes
    ForwardX11Trusted yes
```

最後の`SeverAliveInterval`と`TCPKeepAlive`はインターネットを介してssh接続をつないでい置いた際に勝手に切断される現象を緩和するためのオプション。ここでは説明しない。Jupyter notebookは四つくらいは同時に立ち上げるだろうと見込んで、四つ連番で8888から8891までポートフォワーディングした。ログイン元ではJupyter notebookは同時に使用することはなさそうなので、ポート番号はイントラネットでのものと同じ番号にした。
ポートフォワーディングは、二度立ち上げるとワーニングを吐き、なんとも気持ち悪い。そこで、プレーンなlinuxout, ポートフォワーディングを目的にしたlinuxtunnelout, ポートフォワーディングした状態で引き続くアクセス用にlinuxsecondoutを用意した。ポートフォワーディングをしている際に`GatewayPorts yes`を入れておくと、ポートフォワーディングをした計算機以外からもフォワードしたポートへのアクセスが許可される。
`Host`の部分の名前にはワイルドカード(*)が使える。ここでは、X window systemはすべての接続に使いたかったので、`Host *`の個所に記載してある。ここでは、インターネットからアクセスする場合はすべて\*outという名前にしてあって、それらにはすべてsshが切断されないための設定と、通信の圧縮の設定を入れた。
`man ssh_config`でマニュアルを見ると
> Since the first obtained value for each parameter is used, more host-spe‐
> cific declarations should be given near the beginning of the file, and
> general defaults at the end.

とあるので、環境異存で変えたい変数はファイル上方に、一般の変数は極力下方に書く必要がある。

# リモートアクセス時によく使うコマンド

## リモートの計算機上でファイルをコマンドでダウンロード: `wget`
ダウンロードしたいファイルがあって、そのファイルをリモートで接続しているマシンに置きたいことがある。この時手元の計算機にダウンロードしてからそれをsftp等で移すのはあまりにも愚かに思える。もし、ダウンロードするファイルを指定するURLが分かっていれば

```
wget [URL]
```
で目的が達成できる。

# FAQ
あつめているところ