refactor: 각 어댑터터별 리펙토링 진행
This commit is contained in:
@@ -41,6 +41,12 @@ still waiting on gets claimed by a second worker, and the recipient receives the
|
||||
| `callbacks.enabled` | `APP_NOTIFICATION_PLATFORM_CALLBACKS_ENABLED` | `false` | boolean |
|
||||
| `callbacks.max-body-bytes` | `APP_NOTIFICATION_PLATFORM_CALLBACK_MAX_BODY_BYTES` | `65508` | 1..65508 |
|
||||
| `callbacks.replay-skew` | `APP_NOTIFICATION_PLATFORM_CALLBACK_REPLAY_SKEW` | `5m` | positive |
|
||||
| `callbacks.trusted-proxies` | `APP_NOTIFICATION_PLATFORM_CALLBACK_TRUSTED_PROXIES` | *(empty)* | CSV of peer addresses |
|
||||
|
||||
여러 provider가 요청 URL에 서명하므로, 그 URL을 잘못 재구성하면 정상 webhook이 전부 서명 실패가 된다.
|
||||
`trusted-proxies`가 비어 있으면 forwarded 헤더를 **믿지 않고** 컨테이너가 관측한 값을 쓴다. 무조건 믿으면
|
||||
아무 호출자나 자기 서명이 검증될 URL을 고를 수 있어 서명 자체가 무의미해진다. 로드밸런서 뒤에 있는 배포는
|
||||
그 peer를 명시한다.
|
||||
|
||||
65508 is not a round number by accident: it is the ciphertext column's 65536 bytes minus the AES-GCM
|
||||
nonce and tag. A larger configured value would pass every check above the database and fail the
|
||||
@@ -69,6 +75,43 @@ them, because the keys are deployment-chosen; supply them as YAML or as
|
||||
A profile pins provider type, environment, credential profile, timeouts, concurrency and rate limit.
|
||||
Sender identity and credential profile are separate concerns.
|
||||
|
||||
## SMTP relay
|
||||
|
||||
The one provider profile the template ships, off. A deployment that wants the common case sets
|
||||
`APP_NOTIFICATION_PLATFORM_SMTP_ENABLED=true` and the relay address; one that wants a different
|
||||
profile id or a second family declares it in its own YAML instead.
|
||||
|
||||
The profile and the relay are separate tables below because they answer different questions. The
|
||||
profile says *which* provider serves EMAIL and under what limits; the relay says *what the transport
|
||||
is*. Host, port and credentials are not here at all — they stay `spring.mail.*`, because Spring
|
||||
already owns them and a second spelling would be a second thing to keep in step.
|
||||
|
||||
| Property | Environment variable | Default | Bound |
|
||||
|---|---|---|---|
|
||||
| `providers.smtp.enabled` | `APP_NOTIFICATION_PLATFORM_SMTP_ENABLED` | `false` | boolean |
|
||||
| `providers.smtp.primary-for-channel` | `APP_NOTIFICATION_PLATFORM_SMTP_PRIMARY` | `true` | boolean; exactly one primary per channel |
|
||||
| `providers.smtp.environment` | `APP_NOTIFICATION_PLATFORM_SMTP_ENVIRONMENT` | `local` | required when enabled |
|
||||
| `providers.smtp.credential-profile` | `APP_NOTIFICATION_PLATFORM_SMTP_CREDENTIAL_PROFILE` | `default` | resolved through `SecretMaterialProvider`, never inline material |
|
||||
| `providers.smtp.timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_TIMEOUT` | `10s` | positive, finite |
|
||||
| `providers.smtp.max-concurrency` | `APP_NOTIFICATION_PLATFORM_SMTP_MAX_CONCURRENCY` | `4` | positive |
|
||||
| `providers.smtp.rate-per-second` | `APP_NOTIFICATION_PLATFORM_SMTP_RATE_PER_SECOND` | `10` | positive |
|
||||
|
||||
| Property | Environment variable | Default | Bound |
|
||||
|---|---|---|---|
|
||||
| `smtp.tls-mode` | `APP_NOTIFICATION_PLATFORM_SMTP_TLS_MODE` | `STARTTLS_REQUIRED` | `STARTTLS_REQUIRED` or `IMPLICIT_TLS` |
|
||||
| `smtp.sender-identity` | `APP_NOTIFICATION_PLATFORM_SMTP_SENDER_IDENTITY` | `no-reply@example.invalid` | address |
|
||||
| `smtp.connect-timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_CONNECT_TIMEOUT` | `5s` | positive, finite |
|
||||
| `smtp.read-timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_READ_TIMEOUT` | `10s` | positive, finite |
|
||||
| `smtp.write-timeout` | `APP_NOTIFICATION_PLATFORM_SMTP_WRITE_TIMEOUT` | `10s` | positive, finite |
|
||||
| `smtp.max-concurrency` | `APP_NOTIFICATION_PLATFORM_SMTP_DISPATCH_CONCURRENCY` | `4` | positive |
|
||||
|
||||
The TLS mode enum has no plaintext member. An unencrypted relay is refused by construction rather
|
||||
than by a validator somebody has to remember to run.
|
||||
|
||||
The default sender is an RFC 2606 reserved domain that resolves nowhere, so a deployment that forgot
|
||||
to set one produces a traceable bounce instead of mail apparently sent from an address it does not
|
||||
own.
|
||||
|
||||
## Startup failures
|
||||
|
||||
Startup fails rather than degrading when:
|
||||
@@ -91,6 +134,33 @@ All key material arrives through `SecretMaterialProvider`. Nothing is read from
|
||||
committed file, or from a plaintext log. Contact point encryption and lookup HMAC keys must be
|
||||
distinct, and the encryption key must be exactly 256 bits.
|
||||
|
||||
Eight purposes, eight keys. Each is base64 of at least 32 bytes and each must differ from every
|
||||
other; the platform decodes them at startup and refuses to boot if one is blank, short or shared. A
|
||||
blank value used to be skipped, which meant the platform started without the key and found out on
|
||||
the first contact point — in production, on a recipient's notification.
|
||||
|
||||
Every default below is **unset**, deliberately. Supply the values out of band, per environment. Do
|
||||
not write one into this table, into `application.yml`, into an `.env` file that is tracked, or into
|
||||
any example: a value that appears in the repository is a value that has been disclosed.
|
||||
|
||||
| Purpose | Key material | Active key id |
|
||||
|---|---|---|
|
||||
| Contact point encryption | `APP_NOTIFICATION_PLATFORM_CONTACT_ENCRYPTION_KEY` | `APP_NOTIFICATION_PLATFORM_CONTACT_ENCRYPTION_KEY_ID` |
|
||||
| Contact point lookup HMAC | `APP_NOTIFICATION_PLATFORM_CONTACT_LOOKUP_HMAC_KEY` | `APP_NOTIFICATION_PLATFORM_CONTACT_LOOKUP_HMAC_KEY_ID` |
|
||||
| Callback signing | `APP_NOTIFICATION_PLATFORM_CALLBACK_SIGNING_KEY` | `APP_NOTIFICATION_PLATFORM_CALLBACK_SIGNING_KEY_ID` |
|
||||
| Callback fingerprint HMAC | `APP_NOTIFICATION_PLATFORM_CALLBACK_FINGERPRINT_HMAC_KEY` | `APP_NOTIFICATION_PLATFORM_CALLBACK_FINGERPRINT_HMAC_KEY_ID` |
|
||||
| Provider credential encryption | `APP_NOTIFICATION_PLATFORM_PROVIDER_CREDENTIAL_KEY` | `APP_NOTIFICATION_PLATFORM_PROVIDER_CREDENTIAL_KEY_ID` |
|
||||
| Provider request lookup HMAC | `APP_NOTIFICATION_PLATFORM_PROVIDER_REQUEST_LOOKUP_HMAC_KEY` | `APP_NOTIFICATION_PLATFORM_PROVIDER_REQUEST_LOOKUP_HMAC_KEY_ID` |
|
||||
| Payload encryption | `APP_NOTIFICATION_PLATFORM_PAYLOAD_ENCRYPTION_KEY` | `APP_NOTIFICATION_PLATFORM_PAYLOAD_ENCRYPTION_KEY_ID` |
|
||||
| Web Push VAPID signing | `APP_NOTIFICATION_PLATFORM_VAPID_SIGNING_KEY` | `APP_NOTIFICATION_PLATFORM_VAPID_SIGNING_KEY_ID` |
|
||||
|
||||
A key id is not secret — an id identifies key material without revealing it — but it is required,
|
||||
and it has no default on purpose. A constant id makes a rotation indistinguishable from the key it
|
||||
replaced, so nothing could decrypt what was written before it. Change the id in the same deployment
|
||||
that changes the material, and keep the superseded key readable under its old id until the data it
|
||||
wrote has been re-encrypted. The rotation sequence is in
|
||||
[at-rest-threat-model.md](at-rest-threat-model.md).
|
||||
|
||||
## Readiness
|
||||
|
||||
The platform contributes a `notifications` actuator endpoint and a health indicator. It reports DOWN
|
||||
|
||||
Reference in New Issue
Block a user