Files
han-app/ops-monitoring/send_sms.md
T

231 lines
5.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Отправка СМС
## На ВМ выполните:
cd /opt/han-chat/backend
umask 077
read -r -p "Тестовый номер в E.164 (+79...): " TEST_PHONE
CHALLENGE_ID=$(python3 -c 'import uuid; print(uuid.uuid4())')
OTP_CODE=$(python3 -c 'import secrets; print(f"{secrets.randbelow(1000000):06d}")')
SMS_TOKEN=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("SMS_SERVICE_TOKEN="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
else:
raise SystemExit("SMS_SERVICE_TOKEN отсутствует")
PY
)
export TEST_PHONE CHALLENGE_ID OTP_CODE SMS_TOKEN
REQUEST_FILE=$(mktemp)
python3 - "$REQUEST_FILE" <<'PY'
import json
import os
import sys
payload = {
"idempotency_key": f"ops:smoke:{os.environ['CHALLENGE_ID']}",
"template_code": "auth_otp",
"locale": "ru",
"phone_e164": os.environ["TEST_PHONE"],
"substitutions": {
"code": os.environ["OTP_CODE"],
"ttl_min": "1",
},
"customer_ref": os.environ["CHALLENGE_ID"],
"message_ttl_sec": 60,
}
with open(sys.argv[1], "w", encoding="utf-8") as file:
json.dump(payload, file, ensure_ascii=False)
PY
## Создайте функцию отправки:
send_sms_smoke() {
docker compose --env-file .env --profile ops run --rm --no-deps \
--user 0:0 \
--entrypoint sh \
-e SMS_TOKEN \
-v "$REQUEST_FILE:/tmp/sms-request.json:ro" \
toolbox -ec '
curl -sS \
-w "\nHTTP %{http_code}\n" \
-X POST \
-H "Authorization: Bearer $SMS_TOKEN" \
-H "Content-Type: application/json" \
-H "X-Request-ID: ops-sms-smoke" \
--data-binary @/tmp/sms-request.json \
http://sms-service:8080/internal/sms/v1/send
'
}
## Отправка:
send_sms_smoke
Ожидается:
HTTP 202 и JSON с sms_message_id.
## Проверьте журнал:
SELECT
id,
phone_masked,
send_status,
delivery_status,
provider_message_id,
provider_error_code,
attempt_count,
created_at
FROM sms.sms_outbound_message
ORDER BY created_at DESC
LIMIT 5;
## После проверки удалите секретные данные:
shred -u "$REQUEST_FILE" 2>/dev/null || rm -f "$REQUEST_FILE"
unset SMS_TOKEN OTP_CODE TEST_PHONE CHALLENGE_ID REQUEST_FILE
# Тесты
Выполняйте на ВМ из `/opt/han-chat/backend`.
### 1. Проверить запрет публичного internal API
```bash
PUBLIC_WEB_URL=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("PUBLIC_WEB_URL="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
PY
)
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
"$PUBLIC_WEB_URL/internal/sms/v1/messages/00000000-0000-0000-0000-000000000000"
```
Ожидается:
```text
HTTP 404
```
### 2. Проверить callback с неправильного IP
```bash
curl -sS -o /dev/null -w 'HTTP %{http_code}\n' \
-X POST \
-H 'Content-Type: application/json' \
--data '[]' \
"$PUBLIC_WEB_URL/callbacks/idgtl/sms"
```
Ожидается:
```text
HTTP 403
```
Заголовок `X-Forwarded-For` не должен позволять обойти ограничение.
### 3. Проверить Basic auth внутри Docker-сети
Получите credentials из `.env`:
```bash
CB_USER=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("IDGTL_SMS_CALLBACK_USERNAME="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
PY
)
CB_PASS=$(python3 - <<'PY'
from pathlib import Path
for line in Path(".env").read_text().splitlines():
if line.startswith("IDGTL_SMS_CALLBACK_PASSWORD="):
print(line.split("=", 1)[1].strip().strip("\"'"))
break
PY
)
export CB_USER CB_PASS
```
Неверные credentials:
```bash
docker compose --env-file .env --profile ops run --rm --no-deps \
--entrypoint sh toolbox -ec '
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
-u invalid:invalid \
-H "Content-Type: application/json" \
--data "[]" \
http://sms-service:8080/callbacks/idgtl/sms
'
```
Ожидается `HTTP 401`.
Правильные credentials:
```bash
docker compose --env-file .env --profile ops run --rm --no-deps \
--entrypoint sh -e CB_USER -e CB_PASS toolbox -ec '
curl -sS -o /dev/null -w "HTTP %{http_code}\n" \
-u "$CB_USER:$CB_PASS" \
-H "Content-Type: application/json" \
--data "[]" \
http://sms-service:8080/callbacks/idgtl/sms
'
```
Ожидается `HTTP 422`: авторизация прошла, но пустой callback-массив невалиден.
После проверки:
```bash
unset CB_USER CB_PASS
```
### 4. Проверить реальный callback Direct
После тестовой SMS:
```sql
SELECT
id,
send_status,
delivery_status,
provider_message_id,
callback_last_at,
sent_at,
delivered_at
FROM sms.sms_outbound_message
ORDER BY created_at DESC
LIMIT 5;
```
Успешный реальный callback подтверждается:
- `callback_last_at IS NOT NULL`;
- `delivery_status = sent` или `delivered`;
- заполняются `sent_at`/`delivered_at`.
Дополнительно:
```bash
docker compose --env-file .env logs --since=30m nginx sms-service
```
Для callback должен быть ответ `204`. Только реальный запрос Direct может полноценно подтвердить IP allowlist.