| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076 |
- /*
- * Copyright (c) 2016, Psiphon Inc.
- * All rights reserved.
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
- */
- package server
- import (
- "encoding/json"
- "net"
- "strconv"
- "time"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
- "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
- )
- const (
- DEFAULT_IDLE_TCP_PORT_FORWARD_TIMEOUT_MILLISECONDS = 30000
- DEFAULT_IDLE_UDP_PORT_FORWARD_TIMEOUT_MILLISECONDS = 30000
- DEFAULT_DIAL_TCP_PORT_FORWARD_TIMEOUT_MILLISECONDS = 10000
- DEFAULT_MAX_TCP_DIALING_PORT_FORWARD_COUNT = 64
- DEFAULT_MAX_TCP_PORT_FORWARD_COUNT = 512
- DEFAULT_MAX_UDP_PORT_FORWARD_COUNT = 32
- DEFAULT_MEEK_RATE_LIMITER_GARBAGE_COLLECTOR_TRIGGER_COUNT = 5000
- DEFAULT_MEEK_RATE_LIMITER_REAP_HISTORY_FREQUENCY_SECONDS = 300
- DEFAULT_MEEK_RATE_LIMITER_MAX_ENTRIES = 1000000
- )
- // TrafficRulesSet represents the various traffic rules to
- // apply to Psiphon client tunnels. The Reload function supports
- // hot reloading of rules data while the server is running.
- //
- // For a given client, the traffic rules are determined by starting
- // with DefaultRules, then finding the first (if any)
- // FilteredTrafficRules match and overriding the defaults with fields
- // set in the selected FilteredTrafficRules.
- type TrafficRulesSet struct {
- common.ReloadableFile
- // DefaultRules are the base values to use as defaults for all
- // clients.
- DefaultRules TrafficRules
- // FilteredTrafficRules is an ordered list of filter/rules pairs.
- // For each client, the first matching Filter in FilteredTrafficRules
- // determines the additional Rules that are selected and applied
- // on top of DefaultRules.
- //
- // When ExceptFilter is present, a client must match Filter and not match
- // ExceptFilter.
- FilteredRules []struct {
- Filter TrafficRulesFilter
- ExceptFilter *TrafficRulesFilter
- Rules TrafficRules
- }
- // MeekRateLimiterHistorySize enables the late-stage meek rate limiter and
- // sets its history size. The late-stage meek rate limiter acts on client
- // IPs relayed in MeekProxyForwardedForHeaders, and so it must wait for
- // the HTTP headers to be read. This rate limiter immediately terminates
- // any client endpoint request or any request to create a new session, but
- // not any meek request for an existing session, if the
- // MeekRateLimiterHistorySize requests occur in
- // MeekRateLimiterThresholdSeconds.
- //
- // A use case for the the meek rate limiter is to mitigate dangling resource
- // usage that results from meek connections that are partially established
- // and then interrupted (e.g, drop packets after allowing up to the initial
- // HTTP request and header lines). In the case of CDN fronted meek, the CDN
- // itself may hold open the interrupted connection.
- //
- // The scope of rate limiting may be
- // limited using LimitMeekRateLimiterTunnelProtocols/Regions/ISPs/ASNs/Cities.
- //
- // Upon hot reload,
- // MeekRateLimiterHistorySize/MeekRateLimiterThresholdSeconds are not
- // changed for currently tracked client IPs; new values will apply to
- // newly tracked client IPs.
- MeekRateLimiterHistorySize int
- // MeekRateLimiterThresholdSeconds is part of the meek rate limiter
- // specification and must be set when MeekRateLimiterHistorySize is set.
- MeekRateLimiterThresholdSeconds int
- // MeekRateLimiterTunnelProtocols, if set, limits application of the meek
- // late-stage rate limiter to the specified meek protocols. When omitted or
- // empty, meek rate limiting is applied to all meek protocols.
- MeekRateLimiterTunnelProtocols []string
- // MeekRateLimiterRegions, if set, limits application of the meek
- // late-stage rate limiter to clients in the specified list of GeoIP
- // countries. When omitted or empty, meek rate limiting, if configured,
- // is applied to any client country.
- MeekRateLimiterRegions []string
- // MeekRateLimiterISPs, if set, limits application of the meek
- // late-stage rate limiter to clients in the specified list of GeoIP
- // ISPs. When omitted or empty, meek rate limiting, if configured,
- // is applied to any client ISP.
- MeekRateLimiterISPs []string
- // MeekRateLimiterASNs, if set, limits application of the meek
- // late-stage rate limiter to clients in the specified list of GeoIP
- // ASNs. When omitted or empty, meek rate limiting, if configured,
- // is applied to any client ASN.
- MeekRateLimiterASNs []string
- // MeekRateLimiterCities, if set, limits application of the meek
- // late-stage rate limiter to clients in the specified list of GeoIP
- // cities. When omitted or empty, meek rate limiting, if configured,
- // is applied to any client city.
- MeekRateLimiterCities []string
- // MeekRateLimiterGarbageCollectionTriggerCount specifies the number of
- // rate limit events after which garbage collection is manually triggered
- // in order to reclaim memory used by rate limited and other rejected
- // requests.
- //
- // A default of DEFAULT_MEEK_RATE_LIMITER_GARBAGE_COLLECTOR_TRIGGER_COUNT
- // is used when MeekRateLimiterGarbageCollectionTriggerCount is 0.
- MeekRateLimiterGarbageCollectionTriggerCount int
- // MeekRateLimiterReapHistoryFrequencySeconds specifies a schedule for
- // reaping old records from the rate limit history.
- //
- // A default of DEFAULT_MEEK_RATE_LIMITER_REAP_HISTORY_FREQUENCY_SECONDS
- // is used when MeekRateLimiterReapHistoryFrequencySeconds is 0.
- //
- // MeekRateLimiterReapHistoryFrequencySeconds is not applied upon hot
- // reload.
- MeekRateLimiterReapHistoryFrequencySeconds int
- // MeekRateLimiterMaxEntries specifies a maximum size for the rate limit
- // history.
- MeekRateLimiterMaxEntries int
- }
- // TrafficRulesFilter defines a filter to match against client attributes.
- type TrafficRulesFilter struct {
- // TunnelProtocols is a list of client tunnel protocols that must be
- // in use to match this filter. When omitted or empty, any protocol
- // matches.
- TunnelProtocols []string
- // Regions is a list of countries that the client must geolocate to in
- // order to match this filter. When omitted or empty, any client country
- // matches.
- Regions []string
- // ISPs is a list of ISPs that the client must geolocate to in order to
- // match this filter. When omitted or empty, any client ISP matches.
- ISPs []string
- // ASNs is a list of ASNs that the client must geolocate to in order to
- // match this filter. When omitted or empty, any client ASN matches.
- ASNs []string
- // Cities is a list of cities that the client must geolocate to in order to
- // match this filter. When omitted or empty, any client city matches.
- Cities []string
- // APIProtocol specifies whether the client must use the SSH
- // API protocol (when "ssh") or the web API protocol (when "web").
- // When omitted or blank, any API protocol matches.
- APIProtocol string
- // HandshakeParameters specifies handshake API parameter names and
- // a list of values, one of which must be specified to match this
- // filter. Only scalar string API parameters may be filtered.
- // Values may be patterns containing the '*' wildcard.
- HandshakeParameters map[string][]string
- // AuthorizedAccessTypes specifies a list of access types, at least
- // one of which the client must have presented an active authorization
- // for and which must not be revoked.
- // AuthorizedAccessTypes is ignored when AuthorizationsRevoked is true.
- AuthorizedAccessTypes []string
- // ActiveAuthorizationIDs specifies a list of authorization IDs, at least
- // one of which the client must have presented an active authorization
- // for and which must not be revoked.
- // ActiveAuthorizationIDs is ignored when AuthorizationsRevoked is true.
- ActiveAuthorizationIDs []string
- // AuthorizationsRevoked indicates whether the client's authorizations
- // must have been revoked. When true, authorizations must have been
- // revoked. When omitted or false, this field is ignored.
- AuthorizationsRevoked bool
- regionLookup map[string]bool
- ispLookup map[string]bool
- asnLookup map[string]bool
- cityLookup map[string]bool
- activeAuthorizationIDLookup map[string]bool
- }
- // TrafficRules specify the limits placed on client traffic.
- type TrafficRules struct {
- // RateLimits specifies data transfer rate limits for the
- // client traffic.
- RateLimits RateLimits
- // DialTCPPortForwardTimeoutMilliseconds is the timeout period
- // for dialing TCP port forwards. A value of 0 specifies no timeout.
- // When omitted in DefaultRules,
- // DEFAULT_TCP_PORT_FORWARD_DIAL_TIMEOUT_MILLISECONDS is used.
- DialTCPPortForwardTimeoutMilliseconds *int
- // IdleTCPPortForwardTimeoutMilliseconds is the timeout period
- // after which idle (no bytes flowing in either direction)
- // client TCP port forwards are preemptively closed.
- // A value of 0 specifies no idle timeout. When omitted in
- // DefaultRules, DEFAULT_IDLE_TCP_PORT_FORWARD_TIMEOUT_MILLISECONDS
- // is used.
- IdleTCPPortForwardTimeoutMilliseconds *int
- // IdleUDPPortForwardTimeoutMilliseconds is the timeout period
- // after which idle (no bytes flowing in either direction)
- // client UDP port forwards are preemptively closed.
- // A value of 0 specifies no idle timeout. When omitted in
- // DefaultRules, DEFAULT_IDLE_UDP_PORT_FORWARD_TIMEOUT_MILLISECONDS
- // is used.
- IdleUDPPortForwardTimeoutMilliseconds *int
- // MaxTCPDialingPortForwardCount is the maximum number of dialing
- // TCP port forwards each client may have open concurrently. When
- // persistently at the limit, new TCP port forwards are rejected.
- // A value of 0 specifies no maximum. When omitted in
- // DefaultRules, DEFAULT_MAX_TCP_DIALING_PORT_FORWARD_COUNT is used.
- MaxTCPDialingPortForwardCount *int
- // MaxTCPPortForwardCount is the maximum number of established TCP
- // port forwards each client may have open concurrently. If at the
- // limit when a new TCP port forward is established, the LRU
- // established TCP port forward is closed.
- // A value of 0 specifies no maximum. When omitted in
- // DefaultRules, DEFAULT_MAX_TCP_PORT_FORWARD_COUNT is used.
- MaxTCPPortForwardCount *int
- // MaxUDPPortForwardCount is the maximum number of UDP port
- // forwards each client may have open concurrently. If at the
- // limit when a new UDP port forward is created, the LRU
- // UDP port forward is closed.
- // A value of 0 specifies no maximum. When omitted in
- // DefaultRules, DEFAULT_MAX_UDP_PORT_FORWARD_COUNT is used.
- MaxUDPPortForwardCount *int
- // AllowTCPPorts specifies a list of TCP ports that are permitted for port
- // forwarding. When set, only ports in the list are accessible to clients.
- AllowTCPPorts *common.PortList
- // AllowUDPPorts specifies a list of UDP ports that are permitted for port
- // forwarding. When set, only ports in the list are accessible to clients.
- AllowUDPPorts *common.PortList
- // DisallowTCPPorts specifies a list of TCP ports that are not permitted for
- // port forwarding. DisallowTCPPorts takes priority over AllowTCPPorts and
- // AllowSubnets.
- DisallowTCPPorts *common.PortList
- // DisallowUDPPorts specifies a list of UDP ports that are not permitted for
- // port forwarding. DisallowUDPPorts takes priority over AllowUDPPorts and
- // AllowSubnets.
- DisallowUDPPorts *common.PortList
- // AllowSubnets specifies a list of IP address subnets for which all TCP
- // and UDP ports are allowed. This list is consulted if a port is not
- // allowed by the AllowTCPPorts or AllowUDPPorts configuration; but not
- // if a port is disallowed by DisallowTCPPorts, DisallowUDPPorts,
- // DisallowSubnets or DisallowASNs. Each entry is a IP subnet in CIDR
- // notation.
- AllowSubnets []string
- // AllowASNs specifies a list of ASNs for which all TCP and UDP ports are
- // allowed. This list is consulted if a port is not allowed by the
- // AllowTCPPorts or AllowUDPPorts configuration; but not if a port is
- // disallowed by DisallowTCPPorts, DisallowUDPPorts, DisallowSubnets or
- // DisallowASNs.
- AllowASNs []string
- // DisallowSubnets specifies a list of IP address subnets for which all
- // TCP and UDP ports are disallowed. Each entry is a IP subnet in CIDR
- // notation.
- DisallowSubnets []string
- // DisallowASNs specifies a list of ASNs for which all TCP and UDP ports
- // are disallowed.
- DisallowASNs []string
- // DisableDiscovery specifies whether to disable server entry discovery,
- // to manage load on discovery servers.
- DisableDiscovery *bool
- }
- // RateLimits is a clone of common.RateLimits with pointers
- // to fields to enable distinguishing between zero values and
- // omitted values in JSON serialized traffic rules.
- // See common.RateLimits for field descriptions.
- type RateLimits struct {
- ReadUnthrottledBytes *int64
- ReadBytesPerSecond *int64
- WriteUnthrottledBytes *int64
- WriteBytesPerSecond *int64
- CloseAfterExhausted *bool
- // EstablishmentRead/WriteBytesPerSecond are used in place of
- // Read/WriteBytesPerSecond for tunnels in the establishment phase, from the
- // initial network connection up to the completion of the API handshake.
- EstablishmentReadBytesPerSecond *int64
- EstablishmentWriteBytesPerSecond *int64
- // UnthrottleFirstTunnelOnly specifies whether any
- // ReadUnthrottledBytes/WriteUnthrottledBytes apply
- // only to the first tunnel in a session.
- UnthrottleFirstTunnelOnly *bool
- }
- // CommonRateLimits converts a RateLimits to a common.RateLimits.
- func (rateLimits *RateLimits) CommonRateLimits(handshaked bool) common.RateLimits {
- r := common.RateLimits{
- ReadUnthrottledBytes: *rateLimits.ReadUnthrottledBytes,
- ReadBytesPerSecond: *rateLimits.ReadBytesPerSecond,
- WriteUnthrottledBytes: *rateLimits.WriteUnthrottledBytes,
- WriteBytesPerSecond: *rateLimits.WriteBytesPerSecond,
- CloseAfterExhausted: *rateLimits.CloseAfterExhausted,
- }
- if !handshaked {
- r.ReadBytesPerSecond = *rateLimits.EstablishmentReadBytesPerSecond
- r.WriteBytesPerSecond = *rateLimits.EstablishmentWriteBytesPerSecond
- }
- return r
- }
- // NewTrafficRulesSet initializes a TrafficRulesSet with
- // the rules data in the specified config file.
- func NewTrafficRulesSet(filename string) (*TrafficRulesSet, error) {
- set := &TrafficRulesSet{}
- set.ReloadableFile = common.NewReloadableFile(
- filename,
- true,
- func(fileContent []byte, _ time.Time) error {
- var newSet TrafficRulesSet
- err := json.Unmarshal(fileContent, &newSet)
- if err != nil {
- return errors.Trace(err)
- }
- err = newSet.Validate()
- if err != nil {
- return errors.Trace(err)
- }
- // Modify actual traffic rules only after validation
- set.MeekRateLimiterHistorySize = newSet.MeekRateLimiterHistorySize
- set.MeekRateLimiterThresholdSeconds = newSet.MeekRateLimiterThresholdSeconds
- set.MeekRateLimiterTunnelProtocols = newSet.MeekRateLimiterTunnelProtocols
- set.MeekRateLimiterRegions = newSet.MeekRateLimiterRegions
- set.MeekRateLimiterISPs = newSet.MeekRateLimiterISPs
- set.MeekRateLimiterASNs = newSet.MeekRateLimiterASNs
- set.MeekRateLimiterCities = newSet.MeekRateLimiterCities
- set.MeekRateLimiterGarbageCollectionTriggerCount = newSet.MeekRateLimiterGarbageCollectionTriggerCount
- set.MeekRateLimiterReapHistoryFrequencySeconds = newSet.MeekRateLimiterReapHistoryFrequencySeconds
- set.DefaultRules = newSet.DefaultRules
- set.FilteredRules = newSet.FilteredRules
- set.initLookups()
- return nil
- })
- _, err := set.Reload()
- if err != nil {
- return nil, errors.Trace(err)
- }
- return set, nil
- }
- // Validate checks for correct input formats in a TrafficRulesSet.
- func (set *TrafficRulesSet) Validate() error {
- if set.MeekRateLimiterHistorySize < 0 ||
- set.MeekRateLimiterThresholdSeconds < 0 ||
- set.MeekRateLimiterGarbageCollectionTriggerCount < 0 ||
- set.MeekRateLimiterReapHistoryFrequencySeconds < 0 {
- return errors.TraceNew("MeekRateLimiter values must be >= 0")
- }
- if set.MeekRateLimiterHistorySize > 0 {
- if set.MeekRateLimiterThresholdSeconds <= 0 {
- return errors.TraceNew("MeekRateLimiterThresholdSeconds must be > 0")
- }
- }
- validateTrafficRules := func(rules *TrafficRules) error {
- if (rules.RateLimits.ReadUnthrottledBytes != nil && *rules.RateLimits.ReadUnthrottledBytes < 0) ||
- (rules.RateLimits.ReadBytesPerSecond != nil && *rules.RateLimits.ReadBytesPerSecond < 0) ||
- (rules.RateLimits.WriteUnthrottledBytes != nil && *rules.RateLimits.WriteUnthrottledBytes < 0) ||
- (rules.RateLimits.WriteBytesPerSecond != nil && *rules.RateLimits.WriteBytesPerSecond < 0) ||
- (rules.RateLimits.EstablishmentReadBytesPerSecond != nil && *rules.RateLimits.EstablishmentReadBytesPerSecond < 0) ||
- (rules.RateLimits.EstablishmentWriteBytesPerSecond != nil && *rules.RateLimits.EstablishmentWriteBytesPerSecond < 0) ||
- (rules.DialTCPPortForwardTimeoutMilliseconds != nil && *rules.DialTCPPortForwardTimeoutMilliseconds < 0) ||
- (rules.IdleTCPPortForwardTimeoutMilliseconds != nil && *rules.IdleTCPPortForwardTimeoutMilliseconds < 0) ||
- (rules.IdleUDPPortForwardTimeoutMilliseconds != nil && *rules.IdleUDPPortForwardTimeoutMilliseconds < 0) ||
- (rules.MaxTCPDialingPortForwardCount != nil && *rules.MaxTCPDialingPortForwardCount < 0) ||
- (rules.MaxTCPPortForwardCount != nil && *rules.MaxTCPPortForwardCount < 0) ||
- (rules.MaxUDPPortForwardCount != nil && *rules.MaxUDPPortForwardCount < 0) {
- return errors.TraceNew("TrafficRules values must be >= 0")
- }
- for _, subnet := range rules.AllowSubnets {
- _, _, err := net.ParseCIDR(subnet)
- if err != nil {
- return errors.Tracef("invalid subnet: %s %s", subnet, err)
- }
- }
- for _, ASN := range rules.AllowASNs {
- _, err := strconv.Atoi(ASN)
- if err != nil {
- return errors.Tracef("invalid ASN: %s %s", ASN, err)
- }
- }
- for _, subnet := range rules.DisallowSubnets {
- _, _, err := net.ParseCIDR(subnet)
- if err != nil {
- return errors.Tracef("invalid subnet: %s %s", subnet, err)
- }
- }
- for _, ASN := range rules.DisallowASNs {
- _, err := strconv.Atoi(ASN)
- if err != nil {
- return errors.Tracef("invalid ASN: %s %s", ASN, err)
- }
- }
- return nil
- }
- validateFilter := func(filter *TrafficRulesFilter) error {
- for paramName := range filter.HandshakeParameters {
- validParamName := false
- for _, paramSpec := range handshakeRequestParams {
- if paramSpec.name == paramName {
- validParamName = true
- break
- }
- }
- if !validParamName {
- return errors.Tracef("invalid parameter name: %s", paramName)
- }
- }
- return nil
- }
- err := validateTrafficRules(&set.DefaultRules)
- if err != nil {
- return errors.Trace(err)
- }
- for _, filteredRule := range set.FilteredRules {
- err := validateFilter(&filteredRule.Filter)
- if err != nil {
- return errors.Trace(err)
- }
- if filteredRule.ExceptFilter != nil {
- err := validateFilter(filteredRule.ExceptFilter)
- if err != nil {
- return errors.Trace(err)
- }
- }
- err = validateTrafficRules(&filteredRule.Rules)
- if err != nil {
- return errors.Trace(err)
- }
- }
- return nil
- }
- const stringLookupThreshold = 5
- const intLookupThreshold = 10
- // initLookups creates map lookups for filters where the number of string/int
- // values to compare against exceeds a threshold where benchmarks show maps
- // are faster than looping through a string/int slice.
- func (set *TrafficRulesSet) initLookups() {
- initTrafficRulesLookups := func(rules *TrafficRules) {
- rules.AllowTCPPorts.OptimizeLookups()
- rules.AllowUDPPorts.OptimizeLookups()
- rules.DisallowTCPPorts.OptimizeLookups()
- rules.DisallowUDPPorts.OptimizeLookups()
- }
- initTrafficRulesFilterLookups := func(filter *TrafficRulesFilter) {
- if len(filter.Regions) >= stringLookupThreshold {
- filter.regionLookup = make(map[string]bool)
- for _, region := range filter.Regions {
- filter.regionLookup[region] = true
- }
- }
- if len(filter.ISPs) >= stringLookupThreshold {
- filter.ispLookup = make(map[string]bool)
- for _, ISP := range filter.ISPs {
- filter.ispLookup[ISP] = true
- }
- }
- if len(filter.ASNs) >= stringLookupThreshold {
- filter.asnLookup = make(map[string]bool)
- for _, ASN := range filter.ASNs {
- filter.asnLookup[ASN] = true
- }
- }
- if len(filter.Cities) >= stringLookupThreshold {
- filter.cityLookup = make(map[string]bool)
- for _, city := range filter.Cities {
- filter.cityLookup[city] = true
- }
- }
- if len(filter.ActiveAuthorizationIDs) >= stringLookupThreshold {
- filter.activeAuthorizationIDLookup = make(map[string]bool)
- for _, ID := range filter.ActiveAuthorizationIDs {
- filter.activeAuthorizationIDLookup[ID] = true
- }
- }
- }
- initTrafficRulesLookups(&set.DefaultRules)
- for i := range set.FilteredRules {
- initTrafficRulesFilterLookups(&set.FilteredRules[i].Filter)
- if set.FilteredRules[i].ExceptFilter != nil {
- initTrafficRulesFilterLookups(set.FilteredRules[i].ExceptFilter)
- }
- initTrafficRulesLookups(&set.FilteredRules[i].Rules)
- }
- // TODO: add lookups for MeekRateLimiter?
- }
- // GetTrafficRules determines the traffic rules for a client based on its attributes.
- // For the return value TrafficRules, all pointer and slice fields are initialized,
- // so nil checks are not required. The caller must not modify the returned TrafficRules.
- func (set *TrafficRulesSet) GetTrafficRules(
- isFirstTunnelInSession bool,
- tunnelProtocol string,
- geoIPData GeoIPData,
- state handshakeState) TrafficRules {
- set.ReloadableFile.RLock()
- defer set.ReloadableFile.RUnlock()
- // Start with a copy of the DefaultRules, and then select the first
- // matching Rules from FilteredTrafficRules, taking only the explicitly
- // specified fields from that Rules.
- //
- // Notes:
- // - Scalar pointers are used in TrafficRules and RateLimits to distinguish between
- // omitted fields (in serialized JSON) and default values. For example, if a filtered
- // Rules specifies a field value of 0, this will override the default; but if the
- // serialized filtered rule omits the field, the default is to be retained.
- // - We use shallow copies and slices and scalar pointers are shared between the
- // return value TrafficRules, so callers must treat the return value as immutable.
- // This also means that these slices and pointers can remain referenced in memory even
- // after a hot reload.
- trafficRules := set.DefaultRules
- // Populate defaults for omitted DefaultRules fields
- if trafficRules.RateLimits.ReadUnthrottledBytes == nil {
- trafficRules.RateLimits.ReadUnthrottledBytes = new(int64)
- }
- if trafficRules.RateLimits.ReadBytesPerSecond == nil {
- trafficRules.RateLimits.ReadBytesPerSecond = new(int64)
- }
- if trafficRules.RateLimits.WriteUnthrottledBytes == nil {
- trafficRules.RateLimits.WriteUnthrottledBytes = new(int64)
- }
- if trafficRules.RateLimits.WriteBytesPerSecond == nil {
- trafficRules.RateLimits.WriteBytesPerSecond = new(int64)
- }
- if trafficRules.RateLimits.CloseAfterExhausted == nil {
- trafficRules.RateLimits.CloseAfterExhausted = new(bool)
- }
- if trafficRules.RateLimits.EstablishmentReadBytesPerSecond == nil {
- trafficRules.RateLimits.EstablishmentReadBytesPerSecond = new(int64)
- }
- if trafficRules.RateLimits.EstablishmentWriteBytesPerSecond == nil {
- trafficRules.RateLimits.EstablishmentWriteBytesPerSecond = new(int64)
- }
- if trafficRules.RateLimits.UnthrottleFirstTunnelOnly == nil {
- trafficRules.RateLimits.UnthrottleFirstTunnelOnly = new(bool)
- }
- intPtr := func(i int) *int {
- return &i
- }
- if trafficRules.DialTCPPortForwardTimeoutMilliseconds == nil {
- trafficRules.DialTCPPortForwardTimeoutMilliseconds =
- intPtr(DEFAULT_DIAL_TCP_PORT_FORWARD_TIMEOUT_MILLISECONDS)
- }
- if trafficRules.IdleTCPPortForwardTimeoutMilliseconds == nil {
- trafficRules.IdleTCPPortForwardTimeoutMilliseconds =
- intPtr(DEFAULT_IDLE_TCP_PORT_FORWARD_TIMEOUT_MILLISECONDS)
- }
- if trafficRules.IdleUDPPortForwardTimeoutMilliseconds == nil {
- trafficRules.IdleUDPPortForwardTimeoutMilliseconds =
- intPtr(DEFAULT_IDLE_UDP_PORT_FORWARD_TIMEOUT_MILLISECONDS)
- }
- if trafficRules.MaxTCPDialingPortForwardCount == nil {
- trafficRules.MaxTCPDialingPortForwardCount =
- intPtr(DEFAULT_MAX_TCP_DIALING_PORT_FORWARD_COUNT)
- }
- if trafficRules.MaxTCPPortForwardCount == nil {
- trafficRules.MaxTCPPortForwardCount =
- intPtr(DEFAULT_MAX_TCP_PORT_FORWARD_COUNT)
- }
- if trafficRules.MaxUDPPortForwardCount == nil {
- trafficRules.MaxUDPPortForwardCount =
- intPtr(DEFAULT_MAX_UDP_PORT_FORWARD_COUNT)
- }
- if trafficRules.AllowSubnets == nil {
- trafficRules.AllowSubnets = make([]string, 0)
- }
- if trafficRules.AllowASNs == nil {
- trafficRules.AllowASNs = make([]string, 0)
- }
- if trafficRules.DisallowSubnets == nil {
- trafficRules.DisallowSubnets = make([]string, 0)
- }
- if trafficRules.DisallowASNs == nil {
- trafficRules.DisallowASNs = make([]string, 0)
- }
- if trafficRules.DisableDiscovery == nil {
- trafficRules.DisableDiscovery = new(bool)
- }
- // matchFilter is used to check both Filter and any ExceptFilter
- matchFilter := func(filter *TrafficRulesFilter) bool {
- if len(filter.TunnelProtocols) > 0 {
- if !common.Contains(filter.TunnelProtocols, tunnelProtocol) {
- return false
- }
- }
- if len(filter.Regions) > 0 {
- if filter.regionLookup != nil {
- if !filter.regionLookup[geoIPData.Country] {
- return false
- }
- } else {
- if !common.Contains(filter.Regions, geoIPData.Country) {
- return false
- }
- }
- }
- if len(filter.ISPs) > 0 {
- if filter.ispLookup != nil {
- if !filter.ispLookup[geoIPData.ISP] {
- return false
- }
- } else {
- if !common.Contains(filter.ISPs, geoIPData.ISP) {
- return false
- }
- }
- }
- if len(filter.ASNs) > 0 {
- if filter.asnLookup != nil {
- if !filter.asnLookup[geoIPData.ASN] {
- return false
- }
- } else {
- if !common.Contains(filter.ASNs, geoIPData.ASN) {
- return false
- }
- }
- }
- if len(filter.Cities) > 0 {
- if filter.cityLookup != nil {
- if !filter.cityLookup[geoIPData.City] {
- return false
- }
- } else {
- if !common.Contains(filter.Cities, geoIPData.City) {
- return false
- }
- }
- }
- if filter.APIProtocol != "" {
- if !state.completed {
- return false
- }
- if state.apiProtocol != filter.APIProtocol {
- return false
- }
- }
- if filter.HandshakeParameters != nil {
- if !state.completed {
- return false
- }
- for name, values := range filter.HandshakeParameters {
- clientValue, err := getStringRequestParam(state.apiParams, name)
- if err != nil || !common.ContainsWildcard(values, clientValue) {
- return false
- }
- }
- }
- if filter.AuthorizationsRevoked {
- if !state.completed {
- return false
- }
- if !state.authorizationsRevoked {
- return false
- }
- } else {
- if len(filter.ActiveAuthorizationIDs) > 0 {
- if !state.completed {
- return false
- }
- if state.authorizationsRevoked {
- return false
- }
- if filter.activeAuthorizationIDLookup != nil {
- found := false
- for _, ID := range state.activeAuthorizationIDs {
- if filter.activeAuthorizationIDLookup[ID] {
- found = true
- break
- }
- }
- if !found {
- return false
- }
- } else {
- if !common.ContainsAny(filter.ActiveAuthorizationIDs, state.activeAuthorizationIDs) {
- return false
- }
- }
- }
- if len(filter.AuthorizedAccessTypes) > 0 {
- if !state.completed {
- return false
- }
- if state.authorizationsRevoked {
- return false
- }
- if !common.ContainsAny(filter.AuthorizedAccessTypes, state.authorizedAccessTypes) {
- return false
- }
- }
- }
- return true
- }
- // Match filtered rules
- //
- // TODO: faster lookup?
- for _, filteredRules := range set.FilteredRules {
- log.WithTraceFields(LogFields{"filter": filteredRules.Filter}).Debug("filter check")
- match := matchFilter(&filteredRules.Filter)
- if match && filteredRules.ExceptFilter != nil {
- match = !matchFilter(filteredRules.ExceptFilter)
- }
- if !match {
- continue
- }
- log.WithTraceFields(LogFields{"filter": filteredRules.Filter}).Debug("filter match")
- // This is the first match. Override defaults using provided fields from selected rules, and return result.
- if filteredRules.Rules.RateLimits.ReadUnthrottledBytes != nil {
- trafficRules.RateLimits.ReadUnthrottledBytes = filteredRules.Rules.RateLimits.ReadUnthrottledBytes
- }
- if filteredRules.Rules.RateLimits.ReadBytesPerSecond != nil {
- trafficRules.RateLimits.ReadBytesPerSecond = filteredRules.Rules.RateLimits.ReadBytesPerSecond
- }
- if filteredRules.Rules.RateLimits.WriteUnthrottledBytes != nil {
- trafficRules.RateLimits.WriteUnthrottledBytes = filteredRules.Rules.RateLimits.WriteUnthrottledBytes
- }
- if filteredRules.Rules.RateLimits.WriteBytesPerSecond != nil {
- trafficRules.RateLimits.WriteBytesPerSecond = filteredRules.Rules.RateLimits.WriteBytesPerSecond
- }
- if filteredRules.Rules.RateLimits.CloseAfterExhausted != nil {
- trafficRules.RateLimits.CloseAfterExhausted = filteredRules.Rules.RateLimits.CloseAfterExhausted
- }
- if filteredRules.Rules.RateLimits.EstablishmentReadBytesPerSecond != nil {
- trafficRules.RateLimits.EstablishmentReadBytesPerSecond = filteredRules.Rules.RateLimits.EstablishmentReadBytesPerSecond
- }
- if filteredRules.Rules.RateLimits.EstablishmentWriteBytesPerSecond != nil {
- trafficRules.RateLimits.EstablishmentWriteBytesPerSecond = filteredRules.Rules.RateLimits.EstablishmentWriteBytesPerSecond
- }
- if filteredRules.Rules.RateLimits.UnthrottleFirstTunnelOnly != nil {
- trafficRules.RateLimits.UnthrottleFirstTunnelOnly = filteredRules.Rules.RateLimits.UnthrottleFirstTunnelOnly
- }
- if filteredRules.Rules.DialTCPPortForwardTimeoutMilliseconds != nil {
- trafficRules.DialTCPPortForwardTimeoutMilliseconds = filteredRules.Rules.DialTCPPortForwardTimeoutMilliseconds
- }
- if filteredRules.Rules.IdleTCPPortForwardTimeoutMilliseconds != nil {
- trafficRules.IdleTCPPortForwardTimeoutMilliseconds = filteredRules.Rules.IdleTCPPortForwardTimeoutMilliseconds
- }
- if filteredRules.Rules.IdleUDPPortForwardTimeoutMilliseconds != nil {
- trafficRules.IdleUDPPortForwardTimeoutMilliseconds = filteredRules.Rules.IdleUDPPortForwardTimeoutMilliseconds
- }
- if filteredRules.Rules.MaxTCPDialingPortForwardCount != nil {
- trafficRules.MaxTCPDialingPortForwardCount = filteredRules.Rules.MaxTCPDialingPortForwardCount
- }
- if filteredRules.Rules.MaxTCPPortForwardCount != nil {
- trafficRules.MaxTCPPortForwardCount = filteredRules.Rules.MaxTCPPortForwardCount
- }
- if filteredRules.Rules.MaxUDPPortForwardCount != nil {
- trafficRules.MaxUDPPortForwardCount = filteredRules.Rules.MaxUDPPortForwardCount
- }
- if filteredRules.Rules.AllowTCPPorts != nil {
- trafficRules.AllowTCPPorts = filteredRules.Rules.AllowTCPPorts
- }
- if filteredRules.Rules.AllowUDPPorts != nil {
- trafficRules.AllowUDPPorts = filteredRules.Rules.AllowUDPPorts
- }
- if filteredRules.Rules.DisallowTCPPorts != nil {
- trafficRules.DisallowTCPPorts = filteredRules.Rules.DisallowTCPPorts
- }
- if filteredRules.Rules.DisallowUDPPorts != nil {
- trafficRules.DisallowUDPPorts = filteredRules.Rules.DisallowUDPPorts
- }
- if filteredRules.Rules.AllowSubnets != nil {
- trafficRules.AllowSubnets = filteredRules.Rules.AllowSubnets
- }
- if filteredRules.Rules.AllowASNs != nil {
- trafficRules.AllowASNs = filteredRules.Rules.AllowASNs
- }
- if filteredRules.Rules.DisallowSubnets != nil {
- trafficRules.DisallowSubnets = filteredRules.Rules.DisallowSubnets
- }
- if filteredRules.Rules.DisallowASNs != nil {
- trafficRules.DisallowASNs = filteredRules.Rules.DisallowASNs
- }
- if filteredRules.Rules.DisableDiscovery != nil {
- trafficRules.DisableDiscovery = filteredRules.Rules.DisableDiscovery
- }
- break
- }
- if *trafficRules.RateLimits.UnthrottleFirstTunnelOnly && !isFirstTunnelInSession {
- trafficRules.RateLimits.ReadUnthrottledBytes = new(int64)
- trafficRules.RateLimits.WriteUnthrottledBytes = new(int64)
- }
- log.WithTraceFields(LogFields{"trafficRules": trafficRules}).Debug("selected traffic rules")
- return trafficRules
- }
- func (rules *TrafficRules) AllowTCPPort(
- geoIPService *GeoIPService, remoteIP net.IP, port int) bool {
- if rules.disallowSubnet(remoteIP) || rules.disallowASN(geoIPService, remoteIP) {
- return false
- }
- if rules.DisallowTCPPorts.Lookup(port) {
- return false
- }
- if rules.AllowTCPPorts.IsEmpty() {
- return true
- }
- if rules.AllowTCPPorts.Lookup(port) {
- return true
- }
- return rules.allowSubnet(remoteIP) || rules.allowASN(geoIPService, remoteIP)
- }
- func (rules *TrafficRules) AllowUDPPort(
- geoIPService *GeoIPService, remoteIP net.IP, port int) bool {
- if rules.disallowSubnet(remoteIP) || rules.disallowASN(geoIPService, remoteIP) {
- return false
- }
- if rules.DisallowUDPPorts.Lookup(port) {
- return false
- }
- if rules.AllowUDPPorts.IsEmpty() {
- return true
- }
- if rules.AllowUDPPorts.Lookup(port) {
- return true
- }
- return rules.allowSubnet(remoteIP) || rules.allowASN(geoIPService, remoteIP)
- }
- func (rules *TrafficRules) allowSubnet(remoteIP net.IP) bool {
- return ipInSubnets(remoteIP, rules.AllowSubnets)
- }
- func (rules *TrafficRules) allowASN(
- geoIPService *GeoIPService, remoteIP net.IP) bool {
- if len(rules.AllowASNs) == 0 || geoIPService == nil {
- return false
- }
- return common.Contains(
- rules.AllowASNs,
- geoIPService.LookupISPForIP(remoteIP).ASN)
- }
- func (rules *TrafficRules) disallowSubnet(remoteIP net.IP) bool {
- return ipInSubnets(remoteIP, rules.DisallowSubnets)
- }
- func ipInSubnets(remoteIP net.IP, subnets []string) bool {
- for _, subnet := range subnets {
- // TODO: cache parsed results
- // Note: ignoring error as config has been validated
- _, network, _ := net.ParseCIDR(subnet)
- if network.Contains(remoteIP) {
- return true
- }
- }
- return false
- }
- func (rules *TrafficRules) disallowASN(
- geoIPService *GeoIPService, remoteIP net.IP) bool {
- if len(rules.DisallowASNs) == 0 || geoIPService == nil {
- return false
- }
- return common.Contains(
- rules.DisallowASNs,
- geoIPService.LookupISPForIP(remoteIP).ASN)
- }
- // GetMeekRateLimiterConfig gets a snapshot of the meek rate limiter
- // configuration values.
- func (set *TrafficRulesSet) GetMeekRateLimiterConfig() (
- int, int, []string, []string, []string, []string, []string, int, int, int) {
- set.ReloadableFile.RLock()
- defer set.ReloadableFile.RUnlock()
- GCTriggerCount := set.MeekRateLimiterGarbageCollectionTriggerCount
- if GCTriggerCount <= 0 {
- GCTriggerCount = DEFAULT_MEEK_RATE_LIMITER_GARBAGE_COLLECTOR_TRIGGER_COUNT
- }
- reapFrequencySeconds := set.MeekRateLimiterReapHistoryFrequencySeconds
- if reapFrequencySeconds <= 0 {
- reapFrequencySeconds = DEFAULT_MEEK_RATE_LIMITER_REAP_HISTORY_FREQUENCY_SECONDS
- }
- maxEntries := set.MeekRateLimiterMaxEntries
- if maxEntries <= 0 {
- maxEntries = DEFAULT_MEEK_RATE_LIMITER_MAX_ENTRIES
- }
- return set.MeekRateLimiterHistorySize,
- set.MeekRateLimiterThresholdSeconds,
- set.MeekRateLimiterTunnelProtocols,
- set.MeekRateLimiterRegions,
- set.MeekRateLimiterISPs,
- set.MeekRateLimiterASNs,
- set.MeekRateLimiterCities,
- GCTriggerCount,
- reapFrequencySeconds,
- maxEntries
- }
|