SSH
Secure Shell
ネットワークを介した通信を 暗号化 することにより、情報を安全に送受信するためのプロトコル。
通信を行うためのプロトコルとしては TELNET プロトコルがあるが、TELNET は通信内容が暗号化されない。
Linux では SSH の実装プログラムとして OpenSSH が広く利用されている。OpenSSH は openssh-client
、openssh-server
などのパッケージとして提供されている。
SSH 接続を試みる側がクライアント($ ssh
)、SSH 接続先がサーバ(sshd
デーモン)である。
sshd
は、22 番ポートをデフォルトで使用する。
暗号化アルゴリズム
-
RSA
- 広く利用されている
- DSA
- 非推奨
- ECDSA
-
ED25519
- 推奨
- 高速で安全性が高い
SSH トンネル
SSH プロトコルには ポートフォワーディング 機能が内包されている。
ポートフォワーディングとは特定の「IP アドレス」「ポート」宛の通信を別の宛先に転送する機能を指す。
SSH とポートフォワーディングを利用して通信路が確立された場合、その通信路は外側から「どこを経由して、何が転送されているか」が見えなくなるため SSH トンネル と表現される。
SSH トンネルは $ ssh -L
によって作成することができる。
重要な点として SSH 接続において、利用したいサービスは SSH サーバ上で稼働しているとは限らない。
SSH サーバはポートフォワーディングを行うことができるため、通信の中継役として利用されることが多いためである。
認証
SSH で使用される認証はいくつかの種類がある。
パスワード認証
接続しようとしている OS のユーザアカウントでログインを行うための認証。
SSH 接続に限らず、ユーザ名とパスワードを用いてログインする形の認証方式は、一般に「パスワード認証」と呼ばれる。
SSH クライアントは $ ssh-copy-id
コマンドで自身の公開鍵を SSH サーバの ~/.ssh/authorized_keys
に登録しておくことにより、以後は 公開鍵認証 で接続できるようになり、パスワード認証は省略される。
ホスト認証
SSH 接続を試みるクライアントが行う、接続先サーバが信頼できるかの認証。
ホスト認証は SSH サーバの公開鍵を用いて行われる。
具体的には SSH 接続の度にサーバから公開鍵が送信され、クライアントは自身の /etc/ssh/ssh_known_hosts
もしくは ~/.ssh/known_hosts
に保存されたサーバの公開鍵と一致するかが比較、検証される。
ただし、クライアントは初回接続のタイミングではサーバの公開鍵を保持していないため、以下のようなメッセージが表示されれる。このときはホスト認証の判断がユーザに委ねられ、yes
を入力することで初回のホスト鍵がクライアントに保存され、以後は自動で照合されるようになる。
$ 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 サーバの ~/.ssh/authorized_keys
に登録しておくことで、公開鍵認証によるログインが可能になる。
Finger Print
公開鍵のハッシュ値(要約値)。
SSH サーバは初回接続時に公開鍵をクライアントに送ってくるが、公開鍵そのものは長くて見づらいため、その鍵が本物かどうかを簡単に確認できる方法として利用される。
公開鍵と一緒に以下のファイルに保存される。
宛先
以降のコマンドでは 宛先
がたびたび登場する。
宛先は
- ユーザ名
- FQDN
- IP アドレス
- パス
などで構成される。
宛先のマシンは www.example.co.jp
のようなホスト名を含むドメイン名(FQDN)で指定することもできるし、172.0.0.1
のような IP アドレスで指定することもできる。これらは DNS によって紐づけられているので、どちらを指定しても良い。
FQDN:パス
ユーザ名@FQDN:パス
ユーザ名@IPアドレス:パス
user1@198.51.100.1:/home/user1/
/etc/ssh/ssh_config
SSH クライアント側の ssh
設定ファイル。
/etc/ssh/ssh_known_hosts
クライアントが知っている(known)サーバ情報。
具体的にはサーバの公開鍵を含む、サーバに関する情報が格納されたファイル。
クライアント側に配置する。
/etc/.ssh/known_hosts
とは異なり、全てのユーザが利用する SSH サーバの情報が格納される。
~/.ssh/known_hosts
クライアントが知っている(known)サーバ情報。
具体的にはサーバの公開鍵を含む、サーバに関する情報が格納されたファイル。
クライアント側に配置する。
サーバに対して初めて SSH 接続を行ったタイミングで情報が格納される。
/etc/ssh/ssh_known_hosts
とは異なり、特定のユーザが利用する SSH サーバの情報が格納される。
/etc/ssh/sshd_config
SSH サーバの sshd
設定ファイル。
Port 22 # SSH デフォルトポート
PermitRootLogin no # root ログインの可否
PasswordAuthentication yes # パスワード認証を許可するか
PubkeyAuthentication yes # 公開鍵認証の有効化
~/.ssh/authorized_keys
クライアントの公開鍵情報を登録するファイル。
サーバ側に配置する。
※ $ ssh-keygen
を参照
$ ssh-keygen
SSH プロトコルで使用される 公開鍵と秘密鍵 の鍵ペアを生成する。
-
公開鍵
~/.ssh/id_暗号化方式.pub
- サーバーの
~/.ssh/authorized_keys
に配置することで、公開鍵認証が有効になる($ ssh-copy-id
を使用して配置)
-
秘密鍵
~/.ssh/id_暗号化方式
- ローカル環境で安全に保存する必要がある鍵
暗号化方式
には 暗号化アルゴリズムの種類 が入る。
$ ssh-keygen
実行後はパスフレーズの入力を求められる(空文字でも良い)。
$ ssh-keygen -f ファイルパス
指定したファイルパスで秘密鍵が作成され、さらに末尾に .pub
のついたファイルが公開鍵として作成される。
$ ssh-keygen -t 種類
SSH の公開鍵はとても長いデータ(数百〜数千文字の文字列)であり、それをそのまま比べたり、人間が目で見て確認するのは大変であるため、公開鍵の内容をハッシュ関数(SHA256など)で短く変換した フィンガープリント(指紋) を利用することがある。
$ ssh-keygen -lf ファイル
$ ssh-keygen -R FQDN
$ ssh-copy-id
SSH 接続の度にサーバから要求される、OS のユーザアカウントのパスワード入力(パスワード認証)を省略するためのコマンド。
実行すると SSH クライアント自身の公開鍵(~/.ssh/id_暗号化方式.pub
)が、サーバの ~/.ssh/authorized_keys
末尾に追加される。
$ ssh-copy-id サーバのIPアドレス
$ ssh-agent
ssh-agent
デーモン を操作するためのコマンド。
ssh-agent
は SSH の秘密鍵をメモリ上に保持することによって、ユーザが SSH 接続のたびにパスフレーズを入力する手間を省く機能を提供する。
パスフレーズの省略をしたい場合、ssh-agent
を起動した状態で $ ssh-add
コマンドによって ssh-agent
に秘密鍵を登録する必要がある。
現在のシェルで起動する方法($ eval
)と、新しいシェルセッション(サブシェル)で一時的に起動する方法がある。
$ eval $(ssh-agent)
ssh-agent
をバックグラウンドで起動し、その出力(環境変数の設定)を現在のシェルに反映させる。
- 現在のシェルを閉じない限り、
ssh-agent
は有効 - 長時間利用や複数のコマンドを使う用途向き
$ ssh-agent bash
ssh-agent
を起動した上で、新しい bash
サブシェルを起動する。
- このサブシェル内でのみ
ssh-agent
が有効 -
$ exit
すると、サブシェルと同時にssh-agent
も終了する
ssh-agent
は 環境変数 $SSH_AUTH_SOCK
と $SSH_AGENT_PID
を利用して動作しており、これらの環境変数は ssh-agent
を起動したタイミングで自動的にセットされる。
-
$SSH_AUTH_SOCK
- プロセス間通信(IPC)を行うための UNIX ドメインソケットファイルのパス
-
ssh
クライアントはこのソケットファイルを介してssh-agent
から秘密鍵を受け取る
-
$SSH_AGENT_PID
- 実行中の
ssh-agent
のプロセス ID
- 実行中の
サブシェルを閉じると、これらの環境変数も消えるため、以降 ssh-agent
は使用できない。
ただし多くの デスクトップ環境 では、ログイン時に ssh-agent
が自動起動しており、明示的に ssh-agent
を起動しなくても使用できる場合もある。
$ ssh-add
パスフレーズをメモリ上に保持することで、接続の度に毎回要求されるパスフレーズの入力を省略するためのコマンド。
ssh-agent
を起動した後に、$ ssh-add
で秘密鍵を登録する。
$ ssh-add ~/.ssh/id_rsa
ここ入力したパスフレーズは、以降の SSH 接続で入力する必要がなくなる。
$ ssh-add -l
$ ssh-add -D
$ ssh
$ ssh 宛先
宛先
は具体的に以下を指す。
$ ssh -p ポート番号 IPアドレス
$ ssh -p ポート番号 FQDN
$ ssh -p ポート番号 ユーザ名@FQDN
$ ssh -l ユーザ名 宛先
$ ssh -i パス 宛先
※ -i
で明示的に指定しない場合のデフォルトの秘密鍵は ~/.ssh/id_暗号化方式
$ ssh -X
/ X転送
リモートサーバ上の GUI アプリケーションを $ ssh
コマンドを実行したローカル環境で表示し、ローカル環境上で GUI 操作できるようにする機能を X 転送 と言う。
X 転送は X Window System 上で動作する。
$ ssh -X 宛先
-X
と同様に X 転送を行うオプションに -Y
は、-X
よりもセキュリティが弱いものの、アプリケーションの動作上は障害が発生しづらく、信頼性が高い。
-
-X
:安全性のために制限がある -
-Y
:制限が緩いが、リスクが高い
$ ssh -L
/ SSH トンネル
$ ssh -L ローカルポート:接続先:接続先ポート 中継ホストのユーザ名@中継先
GnuPG
Gnu Privacy Guard
ファイルの暗号化、復号を行うためのツール。秘密鍵を利用した電子署名も行うことができる。
また共通鍵暗号方式、公開鍵暗号方式どちらにも同じ $ gpg
コマンドで利用することができる。
アメリカで開発され、アメリカ国内でしか利用できない暗号化ツール PGP(Pretty Good Privacy)と互換性を持つフリーソフトウェアとして $ gpg
に実装された。
$ gpg
暗号化・復号、電子署名、鍵管理を行うための GnuPg 実装プログラム。
対話的、またはスクリプトでの操作が可能。
コンポーネントとして gpg-agent
デーモン が秘密鍵の管理、キャッシュを行っている。
共通鍵によるファイル暗号化 / 復号
$ gpg -c ファイル名
暗号化を行う際は、パスフレーズの入力を求められる。
パスフレーズは文字列だが、内部的には GnuPG がこれをバイナリ形式の共通鍵生成に利用している(パスフレーズ = 共通鍵
ではない)。
共通鍵の生成には、生成の度にランダムな初期化ベクトル(IV)が使用されているため、例えば、複数のファイルに同じパスフレーズを使ったとしても、毎回、異なる共通鍵になる。
また暗号化ファイルは、元ファイル名の末尾に .gpg
が付与された形で作成される。
暗号化ファイルは --output
オプションで明示的に出力名を指定することもできる。
$ gpg -c ファイル名 --output 出力先
ファイルの復号を行う際は、再びパスフレーズの入力が求められる。
$ gpg ファイル名.gpg
公開鍵 / 秘密鍵によるファイルの暗号化 / 復号
$ gpg --gen-key
キーペアを作成する際には本名(Real name)とメールアドレス、パスフレーズの入力を求められる。
また 鍵の種類、鍵の長さ(2048
ビット以上推奨)を細かく設定したい場合には --full-generate-key
オプションを使用する。
$ 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 のファイル名) |
キーボックス |
※ キーボックスは従来のキーリングに代わって導入された新形式であり、より柔軟な構造と性能向上を目的としている。
$ gpg --list-keys
$ gpg --list-secret-keys
エクスポートを行う際は --armor
オプションを付与することで、ASCII
形式のテキストファイル(.asc
)として出力される。
$ gpg --output 出力ファイル名 --armor --export 自分のメールアドレス
受け取ったテキスト形式の鍵ファイルは、あくまでも交換用の形式であるため、受け取った後にインポートを行う必要がある。これにより、ローカル環境の GnuPG 内のデータベースに通信相手の公開鍵が保存される。
$ gpg --import 公開鍵ファイル
通信相手のメールアドレスは、公開鍵ファイル(.asc
)内部の UID
情報に含まれており、これを用いてユーザが識別される。インポート時に通信相手のメールアドレスを指定しないのはそのためである。(参考。UID
は $ gpg --list-keys
で確認可能。)。
インポートした相手の公開鍵は、送信するファイルの暗号化に利用される。
$ gpg --encrypt --recipient 相手のメールアドレス ファイル
$ gpg --encrypt --armor --recipient 相手のメールアドレス ファイル # 暗号化ファイルがテキスト形式になる
$ gpg ファイル
$ gpg --decrypt ファイル
※ なお、共通鍵で暗号化されたファイルも、公開鍵で暗号化されたファイルも gpg ファイル名
または gpg --decrypt ファイル名
のどちらでも復号可能。 GnuPG が内部的にファイル形式を自動判別し、必要に応じて復号・署名検証を行う。
自分の秘密鍵を利用した 署名 を行うこともできる。
$ gpg --clearsign ファイル # ファイルがテキストの場合
$ gpg --sign ファイル # ファイルがバイナリの場合
$ gpg --detach-sign ファイル # ファイルがバイナリの場合
秘密鍵を使った署名は、署名したものの公開鍵によって検証することができる。
$ gpg --verify ファイル
また、秘密鍵が漏洩したり、パスフレーズを忘れたり、鍵を使用不能にしたい場合に、特定の鍵が「無効である」ことを証明する失効証明書(revoke certificate。ファイル名は revoke.asc
とすることが多い)を利用することがある。
失効証明書を公開しておくことで、無効化したいのに公開されている公開鍵が無効化される。
ただし、失効証明書は自身の秘密鍵による署名付きの通知であるため、万が一、秘密鍵を失ってしまった場合、その鍵での署名操作ができず、失効処理も行えない。
そのため、失効証明書は事前に作成しておくことが強く推奨されている。また厳重に保管する必要がある。
$ gpg --output 失効証明書ファイル名 --gen-revoke 自分のメールアドレス
revoke : 取り消す
失効証明書は作成しただけでは効果を持たず、自分の秘密鍵を使って無効化処理を行う必要がある。
$ gpg --import 失効証明書ファイル名
$ scp
Secure Copy
SSH を利用してファイルやディレクトリを転送するコマンド。
転送されるデータは暗号化される。
SSH 自体はシェルにログインするためのプロトコルであり、単体ではファイル転送の仕組みは備えていない。リモート-ローカル間でファイルをやりとりしたい場合には、SSH + ファイル転送
機能を併せ持つ $ scp
を使用する。
$ scp ファイル コピー元 コピー先
コピー元
、コピー先
には 宛先
を参照。
$ scp ファイル -P ポート番号 コピー元 コピー先
$ scp ディレクトリ -r コピー元 コピー先