mirror of
https://github.com/binwiederhier/ntfy.git
synced 2025-07-20 10:04:08 +00:00
Cleanup, examples
This commit is contained in:
parent
eac49feb04
commit
7a33e16945
5 changed files with 57 additions and 18 deletions
10
cmd/serve.go
10
cmd/serve.go
|
@ -89,8 +89,8 @@ var flagsServe = append(
|
|||
altsrc.NewStringFlag(&cli.StringFlag{Name: "visitor-email-limit-replenish", Aliases: []string{"visitor_email_limit_replenish"}, EnvVars: []string{"NTFY_VISITOR_EMAIL_LIMIT_REPLENISH"}, Value: util.FormatDuration(server.DefaultVisitorEmailLimitReplenish), Usage: "interval at which burst limit is replenished (one per x)"}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "visitor-subscriber-rate-limiting", Aliases: []string{"visitor_subscriber_rate_limiting"}, EnvVars: []string{"NTFY_VISITOR_SUBSCRIBER_RATE_LIMITING"}, Value: false, Usage: "enables subscriber-based rate limiting"}),
|
||||
altsrc.NewBoolFlag(&cli.BoolFlag{Name: "behind-proxy", Aliases: []string{"behind_proxy", "P"}, EnvVars: []string{"NTFY_BEHIND_PROXY"}, Value: false, Usage: "if set, use forwarded header (e.g. X-Forwarded-For, X-Client-IP) to determine visitor IP address (for rate limiting)"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "proxy-forwarded-header", Aliases: []string{"proxy_forwarded_header"}, EnvVars: []string{"NTFY_PROXY_FORWARDED_HEADER"}, Value: "X-Forwarded-For", Usage: "if set, use specified header to determine visitor IP address instead of XFF (for rate limiting)"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "proxy-trusted-addrs", Aliases: []string{"proxy_trusted_addrs"}, EnvVars: []string{"NTFY_PROXY_TRUSTED_ADDRS"}, Value: "", Usage: "comma-separated list of trusted IP addresses to remove from forwarded header"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "proxy-forwarded-header", Aliases: []string{"proxy_forwarded_header"}, EnvVars: []string{"NTFY_PROXY_FORWARDED_HEADER"}, Value: "X-Forwarded-For", Usage: "use specified header to determine visitor IP address (for rate limiting)"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "proxy-trusted-addresses", Aliases: []string{"proxy_trusted_addresses"}, EnvVars: []string{"NTFY_PROXY_TRUSTED_ADDRESSES"}, Value: "", Usage: "comma-separated list of trusted IP addresses to remove from forwarded header"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "stripe-secret-key", Aliases: []string{"stripe_secret_key"}, EnvVars: []string{"NTFY_STRIPE_SECRET_KEY"}, Value: "", Usage: "key used for the Stripe API communication, this enables payments"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "stripe-webhook-key", Aliases: []string{"stripe_webhook_key"}, EnvVars: []string{"NTFY_STRIPE_WEBHOOK_KEY"}, Value: "", Usage: "key required to validate the authenticity of incoming webhooks from Stripe"}),
|
||||
altsrc.NewStringFlag(&cli.StringFlag{Name: "billing-contact", Aliases: []string{"billing_contact"}, EnvVars: []string{"NTFY_BILLING_CONTACT"}, Value: "", Usage: "e-mail or website to display in upgrade dialog (only if payments are enabled)"}),
|
||||
|
@ -193,7 +193,7 @@ func execServe(c *cli.Context) error {
|
|||
visitorEmailLimitReplenishStr := c.String("visitor-email-limit-replenish")
|
||||
behindProxy := c.Bool("behind-proxy")
|
||||
proxyForwardedHeader := c.String("proxy-forwarded-header")
|
||||
proxyTrustedAddrs := util.SplitNoEmpty(c.String("proxy-trusted-addrs"), ",")
|
||||
proxyTrustedAddresses := util.SplitNoEmpty(c.String("proxy-trusted-addresses"), ",")
|
||||
stripeSecretKey := c.String("stripe-secret-key")
|
||||
stripeWebhookKey := c.String("stripe-webhook-key")
|
||||
billingContact := c.String("billing-contact")
|
||||
|
@ -322,6 +322,8 @@ func execServe(c *cli.Context) error {
|
|||
}
|
||||
} else if webPushExpiryWarningDuration > 0 && webPushExpiryWarningDuration > webPushExpiryDuration {
|
||||
return errors.New("web push expiry warning duration cannot be higher than web push expiry duration")
|
||||
} else if behindProxy && proxyForwardedHeader == "" {
|
||||
return errors.New("if behind-proxy is set, proxy-forwarded-header must also be set")
|
||||
}
|
||||
|
||||
// Backwards compatibility
|
||||
|
@ -421,7 +423,7 @@ func execServe(c *cli.Context) error {
|
|||
conf.VisitorSubscriberRateLimiting = visitorSubscriberRateLimiting
|
||||
conf.BehindProxy = behindProxy
|
||||
conf.ProxyForwardedHeader = proxyForwardedHeader
|
||||
conf.ProxyTrustedAddrs = proxyTrustedAddrs
|
||||
conf.ProxyTrustedAddresses = proxyTrustedAddresses
|
||||
conf.StripeSecretKey = stripeSecretKey
|
||||
conf.StripeWebhookKey = stripeWebhookKey
|
||||
conf.BillingContact = billingContact
|
||||
|
|
|
@ -554,15 +554,50 @@ using Let's Encrypt using certbot, or simply because you'd like to share the por
|
|||
Whatever your reasons may be, there are a few things to consider.
|
||||
|
||||
If you are running ntfy behind a proxy, you should set the `behind-proxy` flag. This will instruct the
|
||||
[rate limiting](#rate-limiting) logic to use the `X-Forwarded-For` header as the primary identifier for a visitor,
|
||||
as opposed to the remote IP address. If the `behind-proxy` flag is not set, all visitors will
|
||||
be counted as one, because from the perspective of the ntfy server, they all share the proxy's IP address. If your proxy or CDN provider uses a custom header to securely pass the source IP/Client IP to your application, you can specify that header instead of using the XFF. Using the custom header (unique per provide/cdn/proxy), will disable the use of the XFF header.
|
||||
[rate limiting](#rate-limiting) logic to use the header configured in `proxy-forwarded-header` (default is `X-Forwarded-For`)
|
||||
as the primary identifier for a visitor, as opposed to the remote IP address.
|
||||
|
||||
=== "/etc/ntfy/server.yml"
|
||||
If the `behind-proxy` flag is not set, all visitors will be counted as one, because from the perspective of the
|
||||
ntfy server, they all share the proxy's IP address.
|
||||
|
||||
Relevant flags to consider:
|
||||
|
||||
* `behind-proxy`: if set, ntfy will use the `proxy-forwarded-header` to identify visitors (default: `false`)
|
||||
* `proxy-forwarded-header`: the header to use to identify visitors (default: `X-Forwarded-For`)
|
||||
* `proxy-trusted-addresses`: a comma-separated list of IP addresses that are removed from the forwarded header
|
||||
to determine the real IP address (default: empty)
|
||||
|
||||
=== "/etc/ntfy/server.yml (behind a proxy)"
|
||||
``` yaml
|
||||
# Tell ntfy to use "X-Forwarded-For" to identify visitors
|
||||
# Tell ntfy to use "X-Forwarded-For" header to identify visitors for rate limiting
|
||||
#
|
||||
# Example: If "X-Forwarded-For: 9.9.9.9, 1.2.3.4" is set,
|
||||
# the visitor IP will be 1.2.3.4 (right-most address).
|
||||
#
|
||||
behind-proxy: true
|
||||
proxy-client-ip-header: "X-Client-IP"
|
||||
```
|
||||
|
||||
=== "/etc/ntfy/server.yml (with custom header)"
|
||||
``` yaml
|
||||
# Tell ntfy to use "X-Client-IP" header to identify visitors for rate limiting
|
||||
#
|
||||
# Example: If "X-Client-IP: 9.9.9.9" is set,
|
||||
# the visitor IP will be 9.9.9.9.
|
||||
#
|
||||
behind-proxy: true
|
||||
proxy-forwarded-header: "X-Client-IP"
|
||||
```
|
||||
|
||||
=== "/etc/ntfy/server.yml (multiple proxies)"
|
||||
``` yaml
|
||||
# Tell ntfy to use "X-Forwarded-For" header to identify visitors for rate limiting,
|
||||
# and to strip the IP addresses of the proxies 1.2.3.4 and 1.2.3.5
|
||||
#
|
||||
# Example: If "X-Forwarded-For: 9.9.9.9, 1.2.3.4" is set,
|
||||
# the visitor IP will be 9.9.9.9 (right-most unknown address).
|
||||
#
|
||||
behind-proxy: true
|
||||
proxy-trusted-addresses: "1.2.3.4, 1.2.3.5"
|
||||
```
|
||||
|
||||
### TLS/SSL
|
||||
|
@ -1391,7 +1426,9 @@ variable before running the `ntfy` command (e.g. `export NTFY_LISTEN_HTTP=:80`).
|
|||
| `cache-batch-timeout` | `NTFY_CACHE_BATCH_TIMEOUT` | *duration* | 0s | Timeout for batched async writes to the message cache (if zero, writes are synchronous) |
|
||||
| `auth-file` | `NTFY_AUTH_FILE` | *filename* | - | Auth database file used for access control. If set, enables authentication and access control. See [access control](#access-control). |
|
||||
| `auth-default-access` | `NTFY_AUTH_DEFAULT_ACCESS` | `read-write`, `read-only`, `write-only`, `deny-all` | `read-write` | Default permissions if no matching entries in the auth database are found. Default is `read-write`. |
|
||||
| `behind-proxy` | `NTFY_BEHIND_PROXY` | *bool* | false | If set, the X-Forwarded-For header is used to determine the visitor IP address instead of the remote address of the connection. |
|
||||
| `behind-proxy` | `NTFY_BEHIND_PROXY` | *bool* | false | If set, use forwarded header (e.g. X-Forwarded-For, X-Client-IP) to determine visitor IP address (for rate limiting) |
|
||||
| `proxy-forwarded-header` | `NTFY_PROXY_FORWARDED_HEADER` | *string* | `X-Forwarded-For` | Use specified header to determine visitor IP address (for rate limiting) |
|
||||
| `proxy-trusted-addresses` | `NTFY_PROXY_TRUSTED_ADDRESSES` | *comma-separated list of IPs* | - | Comma-separated list of trusted IP addresses to remove from forwarded header |
|
||||
| `attachment-cache-dir` | `NTFY_ATTACHMENT_CACHE_DIR` | *directory* | - | Cache directory for attached files. To enable attachments, this has to be set. |
|
||||
| `attachment-total-size-limit` | `NTFY_ATTACHMENT_TOTAL_SIZE_LIMIT` | *size* | 5G | Limit of the on-disk attachment cache directory. If the limits is exceeded, new attachments will be rejected. |
|
||||
| `attachment-file-size-limit` | `NTFY_ATTACHMENT_FILE_SIZE_LIMIT` | *size* | 15M | Per-file attachment size limit (e.g. 300k, 2M, 100M). Larger attachment will be rejected. |
|
||||
|
|
|
@ -145,7 +145,7 @@ type Config struct {
|
|||
VisitorSubscriberRateLimiting bool // Enable subscriber-based rate limiting for UnifiedPush topics
|
||||
BehindProxy bool // If true, the server will trust the proxy client IP header to determine the client IP address
|
||||
ProxyForwardedHeader string // The header field to read the real/client IP address from, if BehindProxy is true, defaults to "X-Forwarded-For"
|
||||
ProxyTrustedAddrs []string // List of trusted proxy addresses that will be stripped from the Forwarded header if BehindProxy is true
|
||||
ProxyTrustedAddresses []string // List of trusted proxy addresses that will be stripped from the Forwarded header if BehindProxy is true
|
||||
StripeSecretKey string
|
||||
StripeWebhookKey string
|
||||
StripePriceCacheDuration time.Duration
|
||||
|
|
|
@ -1937,7 +1937,7 @@ func (s *Server) authorizeTopic(next handleFunc, perm user.Permission) handleFun
|
|||
// that subsequent logging calls still have a visitor context.
|
||||
func (s *Server) maybeAuthenticate(r *http.Request) (*visitor, error) {
|
||||
// Read the "Authorization" header value and exit out early if it's not set
|
||||
ip := extractIPAddress(r, s.config.BehindProxy, s.config.ProxyForwardedHeader, s.config.ProxyTrustedAddrs)
|
||||
ip := extractIPAddress(r, s.config.BehindProxy, s.config.ProxyForwardedHeader, s.config.ProxyTrustedAddresses)
|
||||
vip := s.visitor(ip, nil)
|
||||
if s.userManager == nil {
|
||||
return vip, nil
|
||||
|
@ -2012,7 +2012,7 @@ func (s *Server) authenticateBearerAuth(r *http.Request, token string) (*user.Us
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ip := extractIPAddress(r, s.config.BehindProxy, s.config.ProxyForwardedHeader, s.config.ProxyTrustedAddrs)
|
||||
ip := extractIPAddress(r, s.config.BehindProxy, s.config.ProxyForwardedHeader, s.config.ProxyTrustedAddresses)
|
||||
go s.userManager.EnqueueTokenUpdate(token, &user.TokenUpdate{
|
||||
LastAccess: time.Now(),
|
||||
LastOrigin: ip,
|
||||
|
|
|
@ -74,9 +74,9 @@ func readQueryParam(r *http.Request, names ...string) string {
|
|||
return ""
|
||||
}
|
||||
|
||||
func extractIPAddress(r *http.Request, behindProxy bool, proxyForwardedHeader string, proxyTrustedAddrs []string) netip.Addr {
|
||||
func extractIPAddress(r *http.Request, behindProxy bool, proxyForwardedHeader string, proxyTrustedAddresses []string) netip.Addr {
|
||||
if behindProxy && proxyForwardedHeader != "" {
|
||||
if addr, err := extractIPAddressFromHeader(r, proxyForwardedHeader, proxyTrustedAddrs); err == nil {
|
||||
if addr, err := extractIPAddressFromHeader(r, proxyForwardedHeader, proxyTrustedAddresses); err == nil {
|
||||
return addr
|
||||
}
|
||||
// Fall back to the remote address if the header is not found or invalid
|
||||
|
@ -94,14 +94,14 @@ func extractIPAddress(r *http.Request, behindProxy bool, proxyForwardedHeader st
|
|||
// X-Forwarded-For can contain multiple addresses (see #328). If we are behind a proxy,
|
||||
// only the right-most address can be trusted (as this is the one added by our proxy server).
|
||||
// See https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For for details.
|
||||
func extractIPAddressFromHeader(r *http.Request, forwardedHeader string, trustedAddrs []string) (netip.Addr, error) {
|
||||
func extractIPAddressFromHeader(r *http.Request, forwardedHeader string, trustedAddresses []string) (netip.Addr, error) {
|
||||
value := strings.TrimSpace(r.Header.Get(forwardedHeader))
|
||||
if value == "" {
|
||||
return netip.IPv4Unspecified(), fmt.Errorf("no %s header found", forwardedHeader)
|
||||
}
|
||||
addrs := util.Map(util.SplitNoEmpty(value, ","), strings.TrimSpace)
|
||||
clientAddrs := util.Filter(addrs, func(addr string) bool {
|
||||
return !slices.Contains(trustedAddrs, addr)
|
||||
return !slices.Contains(trustedAddresses, addr)
|
||||
})
|
||||
if len(clientAddrs) == 0 {
|
||||
return netip.IPv4Unspecified(), fmt.Errorf("no client IP address found in %s header: %s", forwardedHeader, value)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue