Linux セキュリティ

# SSH
Secure Shell

ネットワークを介した通信を **暗号化** することにより、情報を安全に送受信するためのプロトコル。

通信を行うためのプロトコルとしては TELNET プロトコルがあるが、TELNET は通信内容が暗号化されない。

Linux では SSH の実装プログラムとして **OpenSSH** が広く利用されている。OpenSSH は `openssh-client`、`openssh-server` などのパッケージとして提供されている。

SSH 接続を試みる側がクライアント（[`$ ssh`](#-ssh)）、SSH 接続先がサーバ（`sshd` [デーモン](https://qiita.com/AsahinaKei/items/e354a3814639aeac4138#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9--%E3%83%87%E3%83%BC%E3%83%A2%E3%83%B3)）である。

`sshd` は、**22** 番ポートをデフォルトで使用する。

## 暗号化アルゴリズム

- **RSA**
    - 広く利用されている
- DSA
    - 非推奨
- ECDSA
- **ED25519**
    - 推奨
    - 高速で安全性が高い

## SSH トンネル

SSH プロトコルには **ポートフォワーディング** 機能が内包されている。

ポートフォワーディングとは特定の「IP アドレス」「ポート」宛の通信を別の宛先に転送する機能を指す。

SSH とポートフォワーディングを利用して通信路が確立された場合、その通信路は外側から「どこを経由して、何が転送されているか」が見えなくなるため **SSH トンネル** と表現される。

SSH トンネルは [`$ ssh -L`](#-ssh--l--ssh-トンネル) によって作成することができる。

重要な点として SSH 接続において、利用したいサービスは SSH サーバ上で稼働しているとは限らない。

SSH サーバはポートフォワーディングを行うことができるため、通信の中継役として利用されることが多いためである。

![LPIC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/d7c51004-4b10-4d52-a608-e9e6b40cedc3.png)

## 認証

SSH で使用される認証はいくつかの種類がある。
- [パスワード認証](#パスワード認証)
- [ホスト認証](#ホスト認証)
- [公開鍵認証](#公開鍵認証)

### パスワード認証

接続しようとしている OS のユーザアカウントでログインを行うための認証。

SSH 接続に限らず、ユーザ名とパスワードを用いてログインする形の認証方式は、一般に「パスワード認証」と呼ばれる。

SSH クライアントは [`$ ssh-copy-id`](#-ssh-copy-id) コマンドで自身の公開鍵を SSH サーバの [`~/.ssh/authorized_keys`](#sshauthorized_keys) に登録しておくことにより、**以後は [公開鍵認証](#公開鍵認証) で接続できるようになり、パスワード認証は省略される。**

### ホスト認証

SSH 接続を試みるクライアントが行う、接続先サーバが信頼できるかの認証。

ホスト認証は SSH サーバの公開鍵を用いて行われる。

具体的には SSH 接続の度にサーバから公開鍵が送信され、クライアントは自身の [`/etc/ssh/ssh_known_hosts`](#etcsshssh_known_hosts) もしくは [`~/.ssh/known_hosts`](#sshknown_hosts) に保存されたサーバの公開鍵と一致するかが比較、検証される。

ただし、クライアントは初回接続のタイミングではサーバの公開鍵を保持していないため、以下のようなメッセージが表示されれる。このときはホスト認証の判断がユーザに委ねられ、`yes` を入力することで初回のホスト鍵がクライアントに保存され、以後は自動で照合されるようになる。

```console:初回接続時
$ ssh 接続先
ED25519 key fingerprint is SHA256:*******************
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
```

### 公開鍵認証

SSH サーバが行う、SSH 接続を要求してきたクライアントが信頼できるかを確認するための検証。

クライアントは自身の秘密鍵で署名を行い、サーバはクライアントの公開鍵で署名を検証する。

SSH 接続においては、これが `クライアント→サーバ` 方向の認証になる

なお、これとは別に、クライアントがサーバの公開鍵を検証する [ホスト認証](#ホスト認証) も存在し、こちらは `サーバ→クライアント` 方向の認証といえる。

SSH クライアントは [`$ ssh-copy-id`](#-ssh-copy-id) コマンドで自身の公開鍵を SSH サーバの [`~/.ssh/authorized_keys`](#sshauthorized_keys) に登録しておくことで、公開鍵認証によるログインが可能になる。

## Finger Print

公開鍵のハッシュ値（要約値）。

SSH サーバは初回接続時に公開鍵をクライアントに送ってくるが、公開鍵そのものは長くて見づらいため、その鍵が本物かどうかを簡単に確認できる方法として利用される。

公開鍵と一緒に以下のファイルに保存される。
- [`/etc/ssh/ssh_known_hosts`](#etcsshssh_known_hosts)
- [`~/.ssh/known_hosts`](#sshknown_hosts)

## 宛先

以降のコマンドでは `宛先` がたびたび登場する。

宛先は

- ユーザ名
- FQDN
- IP アドレス
- パス

などで構成される。

宛先のマシンは `www.example.co.jp` のようなホスト名を含むドメイン名（FQDN）で指定することもできるし、`172.0.0.1` のような IP アドレスで指定することもできる。これらは [DNS](https://qiita.com/AsahinaKei/items/aa97c398e832d9a22e92) によって紐づけられているので、どちらを指定しても良い。

```text:宛先
FQDN:パス
ユーザ名@FQDN:パス
ユーザ名@IPアドレス:パス
user1@198.51.100.1:/home/user1/
```

## `/etc/ssh/ssh_config`

SSH クライアント側の `ssh` 設定ファイル。

![LPIC (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/f9a6a16f-efbf-478a-9418-188d8717ecf8.png)

```text:設定例
ForwardX11 yes # X 転送を許可するk
```

## `/etc/ssh/ssh_known_hosts`

クライアントが知っている（known）サーバ情報。

具体的にはサーバの公開鍵を含む、サーバに関する情報が格納されたファイル。

クライアント側に配置する。

[`/etc/.ssh/known_hosts`](#sshknown_hosts) とは異なり、全てのユーザが利用する SSH サーバの情報が格納される。

![LPIC (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/8cc8d5c0-8113-4857-bb5d-bb3cfb8e63b8.png)

## `~/.ssh/known_hosts`

クライアントが知っている（known）サーバ情報。

具体的にはサーバの公開鍵を含む、サーバに関する情報が格納されたファイル。

クライアント側に配置する。

サーバに対して初めて SSH 接続を行ったタイミングで情報が格納される。

[`/etc/ssh/ssh_known_hosts`](#etcsshssh_known_hosts) とは異なり、特定のユーザが利用する SSH サーバの情報が格納される。

![LPIC (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/eae88d3a-202c-4e21-a9bb-82fc3f73fae9.png)

## `/etc/ssh/sshd_config`

SSH サーバの `sshd` 設定ファイル。

![LPIC (7).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/1b7945d9-ff73-4103-b1df-076e41dfc285.png)

```text:設定例
Port 22                  # SSH デフォルトポート
PermitRootLogin no       # root ログインの可否
PasswordAuthentication yes  # パスワード認証を許可するか
PubkeyAuthentication yes # 公開鍵認証の有効化
X11Forwarding yes # X 転送を許可するか
```

## `~/.ssh/authorized_keys`

クライアントの公開鍵情報を登録するファイル。

サーバ側に配置する。

※ [`$ ssh-keygen`](#-ssh-keygen) を参照

![LPIC (8).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1069520/e13a51b2-607a-4892-b5a8-1f8a70ff868f.png)

## `$ ssh-keygen`

SSH プロトコルで使用される [公開鍵と秘密鍵](https://qiita.com/AsahinaKei/items/4619aade8654864dc205) の鍵ペアを生成する。

- **公開鍵**
    - **`~/.ssh/id_暗号化方式.pub`**
    - サーバーの **`~/.ssh/authorized_keys`** に配置することで、公開鍵認証が有効になる（[`$ ssh-copy-id`](#-ssh-copy-id) を使用して配置）
- **秘密鍵**
    - **`~/.ssh/id_暗号化方式`**
    - ローカル環境で安全に保存する必要がある鍵

`暗号化方式` には [暗号化アルゴリズムの種類](#暗号化アルゴリズム) が入る。

```console:鍵ペアを生成する
$ ssh-keygen 
```

実行後はパスフレーズの入力を求められる（空文字でも良い）。

```console:鍵の保存先を指定する
$ ssh-keygen -f ファイルパス
```

指定したファイルパスで秘密鍵が作成され、さらに末尾に `.pub` のついたファイルが公開鍵として作成される。

```console:鍵の種類（アルゴリズム）を指定する（type）
$ ssh-keygen -t 種類
```

※ [アルゴリズムの種類](#暗号化アルゴリズム)

SSH の公開鍵はとても長いデータ（数百〜数千文字の文字列）であり、それをそのまま比べたり、人間が目で見て確認するのは大変であるため、公開鍵の内容をハッシュ関数（SHA256など）で短く変換した **フィンガープリント**（指紋） を利用することがある。

```console:指定した公開鍵のフィンガープリント（指紋）を表示する
$ ssh-keygen -lf ファイル
```

```console:指定したホストの公開鍵を削除する（remove）
$ ssh-keygen -R FQDN
```


## `$ ssh-copy-id`

SSH 接続の度にサーバから要求される、OS のユーザアカウントのパスワード入力（[パスワード認証](#パスワード認証)）を省略するためのコマンド。

実行すると SSH クライアント自身の公開鍵（`~/.ssh/id_暗号化方式.pub`）が、サーバの [`~/.ssh/authorized_keys`](#sshauthorized_keys) 末尾に追加される。

```console:クライアントの公開鍵をサーバに配置する
$ ssh-copy-id サーバのIPアドレス
```

## `$ ssh-agent`

`ssh-agent` [デーモン](https://qiita.com/AsahinaKei/items/e354a3814639aeac4138#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9--%E3%83%87%E3%83%BC%E3%83%A2%E3%83%B3) を操作するためのコマンド。

`ssh-agent` は SSH の秘密鍵をメモリ上に保持することによって、ユーザが SSH 接続のたびにパスフレーズを入力する手間を省く機能を提供する。

秘密鍵自体も暗号化されて保存されており、パスフレーズは暗号化された秘密鍵を復号するために用いられる。なお、秘密鍵をパスフレーズで暗号化するかは任意。

パスフレーズの省略をしたい場合、**`ssh-agent` を起動した状態で [`$ ssh-add`](#-sshadd) コマンドによって `ssh-agent` に秘密鍵を登録する必要がある**。

現在のシェルで起動する方法（[`$ eval`](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#-eval)）と、新しいシェルセッション（サブシェル）で一時的に起動する方法がある。

```console:現在のシェルで起動する方法
$ eval $(ssh-agent)
```

`ssh-agent` をバックグラウンドで起動し、その出力（環境変数の設定）を現在のシェルに反映させる。

- 現在のシェルを閉じない限り、`ssh-agent` は有効
- 長時間利用や複数のコマンドを使う用途向き
    
```console:サブシェルで起動する方法
$ ssh-agent bash
```

`ssh-agent` を起動した上で、新しい `bash` サブシェルを起動する。

- このサブシェル内でのみ `ssh-agent` が有効
- `$ exit` すると、サブシェルと同時に `ssh-agent` も終了する

`ssh-agent` は [環境変数](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0) `$SSH_AUTH_SOCK` と `$SSH_AGENT_PID` を利用して動作しており、これらの環境変数は `ssh-agent` を起動したタイミングで自動的にセットされる。

- **`$SSH_AUTH_SOCK`**
    - プロセス間通信(IPC)を行うための UNIX ドメインソケットファイルのパス
    - `ssh` クライアントはこのソケットファイルを介して `ssh-agent` から秘密鍵を受け取る
- **`$SSH_AGENT_PID`**
    - 実行中の `ssh-agent` のプロセス ID

サブシェルを閉じると、これらの環境変数も消えるため、以降 `ssh-agent` は使用できない。

ただし多くの [デスクトップ環境](https://qiita.com/AsahinaKei/items/63085a778cbdd29dad4a#%E3%83%87%E3%82%B9%E3%82%AF%E3%83%88%E3%83%83%E3%83%97%E7%92%B0%E5%A2%83) では、ログイン時に `ssh-agent` が自動起動しており、明示的に `ssh-agent` を起動しなくても使用できる場合もある。

## `$ ssh-add`

パスフレーズをメモリ上に保持することで、接続の度に毎回要求されるパスフレーズの入力を省略するためのコマンド。

[`ssh-agent`](#-ssh-agent) を起動した後に、`$ ssh-add` で秘密鍵を登録する。

```console:秘密鍵を登録する
$ ssh-add ~/.ssh/id_rsa
```

ここ入力したパスフレーズは、以降の SSH 接続で入力する必要がなくなる。

```console:登録済みの秘密鍵を確認する
$ ssh-add -l
```

```console:登録済みの鍵を全て削除する
$ ssh-add -D
```


## `$ ssh`

```console:SSH 接続する
$ ssh 宛先
```

[`宛先`](#宛先) は具体的に以下を指す。

```console:ポート番号を指定する
$ ssh -p ポート番号 IPアドレス
$ ssh -p ポート番号 FQDN
$ ssh -p ポート番号 ユーザ名@FQDN
```

```console:ログインユーザを指定する
$ ssh -l ユーザ名 宛先
```

```console:SSH 認証用の秘密鍵のファイルパスを指定する（identity file）
$ ssh -i パス 宛先　
```

※ `-i` で明示的に指定しない場合のデフォルトの秘密鍵は [`~/.ssh/id_暗号化方式`](#-ssh-keygen)

※ [仮想環境を再インストールしたら SSH 接続に失敗する](https://qiita.com/AsahinaKei/items/63085a778cbdd29dad4a#%E5%86%8D%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E6%99%82%E3%81%AB-ssh-%E6%8E%A5%E7%B6%9A%E3%81%AB%E5%A4%B1%E6%95%97%E3%81%99%E3%82%8B)

## `$ ssh -X` / X転送

リモートサーバ上の GUI アプリケーションを `$ ssh` コマンドを実行したローカル環境で表示し、ローカル環境上で GUI 操作できるようにする機能を **X 転送** と言う。

X 転送は [X Window System](https://qiita.com/AsahinaKei/items/22d9fae93cabfbf137ee#x-window-system) 上で動作する。

```console:X 転送を行う
$ ssh -X 宛先
```

`-X` と同様に X 転送を行うオプションに `-Y` は、`-X` よりもセキュリティが弱いものの、アプリケーションの動作上は障害が発生しづらく、信頼性が高い。

- `-X`：安全性のために制限がある
- `-Y`：制限が緩いが、リスクが高い


## `$ ssh -L` / SSH トンネル

```console:ポートフォワーディングを行なって SSH トンネルを確立する
$ ssh -L ローカルポート:接続先:接続先ポート 中継ホストのユーザ名@中継先
```

# GnuPG
Gnu Privacy Guard

ファイルの暗号化、復号を行うためのツール。秘密鍵を利用した電子署名も行うことができる。

また共通鍵暗号方式、公開鍵暗号方式のどちらも同じ [`$ gpg`](#-pgp) コマンドで利用することができる。

米国で開発され、米国内でしか利用できない暗号化ツール **PGP**（Pretty Good Privacy）と互換性を持つフリーソフトウェアとして `$ gpg` に実装された。

## `$ gpg`

暗号化、復号、電子署名、鍵管理を行うための [GnuPg](#gnupg) 実装プログラム。

対話的、またはスクリプトでの操作が可能。

コンポーネントとして `gpg-agent` [デーモン](https://qiita.com/AsahinaKei/items/e354a3814639aeac4138#%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9--%E3%83%87%E3%83%BC%E3%83%A2%E3%83%B3) が秘密鍵の管理、キャッシュを行っている。

## 共通鍵によるファイル暗号化 / 復号

```console:共通鍵でファイルを暗号化する
$ gpg -c ファイル名
```

暗号化を行う際は、パスフレーズの入力を求められる。

パスフレーズは文字列だが、内部的には GnuPG がこれをバイナリ形式の共通鍵生成に利用している（`パスフレーズ = 共通鍵` ではない）。

共通鍵の生成には、生成の度にランダムな初期化ベクトル（IV）が使用されているため、例えば、複数のファイルに同じパスフレーズを使ったとしても、毎回、異なる共通鍵になる。
    
また暗号化ファイルは、元ファイル名の末尾に `.gpg` が付与された形で作成される。

暗号化ファイルは `--output` オプションで明示的に出力名を指定することもできる。

```console:共通鍵でファイルを暗号化する（出力先を指定）
$ gpg -c ファイル名 --output 出力先
```

ファイルの復号を行う際は、再びパスフレーズの入力が求められる。

```console:共通鍵でファイルを復号する
$ gpg ファイル名.gpg
```

## 公開鍵 / 秘密鍵によるファイルの暗号化 / 復号

```console:公開鍵、秘密鍵のキーペアを作成する（簡易設定版）
$ gpg --gen-key
```

キーペアを作成する際には本名（Real name）とメールアドレス、パスフレーズの入力を求められる。

また [鍵の種類](#暗号化アルゴリズム)、鍵の長さ（`2048` ビット以上推奨）を細かく設定したい場合には `--full-generate-key` オプションを使用する。

```console:公開鍵、秘密鍵のキーペアを作成する（詳細設定版）
$ gpg --full-generate-key
```

GnuPG では、鍵の保存形式として以下の 2 種類がある
- **バイナリ形式**（キーリング / キーボックス）
- **ASCII 形式**（テキストファイル `.asc`）

一つはローカル環境に保存するためのバイナリ形式の鍵ファイル、もう一つは通信相手と交換をするために利用されるテキスト形式（ASCII armored 形式、`.asc`）のファイルである。

バイナリ形式の鍵ファイルは、データベースに **キーリング** または **キーボックス** として保存される（GnuPG のバージョンによって異なる）。

このキーボックスの中の特定の鍵は、テキスト形式で **エクスポート** され、 Web 上で公開したり、GitHub や GitLab に登録したりする際に利用される。公開された自分の公開鍵は、他者が暗号化や署名検証に利用することになる。

| GnuPG のバージョン | 公開鍵（バイナリ） | 秘密鍵（バイナリ） | 名称 |
|:-:|:-|:-|:-:|
| `2.1` 以前 | `~/.gnupg/pubring.gpg` | `~/.gnupg/secring.gpg` | キーリング |
| `2.1` 以降 | `~/.gnupg/pubring.kbx` | `~/.gnupg/private-keys-v1.d/ハッシュ値.gpg-protected` | キーボックス |

※ キーボックスは従来のキーリングに代わって導入された新形式であり、より柔軟な構造と性能向上を目的としている。

```console:公開鍵の一覧を確認する
$ gpg --list-keys
```

```console:秘密鍵の一覧を確認する
$ gpg --list-secret-keys
```

エクスポートを行う際は `--armor` オプションを付与することで、`ASCII` 形式のテキストファイル（`.asc`）として出力される。

```console:自分の公開鍵（バイナリ）をファイル（テキスト）にエクスポートする
$ gpg --output 出力ファイル名 --armor --export 自分のメールアドレス
```

受け取ったテキスト形式の鍵ファイルは、あくまでも交換用の形式であるため、受け取った後にインポートを行う必要がある。これにより、ローカル環境の GnuPG 内のデータベースに通信相手の公開鍵が保存される。

```console:相手の公開鍵（テキスト、.asc）をインポートする
$ gpg --import 公開鍵ファイル
```

通信相手のメールアドレスは、公開鍵ファイル（`.asc`）内部の `UID` 情報に含まれており、これを用いてユーザが識別される。インポート時に通信相手のメールアドレスを指定しないのはそのためである。（[参考](https://qiita.com/AsahinaKei/items/4619aade8654864dc205)。`UID` は `$ gpg --list-keys` で確認可能。）。

インポートした相手の公開鍵は、送信するファイルの暗号化に利用される。

```console:通信相手の公開鍵でファイルを暗号化する
$ gpg --encrypt --recipient 相手のメールアドレス ファイル
$ gpg --encrypt --armor --recipient 相手のメールアドレス ファイル # 暗号化ファイルがテキスト形式になる
```

```console:秘密鍵でファイルを復号する
$ gpg ファイル
$ gpg --decrypt ファイル
```
※ なお、共通鍵で暗号化されたファイルも、公開鍵で暗号化されたファイルも `gpg ファイル名` または `gpg --decrypt ファイル名` のどちらでも復号可能。 GnuPG が内部的にファイル形式を自動判別し、必要に応じて復号・署名検証を行う。

自分の秘密鍵を利用した **署名** を行うこともできる。

```console:自分の秘密鍵を使ってファイルに署名する
$ gpg --clearsign ファイル    # ファイルがテキストの場合
$ gpg --sign ファイル         # ファイルがバイナリの場合
$ gpg --detach-sign ファイル  # ファイルがバイナリの場合
```

秘密鍵を使った署名は、署名したものの公開鍵によって検証することができる。

```console:署名を相手の公開鍵を使って検証する
$ gpg --verify ファイル
```

また、秘密鍵が漏洩したり、パスフレーズを忘れたり、鍵を使用不能にしたい場合に、特定の鍵が「無効である」ことを証明する失効証明書（revoke certificate。ファイル名は `revoke.asc` とすることが多い）を利用することがある。

失効証明書を公開しておくことで、無効化したいのに公開されている公開鍵が無効化される。

ただし、失効証明書は自身の秘密鍵による署名付きの通知であるため、万が一、秘密鍵を失ってしまった場合、その鍵での署名操作ができず、失効処理も行えない。


そのため、失効証明書は事前に作成しておくことが強く推奨されている。また厳重に保管する必要がある。

```console:失効証明書を作成する
$ gpg --output 失効証明書ファイル名 --gen-revoke 自分のメールアドレス
```
revoke : 取り消す

失効証明書は作成しただけでは効果を持たず、自分の秘密鍵を使って無効化処理を行う必要がある。

```console:鍵を無効化する
$ gpg --import 失効証明書ファイル名
```

# `$ scp`
Secure Copy

SSH を利用してファイルやディレクトリを転送するコマンド。

転送されるデータは暗号化される。

SSH 自体はシェルにログインするためのプロトコルであり、単体ではファイル転送の仕組みは備えていない。リモート-ローカル間でファイルをやりとりしたい場合には、`SSH + ファイル転送` 機能を併せ持つ `$ scp` を使用する。

```console:ファイルを SSH で転送する
$ scp ファイル コピー元 コピー先
```

`コピー元`、`コピー先` には [`宛先`](#宛先) を参照。

```console:ポート番号を指定する（デフォルト:22）
$ scp ファイル -P ポート番号 コピー元 コピー先
```

```console:ディレクトリを再帰的にコピーする
$ scp ディレクトリ -r コピー元 コピー先
```