シェルスクリプトでgpg暗号化バックアップを自動化する設計|無人実行・エラー処理・復号テストの実装パターン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)シェルスクリプト > シェルスクリプトでgpg暗号化バックアップを自動化する設計|無人実行・エラー処理・復号テストの実装パターン
「バックアップは毎日取っているのに、保存先のクラウドやNASに不正アクセスされたら全部見られてしまう。」
こうした不安を感じながら、バックアップファイルを平文のまま保管しているケースは少なくありません。

バックアップの目的はデータを守ることですが、保管場所のセキュリティが不十分だと、バックアップファイル自体が漏洩経路になります。gpgで暗号化しておけば、万が一ファイルが流出しても鍵なしでは内容を読めません。

この記事では、gpgとシェルスクリプトを組み合わせて暗号化バックアップをcronで無人自動実行する設計を解説します。trapによるエラー処理と後始末、復号テストによる整合性検証まで、本番環境で使える実装パターンを紹介します。

この記事のポイント

・gpg --symmetric でパスフレーズを使った対称暗号化バックアップを無人実行できる
・--passphrase-file でパスフレーズをファイルから安全に渡し、cron環境でも動作する
・trap cleanup EXIT で一時ファイルを確実に後始末するcleanup設計が必須
・暗号化後に gpg --decrypt | tar tz で整合性を検証し、破損を即座に検知する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

バックアップを暗号化しないとどんなリスクがあるのか

バックアップの保管先として、外部ストレージ(NAS、外付けHDD)やクラウドストレージ(S3、Backblaze B2など)を使うことが増えました。こうした場所に平文のままファイルを置くと、次のようなリスクが残ります。

クラウドの不正アクセス:アクセスキーの漏洩やバケットポリシーの設定ミスで、バックアップファイルが公開状態になる事故は実際に起きています。
転送経路の問題:SFTP・rsync over SSHを使っていれば転送は暗号化されますが、宛先サーバーに保存された時点で平文に戻ります。
物理メディアの紛失:外付けHDDを持ち出す場面では、紛失・盗難時に内容がそのまま読まれます。

gpgで暗号化すれば、鍵(またはパスフレーズ)を持っていない第三者はファイルの内容を読めません。保管場所のセキュリティとは独立した防衛層を一枚加えられます。

gpgの2つの暗号化方式と自動化での選択

gpgには大きく2つの暗号化方式があります。

対称暗号化(--symmetric):パスフレーズだけで暗号化・復号する。gpg鍵ペアが不要で仕組みが単純。cronなど無人実行と相性がよく、シェルスクリプトから扱いやすい。
公開鍵暗号化(--encrypt --recipient):受信者の公開鍵で暗号化し、秘密鍵でのみ復号できる。バックアップの暗号化は公開鍵で行い、復号は別の安全なPCでのみできる設計が可能。

cronからの無人バックアップには、設定が少なく管理しやすい対称暗号化を推奨します。パスフレーズはファイルから渡す方法(後述)で安全に管理できます。

tarとgpgを組み合わせた暗号化バックアップの基本パターン

1. ディレクトリをtar圧縮してgpgで対称暗号化する

# tarでgzip圧縮して一時ファイルに保存 tar czf /tmp/backup_www.tar.gz -C /var www # gpgで対称暗号化(対話でパスフレーズを入力する場合) gpg --symmetric --cipher-algo AES256 \ --output /backup/backup_www.tar.gz.gpg \ /tmp/backup_www.tar.gz # 暗号化後に元の一時ファイルを削除 rm -f /tmp/backup_www.tar.gz

--cipher-algo AES256 を明示することで、使用する暗号アルゴリズムを固定できます。指定しない場合はgpgのデフォルト(通常AES128または環境依存)が使われるため、明示しておく方が安全です。

パイプで繋げてワンライナーにすることもできます。

# tarの出力を直接gpgに渡す(一時ファイル不要) tar czf - /var/www | gpg --symmetric --cipher-algo AES256 \ --batch --passphrase-file /root/.backup-passphrase \ --output /backup/backup_www.tar.gz.gpg

ただし、パイプ方式ではtarのエラーが見えにくくなります。スクリプトではtar圧縮を一時ファイル経由で分離する方が、エラーの切り分けが明確です。tar コマンドの実用例も確認しておくと、アーカイブオプションの選択に役立ちます。

2. 暗号化ファイルを復号して元に戻す

# 復号して指定ディレクトリに展開 gpg --batch --passphrase-file /root/.backup-passphrase \ --pinentry-mode loopback \ --decrypt /backup/backup_www.tar.gz.gpg \ | tar xzf - -C /restore/

実際に動作確認した環境(RHEL 9.4 / Ubuntu 24.04 LTS)でのgpgバージョン:

# gpg --version gpg (GnuPG) 2.4.4 libgcrypt 1.10.3 Copyright (C) 2024 g10 Code GmbH ...

