Proxmox VEのルートCA証明書で仮想サーバの証明書を発行する方法

皆さんこんにちは。
我が家では2025年春からProxmox VEの仮想環境を使っています。

ASUS NUC 14 Proを採用し、この小ささにメモリ64GB、ストレージ3TBと強力なスペックになっています。
仮想サーバやLinuxコンテナを立て放題なので、便利に活用しています。

さてProxmox VEを使い始めた方が最初にはまるのは、管理画面にアクセスすると証明書エラーが出る という件ではないでしょうか。

これはProxmox VE内で自己署名のルートCA証明書(/etc/pve/pve-root-ca.pem)を用いて管理画面のサーバ証明書を発行しているためで、ブラウザから見ると「知らないCAが発行した証明書」に見えるのが原因です。エラーを消すには、商用(公的CA)の証明書に取り替えるか、このルートCA証明書を各デバイスにインポートして信頼させる必要があります。

Certificate Management - Proxmox VE
Import certificate in browser - Proxmox VE

私も、自分が使う各デバイスにこのルートCA証明書をインストールしました。さて、せっかくルートCAを信頼させたのなら、理屈上は このCAで署名した自己発行の証明書も同じように「信頼された証明書」として使えるはず です。仮想サーバごとにこのCAで証明書を発行してやれば、管理画面だけでなく、自宅環境の各サービスもブラウザの証明書エラーなしでHTTPSアクセスできるようになります。

この記事では、Proxmoxの内部CAを使って 仮想サーバ用の証明書をFQDN入力だけで自動発行するスクリプト を作ったので、その手順を解説します。

概要

Proxmoxに標準で用意されている内部CA( /etc/pve/pve-root-ca.pem と秘密鍵 /etc/pve/priv/pve-root-ca.key )を使って、仮想サーバ用のサーバ証明書を発行したい。手動で行う場合は毎回 opensslコマンドを4回叩き、CSRの入力項目を対話で答えるのが面倒となるので、FQDNだけ入力すれば秘密鍵・CSR・証明書まで一気に作る ようにしました。

手動の手順

手動で行う場合の手順はこんな感じで、定期的に行うのはそれなりに面倒です。

  1. 秘密鍵の作成
    openssl genrsa -out server.key 2048
  2. CSRの作成
    openssl req -new -key server.key -out server.csr(Country/State/Org…を対話入力)
  3. SAN定義ファイルの作成
    subjectAltName = DNS:...
  4. Proxmox CAで署名
    openssl x509 -req -CA pve-root-ca.pem -CAkey pve-root-ca.key ...

スクリプト

上記の手順を自動化するシェルスクリプトです。

#!/usr/bin/env bash
#
# issue-vm-cert.sh
#   Proxmox の CA(/etc/pve/pve-root-ca.pem, /etc/pve/priv/pve-root-ca.key)を使って
#   仮想サーバ用の証明書を発行する。
#   ユーザが入力する FQDN だけを変数にし、秘密鍵・CSR・証明書を自動生成する。
#
#   実行例:
#     sudo ./issue-vm-cert.sh
#     sudo ./issue-vm-cert.sh your-vm.example.com
#     sudo ./issue-vm-cert.sh your-vm.example.com --days 825
#
#   ※ CA秘密鍵を読むため root 権限(sudo)が必要です。
#
set -euo pipefail

# ---- 固定設定(手順書のCSR入力項目に対応) ---------------------------------
COUNTRY="JP"
STATE="Tokyo"
LOCALITY="Chiyoda-ku"
ORG="Example Inc."
ORG_UNIT=""          # 手順書では空欄
EMAIL=""             # 手順書では空欄

# CA のパス(Proxmox 既定)
CA_CERT="/etc/pve/pve-root-ca.pem"
CA_KEY="/etc/pve/priv/pve-root-ca.key"

# 証明書の有効日数
#   ブラウザの上限に注意: Chrome は 398 日超、Apple(macOS/iOS/Safari) は 825 日超の
#   サーバ証明書を信頼しない(ERR_CERT_VALIDITY_TOO_LONG 等)。
#   どのブラウザでも通る安全値として 398 日を既定にする。
#   (手順書は 3650 日だが、それだと証明書エラーになるため変更)
DAYS=398

# 出力先ディレクトリ(既定はカレント)
OUTDIR="."

# ---- 引数パース -----------------------------------------------------------
FQDN=""
while [[ $# -gt 0 ]]; do
  case "$1" in
    --days)   DAYS="$2"; shift 2 ;;
    --outdir) OUTDIR="$2"; shift 2 ;;
    -h|--help)
      grep -E '^#( |$)' "$0" | sed 's/^# \{0,1\}//'
      exit 0 ;;
    -*)
      echo "不明なオプション: $1" >&2; exit 1 ;;
    *)
      FQDN="$1"; shift ;;
  esac
