package server import ( "errors" "fmt" "time" "github.com/proxy-pool/proxy-pool/internal/config" "github.com/proxy-pool/proxy-pool/internal/gateway/policy" platformAdmission "github.com/proxy-pool/proxy-pool/internal/platform/admission" ) type Protection struct { Auth Guard Access Guard Admission Guard ClientIPs *ClientIPResolver } func BuildProtection(listener config.Listener) (Protection, error) { clientIPs, err := NewClientIPResolver(listener.Access.TrustedProxies) if err != nil { return Protection{}, err } access, err := NewAccessGuard(clientIPs, listener.Access.AllowCIDRs) if err != nil { return Protection{}, err } auth, err := buildConfiguredAuth(listener.Auth, clientIPs) if err != nil { return Protection{}, err } var admission Guard if listener.Limits.RequestsPerMinute > 0 || listener.Limits.RequestsPerMinutePerClient > 0 { limiter, limiterErr := platformAdmission.NewFixedWindow(platformAdmission.FixedWindowConfig{ Window: time.Minute, Global: listener.Limits.RequestsPerMinute, PerKey: listener.Limits.RequestsPerMinutePerClient, }) if limiterErr != nil { return Protection{}, fmt.Errorf("build gateway admission: %w", limiterErr) } admission = NewAdmissionGuard(clientIPs, limiter) } return Protection{Auth: auth, Access: access, Admission: admission, ClientIPs: clientIPs}, nil } func ConfigFromListener(listener config.Listener) Config { return Config{ MaxAttempts: listener.Retry.MaxAttempts, RetryMethods: append([]string(nil), listener.Retry.RetryMethods...), MaxConcurrentRequests: listener.Limits.MaxConcurrentConnections, } } func TargetPolicyFromListener(listener config.Listener) (*policy.TargetPolicy, error) { destination := listener.DestinationPolicy return policy.NewTargetPolicy(policy.Config{ DenyCIDRs: append([]string(nil), destination.DenyCIDRs...), AllowedPorts: append([]uint16(nil), destination.AllowedPorts...), AllowPrivateNetworks: explicitlyAllowed(destination.DenyPrivateNetworks), AllowLoopback: explicitlyAllowed(destination.DenyLoopback), AllowLinkLocal: explicitlyAllowed(destination.DenyLinkLocal), }) } func explicitlyAllowed(deny *bool) bool { return deny != nil && !*deny } func buildConfiguredAuth(auth config.Auth, clientIPs *ClientIPResolver) (Guard, error) { switch auth.Mode { case "", "none": return nil, nil case "usernamePassword": return NewBasicAuthGuard(auth.Username, auth.Password), nil case "apiKey": return NewAPIKeyGuard(auth.Header, auth.Token), nil case "ipWhitelist": return NewAccessGuard(clientIPs, auth.CIDRs) case "any": methods := make([]Guard, 0, len(auth.Methods)) for index, method := range auth.Methods { guard, err := buildConfiguredMethod(method, clientIPs) if err != nil { return nil, fmt.Errorf("build gateway auth method %d: %w", index, err) } methods = append(methods, guard) } return NewAnyGuard(methods...), nil default: return nil, fmt.Errorf("build gateway auth: unsupported mode %q", auth.Mode) } } func buildConfiguredMethod(method config.AuthMethod, clientIPs *ClientIPResolver) (Guard, error) { switch method.Mode { case "usernamePassword": return NewBasicAuthGuard(method.Username, method.Password), nil case "apiKey": return NewAPIKeyGuard(method.Header, method.Value), nil case "ipWhitelist": return NewAccessGuard(clientIPs, method.CIDRs) default: return nil, errors.New("unsupported authentication method: " + method.Mode) } }