visitor.go 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. package server
  2. import (
  3. "fmt"
  4. "heckel.io/ntfy/log"
  5. "heckel.io/ntfy/user"
  6. "net/netip"
  7. "sync"
  8. "time"
  9. "golang.org/x/time/rate"
  10. "heckel.io/ntfy/util"
  11. )
  12. const (
  13. // oneDay is an approximation of a day as a time.Duration
  14. oneDay = 24 * time.Hour
  15. // visitorExpungeAfter defines how long a visitor is active before it is removed from memory. This number
  16. // has to be very high to prevent e-mail abuse, but it doesn't really affect the other limits anyway, since
  17. // they are replenished faster (typically).
  18. visitorExpungeAfter = oneDay
  19. // visitorDefaultReservationsLimit is the amount of topic names a user without a tier is allowed to reserve.
  20. // This number is zero, and changing it may have unintended consequences in the web app, or otherwise
  21. visitorDefaultReservationsLimit = int64(0)
  22. )
  23. // Constants used to convert a tier-user's MessageLimit (see user.Tier) into adequate request limiter
  24. // values (token bucket). This is only used to increase the values in server.yml, never decrease them.
  25. //
  26. // Example: Assuming a user.Tier's MessageLimit is 10,000:
  27. // - the allowed burst is 500 (= 10,000 * 5%), which is < 1000 (the max)
  28. // - the replenish rate is 2 * 10,000 / 24 hours
  29. const (
  30. visitorMessageToRequestLimitBurstRate = 0.05
  31. visitorMessageToRequestLimitBurstMax = 1000
  32. visitorMessageToRequestLimitReplenishFactor = 2
  33. )
  34. // Constants used to convert a tier-user's EmailLimit (see user.Tier) into adequate email limiter
  35. // values (token bucket). Example: Assuming a user.Tier's EmailLimit is 200, the allowed burst is
  36. // 40 (= 200 * 20%), which is <150 (the max).
  37. const (
  38. visitorEmailLimitBurstRate = 0.2
  39. visitorEmailLimitBurstMax = 150
  40. )
  41. // visitor represents an API user, and its associated rate.Limiter used for rate limiting
  42. type visitor struct {
  43. config *Config
  44. messageCache *messageCache
  45. userManager *user.Manager // May be nil
  46. ip netip.Addr // Visitor IP address
  47. user *user.User // Only set if authenticated user, otherwise nil
  48. requestLimiter *rate.Limiter // Rate limiter for (almost) all requests (including messages)
  49. messagesLimiter *util.FixedLimiter // Rate limiter for messages
  50. emailsLimiter *util.RateLimiter // Rate limiter for emails
  51. callsLimiter *util.FixedLimiter // Rate limiter for calls
  52. subscriptionLimiter *util.FixedLimiter // Fixed limiter for active subscriptions (ongoing connections)
  53. bandwidthLimiter *util.RateLimiter // Limiter for attachment bandwidth downloads
  54. accountLimiter *rate.Limiter // Rate limiter for account creation, may be nil
  55. authLimiter *rate.Limiter // Limiter for incorrect login attempts, may be nil
  56. firebase time.Time // Next allowed Firebase message
  57. seen time.Time // Last seen time of this visitor (needed for removal of stale visitors)
  58. mu sync.RWMutex
  59. }
  60. type visitorInfo struct {
  61. Limits *visitorLimits
  62. Stats *visitorStats
  63. }
  64. type visitorLimits struct {
  65. Basis visitorLimitBasis
  66. RequestLimitBurst int
  67. RequestLimitReplenish rate.Limit
  68. MessageLimit int64
  69. MessageExpiryDuration time.Duration
  70. EmailLimit int64
  71. EmailLimitBurst int
  72. EmailLimitReplenish rate.Limit
  73. CallLimit int64
  74. ReservationsLimit int64
  75. AttachmentTotalSizeLimit int64
  76. AttachmentFileSizeLimit int64
  77. AttachmentExpiryDuration time.Duration
  78. AttachmentBandwidthLimit int64
  79. }
  80. type visitorStats struct {
  81. Messages int64
  82. MessagesRemaining int64
  83. Emails int64
  84. EmailsRemaining int64
  85. Calls int64
  86. CallsRemaining int64
  87. Reservations int64
  88. ReservationsRemaining int64
  89. AttachmentTotalSize int64
  90. AttachmentTotalSizeRemaining int64
  91. }
  92. // visitorLimitBasis describes how the visitor limits were derived, either from a user's
  93. // IP address (default config), or from its tier
  94. type visitorLimitBasis string
  95. const (
  96. visitorLimitBasisIP = visitorLimitBasis("ip")
  97. visitorLimitBasisTier = visitorLimitBasis("tier")
  98. )
  99. func newVisitor(conf *Config, messageCache *messageCache, userManager *user.Manager, ip netip.Addr, user *user.User) *visitor {
  100. var messages, emails, calls int64
  101. if user != nil {
  102. messages = user.Stats.Messages
  103. emails = user.Stats.Emails
  104. calls = user.Stats.Calls
  105. }
  106. v := &visitor{
  107. config: conf,
  108. messageCache: messageCache,
  109. userManager: userManager, // May be nil
  110. ip: ip,
  111. user: user,
  112. firebase: time.Unix(0, 0),
  113. seen: time.Now(),
  114. subscriptionLimiter: util.NewFixedLimiter(int64(conf.VisitorSubscriptionLimit)),
  115. requestLimiter: nil, // Set in resetLimiters
  116. messagesLimiter: nil, // Set in resetLimiters, may be nil
  117. emailsLimiter: nil, // Set in resetLimiters
  118. callsLimiter: nil, // Set in resetLimiters, may be nil
  119. bandwidthLimiter: nil, // Set in resetLimiters
  120. accountLimiter: nil, // Set in resetLimiters, may be nil
  121. authLimiter: nil, // Set in resetLimiters, may be nil
  122. }
  123. v.resetLimitersNoLock(messages, emails, calls, false)
  124. return v
  125. }
  126. func (v *visitor) Context() log.Context {
  127. v.mu.RLock()
  128. defer v.mu.RUnlock()
  129. return v.contextNoLock()
  130. }
  131. func (v *visitor) contextNoLock() log.Context {
  132. info := v.infoLightNoLock()
  133. fields := log.Context{
  134. "visitor_id": visitorID(v.ip, v.user),
  135. "visitor_ip": v.ip.String(),
  136. "visitor_seen": util.FormatTime(v.seen),
  137. "visitor_messages": info.Stats.Messages,
  138. "visitor_messages_limit": info.Limits.MessageLimit,
  139. "visitor_messages_remaining": info.Stats.MessagesRemaining,
  140. "visitor_request_limiter_limit": v.requestLimiter.Limit(),
  141. "visitor_request_limiter_tokens": v.requestLimiter.Tokens(),
  142. }
  143. if v.config.SMTPSenderFrom != "" {
  144. fields["visitor_emails"] = info.Stats.Emails
  145. fields["visitor_emails_limit"] = info.Limits.EmailLimit
  146. fields["visitor_emails_remaining"] = info.Stats.EmailsRemaining
  147. }
  148. if v.config.TwilioAccount != "" {
  149. fields["visitor_calls"] = info.Stats.Calls
  150. fields["visitor_calls_limit"] = info.Limits.CallLimit
  151. fields["visitor_calls_remaining"] = info.Stats.CallsRemaining
  152. }
  153. if v.authLimiter != nil {
  154. fields["visitor_auth_limiter_limit"] = v.authLimiter.Limit()
  155. fields["visitor_auth_limiter_tokens"] = v.authLimiter.Tokens()
  156. }
  157. if v.user != nil {
  158. fields["user_id"] = v.user.ID
  159. fields["user_name"] = v.user.Name
  160. if v.user.Tier != nil {
  161. for field, value := range v.user.Tier.Context() {
  162. fields[field] = value
  163. }
  164. }
  165. if v.user.Billing.StripeCustomerID != "" {
  166. fields["stripe_customer_id"] = v.user.Billing.StripeCustomerID
  167. }
  168. if v.user.Billing.StripeSubscriptionID != "" {
  169. fields["stripe_subscription_id"] = v.user.Billing.StripeSubscriptionID
  170. }
  171. }
  172. return fields
  173. }
  174. func visitorExtendedInfoContext(info *visitorInfo) log.Context {
  175. return log.Context{
  176. "visitor_reservations": info.Stats.Reservations,
  177. "visitor_reservations_limit": info.Limits.ReservationsLimit,
  178. "visitor_reservations_remaining": info.Stats.ReservationsRemaining,
  179. "visitor_attachment_total_size": info.Stats.AttachmentTotalSize,
  180. "visitor_attachment_total_size_limit": info.Limits.AttachmentTotalSizeLimit,
  181. "visitor_attachment_total_size_remaining": info.Stats.AttachmentTotalSizeRemaining,
  182. }
  183. }
  184. func (v *visitor) RequestAllowed() bool {
  185. v.mu.RLock() // limiters could be replaced!
  186. defer v.mu.RUnlock()
  187. return v.requestLimiter.Allow()
  188. }
  189. func (v *visitor) FirebaseAllowed() bool {
  190. v.mu.RLock()
  191. defer v.mu.RUnlock()
  192. return !time.Now().Before(v.firebase)
  193. }
  194. func (v *visitor) FirebaseTemporarilyDeny() {
  195. v.mu.Lock()
  196. defer v.mu.Unlock()
  197. v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
  198. }
  199. func (v *visitor) MessageAllowed() bool {
  200. v.mu.RLock() // limiters could be replaced!
  201. defer v.mu.RUnlock()
  202. return v.messagesLimiter.Allow()
  203. }
  204. func (v *visitor) EmailAllowed() bool {
  205. v.mu.RLock() // limiters could be replaced!
  206. defer v.mu.RUnlock()
  207. return v.emailsLimiter.Allow()
  208. }
  209. func (v *visitor) CallAllowed() bool {
  210. v.mu.RLock() // limiters could be replaced!
  211. defer v.mu.RUnlock()
  212. return v.callsLimiter.Allow()
  213. }
  214. func (v *visitor) SubscriptionAllowed() bool {
  215. v.mu.RLock() // limiters could be replaced!
  216. defer v.mu.RUnlock()
  217. return v.subscriptionLimiter.Allow()
  218. }
  219. // AuthAllowed returns true if an auth request can be attempted (> 1 token available)
  220. func (v *visitor) AuthAllowed() bool {
  221. v.mu.RLock() // limiters could be replaced!
  222. defer v.mu.RUnlock()
  223. if v.authLimiter == nil {
  224. return true
  225. }
  226. return v.authLimiter.Tokens() > 1
  227. }
  228. // AuthFailed records an auth failure
  229. func (v *visitor) AuthFailed() {
  230. v.mu.RLock() // limiters could be replaced!
  231. defer v.mu.RUnlock()
  232. if v.authLimiter != nil {
  233. v.authLimiter.Allow()
  234. }
  235. }
  236. // AccountCreationAllowed returns true if a new account can be created
  237. func (v *visitor) AccountCreationAllowed() bool {
  238. v.mu.RLock() // limiters could be replaced!
  239. defer v.mu.RUnlock()
  240. if v.accountLimiter == nil || (v.accountLimiter != nil && v.accountLimiter.Tokens() < 1) {
  241. return false
  242. }
  243. return true
  244. }
  245. // AccountCreated decreases the account limiter. This is to be called after an account was created.
  246. func (v *visitor) AccountCreated() {
  247. v.mu.RLock() // limiters could be replaced!
  248. defer v.mu.RUnlock()
  249. if v.accountLimiter != nil {
  250. v.accountLimiter.Allow()
  251. }
  252. }
  253. func (v *visitor) BandwidthAllowed(bytes int64) bool {
  254. v.mu.RLock() // limiters could be replaced!
  255. defer v.mu.RUnlock()
  256. return v.bandwidthLimiter.AllowN(bytes)
  257. }
  258. func (v *visitor) RemoveSubscription() {
  259. v.mu.RLock()
  260. defer v.mu.RUnlock()
  261. v.subscriptionLimiter.AllowN(-1)
  262. }
  263. func (v *visitor) Keepalive() {
  264. v.mu.Lock()
  265. defer v.mu.Unlock()
  266. v.seen = time.Now()
  267. }
  268. func (v *visitor) BandwidthLimiter() util.Limiter {
  269. v.mu.RLock() // limiters could be replaced!
  270. defer v.mu.RUnlock()
  271. return v.bandwidthLimiter
  272. }
  273. func (v *visitor) Stale() bool {
  274. v.mu.RLock()
  275. defer v.mu.RUnlock()
  276. return time.Since(v.seen) > visitorExpungeAfter
  277. }
  278. func (v *visitor) Stats() *user.Stats {
  279. v.mu.RLock() // limiters could be replaced!
  280. defer v.mu.RUnlock()
  281. return &user.Stats{
  282. Messages: v.messagesLimiter.Value(),
  283. Emails: v.emailsLimiter.Value(),
  284. Calls: v.callsLimiter.Value(),
  285. }
  286. }
  287. func (v *visitor) ResetStats() {
  288. v.mu.RLock() // limiters could be replaced!
  289. defer v.mu.RUnlock()
  290. v.emailsLimiter.Reset()
  291. v.messagesLimiter.Reset()
  292. v.callsLimiter.Reset()
  293. }
  294. // User returns the visitor user, or nil if there is none
  295. func (v *visitor) User() *user.User {
  296. v.mu.RLock()
  297. defer v.mu.RUnlock()
  298. return v.user // May be nil
  299. }
  300. // IP returns the visitor IP address
  301. func (v *visitor) IP() netip.Addr {
  302. v.mu.RLock()
  303. defer v.mu.RUnlock()
  304. return v.ip
  305. }
  306. // Authenticated returns true if a user successfully authenticated
  307. func (v *visitor) Authenticated() bool {
  308. v.mu.RLock()
  309. defer v.mu.RUnlock()
  310. return v.user != nil
  311. }
  312. // SetUser sets the visitors user to the given value
  313. func (v *visitor) SetUser(u *user.User) {
  314. v.mu.Lock()
  315. defer v.mu.Unlock()
  316. shouldResetLimiters := v.user.TierID() != u.TierID() // TierID works with nil receiver
  317. v.user = u // u may be nil!
  318. if shouldResetLimiters {
  319. var messages, emails, calls int64
  320. if u != nil {
  321. messages, emails, calls = u.Stats.Messages, u.Stats.Emails, u.Stats.Calls
  322. }
  323. v.resetLimitersNoLock(messages, emails, calls, true)
  324. }
  325. }
  326. // MaybeUserID returns the user ID of the visitor (if any). If this is an anonymous visitor,
  327. // an empty string is returned.
  328. func (v *visitor) MaybeUserID() string {
  329. v.mu.RLock()
  330. defer v.mu.RUnlock()
  331. if v.user != nil {
  332. return v.user.ID
  333. }
  334. return ""
  335. }
  336. func (v *visitor) resetLimitersNoLock(messages, emails, calls int64, enqueueUpdate bool) {
  337. limits := v.limitsNoLock()
  338. v.requestLimiter = rate.NewLimiter(limits.RequestLimitReplenish, limits.RequestLimitBurst)
  339. v.messagesLimiter = util.NewFixedLimiterWithValue(limits.MessageLimit, messages)
  340. v.emailsLimiter = util.NewRateLimiterWithValue(limits.EmailLimitReplenish, limits.EmailLimitBurst, emails)
  341. v.callsLimiter = util.NewFixedLimiterWithValue(limits.CallLimit, calls)
  342. v.bandwidthLimiter = util.NewBytesLimiter(int(limits.AttachmentBandwidthLimit), oneDay)
  343. if v.user == nil {
  344. v.accountLimiter = rate.NewLimiter(rate.Every(v.config.VisitorAccountCreationLimitReplenish), v.config.VisitorAccountCreationLimitBurst)
  345. v.authLimiter = rate.NewLimiter(rate.Every(v.config.VisitorAuthFailureLimitReplenish), v.config.VisitorAuthFailureLimitBurst)
  346. } else {
  347. v.accountLimiter = nil // Users cannot create accounts when logged in
  348. v.authLimiter = nil // Users are already logged in, no need to limit requests
  349. }
  350. if enqueueUpdate && v.user != nil {
  351. go v.userManager.EnqueueUserStats(v.user.ID, &user.Stats{
  352. Messages: messages,
  353. Emails: emails,
  354. Calls: calls,
  355. })
  356. }
  357. log.Fields(v.contextNoLock()).Debug("Rate limiters reset for visitor") // Must be after function, because contextNoLock() describes rate limiters
  358. }
  359. func (v *visitor) Limits() *visitorLimits {
  360. v.mu.RLock()
  361. defer v.mu.RUnlock()
  362. return v.limitsNoLock()
  363. }
  364. func (v *visitor) limitsNoLock() *visitorLimits {
  365. if v.user != nil && v.user.Tier != nil {
  366. return tierBasedVisitorLimits(v.config, v.user.Tier)
  367. }
  368. return configBasedVisitorLimits(v.config)
  369. }
  370. func tierBasedVisitorLimits(conf *Config, tier *user.Tier) *visitorLimits {
  371. return &visitorLimits{
  372. Basis: visitorLimitBasisTier,
  373. RequestLimitBurst: util.MinMax(int(float64(tier.MessageLimit)*visitorMessageToRequestLimitBurstRate), conf.VisitorRequestLimitBurst, visitorMessageToRequestLimitBurstMax),
  374. RequestLimitReplenish: util.Max(rate.Every(conf.VisitorRequestLimitReplenish), dailyLimitToRate(tier.MessageLimit*visitorMessageToRequestLimitReplenishFactor)),
  375. MessageLimit: tier.MessageLimit,
  376. MessageExpiryDuration: tier.MessageExpiryDuration,
  377. EmailLimit: tier.EmailLimit,
  378. EmailLimitBurst: util.MinMax(int(float64(tier.EmailLimit)*visitorEmailLimitBurstRate), conf.VisitorEmailLimitBurst, visitorEmailLimitBurstMax),
  379. EmailLimitReplenish: dailyLimitToRate(tier.EmailLimit),
  380. CallLimit: tier.CallLimit,
  381. ReservationsLimit: tier.ReservationLimit,
  382. AttachmentTotalSizeLimit: tier.AttachmentTotalSizeLimit,
  383. AttachmentFileSizeLimit: tier.AttachmentFileSizeLimit,
  384. AttachmentExpiryDuration: tier.AttachmentExpiryDuration,
  385. AttachmentBandwidthLimit: tier.AttachmentBandwidthLimit,
  386. }
  387. }
  388. func configBasedVisitorLimits(conf *Config) *visitorLimits {
  389. messagesLimit := replenishDurationToDailyLimit(conf.VisitorRequestLimitReplenish) // Approximation!
  390. if conf.VisitorMessageDailyLimit > 0 {
  391. messagesLimit = int64(conf.VisitorMessageDailyLimit)
  392. }
  393. return &visitorLimits{
  394. Basis: visitorLimitBasisIP,
  395. RequestLimitBurst: conf.VisitorRequestLimitBurst,
  396. RequestLimitReplenish: rate.Every(conf.VisitorRequestLimitReplenish),
  397. MessageLimit: messagesLimit,
  398. MessageExpiryDuration: conf.CacheDuration,
  399. EmailLimit: replenishDurationToDailyLimit(conf.VisitorEmailLimitReplenish), // Approximation!
  400. EmailLimitBurst: conf.VisitorEmailLimitBurst,
  401. EmailLimitReplenish: rate.Every(conf.VisitorEmailLimitReplenish),
  402. CallLimit: int64(conf.VisitorCallDailyLimit),
  403. ReservationsLimit: visitorDefaultReservationsLimit,
  404. AttachmentTotalSizeLimit: conf.VisitorAttachmentTotalSizeLimit,
  405. AttachmentFileSizeLimit: conf.AttachmentFileSizeLimit,
  406. AttachmentExpiryDuration: conf.AttachmentExpiryDuration,
  407. AttachmentBandwidthLimit: conf.VisitorAttachmentDailyBandwidthLimit,
  408. }
  409. }
  410. func (v *visitor) Info() (*visitorInfo, error) {
  411. v.mu.RLock()
  412. info := v.infoLightNoLock()
  413. v.mu.RUnlock()
  414. // Attachment stats from database
  415. var attachmentsBytesUsed int64
  416. var err error
  417. u := v.User()
  418. if u != nil {
  419. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedByUser(u.ID)
  420. } else {
  421. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedBySender(v.IP().String())
  422. }
  423. if err != nil {
  424. return nil, err
  425. }
  426. info.Stats.AttachmentTotalSize = attachmentsBytesUsed
  427. info.Stats.AttachmentTotalSizeRemaining = zeroIfNegative(info.Limits.AttachmentTotalSizeLimit - attachmentsBytesUsed)
  428. // Reservation stats from database
  429. var reservations int64
  430. if v.userManager != nil && u != nil {
  431. reservations, err = v.userManager.ReservationsCount(u.Name)
  432. if err != nil {
  433. return nil, err
  434. }
  435. }
  436. info.Stats.Reservations = reservations
  437. info.Stats.ReservationsRemaining = zeroIfNegative(info.Limits.ReservationsLimit - reservations)
  438. return info, nil
  439. }
  440. func (v *visitor) infoLightNoLock() *visitorInfo {
  441. messages := v.messagesLimiter.Value()
  442. emails := v.emailsLimiter.Value()
  443. calls := v.callsLimiter.Value()
  444. limits := v.limitsNoLock()
  445. stats := &visitorStats{
  446. Messages: messages,
  447. MessagesRemaining: zeroIfNegative(limits.MessageLimit - messages),
  448. Emails: emails,
  449. EmailsRemaining: zeroIfNegative(limits.EmailLimit - emails),
  450. Calls: calls,
  451. CallsRemaining: zeroIfNegative(limits.CallLimit - calls),
  452. }
  453. return &visitorInfo{
  454. Limits: limits,
  455. Stats: stats,
  456. }
  457. }
  458. func zeroIfNegative(value int64) int64 {
  459. if value < 0 {
  460. return 0
  461. }
  462. return value
  463. }
  464. func replenishDurationToDailyLimit(duration time.Duration) int64 {
  465. return int64(oneDay / duration)
  466. }
  467. func dailyLimitToRate(limit int64) rate.Limit {
  468. return rate.Limit(limit) * rate.Every(oneDay)
  469. }
  470. func visitorID(ip netip.Addr, u *user.User) string {
  471. if u != nil && u.Tier != nil {
  472. return fmt.Sprintf("user:%s", u.ID)
  473. }
  474. return fmt.Sprintf("ip:%s", ip.String())
  475. }