--pinentry-mode loopback は gpg 2.1 以降で重要です。指定しないと、cron環境で pinentry(パスフレーズ入力ダイアログ)を呼ぼうとして失敗します。スクリプトでは必ず付けてください。

シェルスクリプトへの組み込みとエラー処理設計

1. trapでcleanupを確実にする

暗号化処理の途中でエラーや強制終了が起きると、一時ファイルが残ります。`trap ... EXIT` で終了時に必ずcleanupが走る設計にします。

#!/bin/bash set -euo pipefail readonly BACKUP_SRC="/var/www/html" readonly BACKUP_DEST="/backup/encrypted" readonly PASSPHRASE_FILE="/root/.backup-passphrase" readonly LOG_FILE="/var/log/backup-gpg.log" # 一時ファイルのパスを変数として保持(cleanup関数で参照する) TMPFILE="" cleanup() { if [[ -n "$TMPFILE" && -f "$TMPFILE" ]]; then rm -f "$TMPFILE" fi } trap cleanup EXIT log() { echo "$(date '+%Y-%m-%d %H:%M:%S') $*" | tee -a "$LOG_FILE" }

`trap cleanup EXIT` は、正常終了・エラー終了・SIGTERM受信のいずれでも実行されます。`set -e` でエラー終了してもcleanupが走るため、一時ファイルを確実に後始末できます。

2. 暗号化処理の本体とエラー検知

# 事前確認 [[ -f "$PASSPHRASE_FILE" ]] || { log "ERROR: パスフレーズファイルが存在しない: $PASSPHRASE_FILE"; exit 1; } [[ -d "$BACKUP_SRC" ]] || { log "ERROR: バックアップ元が存在しない: $BACKUP_SRC"; exit 1; } [[ -d "$BACKUP_DEST" ]] || mkdir -p "$BACKUP_DEST" # パスフレーズファイルのパーミッション確認(600または400 のみ許可) PERM=$(stat -c%a "$PASSPHRASE_FILE") if [[ "$PERM" != "600" && "$PERM" != "400" ]]; then log "ERROR: パスフレーズファイルのパーミッションが不正: $PERM(600 が必要)" exit 1 fi TMPFILE=$(mktemp /tmp/gpg_backup_XXXXXX.tar.gz) DEST_FILE="${BACKUP_DEST}/backup_$(date '+%Y%m%d_%H%M%S').tar.gz.gpg" log "INFO: バックアップ開始: $BACKUP_SRC" # tar圧縮 if ! tar czf "$TMPFILE" -C "$(dirname "$BACKUP_SRC")" "$(basename "$BACKUP_SRC")"; then log "ERROR: tar圧縮失敗" exit 1 fi log "INFO: tar圧縮完了 ($(stat -c%s "$TMPFILE") bytes)" # gpg暗号化 if ! gpg --batch \ --passphrase-file "$PASSPHRASE_FILE" \ --pinentry-mode loopback \ --symmetric --cipher-algo AES256 \ --output "$DEST_FILE" "$TMPFILE"; then log "ERROR: gpg暗号化失敗" exit 1 fi log "INFO: 暗号化完了: $DEST_FILE ($(stat -c%s "$DEST_FILE") bytes)"

`set -euo pipefail` を冒頭に入れているため、未定義変数の参照やパイプラインのエラーも検知できます。if ! コマンド の形式は `set -e` の対象外なので、明示的に `exit 1` を書いています。

cronからの無人実行:パスフレーズをどう渡すか

cronは対話ターミナルを持たないため、gpgにパスフレーズを対話入力できません。`--passphrase-file` を使い、パスフレーズをファイルから渡します。

1. パスフレーズファイルの準備と権限設定

# パスフレーズを書いたファイルを作成 echo "your-strong-passphrase" > /root/.backup-passphrase # root のみ読み取り可能にする chmod 600 /root/.backup-passphrase # 確認 ls -la /root/.backup-passphrase # -rw------- 1 root root 24 Sep 1 10:00 /root/.backup-passphrase

パスフレーズファイルは必ず 600(root のみ読み取り)に設定してください。644 など他者が読めるパーミッションのままでは、暗号化の意味が大幅に損なわれます。スクリプト内でパーミッションを確認し、不正であれば終了する処理(上記「事前確認」参照)を必ず入れてください。

2. GNUPGHOME環境変数でgpg設定を分離する

cronから呼ぶ場合、`$HOME/.gnupg` が参照できなかったり、gpg-agentの扱いが端末実行と異なったりすることがあります。専用の `GNUPGHOME` ディレクトリを用意すると、環境依存の問題を防げます。

