固定IPじゃなくてもOK!MyDNSで自宅回線のグローバルIPを自動更新するシェルスクリプト

Linux


自宅回線(動的IP)でも、MyDNSを使えば独自のホスト名で外部公開が可能です。この記事では、**グローバルIPの変化を検知してMyDNSへ自動更新(ログイン更新)**するシェルスクリプトと、cron/systemdでの定期実行方法、安全に運用するためのポイントを解説します。


この記事でできること

  • 自分のグローバルIPアドレスを複数サービスで取得して照合
  • IPが変わっていたら**MyDNSに更新(ログイン)**を投げる
  • 1分おきなどで自動実行(cron / systemd)
  • 認証情報を安全に扱うための実装例つき

前提条件

  • Linux系OS(bash可動環境)
  • curl と(オリジナル同様)wget のいずれか
  • MyDNSのアカウントを取得済み(ゾーン/ホスト設定済み)

⚠️ セキュリティ注意
記事やGitリポジトリにID/パスワードの平文を載せないでください。既に公開されてしまった場合は直ちにパスワード変更を。


仕組み(概要)

  1. 外部のIP照会サービスから現在のグローバルIPを取得
  2. 前回保存したIP(oldip)と比較
  3. 変化があれば、MyDNSへBasic認証でアクセス(=更新トリガ)
  4. 正常終了後、oldipを新しいIPで上書き

シンプル版スクリプト(元ロジック準拠・伏せ字済)

まずは「動く最小例」。後半に**強化版(HTTPS/リトライ/IPv6対策/ロック)**も用意しています。

#!/bin/sh
# MyDNS: グローバルIP変化時のみ更新(簡易版)

ACC="mydns_account"      # ← あなたのアカウント名に置換
PASSWORD="********"      # ← パスワードは直書きせず環境変数化推奨
STATE_FILE="/opt/oldip"

IPINFO="$(curl -s inet-ip.info)"
KAKUNIN="$(curl -s kakunin.teraren.com)"
GLOBALIP="$(curl -s globalip.me)"

# 初回ファイル作成
if [ ! -e "$STATE_FILE" ]; then
  echo "0.0.0.0" > "$STATE_FILE"
  echo "状態ファイルを作成: $STATE_FILE"
fi

OLDIP="$(cat "$STATE_FILE")"

UPDATE() {
  # ※ 本番はHTTPS推奨。wgetでもcurlでもOK
  /usr/bin/wget -O - "http://${ACC}:${PASSWORD}@www.mydns.jp/login.html" >/dev/null 2>&1
}

if [ "$IPINFO" != "$OLDIP" ]; then
  UPDATE && echo "$IPINFO" > "$STATE_FILE" && exit 0
elif [ "$KAKUNIN" != "$OLDIP" ]; then
  UPDATE && echo "$KAKUNIN" > "$STATE_FILE" && exit 0
elif [ "$GLOBALIP" != "$OLDIP" ]; then
  UPDATE && echo "$GLOBALIP" > "$STATE_FILE" && exit 0
else
  echo "グローバルIPの変更はありませんでした。"
fi

ポイント

  • 取得先を3つ用意して取りこぼしを回避(どれかが落ちていてもOK)。
  • STATE_FILE/opt/oldip)に前回IPを保存し、変化時のみ更新
  • 可能なら HTTPS環境変数(例:$MYDNS_ACCOUNT/$MYDNS_PASSWORD)の利用を強く推奨。

