visitor.go 16 KB

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