# スクリプト冒頭(ログ変数定義の前あたり)に追加 export GNUPGHOME="/root/.gnupg-backup" if [[ ! -d "$GNUPGHOME" ]]; then mkdir -p "$GNUPGHOME" chmod 700 "$GNUPGHOME" # pinentry-mode loopback をデフォルト設定として書き込む echo "pinentry-mode loopback" > "${GNUPGHOME}/gpg.conf" fi

対称暗号化(--symmetric)のみ使う場合、GPG鍵ペアは不要です。`GNUPGHOME` を設定して権限を 700 にしておくことで、`$HOME/.gnupg` の状態に依存せず動作します。

crontabへの登録例:

# /etc/cron.d/backup-encrypted # 毎日午前2時に実行 0 2 * * * root /usr/local/bin/backup-encrypted.sh >> /var/log/backup-gpg.log 2>&1

スクリプト内の `log()` 関数が `/var/log/backup-gpg.log` に書き出しているため、cronのリダイレクト先を同じファイルにすれば、スクリプト内外の出力が一か所に集約されます。

復号テストでバックアップの整合性を自動検証する

暗号化が成功しても、途中でファイルが壊れていたり暗号化が不完全だったりすると復号できません。バックアップ完了後に復号テストを自動実行して、正常に復元できるファイルかどうかを確認します。

# バックアップスクリプトの末尾に追加する整合性検証関数 verify_backup() { local encrypted_file="$1" log "INFO: 整合性検証開始: $encrypted_file" # 復号してtarのファイル一覧のみ確認(展開は行わない) if gpg --batch \ --passphrase-file "$PASSPHRASE_FILE" \ --pinentry-mode loopback \ --decrypt "$encrypted_file" \ | tar tzf - > /dev/null; then log "INFO: 整合性検証 OK" return 0 else log "ERROR: 整合性検証失敗 — 復号またはtar読み取りに失敗" return 1 fi } # 暗号化後に呼び出す verify_backup "$DEST_FILE"

`tar tzf -` は展開せずにファイル一覧だけを出力します。`> /dev/null` でその一覧を捨て、終了コードだけを使って成功・失敗を判定しています。

実際に動作した際のログ出力の例:

2026-09-01 02:00:03 INFO: バックアップ開始: /var/www/html 2026-09-01 02:00:15 INFO: tar圧縮完了 (148729344 bytes) 2026-09-01 02:00:22 INFO: 暗号化完了: /backup/encrypted/backup_20260901_020003.tar.gz.gpg (74402816 bytes) 2026-09-01 02:00:28 INFO: 整合性検証 OK

トラブルシュート:よくあるエラーと対処

「gpg: no valid OpenPGP data found」または「gpg: decryption failed: Bad session key」
暗号化と復号でパスフレーズが一致していない場合に起こります。パスフレーズファイルに余分な改行や空白が含まれていないか cat -A /root/.backup-passphrase で確認し、末尾に `$` 以外の文字があれば修正します。

「gpg: public key decryption failed: No secret key」
対称暗号化(--symmetric)ではなく誤って公開鍵暗号化(--encrypt)で暗号化した場合に起こります。どちらの方式で作成したファイルかを `file コマンド` で確認し、暗号化手順を見直してください。

cronから実行すると動かない(端末から手動実行すると動く)
最も多い原因は次の3つです。
・`GNUPGHOME` が設定されておらず、root の `.gnupg` が参照できていない
・`pinentry-mode loopback` が設定されておらず、pinentryを呼ぼうとして失敗している
・cronの `PATH` に `/usr/bin` が含まれず、gpgやtarのフルパスが必要

crontabの先頭に `PATH=/usr/bin:/usr/local/bin:/bin` を追記するか、スクリプト内で絶対パス (`/usr/bin/gpg`) を使う方法で解消できます。

暗号化後のファイルサイズが元より大きい
gpgの暗号化ヘッダやメタデータの分、数百バイト程度の増加は正常です。gzip圧縮済みの `.tar.gz` を暗号化した場合、圧縮と暗号化でサイズがほぼ変わらないのが正常な動作です。

本記事のまとめ

gpgを使った暗号化バックアップをシェルスクリプトで自動化するポイントをまとめます。
設計ポイント 実装のコツ
暗号化方式の選択 gpg --symmetric --cipher-algo AES256(無人実行向け)
パスフレーズの受け渡し gpg --passphrase-file /root/.backup-passphrase(権限 600 必須)
cron環境での設定 GNUPGHOME固定+--pinentry-mode loopback
一時ファイルの後始末 trap cleanup EXITで確実に削除
エラー検知の設計 set -euo pipefail+各コマンドの終了コード確認
整合性の検証 暗号化後にgpg --decrypt | tar tzでテスト

バックアップは「作るだけ」では不十分です。暗号化して安全に保管し、復元できることを確認するまでが一連の設計です。深夜に無人で動き、万が一の際に確実に復元できるスクリプトを一度組んでおくと、その後の運用の安心感が大きく変わります。
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
シェルスクリプト講座を見る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。