proxy-pool/internal/gateway/server/bootstrap.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)
}
}