visitor.go 16 KB

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