自宅回線(動的IP)でも、MyDNSを使えば独自のホスト名で外部公開が可能です。この記事では、**グローバルIPの変化を検知してMyDNSへ自動更新(ログイン更新)**するシェルスクリプトと、cron/systemdでの定期実行方法、安全に運用するためのポイントを解説します。
curl と(オリジナル同様)wget のいずれか⚠️ セキュリティ注意
記事やGitリポジトリにID/パスワードの平文を載せないでください。既に公開されてしまった場合は直ちにパスワード変更を。
oldip)と比較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)の利用を強く推奨。
MYDNS_ACCOUNT / MYDNS_PASSWORD)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利用も検討してください。
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
# /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
STATE_FILE(例:/var/lib/mydns/oldip)のIPが更新されるか確認curl -4 を付けるflock)で防止STATE_FILE を置くディレクトリの所有者/権限を見直し.netrc、秘密管理)