You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

213 lines
5.4 KiB

  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 precendence 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. type IPFilter struct {
  38. opts IPFilterOptions
  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. //New constructs IPFilter instance.
  52. func NewIPFilter(opts IPFilterOptions) *IPFilter {
  53. if opts.Logger == nil {
  54. flags := log.LstdFlags
  55. opts.Logger = log.New(os.Stdout, "", flags)
  56. }
  57. f := &IPFilter{
  58. opts: opts,
  59. ips: map[string]bool{},
  60. defaultAllowed: !opts.BlockByDefault,
  61. }
  62. for _, ip := range opts.BlockedIPs {
  63. f.BlockIP(ip)
  64. }
  65. for _, ip := range opts.AllowedIPs {
  66. f.AllowIP(ip)
  67. }
  68. return f
  69. }
  70. func (f *IPFilter) AllowIP(ip string) bool {
  71. return f.ToggleIP(ip, true)
  72. }
  73. func (f *IPFilter) BlockIP(ip string) bool {
  74. return f.ToggleIP(ip, false)
  75. }
  76. func (f *IPFilter) ToggleIP(str string, allowed bool) bool {
  77. //check if has subnet
  78. if ip, net, err := net.ParseCIDR(str); err == nil {
  79. // containing only one ip?
  80. if n, total := net.Mask.Size(); n == total {
  81. f.mut.Lock()
  82. f.ips[ip.String()] = allowed
  83. f.mut.Unlock()
  84. return true
  85. }
  86. //check for existing
  87. f.mut.Lock()
  88. found := false
  89. for _, subnet := range f.subnets {
  90. if subnet.str == str {
  91. found = true
  92. subnet.allowed = allowed
  93. break
  94. }
  95. }
  96. if !found {
  97. f.subnets = append(f.subnets, &subnet{
  98. str: str,
  99. ipnet: net,
  100. allowed: allowed,
  101. })
  102. }
  103. f.mut.Unlock()
  104. return true
  105. }
  106. //check if plain ip
  107. if ip := net.ParseIP(str); ip != nil {
  108. f.mut.Lock()
  109. f.ips[ip.String()] = allowed
  110. f.mut.Unlock()
  111. return true
  112. }
  113. return false
  114. }
  115. //ToggleDefault alters the default setting
  116. func (f *IPFilter) ToggleDefault(allowed bool) {
  117. f.mut.Lock()
  118. f.defaultAllowed = allowed
  119. f.mut.Unlock()
  120. }
  121. //Allowed returns if a given IP can pass through the filter
  122. func (f *IPFilter) Allowed(ipstr string) bool {
  123. return f.NetAllowed(net.ParseIP(ipstr))
  124. }
  125. //NetAllowed returns if a given net.IP can pass through the filter
  126. func (f *IPFilter) NetAllowed(ip net.IP) bool {
  127. //invalid ip
  128. if ip == nil {
  129. return false
  130. }
  131. //read lock entire function
  132. //except for db access
  133. f.mut.RLock()
  134. defer f.mut.RUnlock()
  135. //check single ips
  136. allowed, ok := f.ips[ip.String()]
  137. if ok {
  138. return allowed
  139. }
  140. //scan subnets for any allow/block
  141. blocked := false
  142. for _, subnet := range f.subnets {
  143. if subnet.ipnet.Contains(ip) {
  144. if subnet.allowed {
  145. return true
  146. }
  147. blocked = true
  148. }
  149. }
  150. if blocked {
  151. return false
  152. }
  153. //use default setting
  154. return f.defaultAllowed
  155. }
  156. //Blocked returns if a given IP can NOT pass through the filter
  157. func (f *IPFilter) Blocked(ip string) bool {
  158. return !f.Allowed(ip)
  159. }
  160. //NetBlocked returns if a given net.IP can NOT pass through the filter
  161. func (f *IPFilter) NetBlocked(ip net.IP) bool {
  162. return !f.NetAllowed(ip)
  163. }
  164. //WrapIPFilter the provided handler with simple IP blocking middleware
  165. //using this IP filter and its configuration
  166. func (f *IPFilter) Wrap(next http.Handler) http.Handler {
  167. return &ipFilterMiddleware{IPFilter: f, next: next}
  168. }
  169. //WrapIPFilter is equivalent to NewIPFilter(opts) then Wrap(next)
  170. func WrapIPFilter(next http.Handler, opts IPFilterOptions) http.Handler {
  171. return NewIPFilter(opts).Wrap(next)
  172. }
  173. type ipFilterMiddleware struct {
  174. *IPFilter
  175. next http.Handler
  176. }
  177. func (m *ipFilterMiddleware) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  178. remoteIP := realip.FromRequest(r)
  179. if !m.IPFilter.Allowed(remoteIP) {
  180. //show simple forbidden text
  181. http.Error(w, http.StatusText(http.StatusForbidden), http.StatusForbidden)
  182. return
  183. }
  184. //success!
  185. m.next.ServeHTTP(w, r)
  186. }