visitor.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493
  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. subscriptionLimiter *util.FixedLimiter // Fixed limiter for active subscriptions (ongoing connections)
  52. bandwidthLimiter *util.RateLimiter // Limiter for attachment bandwidth downloads
  53. accountLimiter *rate.Limiter // Rate limiter for account creation, may be nil
  54. authLimiter *rate.Limiter // Limiter for incorrect login attempts, may be nil
  55. firebase time.Time // Next allowed Firebase message
  56. seen time.Time // Last seen time of this visitor (needed for removal of stale visitors)
  57. mu sync.RWMutex
  58. }
  59. type visitorInfo struct {
  60. Limits *visitorLimits
  61. Stats *visitorStats
  62. }
  63. type visitorLimits struct {
  64. Basis visitorLimitBasis
  65. RequestLimitBurst int
  66. RequestLimitReplenish rate.Limit
  67. MessageLimit int64
  68. MessageExpiryDuration time.Duration
  69. EmailLimit int64
  70. EmailLimitBurst int
  71. EmailLimitReplenish rate.Limit
  72. ReservationsLimit int64
  73. AttachmentTotalSizeLimit int64
  74. AttachmentFileSizeLimit int64
  75. AttachmentExpiryDuration time.Duration
  76. AttachmentBandwidthLimit int64
  77. }
  78. type visitorStats struct {
  79. Messages int64
  80. MessagesRemaining int64
  81. Emails int64
  82. EmailsRemaining int64
  83. Reservations int64
  84. ReservationsRemaining int64
  85. AttachmentTotalSize int64
  86. AttachmentTotalSizeRemaining int64
  87. }
  88. // visitorLimitBasis describes how the visitor limits were derived, either from a user's
  89. // IP address (default config), or from its tier
  90. type visitorLimitBasis string
  91. const (
  92. visitorLimitBasisIP = visitorLimitBasis("ip")
  93. visitorLimitBasisTier = visitorLimitBasis("tier")
  94. )
  95. func newVisitor(conf *Config, messageCache *messageCache, userManager *user.Manager, ip netip.Addr, user *user.User) *visitor {
  96. var messages, emails int64
  97. if user != nil {
  98. messages = user.Stats.Messages
  99. emails = user.Stats.Emails
  100. }
  101. v := &visitor{
  102. config: conf,
  103. messageCache: messageCache,
  104. userManager: userManager, // May be nil
  105. ip: ip,
  106. user: user,
  107. firebase: time.Unix(0, 0),
  108. seen: time.Now(),
  109. subscriptionLimiter: util.NewFixedLimiter(int64(conf.VisitorSubscriptionLimit)),
  110. requestLimiter: nil, // Set in resetLimiters
  111. messagesLimiter: nil, // Set in resetLimiters, may be nil
  112. emailsLimiter: nil, // Set in resetLimiters
  113. bandwidthLimiter: nil, // Set in resetLimiters
  114. accountLimiter: nil, // Set in resetLimiters, may be nil
  115. authLimiter: nil, // Set in resetLimiters, may be nil
  116. }
  117. v.resetLimitersNoLock(messages, emails, false)
  118. return v
  119. }
  120. func (v *visitor) Context() log.Context {
  121. v.mu.RLock()
  122. defer v.mu.RUnlock()
  123. return v.contextNoLock()
  124. }
  125. func (v *visitor) contextNoLock() log.Context {
  126. info := v.infoLightNoLock()
  127. fields := log.Context{
  128. "visitor_ip": v.ip.String(),
  129. "visitor_messages": info.Stats.Messages,
  130. "visitor_messages_limit": info.Limits.MessageLimit,
  131. "visitor_messages_remaining": info.Stats.MessagesRemaining,
  132. "visitor_emails": info.Stats.Emails,
  133. "visitor_emails_limit": info.Limits.EmailLimit,
  134. "visitor_emails_remaining": info.Stats.EmailsRemaining,
  135. "visitor_request_limiter_limit": v.requestLimiter.Limit(),
  136. "visitor_request_limiter_tokens": v.requestLimiter.Tokens(),
  137. }
  138. if v.authLimiter != nil {
  139. fields["visitor_auth_limiter_limit"] = v.authLimiter.Limit()
  140. fields["visitor_auth_limiter_tokens"] = v.authLimiter.Tokens()
  141. }
  142. if v.user != nil {
  143. fields["user_id"] = v.user.ID
  144. fields["user_name"] = v.user.Name
  145. if v.user.Tier != nil {
  146. fields["tier_id"] = v.user.Tier.ID
  147. fields["tier_name"] = v.user.Tier.Name
  148. }
  149. if v.user.Billing.StripeCustomerID != "" {
  150. fields["stripe_customer_id"] = v.user.Billing.StripeCustomerID
  151. }
  152. if v.user.Billing.StripeSubscriptionID != "" {
  153. fields["stripe_subscription_id"] = v.user.Billing.StripeSubscriptionID
  154. }
  155. }
  156. return fields
  157. }
  158. func visitorExtendedInfoContext(info *visitorInfo) log.Context {
  159. return log.Context{
  160. "visitor_reservations": info.Stats.Reservations,
  161. "visitor_reservations_limit": info.Limits.ReservationsLimit,
  162. "visitor_reservations_remaining": info.Stats.ReservationsRemaining,
  163. "visitor_attachment_total_size": info.Stats.AttachmentTotalSize,
  164. "visitor_attachment_total_size_limit": info.Limits.AttachmentTotalSizeLimit,
  165. "visitor_attachment_total_size_remaining": info.Stats.AttachmentTotalSizeRemaining,
  166. }
  167. }
  168. func (v *visitor) RequestAllowed() bool {
  169. v.mu.RLock() // limiters could be replaced!
  170. defer v.mu.RUnlock()
  171. return v.requestLimiter.Allow()
  172. }
  173. func (v *visitor) FirebaseAllowed() bool {
  174. v.mu.RLock()
  175. defer v.mu.RUnlock()
  176. return !time.Now().Before(v.firebase)
  177. }
  178. func (v *visitor) FirebaseTemporarilyDeny() {
  179. v.mu.Lock()
  180. defer v.mu.Unlock()
  181. v.firebase = time.Now().Add(v.config.FirebaseQuotaExceededPenaltyDuration)
  182. }
  183. func (v *visitor) MessageAllowed() bool {
  184. v.mu.RLock() // limiters could be replaced!
  185. defer v.mu.RUnlock()
  186. return v.messagesLimiter.Allow()
  187. }
  188. func (v *visitor) EmailAllowed() bool {
  189. v.mu.RLock() // limiters could be replaced!
  190. defer v.mu.RUnlock()
  191. return v.emailsLimiter.Allow()
  192. }
  193. func (v *visitor) SubscriptionAllowed() bool {
  194. v.mu.RLock() // limiters could be replaced!
  195. defer v.mu.RUnlock()
  196. return v.subscriptionLimiter.Allow()
  197. }
  198. // AuthAllowed returns true if an auth request can be attempted (> 1 token available)
  199. func (v *visitor) AuthAllowed() bool {
  200. v.mu.RLock() // limiters could be replaced!
  201. defer v.mu.RUnlock()
  202. if v.authLimiter == nil {
  203. return true
  204. }
  205. return v.authLimiter.Tokens() > 1
  206. }
  207. // AuthFailed records an auth failure
  208. func (v *visitor) AuthFailed() {
  209. v.mu.RLock() // limiters could be replaced!
  210. defer v.mu.RUnlock()
  211. if v.authLimiter != nil {
  212. v.authLimiter.Allow()
  213. }
  214. }
  215. // AccountCreationAllowed returns true if a new account can be created
  216. func (v *visitor) AccountCreationAllowed() bool {
  217. v.mu.RLock() // limiters could be replaced!
  218. defer v.mu.RUnlock()
  219. if v.accountLimiter == nil || (v.accountLimiter != nil && v.accountLimiter.Tokens() < 1) {
  220. return false
  221. }
  222. return true
  223. }
  224. // AccountCreated decreases the account limiter. This is to be called after an account was created.
  225. func (v *visitor) AccountCreated() {
  226. v.mu.RLock() // limiters could be replaced!
  227. defer v.mu.RUnlock()
  228. if v.accountLimiter != nil {
  229. v.accountLimiter.Allow()
  230. }
  231. }
  232. func (v *visitor) BandwidthAllowed(bytes int64) bool {
  233. v.mu.RLock() // limiters could be replaced!
  234. defer v.mu.RUnlock()
  235. return v.bandwidthLimiter.AllowN(bytes)
  236. }
  237. func (v *visitor) RemoveSubscription() {
  238. v.mu.RLock()
  239. defer v.mu.RUnlock()
  240. v.subscriptionLimiter.AllowN(-1)
  241. }
  242. func (v *visitor) Keepalive() {
  243. v.mu.Lock()
  244. defer v.mu.Unlock()
  245. v.seen = time.Now()
  246. }
  247. func (v *visitor) BandwidthLimiter() util.Limiter {
  248. v.mu.RLock() // limiters could be replaced!
  249. defer v.mu.RUnlock()
  250. return v.bandwidthLimiter
  251. }
  252. func (v *visitor) Stale() bool {
  253. v.mu.RLock()
  254. defer v.mu.RUnlock()
  255. return time.Since(v.seen) > visitorExpungeAfter
  256. }
  257. func (v *visitor) Stats() *user.Stats {
  258. v.mu.RLock() // limiters could be replaced!
  259. defer v.mu.RUnlock()
  260. return &user.Stats{
  261. Messages: v.messagesLimiter.Value(),
  262. Emails: v.emailsLimiter.Value(),
  263. }
  264. }
  265. func (v *visitor) ResetStats() {
  266. v.mu.RLock() // limiters could be replaced!
  267. defer v.mu.RUnlock()
  268. v.emailsLimiter.Reset()
  269. v.messagesLimiter.Reset()
  270. }
  271. // User returns the visitor user, or nil if there is none
  272. func (v *visitor) User() *user.User {
  273. v.mu.RLock()
  274. defer v.mu.RUnlock()
  275. return v.user // May be nil
  276. }
  277. // IP returns the visitor IP address
  278. func (v *visitor) IP() netip.Addr {
  279. v.mu.RLock()
  280. defer v.mu.RUnlock()
  281. return v.ip
  282. }
  283. // Authenticated returns true if a user successfully authenticated
  284. func (v *visitor) Authenticated() bool {
  285. v.mu.RLock()
  286. defer v.mu.RUnlock()
  287. return v.user != nil
  288. }
  289. // SetUser sets the visitors user to the given value
  290. func (v *visitor) SetUser(u *user.User) {
  291. v.mu.Lock()
  292. defer v.mu.Unlock()
  293. shouldResetLimiters := v.user.TierID() != u.TierID() // TierID works with nil receiver
  294. v.user = u
  295. if shouldResetLimiters {
  296. v.resetLimitersNoLock(0, 0, true)
  297. }
  298. }
  299. // MaybeUserID returns the user ID of the visitor (if any). If this is an anonymous visitor,
  300. // an empty string is returned.
  301. func (v *visitor) MaybeUserID() string {
  302. v.mu.RLock()
  303. defer v.mu.RUnlock()
  304. if v.user != nil {
  305. return v.user.ID
  306. }
  307. return ""
  308. }
  309. func (v *visitor) resetLimitersNoLock(messages, emails int64, enqueueUpdate bool) {
  310. limits := v.limitsNoLock()
  311. v.requestLimiter = rate.NewLimiter(limits.RequestLimitReplenish, limits.RequestLimitBurst)
  312. v.messagesLimiter = util.NewFixedLimiterWithValue(limits.MessageLimit, messages)
  313. v.emailsLimiter = util.NewRateLimiterWithValue(limits.EmailLimitReplenish, limits.EmailLimitBurst, emails)
  314. v.bandwidthLimiter = util.NewBytesLimiter(int(limits.AttachmentBandwidthLimit), oneDay)
  315. if v.user == nil {
  316. v.accountLimiter = rate.NewLimiter(rate.Every(v.config.VisitorAccountCreationLimitReplenish), v.config.VisitorAccountCreationLimitBurst)
  317. v.authLimiter = rate.NewLimiter(rate.Every(v.config.VisitorAuthFailureLimitReplenish), v.config.VisitorAuthFailureLimitBurst)
  318. } else {
  319. v.accountLimiter = nil // Users cannot create accounts when logged in
  320. v.authLimiter = nil // Users are already logged in, no need to limit requests
  321. }
  322. if enqueueUpdate && v.user != nil {
  323. go v.userManager.EnqueueUserStats(v.user.ID, &user.Stats{
  324. Messages: messages,
  325. Emails: emails,
  326. })
  327. }
  328. log.Fields(v.contextNoLock()).Debug("Rate limiters reset for visitor") // Must be after function, because contextNoLock() describes rate limiters
  329. }
  330. func (v *visitor) Limits() *visitorLimits {
  331. v.mu.RLock()
  332. defer v.mu.RUnlock()
  333. return v.limitsNoLock()
  334. }
  335. func (v *visitor) limitsNoLock() *visitorLimits {
  336. if v.user != nil && v.user.Tier != nil {
  337. return tierBasedVisitorLimits(v.config, v.user.Tier)
  338. }
  339. return configBasedVisitorLimits(v.config)
  340. }
  341. func tierBasedVisitorLimits(conf *Config, tier *user.Tier) *visitorLimits {
  342. return &visitorLimits{
  343. Basis: visitorLimitBasisTier,
  344. RequestLimitBurst: util.MinMax(int(float64(tier.MessageLimit)*visitorMessageToRequestLimitBurstRate), conf.VisitorRequestLimitBurst, visitorMessageToRequestLimitBurstMax),
  345. RequestLimitReplenish: util.Max(rate.Every(conf.VisitorRequestLimitReplenish), dailyLimitToRate(tier.MessageLimit*visitorMessageToRequestLimitReplenishFactor)),
  346. MessageLimit: tier.MessageLimit,
  347. MessageExpiryDuration: tier.MessageExpiryDuration,
  348. EmailLimit: tier.EmailLimit,
  349. EmailLimitBurst: util.MinMax(int(float64(tier.EmailLimit)*visitorEmailLimitBurstRate), conf.VisitorEmailLimitBurst, visitorEmailLimitBurstMax),
  350. EmailLimitReplenish: dailyLimitToRate(tier.EmailLimit),
  351. ReservationsLimit: tier.ReservationLimit,
  352. AttachmentTotalSizeLimit: tier.AttachmentTotalSizeLimit,
  353. AttachmentFileSizeLimit: tier.AttachmentFileSizeLimit,
  354. AttachmentExpiryDuration: tier.AttachmentExpiryDuration,
  355. AttachmentBandwidthLimit: tier.AttachmentBandwidthLimit,
  356. }
  357. }
  358. func configBasedVisitorLimits(conf *Config) *visitorLimits {
  359. messagesLimit := replenishDurationToDailyLimit(conf.VisitorRequestLimitReplenish) // Approximation!
  360. if conf.VisitorMessageDailyLimit > 0 {
  361. messagesLimit = int64(conf.VisitorMessageDailyLimit)
  362. }
  363. return &visitorLimits{
  364. Basis: visitorLimitBasisIP,
  365. RequestLimitBurst: conf.VisitorRequestLimitBurst,
  366. RequestLimitReplenish: rate.Every(conf.VisitorRequestLimitReplenish),
  367. MessageLimit: messagesLimit,
  368. MessageExpiryDuration: conf.CacheDuration,
  369. EmailLimit: replenishDurationToDailyLimit(conf.VisitorEmailLimitReplenish), // Approximation!
  370. EmailLimitBurst: conf.VisitorEmailLimitBurst,
  371. EmailLimitReplenish: rate.Every(conf.VisitorEmailLimitReplenish),
  372. ReservationsLimit: visitorDefaultReservationsLimit,
  373. AttachmentTotalSizeLimit: conf.VisitorAttachmentTotalSizeLimit,
  374. AttachmentFileSizeLimit: conf.AttachmentFileSizeLimit,
  375. AttachmentExpiryDuration: conf.AttachmentExpiryDuration,
  376. AttachmentBandwidthLimit: conf.VisitorAttachmentDailyBandwidthLimit,
  377. }
  378. }
  379. func (v *visitor) Info() (*visitorInfo, error) {
  380. v.mu.RLock()
  381. info := v.infoLightNoLock()
  382. v.mu.RUnlock()
  383. // Attachment stats from database
  384. var attachmentsBytesUsed int64
  385. var err error
  386. u := v.User()
  387. if u != nil {
  388. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedByUser(u.ID)
  389. } else {
  390. attachmentsBytesUsed, err = v.messageCache.AttachmentBytesUsedBySender(v.IP().String())
  391. }
  392. if err != nil {
  393. return nil, err
  394. }
  395. info.Stats.AttachmentTotalSize = attachmentsBytesUsed
  396. info.Stats.AttachmentTotalSizeRemaining = zeroIfNegative(info.Limits.AttachmentTotalSizeLimit - attachmentsBytesUsed)
  397. // Reservation stats from database
  398. var reservations int64
  399. if v.userManager != nil && u != nil {
  400. reservations, err = v.userManager.ReservationsCount(u.Name)
  401. if err != nil {
  402. return nil, err
  403. }
  404. }
  405. info.Stats.Reservations = reservations
  406. info.Stats.ReservationsRemaining = zeroIfNegative(info.Limits.ReservationsLimit - reservations)
  407. return info, nil
  408. }
  409. func (v *visitor) infoLightNoLock() *visitorInfo {
  410. messages := v.messagesLimiter.Value()
  411. emails := v.emailsLimiter.Value()
  412. limits := v.limitsNoLock()
  413. stats := &visitorStats{
  414. Messages: messages,
  415. MessagesRemaining: zeroIfNegative(limits.MessageLimit - messages),
  416. Emails: emails,
  417. EmailsRemaining: zeroIfNegative(limits.EmailLimit - emails),
  418. }
  419. return &visitorInfo{
  420. Limits: limits,
  421. Stats: stats,
  422. }
  423. }
  424. func zeroIfNegative(value int64) int64 {
  425. if value < 0 {
  426. return 0
  427. }
  428. return value
  429. }
  430. func replenishDurationToDailyLimit(duration time.Duration) int64 {
  431. return int64(oneDay / duration)
  432. }
  433. func dailyLimitToRate(limit int64) rate.Limit {
  434. return rate.Limit(limit) * rate.Every(oneDay)
  435. }
  436. func visitorID(ip netip.Addr, u *user.User) string {
  437. if u != nil && u.Tier != nil {
  438. return fmt.Sprintf("user:%s", u.ID)
  439. }
  440. return fmt.Sprintf("ip:%s", ip.String())
  441. }