done

# ---- FQDN の取得(未指定なら対話入力) ------------------------------------
if [[ -z "$FQDN" ]]; then
  read -rp "仮想サーバの FQDN を入力してください (例: your-vm.example.com): " FQDN
fi

# 前後空白除去
FQDN="$(echo "$FQDN" | tr -d '[:space:]')"

if [[ -z "$FQDN" ]]; then
  echo "エラー: FQDN が空です。" >&2
  exit 1
fi

# 簡易バリデーション(英数字・ハイフン・ドットのみ許可)
if ! [[ "$FQDN" =~ ^[A-Za-z0-9.-]+$ ]]; then
  echo "エラー: FQDN に使用できない文字が含まれています: $FQDN" >&2
  exit 1
fi

# ---- 事前チェック ---------------------------------------------------------
for f in "$CA_CERT" "$CA_KEY"; do
  if [[ ! -r "$f" ]]; then
    echo "エラー: CA ファイルを読めません: $f" >&2
    echo "  root 権限(sudo)で実行しているか、Proxmox ホスト上で実行しているか確認してください。" >&2
    exit 1
  fi
done

mkdir -p "$OUTDIR"

# 出力ファイル名は FQDN ベースにして複数VMでも衝突しないようにする
KEY="$OUTDIR/$FQDN.key"
CSR="$OUTDIR/$FQDN.csr"
CRT="$OUTDIR/$FQDN.crt"
EXT="$OUTDIR/$FQDN.ext.cnf"

# 既存ファイルがあれば確認
for f in "$KEY" "$CSR" "$CRT"; do
  if [[ -e "$f" ]]; then
    read -rp "既存ファイル $f を上書きしますか? [y/N]: " ans
    [[ "$ans" =~ ^[Yy]$ ]] || { echo "中止しました。"; exit 1; }
  fi
done

echo "=========================================="
echo " FQDN     : $FQDN"
echo " 出力先   : $OUTDIR"
echo " CA cert  : $CA_CERT"
echo " 有効日数 : $DAYS"
echo "=========================================="

# ---- ① 秘密鍵の作成 -------------------------------------------------------
echo "[1/4] 秘密鍵を作成: $KEY"
openssl genrsa -out "$KEY" 2048
chmod 600 "$KEY"

# ---- ② CSR の作成(-subj で入力項目を非対話で指定、CN=FQDN) --------------
echo "[2/4] CSR を作成: $CSR"
SUBJ="/C=$COUNTRY/ST=$STATE/L=$LOCALITY/O=$ORG"
[[ -n "$ORG_UNIT" ]] && SUBJ="$SUBJ/OU=$ORG_UNIT"
SUBJ="$SUBJ/CN=$FQDN"
[[ -n "$EMAIL" ]] && SUBJ="$SUBJ/emailAddress=$EMAIL"
openssl req -new -key "$KEY" -out "$CSR" -subj "$SUBJ"

# ---- ③ SAN 定義ファイルの作成(DNS=FQDN) ---------------------------------
echo "[3/4] SAN 定義ファイルを作成: $EXT"
cat > "$EXT" <

使い方はこんな感じです。

chmod +x issue-vm-cert.sh
sudo ./issue-vm-cert.sh

FQDNを聞かれますので、証明書を作成したいFQDNを入れてください。
カレントディレクトリに 秘密鍵(.key)、証明書署名要求(.csr)、証明書(.crt)が出力されます。
これらを利用したいサーバに転送して活用してください

 

Topics サーバ証明書の期限について

最近、サーバ証明書の期限はどんどん短くなる傾向にあります。
今年、CA/Browser Forumがサーバ証明書の有効期間を段階的に短縮する決議をしておりますが、段階的に証明書の期限が短くなっており、2029年3月以降は47日以下の証明書しか利用できない(それ以上はエラーになる)予定です。

但し あくまで現時点においては、上記手順で発行した自己サーバ証明書は制約の影響を受けないようです。

  • 47日ルールはCA/Browser Forumのパブリックルートプログラム向け規則で、ブラウザ/OSのルートストアに載る公的CA(DigiCert、Let’s Encrypt等)が対象
  • Proxmoxの内部CAはどのルートストアにも載っていないプライベートCAなので規制対象外
  • 期間上限を強制するブラウザ検証も公的チェーンにのみ適用され、端末に手動インストールした内部CAには効かない

上記のスクリプトも活用いただければと思います。

コメント