mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 04:42:33 +02:00

* feat: idle sleep for proxmox LXCs * refactor: replace deprecated docker api types * chore(api): remove debug task list endpoint * refactor: move servemux to gphttp/servemux; favicon.go to v1/favicon * refactor: introduce Pool interface, move agent_pool to agent module * refactor: simplify api code * feat: introduce debug api * refactor: remove net.URL and net.CIDR types, improved unmarshal handling * chore: update Makefile for debug build tag, update README * chore: add gperr.Unwrap method * feat: relative time and duration formatting * chore: add ROOT_DIR environment variable, refactor * migration: move homepage override and icon cache to $BASE_DIR/data, add migration code * fix: nil dereference on marshalling service health * fix: wait for route deletion * chore: enhance tasks debuggability * feat: stdout access logger and MultiWriter * fix(agent): remove agent properly on verify error * fix(metrics): disk exclusion logic and added corresponding tests * chore: update schema and prettify, fix package.json and Makefile * fix: I/O buffer not being shrunk before putting back to pool * feat: enhanced error handling module * chore: deps upgrade * feat: better value formatting and handling --------- Co-authored-by: yusing <yusing@6uo.me>
81 lines
2 KiB
Go
81 lines
2 KiB
Go
package middleware
|
|
|
|
import (
|
|
"net"
|
|
"net/http"
|
|
|
|
"github.com/go-playground/validator/v10"
|
|
gphttp "github.com/yusing/go-proxy/internal/net/gphttp"
|
|
"github.com/yusing/go-proxy/internal/utils"
|
|
F "github.com/yusing/go-proxy/internal/utils/functional"
|
|
)
|
|
|
|
type (
|
|
cidrWhitelist struct {
|
|
CIDRWhitelistOpts
|
|
Tracer
|
|
cachedAddr F.Map[string, bool] // cache for trusted IPs
|
|
}
|
|
CIDRWhitelistOpts struct {
|
|
Allow []*net.IPNet `validate:"min=1"`
|
|
StatusCode int `json:"status_code" aliases:"status" validate:"omitempty,status_code"`
|
|
Message string
|
|
}
|
|
)
|
|
|
|
var (
|
|
CIDRWhiteList = NewMiddleware[cidrWhitelist]()
|
|
cidrWhitelistDefaults = CIDRWhitelistOpts{
|
|
Allow: []*net.IPNet{},
|
|
StatusCode: http.StatusForbidden,
|
|
Message: "IP not allowed",
|
|
}
|
|
)
|
|
|
|
func init() {
|
|
utils.MustRegisterValidation("status_code", func(fl validator.FieldLevel) bool {
|
|
statusCode := fl.Field().Int()
|
|
return gphttp.IsStatusCodeValid(int(statusCode))
|
|
})
|
|
}
|
|
|
|
// setup implements MiddlewareWithSetup.
|
|
func (wl *cidrWhitelist) setup() {
|
|
wl.CIDRWhitelistOpts = cidrWhitelistDefaults
|
|
wl.cachedAddr = F.NewMapOf[string, bool]()
|
|
}
|
|
|
|
// before implements RequestModifier.
|
|
func (wl *cidrWhitelist) before(w http.ResponseWriter, r *http.Request) bool {
|
|
return wl.checkIP(w, r)
|
|
}
|
|
|
|
// checkIP checks if the IP address is allowed.
|
|
func (wl *cidrWhitelist) checkIP(w http.ResponseWriter, r *http.Request) bool {
|
|
var allow, ok bool
|
|
if allow, ok = wl.cachedAddr.Load(r.RemoteAddr); !ok {
|
|
ipStr, _, err := net.SplitHostPort(r.RemoteAddr)
|
|
if err != nil {
|
|
ipStr = r.RemoteAddr
|
|
}
|
|
ip := net.ParseIP(ipStr)
|
|
for _, cidr := range wl.CIDRWhitelistOpts.Allow {
|
|
if cidr.Contains(ip) {
|
|
wl.cachedAddr.Store(r.RemoteAddr, true)
|
|
allow = true
|
|
wl.AddTracef("client %s is allowed", ipStr).With("allowed CIDR", cidr)
|
|
break
|
|
}
|
|
}
|
|
if !allow {
|
|
wl.cachedAddr.Store(r.RemoteAddr, false)
|
|
wl.AddTracef("client %s is forbidden", ipStr).With("allowed CIDRs", wl.CIDRWhitelistOpts.Allow)
|
|
}
|
|
}
|
|
if !allow {
|
|
http.Error(w, wl.Message, wl.StatusCode)
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|