関連

対称暗号(共通鍵暗号)方式でファイルの暗号化と復号

暗号化には-c,--symmetricオプションを指定します。そして暗号化したいファイルをパラメーターとして渡します。
暗号化のアルゴリズムはデフォルトでAES128になります。これを変えたい場合は--cipher-algoオプションを使います。

例えば周りに中身を隠したいsecrets.jsonファイルを暗号化するには以下のようなコマンドを実行します。

# cat password
# foo

cat password \
| gpg --passfrase-fd 0 --batch --yes --symmetric --cipher-algo AES256 secrets.json

いくつか追加のオプションが出てきました。

--passfrase-fd 0は、標準入力からパスフレーズを読み込む為のオプションです。passwordファイルにパスフレーズを記載して、そこからcatで標準入力へ渡します。

--passfrase-fd--batchと共に使う必要がありますが、[y/N]と聞かれるようなユーザーの入力待ちの状態にしてしまうとエラーになってしまうので、さらに--yesを追加して全てyesで通します。
[y/N]で聞かれるのは例えば、「ファイルを上書きしますか」などがあります。

ログに残っても大丈夫なら--passphrase $passphraseと直接パスフレーズを指定しても大丈夫です。

これがうまくいくとsecrets.json.pgpというファイルが作られます。

次にsecrets.json.pgpを復号してみます。(長いので 1 オプション毎改行します)

gpg \
  --passphrase="$LARGE_SECRET_PASSPHRASE" \
  --batch \
  --yes \
  --output secrets.json \
  --decrypt \
  secrets.json.gpg

--decryptは「復号」コマンドになります。復号する対象はパラメーターで渡します。--outputは復号した内容を写すファイルを指定できます。これを省略すると内容は標準出力されます。よって以下でも同じ事になります。

gpg \
  --passphrase="$LARGE_SECRET_PASSPHRASE" \
  --batch \
  --yes \
  --quiet \
  --decrypt \
  secrets.json.gpg > secrets.json

--output--quietになりました。デフォルトでは以下のようなログが出る為、リダイレクトで写す為に、それらを出ないようにします。

# gpg: AES256 encrypted data
# gpg: encrypted with 1 passphrase

キーペアを生成

--full-generate-keyオプションを使います。このオプションを指定するとインタラクティブに作成できます。例えば、GitHub アカウントに登録する GPG キーなら、鍵の種類を RSA and RSA、サイズを4096にする必要があります。

正常に作成できたら以下を実行します。

gpg --list-secret-keys --keyid-format LONG
# ...
# sec   rsa4096/CAE49ABA5B7E19AB 2020-04-19 [SC]
# ...

出力結果にsecから始まる行があるので探し、この例であればCAE49ABA5B7E19ABに当たる部分をコピーします。そして「コピーした内容」を次のコマンドに渡します。うまくいけば公開鍵が表示されます。

gpg --armor --export <コピーした内容>
# -----BEGIN PGP PUBLIC KEY BLOCK-----
# ...
# -----END PGP PUBLIC KEY BLOCK-----

キーのエクスポートとインポート

以下で鍵を取り出し、

gpg --export-secret-key -a > secretkey.asc

以下のように取り込みます。

gpg --batch --import secretkey.asc

sshできる別のsshマシンにエクスポートする場合これは一行で書けます。

gpg --export-secret-key -a | ssh $machine gpg --batch --import -

ubuntu のバージョンは18.04を使います。また、ubuntu のインストールが終わって最初にターミナルを立ち上げた直後を想定してます。

cat /etc/lsb-release
# DISTRIB_ID=Ubuntu
# DISTRIB_RELEASE=18.04
# DISTRIB_CODENAME=bionic
# DISTRIB_DESCRIPTION="Ubuntu 18.04.4 LTS"

また ngrok pro アカウントなので、このアカウント上で以下を行ってます。異なるプランの場合は参考程度にしてください。
また、同じプランの場合でも、悩みながらやった為、無駄な過程が含まれてる可能性があります。

流れは以下の通りです。

  1. ngrok ダウンロード

  2. sshd 周り

  3. ssh 周り

  4. tcpトンネリング

1. ngrok ダウンロード

まず以下で ngrok バイナリが ZIP 化されたファイルをダウンロードしてきます。

