はじめに
QNAP NASの通知をSlackで受け取るための設定を紹介します。QNAP NASの通知機能だけでは直接Slackに通知できません。そこで、以下の記事のアイディアを元にして、転送プログラムをDockerコンテナで動かすように変更しました。
このコンテナを使うとイベントやアラートの種類別にSlackのメンション先を変えて通知することが出来ます。
手順
Incoming webhookを持つSlack APPを作成する
- a. Slack Webhook作成:SORACOMの記事を参考にアプリを作成し、incoming webhookの作成をします。以下のようなURLが生成されるので、それを記録しておきます。
https://hooks.slack.com/services/Txxxxxxxxxx/Bxxxxxxxxxx/Yxxxxxxxxxxxxxxxxxxxxxxx
- b. アイコンはこちらのものを借用しました。
- c. 設定したSlackチャンネルに通知が飛ぶかどうかをテストします。QNAP NASに管理者権限のアカウントにsshでログインし、curlで通知を送ります。URLは上記で記録したものを使います。
curl -X POST -H 'Content-type: application/json' --data '{"text":"Hello, World!"}' https://hooks.slack.com/services/Txxxxxxxxxx/Bxxxxxxxxxx/Yxxxxxxxxxxxxxxxxxxxxxxx
QNAP NASにSlackへの通知転送用コンテナを立てる
- a. Container Station APPが必要ですので、事前にインストールしてください。QNAP NASの管理ウェブページで「Container Station」というアイコンがあり、以下のようなウィンドウを開くことができればOKです。
-
b. QNAP NASにsshで管理者アカウントでログインし、コンテナ関連ファイルを置く場所を作ります。私はホームディレクトリ直下に
$HOME/Container/qnap-slack-proxyを作成しました。 -
c. QNAP NASの通知とコンテナとの間の簡易認証として専用TOKENを作成します。TOKEN用文字列は何文字でも良いのですが、ここでは64文字とします。QNAP NASターミナル上で以下のスクリプトを実行すると64文字のランダム文字列が得られますので、それを記録して下さい。実行するたびに違うランダム文字列となりますので、ご注意下さい。
awk 'BEGIN { \
srand(); \
for ( i = 0; i < 64; i++) { \
p = rand() * 62 + 1; \
printf( "%c", \
substr( \
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" \
"abcdefghijklmnopqrstuvwxyz" \
"1234567890", \
p, 1 \
) \
); \
} \
print ""; \
}'
- d.
qnap-slack-proxy以下のディレクトリに下記の通り4つのファイルを置きます。
Container
└─ qnap-slack-proxy
├─ docker-compose.yml
├─ Dockerfile
├─ requirements.txt
└─ app.py
-
docker-compose.yml
- SLACK_WEBHOOK_URL: Slack APP作成時に記録したURLを記述します。
- URL_TOKEN: 先程生成したランダム文字列を記述します。
services:
qnap-slack-proxy:
build: .
container_name: qnap-slack-proxy
environment:
SLACK_WEBHOOK_URL: "https://hooks.slack.com/services/Txxxxxxxxxx/Bxxxxxxxxxx/Yxxxxxxxxxxxxxxxxxxxxxxx"
URL_TOKEN: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
ports:
- "127.0.0.1:18080:8080"
restart: unless-stopped
- Dockerfile
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app.py .
ENV PORT=8080
EXPOSE 8080
CMD ["python", "app.py"]
- requirements.txt
aiohttp==3.*
- app.py
from aiohttp import web, ClientSession
import os
SLACK_WEBHOOK_URL = os.environ["SLACK_WEBHOOK_URL"]
URL_TOKEN = os.environ.get("URL_TOKEN", "")
PORT = int(os.environ.get("PORT", "8080"))
MENTION_TAGS = {
"none": "",
"here": "<!here> ",
"channel": "<!channel> ",
"everyone": "<!everyone> ",
}
LEVEL_TO_MENTION = {
"info": "none",
"warn": "here",
"warning": "here",
"alert": "channel",
"error": "channel",
"critical": "channel",
}
def pick_mention(qs):
# 明示の mention があれば最優先
m = (qs.get("mention") or "").lower().strip()
if m in MENTION_TAGS:
return m
# なければ level から推定
lvl = (qs.get("level") or "").lower().strip()
return LEVEL_TO_MENTION.get(lvl, "none")
async def proxy(request):
token = request.match_info.get("token", "")
if URL_TOKEN and token != URL_TOKEN:
return web.json_response({"error": "unauthorized"}, status=401)
qs = request.query
text = qs.get("text", "").strip()
level = (qs.get("level") or "").upper() # 表示用
mention = pick_mention(qs)
# 先頭にメンション & レベルタグを付けて整形
prefix_level = f"[{level}] " if level else ""
final_text = f"{MENTION_TAGS[mention]}{prefix_level}{text}"
payload = {"text": final_text}
async with ClientSession() as session:
async with session.post(SLACK_WEBHOOK_URL, json=payload) as r:
body = await r.text()
if r.status >= 400:
return web.json_response(
{"status": "error", "slack_status": r.status, "slack_resp": body},
status=500
)
return web.json_response({"status": "ok"})
app = web.Application()
app.router.add_get("/qnap_notify/{token}", proxy)
app.router.add_post("/qnap_notify/{token}", proxy)
web.run_app(app, port=PORT)
- e. コンテナをビルド、立ち上げて、テストします。以下のコマンドを実行します。qnap_notifyの後ろには先程生成したランダム文字列に置き換えます。このコンテナの中継プログラムではオプションにlevelとしてinfo, warn, warning, alert, error, criticalを指定するとそれに合わせてメンションを変えたり、オプションをmentionとして、here, channel, everyoneを記述したりすることが出来ます。
docker compose up -d --build
# info(メンション無)
curl -sS -G "http://127.0.0.1:18080/qnap_notify/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--data-urlencode "text=backup finished" --data "level=info" -i
# alert(@channel)
curl -sS -G "http://127.0.0.1:18080/qnap_notify/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--data-urlencode "text=RAID degraded!" --data "level=alert" -i
# 明示 @here
curl -sS -G "http://127.0.0.1:18080/qnap_notify/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
--data-urlencode "text=check this now" --data "mention=here" -i
QNAP NASの通知センターを設定する
- a. コントロールパネル → 通知センター → サービスアカウント & デバイスのペアリング → SMS → [SMSCサービスの追加] → [Custom] を選択します。
-
b. ここで、エイリアスとURLテンプレートを入力します。アラート通知では情報、警告、エラーがありますので、それぞれに合わせてメンション先を帰る場合はそれぞれに合わせて3つの設定をします。ここではアラートを一つだけ設定します。
-
エイリアスには適切な名前を記述します。ここではQNAP Alertとします。
-
URLテンプレートは以下のように設定します。
qnap_notify/の後ろにはdocker-compose.ymlで設定したTOKENを記述します。textオプションやpオプションはそのまま残して下さい。levelオプションの値を変えるとメンション先を変えられます。http://127.0.0.1:18080/qnap_notify/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?text=@@Text@@&p=@@PhoneNumber@@&level=alert
-
-
c. 設定をテストするために、「紙飛行機」アイコンをクリックすると以下のようなウィンドウが表示されるので、電話番号に適当な番号を入れて、「送信」ボタンをクリックします。Slackでメンション付き通知が受けられたら正しく設定できています。通知を確認できたら、一つ前のウィンドウの「確認」ボタンを押します。
- d. 次に通知ルールを設定します。コントロールパネル → 通知センター →システム通知ルール→アラート通知→ルールを選択します。以下のウィンドウが表示されます。
- e. 適切なルール名を設定し、ここでは「警告」と「エラー」だけをチェックして「次へ」のボタンをクリックします。「情報」までチェックするとかなりの量の通知が行くので注意して下さい。
- f. 以下のウィンドウが表示されたら先程設定した通知を割り当てます。下記の設定をしたら「次へ」のボタンをクリックします。
- 方法はSMSとします。
- SMSCサービスを選択では先程設定した通知方法を選びます。
- 電話番号は適当な数字列を記述します。
- g. 次のウィンドウが表示されたら、内容を確認した上で「完了」ボタンをクリックして下さい。
- h. 通知ルール設定が適切かどうかをテストします。コントロールパネル → 通知センター →システム通知ルール→アラート通知を選択し、先程設定した通知ルールの「編集」アイコンをクリックします。
- i. 方法と受診者を選び右側にある「封筒」アイコンをクリックします。Slackに通知が飛んだことを確認して下さい。通知が確認できたら「確認」ボタンをクリックします。
私のところでは以下のとおり、イベント通知とアラート通知を一つずつ設定しています。
注意点
-
露出最小化:
127.0.0.1バインドでNAS内限定に。必要ならNASのIP直書きでもOKですが、QuFirewallで外部からの 18080/TCP を閉じる必要があります。 -
認証トークン:URLパスの
TOKENは長いランダム文字列にすることをお勧めします。この記事でも64文字のランダム文字列にしました。 -
失敗時ログ:Slack Webhookが無効化/権限変更だと 4xx を返すので、
docker logs qnap-slack-proxyで確認することが出来ます。










