はじめに
こんにちは。あやかです。
最近、固定電話への迷惑電話が増えてきて困っていました。 0120や0570から始まる営業電話、非通知の不審な着信。 電話に出るたびに「またか」と思う日々。
そこで、Asteriskを使って迷惑電話を自動でブロックし、Google Chatに通知する仕組みを作りました。 今回は、その構築手順を共有します。
システム概要
構築したシステムの概要は以下の通りです。
- 着信時にブロックリストと照合
- ブロックリストに該当したら自動切断
- 許可・拒否の両方をGoogle Chatに通知
- ログは180日間保持(ディスク容量に応じて調整可能)
- Docker環境で構築(Synology NAS上で稼働)
通知例
実際にGoogle Chatに届く通知は、こんな感じです。
許可された着信:
CALL ALLOW / 0312345678 / allow / 2026-01-26 09:28:12
ブロックされた着信:
CALL BLOCK / 0120999999 / blocklist_file / 2026-01-26 09:30:45
CALL BLOCK / anonymous / anonymous / 2026-01-26 10:15:33
ブロック理由も記録されるので、後から確認できて便利です。
環境情報
今回使用した環境は以下の通りです。
- 実行環境: Synology NAS(Container Manager)
- ベースイメージ: Ubuntu 24.04
- Asterisk: Ubuntu公式リポジトリ版
- タイムゾーン: Asia/Tokyo
- ログ保持期間: 180日
Docker環境があれば、Synology NAS以外でも動作します。
構築手順
ディレクトリ構成
まず、以下のディレクトリ構成で準備します。
asterisk-project/
├── compose.yaml
├── Dockerfile
├── entrypoint.sh
├── conf/
│ ├── pjsip.conf
│ ├── extensions.conf
│ └── blocklist.conf
├── data/ # Asteriskのデータディレクトリ
└── log/ # ログディレクトリ
compose.yaml
Docker Composeの設定ファイルです。
services:
asterisk:
build: .
container_name: asterisk
network_mode: host
restart: unless-stopped
volumes:
- ./conf/pjsip.conf:/etc/asterisk/pjsip.conf:ro
- ./conf/extensions.conf:/etc/asterisk/extensions.conf:ro
- ./conf/blocklist.conf:/etc/asterisk/blocklist.conf:ro
- ./data:/var/lib/asterisk
- ./log:/var/log/asterisk
network_mode: hostを使用しているのは、SIPプロトコルの都合でポートマッピングが複雑になるためです。
Dockerfile
Asteriskコンテナのイメージを定義します。
FROM ubuntu:24.04
ENV DEBIAN_FRONTEND=noninteractive
ENV TZ=Asia/Tokyo
RUN apt-get update && apt-get install -y --no-install-recommends \
asterisk curl ca-certificates jq tzdata logrotate cron \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /var/log/asterisk \
&& ln -snf /usr/share/zoneinfo/$TZ /etc/localtime \
&& echo $TZ > /etc/timezone
# logger.conf
RUN echo '[logfiles]' > /etc/asterisk/logger.conf \
&& echo 'console => warning,error' >> /etc/asterisk/logger.conf \
&& echo 'messages => notice,warning,error,verbose(3)' >> /etc/asterisk/logger.conf
# logrotate設定(180日保持版)
RUN echo '/var/log/asterisk/messages {' > /etc/logrotate.d/asterisk \
&& echo ' daily' >> /etc/logrotate.d/asterisk \
&& echo ' rotate 180' >> /etc/logrotate.d/asterisk \
&& echo ' delaycompress' >> /etc/logrotate.d/asterisk \
&& echo ' missingok' >> /etc/logrotate.d/asterisk \
&& echo ' notifempty' >> /etc/logrotate.d/asterisk \
&& echo ' dateext' >> /etc/logrotate.d/asterisk \
&& echo ' dateformat -%Y%m%d' >> /etc/logrotate.d/asterisk \
&& echo ' sharedscripts' >> /etc/logrotate.d/asterisk \
&& echo ' postrotate' >> /etc/logrotate.d/asterisk \
&& echo ' /usr/sbin/asterisk -rx "logger reload" > /dev/null 2>&1 || true' >> /etc/logrotate.d/asterisk \
&& echo ' endscript' >> /etc/logrotate.d/asterisk \
&& echo '}' >> /etc/logrotate.d/asterisk
# cron設定(毎日0時にlogrotate実行)
RUN echo '0 0 * * * /usr/sbin/logrotate /etc/logrotate.conf' > /etc/cron.d/logrotate \
&& chmod 0644 /etc/cron.d/logrotate
# 起動時スクリプト
COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
CMD ["/entrypoint.sh"]
ポイントは以下の3点です。
- logger.conf: ログレベルを適切に設定
- logrotate: 180日間のログを日次でローテーション(圧縮なし)
- cron: 毎日0時にlogrotateを実行
entrypoint.sh
コンテナ起動時に実行されるスクリプトです。
#!/bin/bash
set -e
echo "=== Asterisk Container Starting ==="
# 起動時に古いログがあればローテーション
echo "[INFO] Checking for old log files..."
if [ -f /var/log/asterisk/messages ]; then
log_date=$(date -r /var/log/asterisk/messages +%Y%m%d)
today=$(date +%Y%m%d)
if [ "$log_date" != "$today" ]; then
echo "[INFO] Old log detected (${log_date}). Rotating now..."
/usr/sbin/logrotate -f /etc/logrotate.conf
else
echo "[INFO] Log is from today. No rotation needed."
fi
else
echo "[INFO] No existing log file. Skipping rotation."
fi
# cronデーモン起動
echo "[INFO] Starting cron daemon..."
service cron start
# Asterisk起動
echo "[INFO] Starting Asterisk..."
exec /usr/sbin/asterisk -f
extensions.conf
Asteriskのダイヤルプランを定義します。このファイルが、ブロック判定のコアロジックです。
[globals]
GCHAT_WEBHOOK_URL=https://chat.googleapis.com/v1/spaces/XXXXXX/messages?key=XXXXXX&token=XXXXXX
BLOCKLIST_FILE=/etc/asterisk/blocklist.conf
[gchat-notify]
exten => s,1,Set(ts=${STRFTIME(${EPOCH},Asia/Tokyo,%Y-%m-%d %H:%M:%S)})
same => n,Set(msg=CALL ${ARG1} / ${ARG2} / ${ARG3} / ${ts})
same => n,Set(tmp=/tmp/gchat_${UNIQUEID}.json)
same => n,System(jq -n --arg txt "${msg}" '{text: $txt}' > ${tmp})
same => n,System(timeout 3 curl -s -X POST -H "Content-Type: application/json" "${GCHAT_WEBHOOK_URL}" --data-binary @${tmp})
same => n,System(rm -f ${tmp})
same => n,Return()
[from-rt500mi]
exten => s,1,NoOp(INCOMING ${CALLERID(all)})
same => n,Set(src=${CALLERID(num)})
same => n,Set(reason=allow)
; 非通知
same => n,GotoIf($["${src}"=""]?setanon,1)
; +81 → 0
same => n,ExecIf($["${src:0:3}"="+81"]?Set(src=0${src:3}))
; ハイフン除去
same => n,Set(src=${REPLACE(src,-,)})
; プレフィックス一括拒否(必要に応じてコメント解除)
;same => n,GotoIf($["${src:0:3}"="050"]?set050,1)
;same => n,GotoIf($["${src:0:4}"="0570"]?set0570,1)
;same => n,GotoIf($["${src:0:4}"="0120"]?set0120,1)
;same => n,GotoIf($["${src:0:4}"="0800"]?set0800,1)
; ブロックリスト照合
same => n,Set(match=${SHELL(grep -Fx "${src}" ${BLOCKLIST_FILE} 2>/dev/null)})
same => n,GotoIf($["${match}"!=""]?setbl,1)
; ---- 許可 ----
same => n,Gosub(gchat-notify,s,1(ALLOW,${src},${reason}))
same => n,Hangup()
; ---- 拒否理由セット → blockedへ ----
exten => setanon,1,Set(src=anonymous)
same => n,Set(reason=anonymous)
same => n,Goto(blocked,1)
exten => set050,1,Set(reason=prefix_050)
same => n,Goto(blocked,1)
exten => set0570,1,Set(reason=prefix_0570)
same => n,Goto(blocked,1)
exten => set0120,1,Set(reason=prefix_0120)
same => n,Goto(blocked,1)
exten => set0800,1,Set(reason=prefix_0800)
same => n,Goto(blocked,1)
exten => setbl,1,Set(reason=blocklist_file)
same => n,Goto(blocked,1)
; ---- ブロック処理 ----
exten => blocked,1,Answer()
same => n,Gosub(gchat-notify,s,1(BLOCK,${src},${reason}))
same => n,Hangup()
ロジックの解説
1. 非通知の拒否
発信者番号が空の場合、非通知と判定して拒否します。
same => n,GotoIf($["${src}"=""]?setanon,1)
2. 国際電話番号の正規化
+81から始まる番号を0に変換します。
same => n,ExecIf($["${src:0:3}"="+81"]?Set(src=0${src:3}))
3. ハイフン除去
03-1234-5678を0312345678に正規化します。
same => n,Set(src=${REPLACE(src,-,)})
4. プレフィックス一括拒否(オプション)
0120、0570などの番号を一括で拒否できます。 現在はコメントアウトしていますが、迷惑電話が増えたら有効化する予定です。
;same => n,GotoIf($["${src:0:4}"="0120"]?set0120,1)
5. ブロックリスト照合
blocklist.confに登録された番号と照合します。
same => n,Set(match=${SHELL(grep -Fx "${src}" ${BLOCKLIST_FILE} 2>/dev/null)})
same => n,GotoIf($["${match}"!=""]?setbl,1)
6. Google Chat通知
許可・拒否のどちらの場合も、Google Chatに通知します。
same => n,Gosub(gchat-notify,s,1(ALLOW,${src},${reason}))
gchat-notifyサブルーチンでは、以下の処理を行います。
- タイムスタンプ生成(Asia/Tokyo)
- 通知メッセージ作成(
CALL ALLOW / 番号 / 理由 / 時刻) - jqでJSON生成
- curlでWebhook送信(タイムアウト3秒)
- 一時ファイル削除
timeout 3を指定することで、Google Chatへの通知が遅延しても着信処理をブロックしないようにしています。
blocklist.conf
ブロックする電話番号を1行1番号で記載します。
0120123456
0570987654
0312345678
ハイフンは不要です。extensions.confで正規化されるため、どちらの形式でも照合できます。
pjsip.conf(参考)
SIPトランクの設定ファイルです。 環境によって内容が異なるため、詳細は割愛します。
ひかり電話ルーター(RT-500MI)と接続する場合の最小構成例のみ示します。
[transport-udp]
type=transport
protocol=udp
bind=0.0.0.0
[rt500mi]
type=endpoint
context=from-rt500mi
disallow=all
allow=ulaw
from_user=your_phone_number
ビルドと起動
Google Chat Webhookの取得
事前に、Google ChatのWebhook URLを取得してください。 取得方法は、Google Chatの公式ドキュメントを参照してください。
取得したWebhook URLを、extensions.confのGCHAT_WEBHOOK_URLに設定します。
ビルドと起動(Synology NASの場合)
Synology NASのContainer Managerを使用する場合、以下の手順で構築します。
- File Stationで
asterisk-projectディレクトリ全体をNASにアップロード - Container Managerを開く
- 「プロジェクト」タブを選択
- 「新規作成」をクリック
- プロジェクト名を入力(例:asterisk)
- 「ソースを設定」で「既存のDocker Composeファイルを使用」を選択
- パスに
/path/to/asterisk-projectを指定 - 「次へ」→「完了」でビルドと起動が実行される
起動後、「コンテナ」タブでasteriskコンテナが実行中になっていることを確認します。
ログを確認するには、コンテナを選択して「詳細」→「ログ」タブを開きます。
ビルドと起動(Linux環境の場合)
Linuxのコマンドラインから構築する場合は、以下の手順で実行します。
cd asterisk-project
docker compose build
docker compose up -d
ログ確認:
docker compose logs -f
起動ログで、以下のメッセージが表示されれば成功です。
=== Asterisk Container Starting ===
[INFO] Checking for old log files...
[INFO] No existing log file. Skipping rotation.
[INFO] Starting cron daemon...
[INFO] Starting Asterisk...
動作確認
着信テスト
実際に電話をかけて、動作を確認します。
ブロックリストに登録されていない番号からの着信:
Google Chatに以下のような通知が届きます。
CALL ALLOW / 0312345678 / allow / 2026-01-26 09:28:12
ブロックリストに登録されている番号からの着信:
CALL BLOCK / 0120999999 / blocklist_file / 2026-01-26 09:30:45
電話は自動的に切断されます。
ログファイルの確認
Synology NASの場合:
File Stationでasterisk-project/logディレクトリを開きます。
Linux環境の場合:
docker exec -it asterisk ls -lh /var/log/asterisk/
以下のようにログがローテーションされていることを確認します。
-rw-r--r-- 1 root root 12K Jan 27 00:00 messages
-rw-r--r-- 1 root root 8.5K Jan 26 23:59 messages-20260126
-rw-r--r-- 1 root root 7.2K Jan 25 23:59 messages-20260125
-rw-r--r-- 1 root root 6.8K Jan 24 23:59 messages-20260124
日付が古いログは日次でローテーションされ、180日を超えたログは自動削除されます。
今回の設定では圧縮を無効化しているため、.gz拡張子は付きません。
カスタマイズのポイント
ブロックリストの追加
conf/blocklist.confに番号を追記するだけです。
コンテナの再起動は不要で、次の着信から反映されます。
Synology NASの場合:
File Stationでconf/blocklist.confを編集します。
Linux環境の場合:
echo "0120123456" >> conf/blocklist.conf
プレフィックス一括拒否の有効化
迷惑電話が増えてきたら、extensions.confの以下のコメントを解除します。
same => n,GotoIf($["${src:0:4}"="0120"]?set0120,1)
same => n,GotoIf($["${src:0:4}"="0570"]?set0570,1)
設定変更後は、コンテナを再起動します。
Synology NASの場合:
Container Managerで「asterisk」コンテナを選択し、「アクション」→「再起動」をクリックします。
Linux環境の場合:
docker compose restart
ログ保持期間の変更
Dockerfileのrotate 180を変更します。
RUN echo ' rotate 14' >> /etc/logrotate.d/asterisk
14日間保持ならrotate 14、30日間保持ならrotate 30です。
変更後は、イメージの再ビルドが必要です。
Synology NASの場合:
- Container Managerで「asterisk」プロジェクトを削除
- 再度「新規作成」から構築
Linux環境の場合:
docker compose down
docker compose build
docker compose up -d
通知メッセージのカスタマイズ
extensions.confのgchat-notifyサブルーチンで、メッセージ形式を変更できます。
same => n,Set(msg=着信: ${ARG2} / 判定: ${ARG1} / 理由: ${ARG3} / 時刻: ${ts})
トラブルシューティング
Google Chatに通知が届かない
Webhook URLの確認
extensions.confのGCHAT_WEBHOOK_URLが正しいか確認します。
手動テスト
コンテナ内で手動で送信してみます。
Synology NASの場合:
Container Managerで「asterisk」コンテナを選択し、「詳細」→「ターミナル」を開いて以下を実行します。
Linux環境の場合:
docker exec -it asterisk bash
いずれの場合も、以下のコマンドで送信テストします。
curl -X POST -H "Content-Type: application/json" \
"https://chat.googleapis.com/v1/spaces/XXXXXX/messages?key=XXXXXX&token=XXXXXX" \
-d '{"text": "Test message"}'
成功すれば、Google Chatに「Test message」が届きます。
ログローテーションが動作しない
cron動作確認
ps aux | grep cron
cronデーモンが起動していることを確認します。
手動実行テスト
logrotate -f /etc/logrotate.conf
ls -lh /var/log/asterisk/
手動でログローテーションを実行して、ファイルが生成されるか確認します。
entrypoint.shのエラー(CRLF問題)
Windowsでentrypoint.shを作成した場合、改行コードがCRLF(\r\n)になっている可能性があります。
起動時に以下のエラーが出る場合は、CRLF問題です。
exec /entrypoint.sh: no such file or directory
改行コードの確認:
file entrypoint.sh
with CRLF line terminatorsと表示される場合は、以下のコマンドで修正します。
sed -i 's/\r$//' entrypoint.sh
VSCodeを使っている場合:
右下のステータスバーで「CRLF」→「LF」に変更できます。
予防策:
.editorconfigを作成して、プロジェクトルートに配置しておくと、エディタが自動的にLFを使うようになります。
root = true
[*.sh]
end_of_line = lf
おわりに
Asteriskで迷惑電話ブロックシステムを構築しました。
運用開始から数週間経ちますが、迷惑電話は確実に減りました。 Google Chatに通知が届くので、外出先でも着信状況を確認できて便利です。
ブロックリストは手動メンテナンスが必要ですが、プレフィックス一括拒否を有効にすれば、ほとんどの迷惑電話は自動で処理できます。
同じように迷惑電話に困っている方の参考になれば幸いです。