proxy-pool/internal/gateway/server/bootstrap.go

139 lines
4.2 KiB
Go

package server
import (
"fmt"
"time"
"proxy-pool/internal/config"
"proxy-pool/internal/domain/clientpolicy"
"proxy-pool/internal/gateway/policy"
platformAdmission "proxy-pool/internal/platform/admission"
"proxy-pool/internal/platform/httpsecurity"
)
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)
if err != nil {
return Protection{}, err
}
var admissionGuards []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)
}
admissionGuards = append(admissionGuards, NewAdmissionGuard(clientIPs, limiter))
}
credentialAdmission, err := buildCredentialAdmission(listener.Auth)
if err != nil {
return Protection{}, err
}
admissionGuards = append(admissionGuards, credentialAdmission)
return Protection{Auth: auth, Access: access, Admission: chainGuards(admissionGuards...), ClientIPs: clientIPs}, nil
}
func buildCredentialAdmission(authentication config.Auth) (*CredentialAdmissionGuard, error) {
limits := make(map[int]Admitter)
for _, policy := range credentialPolicies(authentication) {
limit := policy.RequestsPerMinute
if limit == 0 {
continue
}
if _, exists := limits[limit]; exists {
continue
}
limiter, err := platformAdmission.NewFixedWindow(platformAdmission.FixedWindowConfig{
Window: time.Minute,
PerKey: limit,
})
if err != nil {
return nil, fmt.Errorf("build gateway credential admission: %w", err)
}
limits[limit] = limiter
}
return NewCredentialAdmissionGuard(limits), nil
}
func credentialPolicies(authentication config.Auth) []clientpolicy.Policy {
policies := make([]clientpolicy.Policy, 0, len(authentication.Methods)+1)
policies = append(policies, authentication.ClientPolicy)
for _, method := range authentication.Methods {
policies = append(policies, method.ClientPolicy)
}
return policies
}
func ConfigFromListener(listener config.Listener) Config {
result := Config{
MaxAttempts: listener.Retry.MaxAttempts,
RetryMethods: append([]string(nil), listener.Retry.RetryMethods...),
MaxConcurrentRequests: listener.Limits.MaxConcurrentConnections,
}
if listener.StickySession.Enabled {
result.StickySession = StickySessionConfig{
Header: listener.StickySession.Header,
TTL: listener.StickySession.TTL.Value(),
MaxEntries: listener.StickySession.MaxEntries,
}
}
return result
}
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(listener config.Listener) (Guard, error) {
switch listener.Auth.Mode {
case "", "none":
return nil, nil
}
authListener := config.Listener{
Access: config.Access{
TrustedProxies: append([]string(nil), listener.Access.TrustedProxies...),
},
Auth: listener.Auth,
}
protection, err := httpsecurity.NewFromListener(
authListener,
httpsecurity.ClientAuthenticatedOrSourceIP,
httpsecurity.ProxySemantics,
nil,
)
if err != nil {
return nil, fmt.Errorf("build gateway authentication: %w", err)
}
return protection, nil
}