備忘録:AdGuard DNS ルール追加の自動化と権限分離の構築

1. 目的

標準ユーザー環境から、管理者権限やAPIキーを直接参照することなく、安全にAdGuard DNSのフィルタリングルールを追加するシステムを構築する。

2. システム構成

  • ロジック層: Python(urllibまたはcurlを利用したREST API操作)
  • インターフェース層: AppleScript(アプリ化し、ユーザー入力を受け取る)
  • セキュリティ層: macOSキーチェーン(APIキーの秘匿)およびコード署名(改ざん検知)

3. 技術的実装の詳細

A. PythonによるAPI操作

AdGuard DNS API(v1)を用いたルール更新には、特定の階層構造を持つJSONをPUTメソッドで送信する必要がある。

  • エンドポイント: https://api.adguard-dns.io/oapi/v1/dns_servers/{SERVER_ID}/settings
  • 認証: ヘッダーに Authorization: ApiKey {API_KEY} を付与。
  • データ構造: user_rules_settings をルートとしたオブジェクトで送信。既存のルールを上書きしないよう、一度GETで現在のリストを取得し、配列に新規ドメインを追加して投げ返す。

Python

# ペイロード構造の例
payload = {
    "user_rules_settings": {
        "rules": current_rules_list,
        "enabled": True
    }
}
B. キーチェーンによる秘匿化

APIキーをコード内にハードコードせず、macOSのキーチェーンに保存。Pythonのkeyringライブラリを使用して取得する。 管理者ユーザーで鍵を登録し、標準ユーザーからのアクセス時に一度だけ管理者パスワードで「常に許可」を与えることで、以降のパスワード入力を省略する。

Bash

# キーチェーンへの登録コマンド
security add-generic-password -a "AdGuardUser" -s "AdGuardAPI" -w "YOUR_API_KEY"
C. AppleScriptによるGUI化

ユーザーがドメインを入力するためのダイアログを表示し、Pythonスクリプトへ引数として渡す。

AppleScript

set theDomain to text returned of (display dialog "Enter Domain:" default answer "")
do shell script "python3 /path/to/logic.py " & quoted form of theDomain

4. ハマりどころと解決策

  1. エンコード問題: Pythonから日本語メッセージを出力すると、実行環境のエンコード設定(ASCII)によりエラーとなる。出力メッセージを英語に統一することで回避。
  2. APIの405/404エラー: /settings エンドポイントに対し、サーバー情報全体をPUTすると拒否される。更新対象である user_rules_settings だけを内包したJSONを送ることで解決。
  3. ライブラリのパス: 管理者側でインストールしたPythonライブラリ(keyring等)が標準ユーザー側で見えない場合がある。python3 -m pip install でユーザー環境にも個別に導入するか、システム全体に導入して対応。

5. セキュリティ・プロトコル

  • コード署名: 管理者権限で codesign を行い、アプリの整合性を保証する。
  • 権限分離: アプリの実行を標準ユーザー、鍵の所有を管理者ユーザーに分離。アプリ改ざん時には再署名が必要となり、管理者パスワードが必須となるため、セルフコントロールの強度が向上する。