visitor.go 17 KB

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