セキュア & 堅牢な「改良版」スクリプト

  • HTTPSでMyDNSへ接続
  • 複数IPソースを順に試し、正規表現でIPv4を検証
  • 環境変数でID/パスを注入(例:MYDNS_ACCOUNT / MYDNS_PASSWORD
  • ロックで多重起動防止、原子的書き込みで状態ファイルの破損防止
  • IPv6経路での誤検出を避けるため**curl -4**使用
#!/usr/bin/env bash
set -euo pipefail

#=== 設定 ===#
: "${MYDNS_ACCOUNT:?MYDNS_ACCOUNT を環境変数で指定してください}"
: "${MYDNS_PASSWORD:?MYDNS_PASSWORD を環境変数で指定してください}"

STATE_DIR="/var/lib/mydns"
STATE_FILE="$STATE_DIR/oldip"
LOCK_FILE="/run/mydns-updater.lock"

IP_SOURCES=(
  "https://api.ipify.org"
  "https://ifconfig.me/ip"
  "https://inet-ip.info"          # 通らない場合あり → フォールバックでカバー
  "http://globalip.me"
  "http://kakunin.teraren.com"
)

#=== 初期化 ===#
mkdir -p "$STATE_DIR"
[ -f "$STATE_FILE" ] || echo "0.0.0.0" > "$STATE_FILE"

#=== ロック ===#
exec 9>"$LOCK_FILE"
flock -n 9 || { echo "別プロセスが実行中のため終了"; exit 0; }

#=== IPv4判定(簡易) ===#
is_ipv4() {
  [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
}

#=== IP取得 ===#
get_public_ip() {
  for url in "${IP_SOURCES[@]}"; do
    ip="$(curl -4sSf "$url" || true)"
    ip="${ip//$'\r'/}"; ip="${ip//$'\n'/}"
    if is_ipv4 "$ip"; then
      echo "$ip"; return 0
    fi
  done
  return 1
}

CURRENT_IP="$(get_public_ip || true)"
if [ -z "${CURRENT_IP:-}" ]; then
  echo "IP取得に失敗しました"; exit 1
fi

OLD_IP="$(cat "$STATE_FILE")"

if [ "$CURRENT_IP" != "$OLD_IP" ]; then
  # MyDNSへHTTPSでログイン(更新トリガ)
  curl -4sSfu "${MYDNS_ACCOUNT}:${MYDNS_PASSWORD}" "https://www.mydns.jp/login.html" >/dev/null
  # 原子的更新
  tmp="$(mktemp)"
  echo "$CURRENT_IP" > "$tmp"
  mv "$tmp" "$STATE_FILE"
  echo "$(date +'%F %T') MyDNS更新: $OLD_IP -> $CURRENT_IP"
else
  echo "$(date +'%F %T') 変更なし: $CURRENT_IP"
fi

使い方(例)

# 一時的に環境変数で注入して実行
MYDNS_ACCOUNT="mydns_account" \
MYDNS_PASSWORD="secret_password" \
/usr/local/sbin/mydns-updater.sh

さらに安全にするなら、~/.netrc(600権限)+curl -n利用も検討してください。


定期実行の設定

1) cronで1分おきに実行(/etc/cron.d)

sudo tee /etc/cron.d/mydns-updater >/dev/null <<'EOF'
* * * * * root MYDNS_ACCOUNT=mydns_account MYDNS_PASSWORD=secret_password /usr/local/sbin/mydns-updater.sh >> /var/log/mydns-updater.log 2>&1
EOF

いただいた例のように run-parts を使う場合は、以下のようにスクリプトを配置します。

# 例:/etc/cron.1time を毎分実行
# (/etc/crontab に既に)
# */1 * * * * root run-parts /etc/cron.1time

sudo install -m 0755 /usr/local/sbin/mydns-updater.sh /etc/cron.1time/mydns-updater

2) systemdタイマー(安定運用向け)

# /etc/systemd/system/mydns-updater.service
[Unit]
Description=MyDNS updater

[Service]
Type=oneshot
Environment=MYDNS_ACCOUNT=mydns_account
Environment=MYDNS_PASSWORD=secret_password
ExecStart=/usr/local/sbin/mydns-updater.sh
# /etc/systemd/system/mydns-updater.timer
[Unit]
Description=Run MyDNS updater every minute

[Timer]
OnUnitActiveSec=60s
AccuracySec=10s
Unit=mydns-updater.service

[Install]
WantedBy=timers.target
sudo systemctl daemon-reload
sudo systemctl enable --now mydns-updater.timer
sudo systemctl status mydns-updater.timer

動作確認

  1. スクリプトを手動実行してエラーがないか確認
  2. STATE_FILE(例:/var/lib/mydns/oldip)のIPが更新されるか確認
  3. 実際にMyDNSのホスト名へ名前解決して、新しいIPを引けることを確認

トラブルシュート

  • 認証エラー:ID/パスに誤りがないか。環境変数のスコープ(cronやsystemd)に注意
  • IPv6を拾ってしまうcurl -4 を付ける
  • IP取得に失敗:利用サイトが落ちている可能性 → 複数ソース+リトライで回避
  • 多重起動:ロックファイル(flock)で防止
  • 権限エラーSTATE_FILE を置くディレクトリの所有者/権限を見直し

まとめ(安全運用の要点)

  • 認証情報は平文で直書きしない(環境変数、.netrc、秘密管理)
  • MyDNSへの更新はHTTPS+Basic認証
  • IP取得は複数ソースIPv4強制で安定化
  • 定期実行はcron か systemd timerで好みを選択
  • 公開記事・公開リポジトリに本物の資格情報を書かない(書いてしまったら即変更

これだけ見とけばシェルマスター

コメント

タイトルとURLをコピーしました