はじめに

こんにちは。あやかです。

最近、固定電話への迷惑電話が増えてきて困っていました。 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点です。

  1. logger.conf: ログレベルを適切に設定
  2. logrotate: 180日間のログを日次でローテーション(圧縮なし)
  3. 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-56780312345678に正規化します。

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サブルーチンでは、以下の処理を行います。

  1. タイムスタンプ生成(Asia/Tokyo)
  2. 通知メッセージ作成(CALL ALLOW / 番号 / 理由 / 時刻
  3. jqでJSON生成
  4. curlでWebhook送信(タイムアウト3秒)
  5. 一時ファイル削除

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.confGCHAT_WEBHOOK_URLに設定します。

ビルドと起動(Synology NASの場合)

Synology NASのContainer Managerを使用する場合、以下の手順で構築します。

  1. File Stationでasterisk-projectディレクトリ全体をNASにアップロード
  2. Container Managerを開く
  3. 「プロジェクト」タブを選択
  4. 「新規作成」をクリック
  5. プロジェクト名を入力(例:asterisk)
  6. 「ソースを設定」で「既存のDocker Composeファイルを使用」を選択
  7. パスに/path/to/asterisk-projectを指定
  8. 「次へ」→「完了」でビルドと起動が実行される

起動後、「コンテナ」タブで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

ログ保持期間の変更

Dockerfilerotate 180を変更します。

RUN echo '    rotate 14' >> /etc/logrotate.d/asterisk

14日間保持ならrotate 14、30日間保持ならrotate 30です。

変更後は、イメージの再ビルドが必要です。

Synology NASの場合:

  1. Container Managerで「asterisk」プロジェクトを削除
  2. 再度「新規作成」から構築

Linux環境の場合:

docker compose down
docker compose build
docker compose up -d

通知メッセージのカスタマイズ

extensions.confgchat-notifyサブルーチンで、メッセージ形式を変更できます。

same => n,Set(msg=着信: ${ARG2} / 判定: ${ARG1} / 理由: ${ARG3} / 時刻: ${ts})

トラブルシューティング

Google Chatに通知が届かない

Webhook URLの確認

extensions.confGCHAT_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に通知が届くので、外出先でも着信状況を確認できて便利です。

ブロックリストは手動メンテナンスが必要ですが、プレフィックス一括拒否を有効にすれば、ほとんどの迷惑電話は自動で処理できます。

同じように迷惑電話に困っている方の参考になれば幸いです。