curl -O https://bin.equinox.io/c/4VmDzA7iaHb/ngrok-stable-linux-amd64.zip
# ngrok-stable-linux-amd64.zip が作られる

ubuntu には最初この ZIP を解凍する為のコマンドが無いので、そのためのコマンドunzipをインストールします。

sudo apt update
sudo apt install unzip -y

先程のファイルを解凍します。

unzip ngrok-stable-linux-amd64.zip
ls 
# ngrok があるハズ

解凍すると同じディレクトリにngrokバイナリができてます。ngrokを使うために認証が必要なので以下で認証も行っておきます。

./ngrok authtoken 

2. sshd 周り

どうやらsshdを起動していないと、ssh時にssh_exchange_identification: Connection closed by remote hostというエラーに遭遇します。

これを回避する為にまず、sudo vim /etc/ssh/sshd_configで設定ファイルを開き以下の2行のコメントアウトと編集をします。(2行は続けてではなく離れてます)

ubuntu on WSL で作業します。

# TCPKeepAlive yes
# ClientAliveInterval 0

を以下に編集。

TCPKeepAlive yes
ClientAliveInterval 600

また以下で/etc/ssh/ssh_host_rsa_keyというファイルも作ります。

ssh-keygen -f /etc/ssh/ssh_host_rsa_key -N '' -C '' -b 4096 -t rsa -m PEM
# `/etc/ssh/ssh_host_rsa_key` と `/etc/ssh/ssh_host_rsa_key.pub` ができる

できたらsshdを起動します。

/etc/init.d/sshd start

3. ssh 周り

鍵でsshで接続できるようにする為authorized_keyなどを作ります。まだ ubuntu on WSL で作業します。

# `~/.ssh`ディレクトリ作成
mkdir ~/.ssh && chmod 700 ~/.ssh
# `~/.ssh/id_rsa`と`~/.ssh/id_rsa.pub`作成
ssh-keygen -f ~/.ssh/id_rsa -N '' -C '' -b 4096 -t rsa -m PEM
# 公開鍵で`~/.ssh/authorized_keys`を作る
cat ~/.ssh/id_rsa.pub > ~/.ssh/authorized_keys \
&& chmod 600 ~/.ssh/authorized_keys

ここまで済んだら、以下のコマンドで表示される、-----BEGIN RSA PRIVATE KEY-----から-----END RSA PRIVATE KEY-----\nまでをコピーしてローカルマシンで利用しやすいようにslackなどに貼り付けて送ります。

cat ~/.ssh/id_rsa
# -----BEGIN RSA PRIVATE KEY-----
# ...
# -----END RSA PRIVATE KEY-----
#

ローカルマシンに戻り、先程コピペしたものから秘密鍵ファイル(ここでは~/.ssh/wsl)を作り、以下でパーミッションも整えます。

chmod 600 ~/.ssh/wsl
  1. tcpトンネリング

ubuntu on WSL に戻り以下で tcp トンネリングを行います。

./ngrok tcp 22 --region jp

すると以下のような感じでログがでます。

Session Status                online
Account                       純 (Plan: Pro)
Version                       2.3.35
Region                        Japan (jp)
Web Interface                 http://127.0.0.1:4040
Forwarding                    tcp://0.tcp.jp.ngrok.io:15368 -> localhost:22

ここのForwardingというのがsshするのに必要な情報です。ここで上記の場合ホストが0.tcp.jp.ngrok.io、ポートが15368で待ち構えられてるというのが分かります。

