Documentation ¶
Overview ¶
Package server implements the core tunnel functionality of a Psiphon server. The main function is RunServices, which runs one or all of a Psiphon API web server, a tunneling SSH server, and an Obfuscated SSH protocol server. The server configuration is created by the GenerateConfig function.
Index ¶
- Constants
- func CommonLogger(traceLogger *TraceLogger) *commonLogger
- func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, []byte, []byte, []byte, error)
- func InitLogging(config *Config) (retErr error)
- func IsLogLevelDebug() bool
- func ListenTLSTunnel(support *SupportServices, listener net.Listener, listenerTunnelProtocol string, ...) (net.Listener, error)
- func NewIntentionalPanicError(errorMessage string) error
- func NewLogWriter() *io.PipeWriter
- func RunServices(configJSON []byte) (retErr error)
- func RunWebServer(support *SupportServices, shutdownBroadcast <-chan struct{}) error
- func ServerBPFEnabled() bool
- type Blocklist
- type BlocklistTag
- type CachedResponse
- func (response *CachedResponse) Available() int
- func (response *CachedResponse) CopyFromPosition(position int, writer io.Writer) (int, error)
- func (response *CachedResponse) HasPosition(position int) bool
- func (response *CachedResponse) Reset()
- func (response *CachedResponse) Write(data []byte) (int, error)
- type CachedResponseBufferPool
- type Config
- func (config *Config) DumpProfilesOnStopEstablishTunnels(establishedClientsCount int) bool
- func (config *Config) GetFrontingProviderID() string
- func (config *Config) GetLogFileReopenConfig() (int, bool, os.FileMode)
- func (config *Config) GetOwnEncodedServerEntry(serverEntryTag string) (string, bool)
- func (config *Config) GetProviderID() string
- func (config *Config) GetRegion() string
- func (config *Config) GetRunningProtocols() []string
- func (config *Config) RunLoadMonitor() bool
- func (config *Config) RunPeriodicGarbageCollection() bool
- func (config *Config) RunWebServer() bool
- type CustomJSONFormatter
- type DNSResolver
- type Discovery
- type GenerateConfigParams
- type GeoIPData
- type GeoIPService
- func (geoIP *GeoIPService) GetSessionCache(sessionID string) GeoIPData
- func (geoIP *GeoIPService) InSessionCache(sessionID string) bool
- func (geoIP *GeoIPService) Lookup(strIP string) GeoIPData
- func (geoIP *GeoIPService) LookupIP(IP net.IP) GeoIPData
- func (geoIP *GeoIPService) LookupISPForIP(IP net.IP) GeoIPData
- func (geoIP *GeoIPService) MarkSessionCacheToExpire(sessionID string)
- func (geoIP *GeoIPService) Reloaders() []common.Reloader
- func (geoIP *GeoIPService) SetSessionCache(sessionID string, geoIPData GeoIPData)
- type HTTPSServer
- type IntentionalPanicError
- type LogFields
- type MeekServer
- type PanickingLogWriter
- type ProtocolStats
- type RateLimits
- type RegionStats
- type ReplayCache
- func (r *ReplayCache) FailedReplayParameters(tunnelProtocol string, geoIPData GeoIPData, packetManipulationSpecName string, ...)
- func (r *ReplayCache) Flush()
- func (r *ReplayCache) GetMetrics() LogFields
- func (r *ReplayCache) GetReplayFragmentor(tunnelProtocol string, geoIPData GeoIPData) (*prng.Seed, bool)
- func (r *ReplayCache) GetReplayPacketManipulation(tunnelProtocol string, geoIPData GeoIPData) (string, bool)
- func (r *ReplayCache) GetReplayTargetDuration(geoIPData GeoIPData) (bool, time.Duration, time.Duration)
- func (r *ReplayCache) SetReplayParameters(tunnelProtocol string, geoIPData GeoIPData, packetManipulationSpecName string, ...)
- type ServerTacticsParametersCache
- type SupportServices
- type TLSTunnelConn
- type TLSTunnelListener
- type TLSTunnelServer
- type TacticsListener
- type TraceLogger
- type TrafficRules
- type TrafficRulesFilter
- type TrafficRulesSet
- func (set *TrafficRulesSet) GetMeekRateLimiterConfig() (int, int, []string, []string, []string, []string, []string, int, int, int)
- func (set *TrafficRulesSet) GetTrafficRules(isFirstTunnelInSession bool, tunnelProtocol string, geoIPData GeoIPData, ...) TrafficRules
- func (set *TrafficRulesSet) Validate() error
- type TunnelServer
- func (server *TunnelServer) AcceptClientDomainBytes(sessionID string) (bool, error)
- func (server *TunnelServer) CheckEstablishTunnels() bool
- func (server *TunnelServer) GetClientDisableDiscovery(sessionID string) (bool, error)
- func (server *TunnelServer) GetClientHandshaked(sessionID string) (bool, bool, error)
- func (server *TunnelServer) GetEstablishTunnelsMetrics() (bool, int64)
- func (server *TunnelServer) GetEstablishedClientCount() int
- func (server *TunnelServer) GetLoadStats() (UpstreamStats, ProtocolStats, RegionStats)
- func (server *TunnelServer) ResetAllClientOSLConfigs()
- func (server *TunnelServer) ResetAllClientTrafficRules()
- func (server *TunnelServer) Run() error
- func (server *TunnelServer) SetClientHandshakeState(sessionID string, state handshakeState, authorizations []string) (*handshakeStateInfo, error)
- func (server *TunnelServer) SetEstablishTunnels(establish bool)
- func (server *TunnelServer) UpdateClientAPIParameters(sessionID string, apiParams common.APIParameters) error
- type UpstreamStats
Constants ¶
const ( MAX_API_PARAMS_SIZE = 256 * 1024 // 256KB PADDING_MAX_BYTES = 16 * 1024 CLIENT_PLATFORM_ANDROID = "Android" CLIENT_PLATFORM_WINDOWS = "Windows" CLIENT_PLATFORM_IOS = "iOS" )
const ( SERVER_CONFIG_FILENAME = "psiphond.config" SERVER_TRAFFIC_RULES_CONFIG_FILENAME = "psiphond-traffic-rules.config" SERVER_OSL_CONFIG_FILENAME = "psiphond-osl.config" SERVER_TACTICS_CONFIG_FILENAME = "psiphond-tactics.config" SERVER_ENTRY_FILENAME = "server-entry.dat" DEFAULT_SERVER_IP_ADDRESS = "127.0.0.1" WEB_SERVER_SECRET_BYTE_LENGTH = 32 DISCOVERY_VALUE_KEY_BYTE_LENGTH = 32 SSH_USERNAME_SUFFIX_BYTE_LENGTH = 8 SSH_PASSWORD_BYTE_LENGTH = 32 SSH_RSA_HOST_KEY_BITS = 2048 SSH_OBFUSCATED_KEY_BYTE_LENGTH = 32 PEAK_UPSTREAM_FAILURE_RATE_MINIMUM_SAMPLE_SIZE = 10 PERIODIC_GARBAGE_COLLECTION = 120 * time.Second STOP_ESTABLISH_TUNNELS_ESTABLISHED_CLIENT_THRESHOLD = 20 DEFAULT_LOG_FILE_REOPEN_RETRIES = 25 )
const ( DISCOVERY_STRATEGY_CLASSIC = "classic" DISCOVERY_STRATEGY_CONSISTENT = "consistent" )
const ( DNS_SYSTEM_CONFIG_FILENAME = "/etc/resolv.conf" DNS_SYSTEM_CONFIG_RELOAD_PERIOD = 5 * time.Second DNS_RESOLVER_PORT = 53 )
const ( GEOIP_SESSION_CACHE_TTL = 60 * time.Minute GEOIP_UNKNOWN_VALUE = "None" GEOIP_DATABASE_TYPE_ISP = "GeoIP2-ISP" )
const ( // Protocol version 1 clients can handle arbitrary length response bodies. Older clients // report no version number and expect at most 64K response bodies. MEEK_PROTOCOL_VERSION_1 = 1 // Protocol version 2 clients initiate a session by sending an encrypted and obfuscated meek // cookie with their initial HTTP request. Connection information is contained within the // encrypted cookie payload. The server inspects the cookie and establishes a new session and // returns a new random session ID back to client via Set-Cookie header. The client uses this // session ID on all subsequent requests for the remainder of the session. MEEK_PROTOCOL_VERSION_2 = 2 // Protocol version 3 clients include resiliency enhancements and will add a Range header // when retrying a request for a partially downloaded response payload. MEEK_PROTOCOL_VERSION_3 = 3 MEEK_MAX_REQUEST_PAYLOAD_LENGTH = 65536 MEEK_MIN_SESSION_ID_LENGTH = 8 MEEK_MAX_SESSION_ID_LENGTH = 20 MEEK_DEFAULT_TURN_AROUND_TIMEOUT = 10 * time.Millisecond MEEK_DEFAULT_EXTENDED_TURN_AROUND_TIMEOUT = 100 * time.Millisecond MEEK_DEFAULT_SKIP_EXTENDED_TURN_AROUND_THRESHOLD = 8192 MEEK_DEFAULT_MAX_SESSION_STALENESS = 45 * time.Second MEEK_DEFAULT_HTTP_CLIENT_IO_TIMEOUT = 45 * time.Second MEEK_DEFAULT_FRONTED_HTTP_CLIENT_IO_TIMEOUT = 360 * time.Second MEEK_DEFAULT_RESPONSE_BUFFER_LENGTH = 65536 MEEK_DEFAULT_POOL_BUFFER_LENGTH = 65536 MEEK_DEFAULT_POOL_BUFFER_COUNT = 2048 )
const ( REPLAY_CACHE_MAX_ENTRIES = 100000 REPLAY_CACHE_CLEANUP_INTERVAL = 1 * time.Minute )
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 )
const ( SSH_AUTH_LOG_PERIOD = 30 * time.Minute SSH_HANDSHAKE_TIMEOUT = 30 * time.Second SSH_BEGIN_HANDSHAKE_TIMEOUT = 1 * time.Second SSH_CONNECTION_READ_DEADLINE = 5 * time.Minute SSH_TCP_PORT_FORWARD_COPY_BUFFER_SIZE = 8192 SSH_TCP_PORT_FORWARD_QUEUE_SIZE = 1024 SSH_KEEP_ALIVE_PAYLOAD_MIN_BYTES = 0 SSH_KEEP_ALIVE_PAYLOAD_MAX_BYTES = 256 SSH_SEND_OSL_INITIAL_RETRY_DELAY = 30 * time.Second SSH_SEND_OSL_RETRY_FACTOR = 2 OSL_SESSION_CACHE_TTL = 5 * time.Minute MAX_AUTHORIZATIONS = 16 PRE_HANDSHAKE_RANDOM_STREAM_MAX_COUNT = 1 RANDOM_STREAM_MAX_BYTES = 10485760 ALERT_REQUEST_QUEUE_BUFFER_SIZE = 16 )
const (
TACTICS_CACHE_MAX_ENTRIES = 10000
)
const WEB_SERVER_IO_TIMEOUT = 10 * time.Second
Variables ¶
This section is empty.
Functions ¶
func CommonLogger ¶
func CommonLogger(traceLogger *TraceLogger) *commonLogger
CommonLogger wraps a TraceLogger instance with an interface that conforms to common.Logger. This is used to make the TraceLogger available to other packages that don't import the "server" package.
func GenerateConfig ¶
GenerateConfig creates a new Psiphon server config. It returns JSON encoded configs and a client-compatible "server entry" for the server. It generates all necessary secrets and key material, which are emitted in the config file and server entry as necessary.
GenerateConfig uses sample values for many fields. The intention is for generated configs to be used for testing or as examples for production setup, not to generate production-ready configurations.
When tactics key material is provided in GenerateConfigParams, tactics capabilities are added for all meek protocols in TunnelProtocolPorts.
func InitLogging ¶
InitLogging configures a logger according to the specified config params. If not called, the default logger set by the package init() is used. Concurrency notes: this should only be called from the main goroutine; InitLogging only has effect on the first call, as the logging facilities it initializes may be in use by other goroutines after that point.
func IsLogLevelDebug ¶
func IsLogLevelDebug() bool
func ListenTLSTunnel ¶
func ListenTLSTunnel( support *SupportServices, listener net.Listener, listenerTunnelProtocol string, listenerPort int, ) (net.Listener, error)
ListenTLSTunnel returns the listener of a new TLSTunnelServer. Note: the first Read or Write call on a connection returned by the listener will trigger the underlying TLS handshake.
func NewIntentionalPanicError ¶
NewIntentionalPanicError creates a new IntentionalPanicError.
func NewLogWriter ¶
func NewLogWriter() *io.PipeWriter
NewLogWriter returns an io.PipeWriter that can be used to write to the global logger. Caller must Close() the writer.
func RunServices ¶
RunServices initializes support functions including logging and GeoIP services; and then starts the server components and runs them until os.Interrupt or os.Kill signals are received. The config determines which components are run.
func RunWebServer ¶
func RunWebServer( support *SupportServices, shutdownBroadcast <-chan struct{}) error
RunWebServer runs a web server which supports tunneled and untunneled Psiphon API requests.
The HTTP request handlers are light wrappers around the base Psiphon API request handlers from the SSH API transport. The SSH API transport is preferred by new clients. The web API transport provides support for older clients.
The API is compatible with all tunnel-core clients but not backwards compatible with all legacy clients.
Note: new features, including authorizations, are not supported in the web API transport.
func ServerBPFEnabled ¶
func ServerBPFEnabled() bool
ServerBPFEnabled indicates if BPF functionality is enabled.
Types ¶
type Blocklist ¶
type Blocklist struct { common.ReloadableFile // contains filtered or unexported fields }
Blocklist provides a fast lookup of IP addresses and domains that are candidates for egress blocking. This is intended to be used to block malware and other malicious traffic.
The Reload function supports hot reloading of rules data while the server is running.
Limitations: the blocklist is implemented with in-memory Go maps, which limits the practical size of the blocklist.
func NewBlocklist ¶
NewBlocklist creates a new block list.
The input file must be a 3 field comma-delimited and optional quote-escaped CSV. Fields: <IPv4 address>,<source>,<subject>.
IP addresses may appear multiple times in the input file; each distinct source/subject is associated with the IP address and returned in the Lookup tag list.
func (*Blocklist) LookupDomain ¶
func (b *Blocklist) LookupDomain(domain string) []BlocklistTag
LookupDomain returns the blocklist tags for any domain that is on the blocklist, or returns nil for any domain not on the blocklist. Lookup may be called concurrently. The caller must not modify the return value.
func (*Blocklist) LookupIP ¶
func (b *Blocklist) LookupIP(IPAddress net.IP) []BlocklistTag
LookupIP returns the blocklist tags for any IP address that is on the blocklist, or returns nil for any IP address not on the blocklist. Lookup may be called concurrently. The caller must not modify the return value.
type BlocklistTag ¶
BlocklistTag indicates the source containing an IP address and the subject, or name of the suspected malicious traffic.
type CachedResponse ¶
type CachedResponse struct {
// contains filtered or unexported fields
}
CachedResponse is a data structure that supports meek protocol connection interruption resiliency: it stores payload data from the most recent response so that it may be resent if the client fails to receive it.
The meek server maintains one CachedResponse for each meek client. Psiphon's variant of meek streams response data, so responses are not fixed size. To limit the memory overhead of response caching, each CachedResponse has a fixed-size buffer that operates as a ring buffer, discarding older response bytes when the buffer fills. A CachedResponse that has discarded data may still satisfy a client retry where the client has already received part of the response payload.
A CachedResponse will also extend its capacity by borrowing buffers from a CachedResponseBufferPool, if available. When Reset is called, borrowed buffers are released back to the pool.
func NewCachedResponse ¶
func NewCachedResponse( bufferSize int, extendedBufferPool *CachedResponseBufferPool) *CachedResponse
NewCachedResponse creates a CachedResponse with a fixed buffer of size bufferSize and borrowing buffers from extendedBufferPool.
func (*CachedResponse) Available ¶
func (response *CachedResponse) Available() int
Available returns the size of the buffered response data.
func (*CachedResponse) CopyFromPosition ¶
CopyFromPosition writes the response data, starting at the specified position, to writer. Any data before the position is skipped. CopyFromPosition will return an error if the specified position is not available. CopyFromPosition will copy no data and return no error if the position is at the end of its available data. CopyFromPosition can be called repeatedly to read the same data -- it does not advance or modify the CachedResponse.
func (*CachedResponse) HasPosition ¶
func (response *CachedResponse) HasPosition(position int) bool
HasPosition checks if the CachedResponse has buffered response data starting at or before the specified position.
func (*CachedResponse) Reset ¶
func (response *CachedResponse) Reset()
Reset reinitializes the CachedResponse state to have no buffered response and releases all extended buffers back to the pool. Reset _must_ be called before discarding a CachedResponse or extended buffers will not be released.
func (*CachedResponse) Write ¶
func (response *CachedResponse) Write(data []byte) (int, error)
Write appends data to the CachedResponse. All writes will succeed, but only the most recent bytes will be retained once the fixed buffer is full and no extended buffers are available.
Write may be called multiple times to record a single response; Reset should be called between responses.
Write conforms to the io.Writer interface.
type CachedResponseBufferPool ¶
type CachedResponseBufferPool struct {
// contains filtered or unexported fields
}
CachedResponseBufferPool is a fixed-size pool of fixed-size buffers that are used to temporarily extend the capacity of CachedResponses.
func NewCachedResponseBufferPool ¶
func NewCachedResponseBufferPool( bufferSize, bufferCount int) *CachedResponseBufferPool
NewCachedResponseBufferPool creates a new CachedResponseBufferPool with the specified number of buffers. Buffers are allocated on demand and once allocated remain allocated.
func (*CachedResponseBufferPool) Get ¶
func (pool *CachedResponseBufferPool) Get() []byte
Get returns a buffer, if one is available, or returns nil when no buffer is available. Get does not block. Call Put to release the buffer back to the pool.
Note: currently, Buffers are not zeroed between use by different CachedResponses owned by different clients. A bug resulting in cross-client data transfer exposes only OSSH ciphertext in the case of meek's use of CachedResponses.
func (*CachedResponseBufferPool) Put ¶
func (pool *CachedResponseBufferPool) Put(buffer []byte)
Put releases a buffer back to the pool. The buffer must have been obtained from Get.
type Config ¶
type Config struct { // LogLevel specifies the log level. Valid values are: // panic, fatal, error, warn, info, debug LogLevel string // LogFilename specifies the path of the file to log // to. When blank, logs are written to stderr. LogFilename string // LogFileReopenRetries specifies how many retries, each with a 1ms delay, // will be attempted after reopening a rotated log file fails. Retries // mitigate any race conditions between writes/reopens and file operations // performed by external log managers, such as logrotate. // // When omitted, DEFAULT_LOG_FILE_REOPEN_RETRIES is used. LogFileReopenRetries *int // LogFileCreateMode specifies that the Psiphon server should create a new // log file when one is not found, such as after rotation with logrotate // configured with nocreate. The value is the os.FileMode value to use when // creating the file. // // When omitted, the Psiphon server does not create log files. LogFileCreateMode *int // SkipPanickingLogWriter disables panicking when // unable to write any logs. SkipPanickingLogWriter bool // DiscoveryValueHMACKey is the network-wide secret value // used to determine a unique discovery strategy. DiscoveryValueHMACKey string // GeoIPDatabaseFilenames are paths of GeoIP2/GeoLite2 // MaxMind database files. When empty, no GeoIP lookups are // performed. Each file is queried, in order, for the // logged fields: country code, city, and ISP. Multiple // file support accommodates the MaxMind distribution where // ISP data in a separate file. GeoIPDatabaseFilenames []string // PsinetDatabaseFilename is the path of the file containing // psinet.Database data. PsinetDatabaseFilename string // HostID identifies the server host; this value is included with all logs. HostID string // HostProvider identifies the server host provider; this value is // included with all logs and logged only when not blank. HostProvider string // ServerIPAddress is the public IP address of the server. ServerIPAddress string // WebServerPort is the listening port of the web server. // When <= 0, no web server component is run. WebServerPort int // WebServerSecret is the unique secret value that the client // must supply to make requests to the web server. WebServerSecret string // WebServerCertificate is the certificate the client uses to // authenticate the web server. WebServerCertificate string // WebServerPrivateKey is the private key the web server uses to // authenticate itself to clients. WebServerPrivateKey string // WebServerPortForwardAddress specifies the expected network // address ("<host>:<port>") specified in a client's port forward // HostToConnect and PortToConnect when the client is making a // tunneled connection to the web server. This address is always // exempted from validation against SSH_DISALLOWED_PORT_FORWARD_HOSTS // and AllowTCPPorts. WebServerPortForwardAddress string // WebServerPortForwardRedirectAddress specifies an alternate // destination address to be substituted and dialed instead of // the original destination when the port forward destination is // WebServerPortForwardAddress. WebServerPortForwardRedirectAddress string // TunnelProtocolPorts specifies which tunnel protocols to run // and which ports to listen on for each protocol. Valid tunnel // protocols include: // "SSH", "OSSH", "TLS-OSSH", "UNFRONTED-MEEK-OSSH", "UNFRONTED-MEEK-HTTPS-OSSH", // "UNFRONTED-MEEK-SESSION-TICKET-OSSH", "FRONTED-MEEK-OSSH", // "FRONTED-MEEK-QUIC-OSSH", "FRONTED-MEEK-HTTP-OSSH", "QUIC-OSSH", // "TAPDANCE-OSSH", abd "CONJURE-OSSH". TunnelProtocolPorts map[string]int // TunnelProtocolPassthroughAddresses specifies passthrough addresses to be // used for tunnel protocols configured in TunnelProtocolPorts. Passthrough // is a probing defense which relays all network traffic between a client and // the passthrough target when the client fails anti-probing tests. // // TunnelProtocolPassthroughAddresses is supported for: // "TLS-OSSH", "UNFRONTED-MEEK-HTTPS-OSSH", // "UNFRONTED-MEEK-SESSION-TICKET-OSSH", "UNFRONTED-MEEK-OSSH". TunnelProtocolPassthroughAddresses map[string]string // LegacyPassthrough indicates whether to expect legacy passthrough messages // from clients attempting to connect. This should be set for existing/legacy // passthrough servers only. LegacyPassthrough bool // EnableGQUIC indicates whether to enable legacy gQUIC QUIC-OSSH // versions, for backwards compatibility with all versions used by older // clients. Enabling gQUIC degrades the anti-probing stance of QUIC-OSSH, // as the legacy gQUIC stack will respond to probing packets. EnableGQUIC bool // SSHPrivateKey is the SSH host key. The same key is used for // all protocols, run by this server instance, which use SSH. SSHPrivateKey string // SSHServerVersion is the server version presented in the // identification string. The same value is used for all // protocols, run by this server instance, which use SSH. SSHServerVersion string // SSHUserName is the SSH user name to be presented by the // the tunnel-core client. The same value is used for all // protocols, run by this server instance, which use SSH. SSHUserName string // SSHPassword is the SSH password to be presented by the // the tunnel-core client. The same value is used for all // protocols, run by this server instance, which use SSH. SSHPassword string // SSHBeginHandshakeTimeoutMilliseconds specifies the timeout // for clients queueing to begin an SSH handshake. The default // is SSH_BEGIN_HANDSHAKE_TIMEOUT. SSHBeginHandshakeTimeoutMilliseconds *int // SSHHandshakeTimeoutMilliseconds specifies the timeout // before which a client must complete its handshake. The default // is SSH_HANDSHAKE_TIMEOUT. SSHHandshakeTimeoutMilliseconds *int // ObfuscatedSSHKey is the secret key for use in the Obfuscated // SSH protocol. The same secret key is used for all protocols, // run by this server instance, which use Obfuscated SSH. ObfuscatedSSHKey string // MeekCookieEncryptionPrivateKey is the NaCl private key used // to decrypt meek cookie payload sent from clients. The same // key is used for all meek protocols run by this server instance. MeekCookieEncryptionPrivateKey string // MeekObfuscatedKey is the secret key used for obfuscating // meek cookies sent from clients. The same key is used for all // meek protocols run by this server instance. // // NOTE: this key is also used by the TLS-OSSH protocol, which allows // clients with legacy unfronted meek-https server entries, that have the // passthrough capability, to connect with TLS-OSSH to the servers // corresponding to those server entries, which now support TLS-OSSH by // demultiplexing meek-https and TLS-OSSH over the meek-https port. MeekObfuscatedKey string // MeekProhibitedHeaders is a list of HTTP headers to check for // in client requests. If one of these headers is found, the // request fails. This is used to defend against abuse. MeekProhibitedHeaders []string // MeekRequiredHeaders is a list of HTTP header names and values that must // appear in requests. This is used to defend against abuse. MeekRequiredHeaders map[string]string // MeekProxyForwardedForHeaders is a list of HTTP headers which // may be added by downstream HTTP proxies or CDNs in front // of clients. These headers supply the original client IP // address, which is geolocated for stats purposes. Headers // include, for example, X-Forwarded-For. The header's value // is assumed to be a comma delimted list of IP addresses where // the client IP is the first IP address in the list. Meek protocols // look for these headers and use the client IP address from // the header if any one is present and the value is a valid // IP address; otherwise the direct connection remote address is // used as the client IP. MeekProxyForwardedForHeaders []string // MeekTurnAroundTimeoutMilliseconds specifies the amount of time meek will // wait for downstream bytes before responding to a request. The default is // MEEK_DEFAULT_TURN_AROUND_TIMEOUT. MeekTurnAroundTimeoutMilliseconds *int // MeekExtendedTurnAroundTimeoutMilliseconds specifies the extended amount of // time meek will wait for downstream bytes, as long as bytes arrive every // MeekTurnAroundTimeoutMilliseconds, before responding to a request. The // default is MEEK_DEFAULT_EXTENDED_TURN_AROUND_TIMEOUT. MeekExtendedTurnAroundTimeoutMilliseconds *int // MeekSkipExtendedTurnAroundThresholdBytes specifies when to skip the // extended turn around. When the number of bytes received in the client // request meets the threshold, optimize for upstream flows with quicker // round trip turn arounds. MeekSkipExtendedTurnAroundThresholdBytes *int // MeekMaxSessionStalenessMilliseconds specifies the TTL for meek sessions. // The default is MEEK_DEFAULT_MAX_SESSION_STALENESS. MeekMaxSessionStalenessMilliseconds *int // MeekHTTPClientIOTimeoutMilliseconds specifies meek HTTP server I/O // timeouts. The default is MEEK_DEFAULT_HTTP_CLIENT_IO_TIMEOUT. MeekHTTPClientIOTimeoutMilliseconds *int // MeekFrontedHTTPClientIOTimeoutMilliseconds specifies meek HTTP server // I/O timeouts for fronted protocols. The default is // MEEK_DEFAULT_FRONTED_HTTP_CLIENT_IO_TIMEOUT. MeekFrontedHTTPClientIOTimeoutMilliseconds *int // MeekCachedResponseBufferSize is the size of a private, // fixed-size buffer allocated for every meek client. The buffer // is used to cache response payload, allowing the client to retry // fetching when a network connection is interrupted. This retry // makes the OSSH tunnel within meek resilient to interruptions // at the HTTP TCP layer. // Larger buffers increase resiliency to interruption, but consume // more memory as buffers as never freed. The maximum size of a // response payload is a function of client activity, network // throughput and throttling. // A default of 64K is used when MeekCachedResponseBufferSize is 0. MeekCachedResponseBufferSize int // MeekCachedResponsePoolBufferSize is the size of a fixed-size, // shared buffer used to temporarily extend a private buffer when // MeekCachedResponseBufferSize is insufficient. Shared buffers // allow some clients to successfully retry longer response payloads // without allocating large buffers for all clients. // A default of 64K is used when MeekCachedResponsePoolBufferSize // is 0. MeekCachedResponsePoolBufferSize int // MeekCachedResponsePoolBufferCount is the number of shared // buffers. Shared buffers are allocated on first use and remain // allocated, so shared buffer count * size is roughly the memory // overhead of this facility. // A default of 2048 is used when MeekCachedResponsePoolBufferCount // is 0. MeekCachedResponsePoolBufferCount int // UDPInterceptUdpgwServerAddress specifies the network address of // a udpgw server which clients may be port forwarding to. When // specified, these TCP port forwards are intercepted and handled // directly by this server, which parses the SSH channel using the // udpgw protocol. Handling includes udpgw transparent DNS: tunneled // UDP DNS packets are rerouted to the host's DNS server. // // The intercept is applied before the port forward destination is // validated against SSH_DISALLOWED_PORT_FORWARD_HOSTS and // AllowTCPPorts. So the intercept address may be any otherwise // prohibited destination. UDPInterceptUdpgwServerAddress string // DNSResolverIPAddress specifies the IP address of a DNS server // to be used when "/etc/resolv.conf" doesn't exist or fails to // parse. When blank, "/etc/resolv.conf" must contain a usable // "nameserver" entry. DNSResolverIPAddress string // LoadMonitorPeriodSeconds indicates how frequently to log server // load information (number of connected clients per tunnel protocol, // number of running goroutines, amount of memory allocated, etc.) // The default, 0, disables load logging. LoadMonitorPeriodSeconds int // PeakUpstreamFailureRateMinimumSampleSize specifies the minimum number // of samples (e.g., upstream port forward attempts) that are required // before taking a failure rate snapshot which may be recorded as // peak_dns_failure_rate/peak_tcp_port_forward_failure_rate. The default // is PEAK_UPSTREAM_FAILURE_RATE_SAMPLE_SIZE. PeakUpstreamFailureRateMinimumSampleSize *int // ProcessProfileOutputDirectory is the path of a directory to which // process profiles will be written when signaled with SIGUSR2. The // files are overwritten on each invocation. When set to the default // value, blank, no profiles are written on SIGUSR2. Profiles include // the default profiles here: https://golang.org/pkg/runtime/pprof/#Profile. ProcessProfileOutputDirectory string // ProcessBlockProfileDurationSeconds specifies the sample duration for // "block" profiling. For the default, 0, no "block" profile is taken. ProcessBlockProfileDurationSeconds int // ProcessCPUProfileDurationSeconds specifies the sample duration for // CPU profiling. For the default, 0, no CPU profile is taken. ProcessCPUProfileDurationSeconds int // TrafficRulesFilename is the path of a file containing a JSON-encoded // TrafficRulesSet, the traffic rules to apply to Psiphon client tunnels. TrafficRulesFilename string // OSLConfigFilename is the path of a file containing a JSON-encoded // OSL Config, the OSL schemes to apply to Psiphon client tunnels. OSLConfigFilename string // RunPacketTunnel specifies whether to run a packet tunnel. RunPacketTunnel bool // PacketTunnelEgressInterface specifies tun.ServerConfig.EgressInterface. PacketTunnelEgressInterface string // PacketTunnelEnableDNSFlowTracking sets // tun.ServerConfig.EnableDNSFlowTracking. PacketTunnelEnableDNSFlowTracking bool // PacketTunnelDownstreamPacketQueueSize specifies // tun.ServerConfig.DownStreamPacketQueueSize. PacketTunnelDownstreamPacketQueueSize int // PacketTunnelSessionIdleExpirySeconds specifies // tun.ServerConfig.SessionIdleExpirySeconds. PacketTunnelSessionIdleExpirySeconds int // PacketTunnelSudoNetworkConfigCommands sets // tun.ServerConfig.SudoNetworkConfigCommands. PacketTunnelSudoNetworkConfigCommands bool // RunPacketManipulator specifies whether to run a packet manipulator. RunPacketManipulator bool // MaxConcurrentSSHHandshakes specifies a limit on the number of concurrent // SSH handshake negotiations. This is set to mitigate spikes in memory // allocations and CPU usage associated with SSH handshakes when many clients // attempt to connect concurrently. When a maximum limit is specified and // reached, additional clients that establish TCP or meek connections will // be disconnected after a short wait for the number of concurrent handshakes // to drop below the limit. // The default, 0 is no limit. MaxConcurrentSSHHandshakes int // PeriodicGarbageCollectionSeconds turns on periodic calls to // debug.FreeOSMemory, every specified number of seconds, to force garbage // collection and memory scavenging. Specify 0 to disable. The default is // PERIODIC_GARBAGE_COLLECTION. PeriodicGarbageCollectionSeconds *int // StopEstablishTunnelsEstablishedClientThreshold sets the established client // threshold for dumping profiles when SIGTSTP is signaled. When there are // less than or equal to the threshold number of established clients, // profiles are dumped to aid investigating unusual load limited states that // occur when few clients are connected and load should be relatively low. A // profile dump is attempted at most once per process lifetime, the first // time the threshold is met. Disabled when < 0. StopEstablishTunnelsEstablishedClientThreshold *int // AccessControlVerificationKeyRing is the access control authorization // verification key ring used to verify signed authorizations presented // by clients. Verified, active (unexpired) access control types will be // available for matching in the TrafficRulesFilter for the client via // AuthorizedAccessTypes. All other authorizations are ignored. AccessControlVerificationKeyRing accesscontrol.VerificationKeyRing // TacticsConfigFilename is the path of a file containing a JSON-encoded // tactics server configuration. TacticsConfigFilename string // BlocklistFilename is the path of a file containing a CSV-encoded // blocklist configuration. See NewBlocklist for more file format // documentation. BlocklistFilename string // BlocklistActive indicates whether to actively prevent blocklist hits in // addition to logging events. BlocklistActive bool // AllowBogons disables port forward bogon checks. This should be used only // for testing. AllowBogons bool // EnableSteeringIPs enables meek server steering IP support. EnableSteeringIPs bool // OwnEncodedServerEntries is a list of the server's own encoded server // entries, idenfified by server entry tag. These values are used in the // handshake API to update clients that don't yet have a signed copy of these // server entries. // // For purposes of compartmentalization, each server receives only its own // server entries here; and, besides the discovery server entries, in // psinet.Database, necessary for the discovery feature, no other server // entries are stored on a Psiphon server. OwnEncodedServerEntries map[string]string // contains filtered or unexported fields }
Config specifies the configuration and behavior of a Psiphon server.
func LoadConfig ¶
LoadConfig loads and validates a JSON encoded server config.
func (*Config) DumpProfilesOnStopEstablishTunnels ¶
DumpProfilesOnStopEstablishTunnels indicates whether dump profiles due to an unexpectedly low number of established clients during high load.
func (*Config) GetFrontingProviderID ¶
GetFrontingProviderID returns the fronting provider ID associated with the server's fronted protocol(s).
func (*Config) GetLogFileReopenConfig ¶
GetLogFileReopenConfig gets the reopen retries, and create/mode inputs for rotate.NewRotatableFileWriter, which is used when writing to log files.
By default, we expect the log files to be managed by logrotate, with logrotate configured to re-create the next log file after rotation. As described in the documentation for rotate.NewRotatableFileWriter, and as observed in production, we occasionally need retries when attempting to reopen the log file post-rotation; and we avoid conflicts, and spurious re-rotations, by disabling file create in rotate.NewRotatableFileWriter. In large scale production, incidents requiring retry are very rare, so the retry delay is not expected to have a significant impact on performance.
The defaults may be overriden in the Config.
func (*Config) GetOwnEncodedServerEntry ¶
GetOwnEncodedServerEntry returns one of the server's own server entries, as identified by the server entry tag.
func (*Config) GetProviderID ¶
GetProviderID returns the provider ID associated with the server.
func (*Config) GetRunningProtocols ¶
GetRunningProtocols returns the list of protcols this server is running. The caller must not mutate the return value.
func (*Config) RunLoadMonitor ¶
RunLoadMonitor indicates whether to monitor and log server load.
func (*Config) RunPeriodicGarbageCollection ¶
RunPeriodicGarbageCollection indicates whether to run periodic garbage collection.
func (*Config) RunWebServer ¶
RunWebServer indicates whether to run a web server component.
type CustomJSONFormatter ¶
type CustomJSONFormatter struct { }
CustomJSONFormatter is a customized version of logrus.JSONFormatter
func (*CustomJSONFormatter) Format ¶
func (f *CustomJSONFormatter) Format(entry *logrus.Entry) ([]byte, error)
Format implements logrus.Formatter. This is a customized version of the standard logrus.JSONFormatter adapted from: https://github.com/Sirupsen/logrus/blob/f1addc29722ba9f7651bc42b4198d0944b66e7c4/json_formatter.go
The changes are: - "time" is renamed to "timestamp" - there's an option to omit the standard "msg" and "level" fields
type DNSResolver ¶
type DNSResolver struct { common.ReloadableFile // contains filtered or unexported fields }
DNSResolver maintains fresh DNS resolver values, monitoring "/etc/resolv.conf" on platforms where it is available; and otherwise using a default value.
func NewDNSResolver ¶
func NewDNSResolver(defaultResolver string) (*DNSResolver, error)
NewDNSResolver initializes a new DNSResolver, loading it with fresh resolver values. The load must succeed, so either "/etc/resolv.conf" must contain valid "nameserver" lines with a DNS server IP address, or a valid "defaultResolver" default value must be provided. On systems without "/etc/resolv.conf", "defaultResolver" is required.
The resolver is considered stale and reloaded if last checked more than 5 seconds before the last Get(), which is similar to frequencies in other implementations:
- https://golang.org/src/net/dnsclient_unix.go, resolverConfig.tryUpdate: 5 seconds
- https://github.com/ambrop72/badvpn/blob/master/udpgw/udpgw.c, maybe_update_dns: 2 seconds
func (*DNSResolver) Get ¶
func (dns *DNSResolver) Get() net.IP
Get returns one of the cached resolvers, selected at random, after first updating the cached values if they're stale. If reloading fails, the previous values are used.
Randomly selecting any one of the configured resolvers is expected to be more resiliant to failure; e.g., if one of the resolvers becomes unavailable.
func (*DNSResolver) GetAll ¶
func (dns *DNSResolver) GetAll() []net.IP
GetAll returns a list of all DNS resolver addresses. Cached values are updated if they're stale. If reloading fails, the previous values are used.
func (*DNSResolver) GetAllIPv4 ¶
func (dns *DNSResolver) GetAllIPv4() []net.IP
GetAllIPv4 returns a list of all IPv4 DNS resolver addresses. Cached values are updated if they're stale. If reloading fails, the previous values are used.
func (*DNSResolver) GetAllIPv6 ¶
func (dns *DNSResolver) GetAllIPv6() []net.IP
GetAllIPv6 returns a list of all IPv6 DNS resolver addresses. Cached values are updated if they're stale. If reloading fails, the previous values are used.
type Discovery ¶
Discovery handles the discovery step of the "handshake" API request. It's safe for concurrent usage.
func (*Discovery) DiscoverServers ¶
DiscoverServers selects new encoded server entries to be "discovered" by the client, using the client's IP address as the input into the discovery algorithm.
type GenerateConfigParams ¶
type GenerateConfigParams struct { LogFilename string SkipPanickingLogWriter bool LogLevel string ServerIPAddress string WebServerPort int EnableSSHAPIRequests bool TunnelProtocolPorts map[string]int TunnelProtocolPassthroughAddresses map[string]string TrafficRulesConfigFilename string OSLConfigFilename string TacticsConfigFilename string TacticsRequestPublicKey string TacticsRequestObfuscatedKey string Passthrough bool LegacyPassthrough bool LimitQUICVersions protocol.QUICVersions EnableGQUIC bool FrontingProviderID string }
GenerateConfigParams specifies customizations to be applied to a generated server config.
type GeoIPData ¶
GeoIPData is GeoIP data for a client session. Individual client IP addresses are neither logged nor explicitly referenced during a session. The GeoIP country, city, and ISP corresponding to a client IP address are resolved and then logged along with usage stats.
func NewGeoIPData ¶
func NewGeoIPData() GeoIPData
NewGeoIPData returns a GeoIPData initialized with the expected GEOIP_UNKNOWN_VALUE values to be used when GeoIP lookup fails.
func (GeoIPData) SetLogFields ¶
SetLogFields adds the GeoIPData fields to LogFields, following Psiphon metric field name and format conventions.
func (GeoIPData) SetLogFieldsWithPrefix ¶
type GeoIPService ¶
type GeoIPService struct {
// contains filtered or unexported fields
}
GeoIPService implements GeoIP lookup and session/GeoIP caching. Lookup is via a MaxMind database; the ReloadDatabase function supports hot reloading of MaxMind data while the server is running.
func NewGeoIPService ¶
func NewGeoIPService(databaseFilenames []string) (*GeoIPService, error)
NewGeoIPService initializes a new GeoIPService.
func (*GeoIPService) GetSessionCache ¶
func (geoIP *GeoIPService) GetSessionCache(sessionID string) GeoIPData
GetSessionCache returns the cached GeoIPData for the specified session ID; a blank GeoIPData is returned if the session ID is not found in the cache.
func (*GeoIPService) InSessionCache ¶
func (geoIP *GeoIPService) InSessionCache(sessionID string) bool
InSessionCache returns whether the session ID is present in the session cache.
func (*GeoIPService) Lookup ¶
func (geoIP *GeoIPService) Lookup(strIP string) GeoIPData
Lookup determines a GeoIPData for a given string client IP address.
func (*GeoIPService) LookupIP ¶
func (geoIP *GeoIPService) LookupIP(IP net.IP) GeoIPData
LookupIP determines a GeoIPData for a given client IP address.
func (*GeoIPService) LookupISPForIP ¶
func (geoIP *GeoIPService) LookupISPForIP(IP net.IP) GeoIPData
LookupISPForIP determines a GeoIPData for a given client IP address. Only ISP, ASN, and ASO fields will be populated. This lookup is faster than a full lookup. Benchmarks show this lookup is <= ~1 microsecond against the production geo IP database.
func (*GeoIPService) MarkSessionCacheToExpire ¶
func (geoIP *GeoIPService) MarkSessionCacheToExpire(sessionID string)
MarkSessionCacheToExpire initiates expiry for an existing session cache entry, if the session ID is found in the cache. Concurrency note: SetSessionCache and MarkSessionCacheToExpire should not be called concurrently for a single session ID.
func (*GeoIPService) Reloaders ¶
func (geoIP *GeoIPService) Reloaders() []common.Reloader
Reloaders gets the list of reloadable databases in use by the GeoIPService. This list is used to hot reload these databases.
func (*GeoIPService) SetSessionCache ¶
func (geoIP *GeoIPService) SetSessionCache(sessionID string, geoIPData GeoIPData)
SetSessionCache adds the sessionID/geoIPData pair to the session cache. This value will not expire; the caller must call MarkSessionCacheToExpire to initiate expiry. Calling SetSessionCache for an existing sessionID will replace the previous value and reset any expiry.
type HTTPSServer ¶
HTTPSServer is a wrapper around http.Server which adds the ServeTLS function.
func (*HTTPSServer) ServeTLS ¶
ServeTLS is similar to http.Serve, but uses TLS.
The http package has both ListenAndServe and ListenAndServeTLS higher- level interfaces, but only Serve (not TLS) offers a lower-level interface that allows the caller to keep a refererence to the Listener, allowing for external shutdown. ListenAndServeTLS also requires the TLS cert and key to be in files and we avoid that here.
Note that the http.Server.TLSConfig field is ignored and the tls.Config parameter is used intead.
type IntentionalPanicError ¶
type IntentionalPanicError struct {
// contains filtered or unexported fields
}
IntentionalPanicError is an error type that is used when calling panic() in a situation where recovers should propagate the panic.
func (IntentionalPanicError) Error ¶
func (err IntentionalPanicError) Error() string
Error implements the error interface.
type MeekServer ¶
type MeekServer struct {
// contains filtered or unexported fields
}
MeekServer implements the meek protocol, which tunnels TCP traffic (in the case of Psiphon, Obfuscated SSH traffic) over HTTP. Meek may be fronted (through a CDN) or direct and may be HTTP or HTTPS.
Upstream traffic arrives in HTTP request bodies and downstream traffic is sent in response bodies. The sequence of traffic for a given flow is associated using a session ID that's set as a HTTP cookie for the client to submit with each request.
MeekServer hooks into TunnelServer via the net.Conn interface by transforming the HTTP payload traffic for a given session into net.Conn conforming Read()s and Write()s via the meekConn struct.
func NewMeekServer ¶
func NewMeekServer( support *SupportServices, listener net.Listener, listenerTunnelProtocol string, listenerPort int, useTLS, isFronted, useObfuscatedSessionTickets, useHTTPNormalizer bool, clientHandler func(clientConn net.Conn, data *additionalTransportData), stopBroadcast <-chan struct{}) (*MeekServer, error)
NewMeekServer initializes a new meek server.
func (*MeekServer) Run ¶
func (server *MeekServer) Run() error
Run runs the meek server; this function blocks while serving HTTP or HTTPS connections on the specified listener. This function also runs a goroutine which cleans up expired meek client sessions.
To stop the meek server, both Close() the listener and set the stopBroadcast signal specified in NewMeekServer.
func (*MeekServer) ServeHTTP ¶
func (server *MeekServer) ServeHTTP(responseWriter http.ResponseWriter, request *http.Request)
ServeHTTP handles meek client HTTP requests, where the request body contains upstream traffic and the response will contain downstream traffic.
type PanickingLogWriter ¶
type PanickingLogWriter struct {
// contains filtered or unexported fields
}
PanickingLogWriter wraps an io.Writer and intentionally panics when a Write() fails.
func NewPanickingLogWriter ¶
func NewPanickingLogWriter( name string, writer io.Writer) *PanickingLogWriter
NewPanickingLogWriter creates a new PanickingLogWriter.
type ProtocolStats ¶
type RateLimits ¶
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 }
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.
func (*RateLimits) CommonRateLimits ¶
func (rateLimits *RateLimits) CommonRateLimits(handshaked bool) common.RateLimits
CommonRateLimits converts a RateLimits to a common.RateLimits.
type RegionStats ¶
type ReplayCache ¶
type ReplayCache struct {
// contains filtered or unexported fields
}
ReplayCache is a cache of recently used and successful network obfuscation parameters that may be replayed -- reused -- for subsequent tunnel connections.
Server-side replay is analogous to client-side replay, with one key difference: server-side replay can be applied across multiple clients in the same GeoIP scope.
Replay is enabled with tactics, and tactics determine the tunnel quality targets for establishing and clearing replay parameters.
ReplayCache has a maximum capacity with an LRU strategy to cap memory overhead.
func NewReplayCache ¶
func NewReplayCache(support *SupportServices) *ReplayCache
NewReplayCache creates a new ReplayCache.
func (*ReplayCache) FailedReplayParameters ¶
func (r *ReplayCache) FailedReplayParameters( tunnelProtocol string, geoIPData GeoIPData, packetManipulationSpecName string, fragmentorSeed *prng.Seed)
FailedReplayParameters increments the count of tunnels which failed to complete any liveness test and API handshake after using replay parameters. Once a failure threshold is reached, cached replay parameters are cleared. Call this function for tunnels which meet the failure criteria.
func (*ReplayCache) Flush ¶
func (r *ReplayCache) Flush()
Flush clears all entries in the ReplayCache. Flush should be called when tactics hot reload and change to clear any cached replay parameters that may be based on stale tactics.
func (*ReplayCache) GetMetrics ¶
func (r *ReplayCache) GetMetrics() LogFields
GetMetrics returns a snapshop of current ReplayCache event counters and resets all counters to zero.
func (*ReplayCache) GetReplayFragmentor ¶
func (r *ReplayCache) GetReplayFragmentor( tunnelProtocol string, geoIPData GeoIPData) (*prng.Seed, bool)
GetReplayFragmentor returns an active replay fragmentor seed for the specified tunnel protocol and GeoIP scope.
func (*ReplayCache) GetReplayPacketManipulation ¶
func (r *ReplayCache) GetReplayPacketManipulation( tunnelProtocol string, geoIPData GeoIPData) (string, bool)
GetReplayPacketManipulation returns an active replay packet manipulation spec for the specified tunnel protocol and GeoIP scope.
While Flush should be called to clear parameters based on stale tactics, it's still possible for GetReplayPacketManipulation to return a spec name that's no longer in the current list of known specs.
func (*ReplayCache) GetReplayTargetDuration ¶
func (r *ReplayCache) GetReplayTargetDuration( geoIPData GeoIPData) (bool, time.Duration, time.Duration)
GetReplayTargetDuration returns the tactics replay target tunnel duration for the specified GeoIP data. Tunnels which are active for the specified duration are candidates for setting or extending replay parameters. Wait for the returned wait duration before evaluating the tunnel duration. Once this target is met, call SetReplayParameters, which will check additional targets and conditionally set replay parameters.
func (*ReplayCache) SetReplayParameters ¶
func (r *ReplayCache) SetReplayParameters( tunnelProtocol string, geoIPData GeoIPData, packetManipulationSpecName string, fragmentorSeed *prng.Seed, tunneledBytesUp int64, tunneledBytesDown int64)
SetReplayParameters sets replay parameters, packetManipulationSpecName and fragmentorSeed, for the specified tunnel protocol and GeoIP scope. Once set, replay parameters are active for a tactics-configurable TTL.
The specified tunneledBytesUp/Down must meet tactics replay bytes transferred targets. SetReplayParameters should be called only after first calling ReplayTargetDuration and ensuring the tunnel meets the active tunnel duration target. When cached replay parameters exist, their TTL is extended and any failure counts are reset to zero.
SetReplayParameters must be called only once per tunnel. Extending replay parameters TTL should only be done only immediately after a successful tunnel dial and target achievement, as this is the part of a tunnel lifecycle at highest risk of blocking.
The value pointed to by fragmentorSeed must not be mutated after calling SetReplayParameters.
type ServerTacticsParametersCache ¶
type ServerTacticsParametersCache struct {
// contains filtered or unexported fields
}
ServerTacticsParametersCache is a cache of filtered server-side tactics, intended to speed-up frequent tactics lookups.
Presently, the cache is targeted at pre-handshake lookups which are both the most time critical and have a low tactic cardinality, as only GeoIP filter inputs are available.
There is no TTL for cache entries as the cached filtered tactics remain valid until the tactics config changes; Flush must be called on tactics config hot reloads.
func NewServerTacticsParametersCache ¶
func NewServerTacticsParametersCache( support *SupportServices) *ServerTacticsParametersCache
NewServerTacticsParametersCache creates a new ServerTacticsParametersCache.
func (*ServerTacticsParametersCache) Flush ¶
func (c *ServerTacticsParametersCache) Flush()
func (*ServerTacticsParametersCache) Get ¶
func (c *ServerTacticsParametersCache) Get( geoIPData GeoIPData) (parameters.ParametersAccessor, error)
Get returns server-side tactics parameters for the specified GeoIP scope. Get is designed to be called before the API handshake and does not filter by API parameters. IsNil guards must be used when accessing the returned ParametersAccessor.
func (*ServerTacticsParametersCache) GetMetrics ¶
func (c *ServerTacticsParametersCache) GetMetrics() LogFields
GetMetrics returns a snapshop of current ServerTacticsParametersCache event counters and resets all counters to zero.
type SupportServices ¶
type SupportServices struct { // TODO: make all fields non-exported, none are accessed outside // of this package. Config *Config TrafficRulesSet *TrafficRulesSet OSLConfig *osl.Config PsinetDatabase *psinet.Database GeoIPService *GeoIPService DNSResolver *DNSResolver TunnelServer *TunnelServer PacketTunnelServer *tun.Server TacticsServer *tactics.Server Blocklist *Blocklist PacketManipulator *packetman.Manipulator ReplayCache *ReplayCache ServerTacticsParametersCache *ServerTacticsParametersCache // contains filtered or unexported fields }
SupportServices carries common and shared data components across different server components. SupportServices implements a hot reload of traffic rules, psinet database, and geo IP database components, which allows these data components to be refreshed without restarting the server process.
func NewSupportServices ¶
func NewSupportServices(config *Config) (*SupportServices, error)
NewSupportServices initializes a new SupportServices.
func (*SupportServices) Reload ¶
func (support *SupportServices) Reload()
Reload reinitializes traffic rules, psinet database, and geo IP database components. If any component fails to reload, an error is logged and Reload proceeds, using the previous state of the component.
type TLSTunnelConn ¶
TLSTunnelConn implements the net.Conn and common.MetricsSource interfaces.
func NewTLSTunnelConn ¶
func NewTLSTunnelConn(conn net.Conn, server *TLSTunnelServer) *TLSTunnelConn
NewTLSTunnelConn initializes a new TLSTunnelConn.
func (*TLSTunnelConn) GetMetrics ¶
func (conn *TLSTunnelConn) GetMetrics() common.LogFields
GetMetrics implements the common.MetricsSource interface.
type TLSTunnelListener ¶
TLSTunnelListener implements the net.Listener interface. Accept returns a net.Conn which implements the common.MetricsSource interface.
func NewTLSTunnelListener ¶
func NewTLSTunnelListener(listener net.Listener, server *TLSTunnelServer) *TLSTunnelListener
NewTLSTunnelListener initializes a new TLSTunnelListener.
type TLSTunnelServer ¶
type TLSTunnelServer struct {
// contains filtered or unexported fields
}
TLSTunnelServer tunnels TCP traffic (in the case of Psiphon, Obfuscated SSH traffic) over TLS.
func NewTLSTunnelServer ¶
func NewTLSTunnelServer( support *SupportServices, listener net.Listener, listenerTunnelProtocol string, listenerPort int) (*TLSTunnelServer, error)
NewTLSTunnelServer initializes a new TLSTunnelServer.
type TacticsListener ¶
TacticsListener wraps a net.Listener and applies server-side implementation of certain tactics parameters to accepted connections. Tactics filtering is limited to GeoIP attributes as the client has not yet sent API parameters. GeoIP uses the immediate peer IP, and so TacticsListener is suitable only for tactics that do not require the original client GeoIP when fronted.
func NewTacticsListener ¶
func NewTacticsListener( support *SupportServices, listener net.Listener, tunnelProtocol string, geoIPLookup func(IPaddress string) GeoIPData) *TacticsListener
NewTacticsListener creates a new TacticsListener.
type TraceLogger ¶
TraceLogger adds single frame stack trace information to the underlying logging facilities.
func (*TraceLogger) LogPanicRecover ¶
func (logger *TraceLogger) LogPanicRecover(recoverValue interface{}, stack []byte)
LogPanicRecover calls LogRawFieldsWithTimestamp with standard fields for logging recovered panics.
func (*TraceLogger) LogRawFieldsWithTimestamp ¶
func (logger *TraceLogger) LogRawFieldsWithTimestamp(fields LogFields)
LogRawFieldsWithTimestamp directly logs the supplied fields adding only an additional "timestamp" field; and "host_id", "provider", and "build_rev" fields identifying this server and build. The stock "msg" and "level" fields are omitted.
This log is emitted at the Error level. This function exists to support API logs which have neither a natural message nor severity; and omitting these values here makes it easier to ship these logs to existing API log consumers.
Note that any existing "trace"/"host_id"/"provider"/"build_rev" field will be renamed to "field.<name>".
func (*TraceLogger) WithTrace ¶
func (logger *TraceLogger) WithTrace() *logrus.Entry
WithTrace adds a "trace" field containing the caller's function name and source file line number; and "host_id", "provider", and "build_rev" fields identifying this server and build. Use this function when the log has no fields.
func (*TraceLogger) WithTraceFields ¶
func (logger *TraceLogger) WithTraceFields(fields LogFields) *logrus.Entry
WithTraceFields adds a "trace" field containing the caller's function name and source file line number; and "host_id", "provider", and "build_rev" fields identifying this server and build. Use this function when the log has fields.
Note that any existing "trace"/"host_id"/"provider"/build_rev" field will be renamed to "field.<name>".
type TrafficRules ¶
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 }
TrafficRules specify the limits placed on client traffic.
func (*TrafficRules) AllowTCPPort ¶
func (rules *TrafficRules) AllowTCPPort( geoIPService *GeoIPService, remoteIP net.IP, port int) bool
func (*TrafficRules) AllowUDPPort ¶
func (rules *TrafficRules) AllowUDPPort( geoIPService *GeoIPService, remoteIP net.IP, port int) bool
type TrafficRulesFilter ¶
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 // contains filtered or unexported fields }
TrafficRulesFilter defines a filter to match against client attributes.
type TrafficRulesSet ¶
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 }
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.
func NewTrafficRulesSet ¶
func NewTrafficRulesSet(filename string) (*TrafficRulesSet, error)
NewTrafficRulesSet initializes a TrafficRulesSet with the rules data in the specified config file.
func (*TrafficRulesSet) GetMeekRateLimiterConfig ¶
func (set *TrafficRulesSet) GetMeekRateLimiterConfig() ( int, int, []string, []string, []string, []string, []string, int, int, int)
GetMeekRateLimiterConfig gets a snapshot of the meek rate limiter configuration values.
func (*TrafficRulesSet) GetTrafficRules ¶
func (set *TrafficRulesSet) GetTrafficRules( isFirstTunnelInSession bool, tunnelProtocol string, geoIPData GeoIPData, state handshakeState) TrafficRules
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 (*TrafficRulesSet) Validate ¶
func (set *TrafficRulesSet) Validate() error
Validate checks for correct input formats in a TrafficRulesSet.
type TunnelServer ¶
type TunnelServer struct {
// contains filtered or unexported fields
}
TunnelServer is the main server that accepts Psiphon client connections, via various obfuscation protocols, and provides port forwarding (TCP and UDP) services to the Psiphon client. At its core, TunnelServer is an SSH server. SSH is the base protocol that provides port forward multiplexing, and transport security. Layered on top of SSH, optionally, is Obfuscated SSH and meek protocols, which provide further circumvention capabilities.
func NewTunnelServer ¶
func NewTunnelServer( support *SupportServices, shutdownBroadcast <-chan struct{}) (*TunnelServer, error)
NewTunnelServer initializes a new tunnel server.
func (*TunnelServer) AcceptClientDomainBytes ¶
func (server *TunnelServer) AcceptClientDomainBytes( sessionID string) (bool, error)
AcceptClientDomainBytes indicates whether to accept domain bytes reported by the client.
func (*TunnelServer) CheckEstablishTunnels ¶
func (server *TunnelServer) CheckEstablishTunnels() bool
CheckEstablishTunnels returns whether new tunnels may be established or not, and increments a metrics counter when establishment is disallowed.
func (*TunnelServer) GetClientDisableDiscovery ¶
func (server *TunnelServer) GetClientDisableDiscovery( sessionID string) (bool, error)
GetClientDisableDiscovery indicates whether discovery is disabled for the client corresponding to sessionID.
func (*TunnelServer) GetClientHandshaked ¶
func (server *TunnelServer) GetClientHandshaked( sessionID string) (bool, bool, error)
GetClientHandshaked indicates whether the client has completed a handshake and whether its traffic rules are immediately exhausted.
func (*TunnelServer) GetEstablishTunnelsMetrics ¶
func (server *TunnelServer) GetEstablishTunnelsMetrics() (bool, int64)
GetEstablishTunnelsMetrics returns whether tunnel establishment is currently allowed and the number of tunnels rejected since due to not establishing since the last GetEstablishTunnelsMetrics call.
func (*TunnelServer) GetEstablishedClientCount ¶
func (server *TunnelServer) GetEstablishedClientCount() int
GetEstablishedClientCount returns the number of currently established clients.
func (*TunnelServer) GetLoadStats ¶
func (server *TunnelServer) GetLoadStats() ( UpstreamStats, ProtocolStats, RegionStats)
GetLoadStats returns load stats for the tunnel server. The stats are broken down by protocol ("SSH", "OSSH", etc.) and type. Types of stats include current connected client count, total number of current port forwards.
func (*TunnelServer) ResetAllClientOSLConfigs ¶
func (server *TunnelServer) ResetAllClientOSLConfigs()
ResetAllClientOSLConfigs resets all established client OSL state to use the latest OSL config. Any existing OSL state is lost, including partial progress towards SLOKs.
func (*TunnelServer) ResetAllClientTrafficRules ¶
func (server *TunnelServer) ResetAllClientTrafficRules()
ResetAllClientTrafficRules resets all established client traffic rules to use the latest config and client properties. Any existing traffic rule state is lost, including throttling state.
func (*TunnelServer) Run ¶
func (server *TunnelServer) Run() error
Run runs the tunnel server; this function blocks while running a selection of listeners that handle connections using various obfuscation protocols.
Run listens on each designated tunnel port and spawns new goroutines to handle each client connection. It halts when shutdownBroadcast is signaled. A list of active clients is maintained, and when halting all clients are cleanly shutdown.
Each client goroutine handles its own obfuscation (optional), SSH handshake, SSH authentication, and then looping on client new channel requests. "direct-tcpip" channels, dynamic port fowards, are supported. When the UDPInterceptUdpgwServerAddress config parameter is configured, UDP port forwards over a TCP stream, following the udpgw protocol, are handled.
A new goroutine is spawned to handle each port forward for each client. Each port forward tracks its bytes transferred. Overall per-client stats for connection duration, GeoIP, number of port forwards, and bytes transferred are tracked and logged when the client shuts down.
Note: client handler goroutines may still be shutting down after Run() returns. See comment in sshClient.stop(). TODO: fully synchronized shutdown.
func (*TunnelServer) SetClientHandshakeState ¶
func (server *TunnelServer) SetClientHandshakeState( sessionID string, state handshakeState, authorizations []string) (*handshakeStateInfo, error)
SetClientHandshakeState sets the handshake state -- that it completed and what parameters were passed -- in sshClient. This state is used for allowing port forwards and for future traffic rule selection. SetClientHandshakeState also triggers an immediate traffic rule re-selection, as the rules selected upon tunnel establishment may no longer apply now that handshake values are set.
The authorizations received from the client handshake are verified and the resulting list of authorized access types are applied to the client's tunnel and traffic rules.
A list of active authorization IDs, authorized access types, and traffic rate limits are returned for responding to the client and logging.
func (*TunnelServer) SetEstablishTunnels ¶
func (server *TunnelServer) SetEstablishTunnels(establish bool)
SetEstablishTunnels sets whether new tunnels may be established or not. When not establishing, incoming connections are immediately closed.
func (*TunnelServer) UpdateClientAPIParameters ¶
func (server *TunnelServer) UpdateClientAPIParameters( sessionID string, apiParams common.APIParameters) error
UpdateClientAPIParameters updates the recorded handshake API parameters for the client corresponding to sessionID.
type UpstreamStats ¶
type UpstreamStats map[string]interface{}