ip_filter.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. /*
  2. MIT License
  3. Copyright © 2016 <dev@jpillora.com>
  4. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
  5. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
  6. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  7. */
  8. package server
  9. import (
  10. "log"
  11. "net"
  12. "net/http"
  13. "os"
  14. "sync"
  15. "github.com/tomasen/realip"
  16. )
  17. // IPFilterOptions for ipFilter. Allowed takes precedence over Blocked.
  18. // IPs can be IPv4 or IPv6 and can optionally contain subnet
  19. // masks (/24). Note however, determining if a given IP is
  20. // included in a subnet requires a linear scan so is less performant
  21. // than looking up single IPs.
  22. //
  23. // This could be improved with some algorithmic magic.
  24. type IPFilterOptions struct {
  25. //explicity allowed IPs
  26. AllowedIPs []string
  27. //explicity blocked IPs
  28. BlockedIPs []string
  29. //block by default (defaults to allow)
  30. BlockByDefault bool
  31. // TrustProxy enable check request IP from proxy
  32. TrustProxy bool
  33. Logger interface {
  34. Printf(format string, v ...interface{})
  35. }
  36. }
  37. // ipFilter
  38. type ipFilter struct {
  39. //mut protects the below
  40. //rw since writes are rare
  41. mut sync.RWMutex
  42. defaultAllowed bool
  43. ips map[string]bool
  44. subnets []*subnet
  45. }
  46. type subnet struct {
  47. str string
  48. ipnet *net.IPNet
  49. allowed bool
  50. }
  51. func newIPFilter(opts *IPFilterOptions) *ipFilter {
  52. if opts.Logger == nil {
  53. flags := log.LstdFlags
  54. opts.Logger = log.New(os.Stdout, "", flags)
  55. }
  56. f := &ipFilter{
  57. ips: map[string]bool{},
  58. defaultAllowed: !opts.BlockByDefault,
  59. }
  60. for _, ip := range opts.BlockedIPs {
  61. f.BlockIP(ip)
  62. }
  63. for _, ip := range opts.AllowedIPs {
  64. f.AllowIP(ip)
  65. }
  66. return f
  67. }
  68. func (f *ipFilter) AllowIP(ip string) bool {
  69. return f.ToggleIP(ip, true)
  70. }
  71. func (f *ipFilter) BlockIP(ip string) bool {
  72. return f.ToggleIP(ip, false)
  73. }
  74. func (f *ipFilter) ToggleIP(str string, allowed bool) bool {
  75. //check if provided string describes a subnet
  76. if ip, network, err := net.ParseCIDR(str); err == nil {
  77. // containing only one ip?
  78. if n, total := network.Mask.Size(); n == total {
  79. f.mut.Lock()
  80. f.ips[ip.String()] = allowed
  81. f.mut.Unlock()
  82. return true
  83. }
  84. //check for existing
  85. f.mut.Lock()
  86. found := false
  87. for _, subnet := range f.subnets {
  88. if subnet.str == str {
  89. found = true
  90. subnet.allowed = allowed
  91. break
  92. }
  93. }
  94. if !found {
  95. f.subnets = append(f.subnets, &subnet{
  96. str: str,
  97. ipnet: network,
  98. allowed: allowed,
  99. })
  100. }
  101. f.mut.Unlock()
  102. return true
  103. }
  104. //check if plain ip
  105. if ip := net.ParseIP(str); ip != nil {
  106. f.mut.Lock()
  107. f.ips[ip.String()] = allowed
  108. f.mut.Unlock()
  109. return true
  110. }
  111. return false
  112. }
  113. // ToggleDefault alters the default setting
  114. func (f *ipFilter) ToggleDefault(allowed bool) {
  115. f.mut.Lock()
  116. f.defaultAllowed = allowed
  117. f.mut.Unlock()
  118. }
  119. // Allowed returns if a given IP can pass through the filter
  120. func (f *ipFilter) Allowed(ipstr string) bool {
  121. return f.NetAllowed(net.ParseIP(ipstr))
  122. }
  123. // NetAllowed returns if a given net.IP can pass through the filter
  124. func (f *ipFilter) NetAllowed(ip net.IP) bool {
  125. //invalid ip
  126. if ip == nil {
  127. return false
  128. }
  129. //read lock entire function
  130. //except for db access
  131. f.mut.RLock()
  132. defer f.mut.RUnlock()
  133. //check single ips
  134. allowed, ok := f.ips[ip.String()]
  135. if ok {
  136. return allowed
  137. }
  138. //scan subnets for any allow/block
  139. blocked := false
  140. for _, subnet := range f.subnets {
  141. if subnet.ipnet.Contains(ip) {
  142. if subnet.allowed {
  143. return true
  144. }
  145. blocked = true
  146. }
  147. }
  148. if blocked {
  149. return false
  150. }
  151. //use default setting
  152. return f.defaultAllowed
  153. }
  154. // Blocked returns if a given IP can NOT pass through the filter
  155. func (f *ipFilter) Blocked(ip string) bool {
  156. return !f.Allowed(ip)
  157. }
  158. // NetBlocked returns if a given net.IP can NOT pass through the filter
  159. func (f *ipFilter) NetBlocked(ip net.IP) bool {
  160. return !f.NetAllowed(ip)
  161. }
  162. // Wrap the provided handler with simple IP blocking middleware
  163. // using this IP filter and its configuration
  164. func (f *ipFilter) Wrap(next http.Handler) http.Handler {
  165. return &ipFilterMiddleware{ipFilter: f, next: next}
  166. }
  167. // WrapIPFilter is equivalent to newIPFilter(opts) then Wrap(next)
  168. func WrapIPFilter(next http.Handler, opts *IPFilterOptions) http.Handler {
  169. return newIPFilter(opts).Wrap(next)
  170. }
  171. type ipFilterMiddleware struct {
  172. *ipFilter
  173. next http.Handler
  174. }
  175. func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  176. remoteIP := realip.FromRequest(r)
  177. if !m.ipFilter.Allowed(remoteIP) {
  178. //show simple forbidden text
  179. http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  180. return
  181. }
  182. //success!
  183. m.next.ServeHTTP(w, r)
  184. }