では、これまでの情報からsshしてみます。(ちなみにユーザー名はnju33

# -o 'StrictHostKeyChecking no' はホスト検証回避の為
ssh -o 'StrictHostKeyChecking no' nju33@0.tcp.jp.ngrok.io -p 15368 -i ~/.ssh/wsl
# Welcome to Ubuntu 18.04.4 LTS (GNU/Linux 4.4.0-18362-Microsoft x86_64)
# ...
# nju33@DESKTOP-MLLARGC:~$

Welcome ~がでたら完了です。

コマンドを実行すると、標準出力または標準エラー出力によってログがターミナル上に表示されることがあります。

このログを取っておきたい時や誰かに共有したい時に、そのログをコピペするのも1つの手ですが、ファイルにそのまま書き出した方が扱いやすかったり変なコピペミスなどを回避できたりします。

標準出力

標準出力をファイルに吐き出すには>または>>(もしくは1>または1>>)を使います。>>>の違いはファイル全体の上書きか、追記か、の違いがあります。
この>はリダイレクトと呼ばれます。

以下はstdout.logにログを出力しているシェルスクリプトの例です。

#!/usr/bin/env bash

# stdout.log をリセット
: > stdout.log

echo foo >> stdout.log
echo bar >> stdout.log

このシェルスクリプトを実行するとstdout.logはこうなっているハズです。

cat stdout.log
# foo
# bar

: > stdout.log部分は、これ無しで何度もこのシェルスクリプトを実行すると、foo\nbar...が追記され続けてしまうので、前回のログを消すために行ってます。

標準エラー出力

エラーの場合は2>または2>>を使ってファイルに出力できます。

以下はstderr.logにログを出力しているシェルスクリプトの例です。
この中で標準エラー出力を手っ取り早く試す為にecho ... >&2を使ってます。この>&2または1>&2は、標準出力を標準エラー出力としてログを出すという意味です。

#!/usr/bin/env bash

# stderr.log をリセット
: > stderr.log

echo foo >&2 2>> stderr.log
echo bar >&2 2>> stderr.log

このシェルスクリプトを実行するとstdout.logも先程の標準出力と同じ結果になります。

cat stderr.log
# foo
# bar

両方

1つのコマンドで両方に対処することもできます。

#!/usr/bin/env bash

log() {
  if [ $1 -eq 1 ]; then
    echo "$2"
  else
    echo "$2" >&2
  fi
}

: >stdout.log >stderr.log

log 1 foo >>stdout.log 2>>stdout.log
log 2 bar >>stderr.log 2>>stderr.log

echoでは分かりづらい為、log関数を実装してます。これは最初の引数が1なら第2引数のテキストを標準出力、そうでないなら標準エラー出力してます。

>>stdout.log 2>>stdout.log部分では、標準出力はstdout.log標準エラー出力はstderr.logにというように 1 つのコマンドに対して同時に設定してます。
このシェルスクリプトを実行するとそれらログファイルが作成され、それぞれ別れて追記されてるのが分かります。

cat stdout.log
# foo
cat stderr.log
# bar

リダイレクトを消す

シェルスクリプトの中では恐らくコマンドが 1 つや 2 つではなく、>>stdout.log 2>>stdout.logという部分を毎回記述するのは面倒だけでなく、タイプミスなど凡ミスも起こしやすいので、回避する為にexecコマンドを使います。

コマンド実行の前にexec > stdout.log 2> stderr.log(>>ではないので注意)とすることで、それ以降のすべての実行ログを、execで行ったリダイレクトに渡してくれます。

例えば先程の両方セクションのシェルスクリプトは以下のようにリファクタリングできます。

#!/usr/bin/env bash

log() {
  if [ $1 -eq 1 ]; then
    echo "$2"
  else
    echo "$2" >&2
  fi
}

exec > stdout.log 2> stderr.log

log 1 foo
log 2 bar
cat stdout.log
# foo
cat stderr.log
# bar

ログファイルに出す領域を切り分けたい

execは後で「実行時にターミナルにログ出したい」などのように思っても切り替えができません。

ですが、1 つのシェルスクリプトの中で、「この領域はファイルに出したいけど、この部分のはいらない」のように切り替えたい場合は、その領域を{...}で囲み、リダイレクトするという手もあります。

以下は上記「リダイレクトを消す」セクションのシェルスクリプトを少し書き直したものです。

#!/usr/bin/env bash

log() {
  if [ $1 -eq 1 ]; then
    echo "$2"
  else
    echo "$2" >&2
  fi
}

# ファイルにログ出し
{
  log 1 foo
  log 2 bar
} >stdout.log 2>stderr.log

# ターミナルにログ出し
log 1 foo
log 2 bar

このシェルスクリプトを実行すると{...}に囲まれた部分はファイルに書き出され、そうじゃない部分は普通にターミナルにログが出力されます。

JavaScript で飯食べたい歴約 9 年、 純( nju33 ) によるノートサイトです。

このサイトではドリンク代や奨学金返済の為、広告などを貼らせて頂いてますがご了承ください。

Change Log