109 lines
3.4 KiB
Go
109 lines
3.4 KiB
Go
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)
|
|
}
|
|
}
|