github.com/psiphon-labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/config.go (about)

     1  /*
     2   * Copyright (c) 2016, Psiphon Inc.
     3   * All rights reserved.
     4   *
     5   * This program is free software: you can redistribute it and/or modify
     6   * it under the terms of the GNU General Public License as published by
     7   * the Free Software Foundation, either version 3 of the License, or
     8   * (at your option) any later version.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package server
    21  
    22  import (
    23  	"crypto/rand"
    24  	"crypto/rsa"
    25  	"crypto/x509"
    26  	"encoding/base64"
    27  	"encoding/hex"
    28  	"encoding/json"
    29  	"encoding/pem"
    30  	"fmt"
    31  	"net"
    32  	"os"
    33  	"strconv"
    34  	"strings"
    35  	"sync/atomic"
    36  	"time"
    37  
    38  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
    39  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/accesscontrol"
    40  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/crypto/ssh"
    41  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/errors"
    42  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/osl"
    43  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
    44  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics"
    45  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/values"
    46  	"golang.org/x/crypto/nacl/box"
    47  )
    48  
    49  const (
    50  	SERVER_CONFIG_FILENAME                              = "psiphond.config"
    51  	SERVER_TRAFFIC_RULES_CONFIG_FILENAME                = "psiphond-traffic-rules.config"
    52  	SERVER_OSL_CONFIG_FILENAME                          = "psiphond-osl.config"
    53  	SERVER_TACTICS_CONFIG_FILENAME                      = "psiphond-tactics.config"
    54  	SERVER_ENTRY_FILENAME                               = "server-entry.dat"
    55  	DEFAULT_SERVER_IP_ADDRESS                           = "127.0.0.1"
    56  	WEB_SERVER_SECRET_BYTE_LENGTH                       = 32
    57  	DISCOVERY_VALUE_KEY_BYTE_LENGTH                     = 32
    58  	SSH_USERNAME_SUFFIX_BYTE_LENGTH                     = 8
    59  	SSH_PASSWORD_BYTE_LENGTH                            = 32
    60  	SSH_RSA_HOST_KEY_BITS                               = 2048
    61  	SSH_OBFUSCATED_KEY_BYTE_LENGTH                      = 32
    62  	PEAK_UPSTREAM_FAILURE_RATE_MINIMUM_SAMPLE_SIZE      = 10
    63  	PERIODIC_GARBAGE_COLLECTION                         = 120 * time.Second
    64  	STOP_ESTABLISH_TUNNELS_ESTABLISHED_CLIENT_THRESHOLD = 20
    65  	DEFAULT_LOG_FILE_REOPEN_RETRIES                     = 25
    66  )
    67  
    68  // Config specifies the configuration and behavior of a Psiphon
    69  // server.
    70  type Config struct {
    71  
    72  	// LogLevel specifies the log level. Valid values are:
    73  	// panic, fatal, error, warn, info, debug
    74  	LogLevel string
    75  
    76  	// LogFilename specifies the path of the file to log
    77  	// to. When blank, logs are written to stderr.
    78  	LogFilename string
    79  
    80  	// LogFileReopenRetries specifies how many retries, each with a 1ms delay,
    81  	// will be attempted after reopening a rotated log file fails. Retries
    82  	// mitigate any race conditions between writes/reopens and file operations
    83  	// performed by external log managers, such as logrotate.
    84  	//
    85  	// When omitted, DEFAULT_LOG_FILE_REOPEN_RETRIES is used.
    86  	LogFileReopenRetries *int
    87  
    88  	// LogFileCreateMode specifies that the Psiphon server should create a new
    89  	// log file when one is not found, such as after rotation with logrotate
    90  	// configured with nocreate. The value is the os.FileMode value to use when
    91  	// creating the file.
    92  	//
    93  	// When omitted, the Psiphon server does not create log files.
    94  	LogFileCreateMode *int
    95  
    96  	// SkipPanickingLogWriter disables panicking when
    97  	// unable to write any logs.
    98  	SkipPanickingLogWriter bool
    99  
   100  	// DiscoveryValueHMACKey is the network-wide secret value
   101  	// used to determine a unique discovery strategy.
   102  	DiscoveryValueHMACKey string
   103  
   104  	// GeoIPDatabaseFilenames are paths of GeoIP2/GeoLite2
   105  	// MaxMind database files. When empty, no GeoIP lookups are
   106  	// performed. Each file is queried, in order, for the
   107  	// logged fields: country code, city, and ISP. Multiple
   108  	// file support accommodates the MaxMind distribution where
   109  	// ISP data in a separate file.
   110  	GeoIPDatabaseFilenames []string
   111  
   112  	// PsinetDatabaseFilename is the path of the file containing
   113  	// psinet.Database data.
   114  	PsinetDatabaseFilename string
   115  
   116  	// HostID is the ID of the server host; this is used for API
   117  	// event logging.
   118  	HostID string
   119  
   120  	// ServerIPAddress is the public IP address of the server.
   121  	ServerIPAddress string
   122  
   123  	// WebServerPort is the listening port of the web server.
   124  	// When <= 0, no web server component is run.
   125  	WebServerPort int
   126  
   127  	// WebServerSecret is the unique secret value that the client
   128  	// must supply to make requests to the web server.
   129  	WebServerSecret string
   130  
   131  	// WebServerCertificate is the certificate the client uses to
   132  	// authenticate the web server.
   133  	WebServerCertificate string
   134  
   135  	// WebServerPrivateKey is the private key the web server uses to
   136  	// authenticate itself to clients.
   137  	WebServerPrivateKey string
   138  
   139  	// WebServerPortForwardAddress specifies the expected network
   140  	// address ("<host>:<port>") specified in a client's port forward
   141  	// HostToConnect and PortToConnect when the client is making a
   142  	// tunneled connection to the web server. This address is always
   143  	// exempted from validation against SSH_DISALLOWED_PORT_FORWARD_HOSTS
   144  	// and AllowTCPPorts.
   145  	WebServerPortForwardAddress string
   146  
   147  	// WebServerPortForwardRedirectAddress specifies an alternate
   148  	// destination address to be substituted and dialed instead of
   149  	// the original destination when the port forward destination is
   150  	// WebServerPortForwardAddress.
   151  	WebServerPortForwardRedirectAddress string
   152  
   153  	// TunnelProtocolPorts specifies which tunnel protocols to run
   154  	// and which ports to listen on for each protocol. Valid tunnel
   155  	// protocols include:
   156  	// "SSH", "OSSH", "UNFRONTED-MEEK-OSSH", "UNFRONTED-MEEK-HTTPS-OSSH",
   157  	// "UNFRONTED-MEEK-SESSION-TICKET-OSSH", "FRONTED-MEEK-OSSH",
   158  	// "FRONTED-MEEK-QUIC-OSSH", "FRONTED-MEEK-HTTP-OSSH", "QUIC-OSSH",
   159  	// "TAPDANCE-OSSH", abd "CONJURE-OSSH".
   160  	TunnelProtocolPorts map[string]int
   161  
   162  	// TunnelProtocolPassthroughAddresses specifies passthrough addresses to be
   163  	// used for tunnel protocols configured in  TunnelProtocolPorts. Passthrough
   164  	// is a probing defense which relays all network traffic between a client and
   165  	// the passthrough target when the client fails anti-probing tests.
   166  	//
   167  	// TunnelProtocolPassthroughAddresses is supported for:
   168  	// "UNFRONTED-MEEK-HTTPS-OSSH", "UNFRONTED-MEEK-SESSION-TICKET-OSSH".
   169  	TunnelProtocolPassthroughAddresses map[string]string
   170  
   171  	// LegacyPassthrough indicates whether to expect legacy passthrough messages
   172  	// from clients attempting to connect. This should be set for existing/legacy
   173  	// passthrough servers only.
   174  	LegacyPassthrough bool
   175  
   176  	// EnableGQUIC indicates whether to enable legacy gQUIC QUIC-OSSH
   177  	// versions, for backwards compatibility with all versions used by older
   178  	// clients. Enabling gQUIC degrades the anti-probing stance of QUIC-OSSH,
   179  	// as the legacy gQUIC stack will respond to probing packets.
   180  	EnableGQUIC bool
   181  
   182  	// SSHPrivateKey is the SSH host key. The same key is used for
   183  	// all protocols, run by this server instance, which use SSH.
   184  	SSHPrivateKey string
   185  
   186  	// SSHServerVersion is the server version presented in the
   187  	// identification string. The same value is used for all
   188  	// protocols, run by this server instance, which use SSH.
   189  	SSHServerVersion string
   190  
   191  	// SSHUserName is the SSH user name to be presented by the
   192  	// the tunnel-core client. The same value is used for all
   193  	// protocols, run by this server instance, which use SSH.
   194  	SSHUserName string
   195  
   196  	// SSHPassword is the SSH password to be presented by the
   197  	// the tunnel-core client. The same value is used for all
   198  	// protocols, run by this server instance, which use SSH.
   199  	SSHPassword string
   200  
   201  	// SSHBeginHandshakeTimeoutMilliseconds specifies the timeout
   202  	// for clients queueing to begin an SSH handshake. The default
   203  	// is SSH_BEGIN_HANDSHAKE_TIMEOUT.
   204  	SSHBeginHandshakeTimeoutMilliseconds *int
   205  
   206  	// SSHHandshakeTimeoutMilliseconds specifies the timeout
   207  	// before which a client must complete its handshake. The default
   208  	// is SSH_HANDSHAKE_TIMEOUT.
   209  	SSHHandshakeTimeoutMilliseconds *int
   210  
   211  	// ObfuscatedSSHKey is the secret key for use in the Obfuscated
   212  	// SSH protocol. The same secret key is used for all protocols,
   213  	// run by this server instance, which use Obfuscated SSH.
   214  	ObfuscatedSSHKey string
   215  
   216  	// MeekCookieEncryptionPrivateKey is the NaCl private key used
   217  	// to decrypt meek cookie payload sent from clients. The same
   218  	// key is used for all meek protocols run by this server instance.
   219  	MeekCookieEncryptionPrivateKey string
   220  
   221  	// MeekObfuscatedKey is the secret key used for obfuscating
   222  	// meek cookies sent from clients. The same key is used for all
   223  	// meek protocols run by this server instance.
   224  	MeekObfuscatedKey string
   225  
   226  	// MeekProhibitedHeaders is a list of HTTP headers to check for
   227  	// in client requests. If one of these headers is found, the
   228  	// request fails. This is used to defend against abuse.
   229  	MeekProhibitedHeaders []string
   230  
   231  	// MeekProxyForwardedForHeaders is a list of HTTP headers which
   232  	// may be added by downstream HTTP proxies or CDNs in front
   233  	// of clients. These headers supply the original client IP
   234  	// address, which is geolocated for stats purposes. Headers
   235  	// include, for example, X-Forwarded-For. The header's value
   236  	// is assumed to be a comma delimted list of IP addresses where
   237  	// the client IP is the first IP address in the list. Meek protocols
   238  	// look for these headers and use the client IP address from
   239  	// the header if any one is present and the value is a valid
   240  	// IP address; otherwise the direct connection remote address is
   241  	// used as the client IP.
   242  	MeekProxyForwardedForHeaders []string
   243  
   244  	// MeekTurnAroundTimeoutMilliseconds specifies the amount of time meek will
   245  	// wait for downstream bytes before responding to a request. The default is
   246  	// MEEK_DEFAULT_TURN_AROUND_TIMEOUT.
   247  	MeekTurnAroundTimeoutMilliseconds *int
   248  
   249  	// MeekExtendedTurnAroundTimeoutMilliseconds specifies the extended amount of
   250  	// time meek will wait for downstream bytes, as long as bytes arrive every
   251  	// MeekTurnAroundTimeoutMilliseconds, before responding to a request. The
   252  	// default is MEEK_DEFAULT_EXTENDED_TURN_AROUND_TIMEOUT.
   253  	MeekExtendedTurnAroundTimeoutMilliseconds *int
   254  
   255  	// MeekSkipExtendedTurnAroundThresholdBytes specifies when to skip the
   256  	// extended turn around. When the number of bytes received in the client
   257  	// request meets the threshold, optimize for upstream flows with quicker
   258  	// round trip turn arounds.
   259  	MeekSkipExtendedTurnAroundThresholdBytes *int
   260  
   261  	// MeekMaxSessionStalenessMilliseconds specifies the TTL for meek sessions.
   262  	// The default is MEEK_DEFAULT_MAX_SESSION_STALENESS.
   263  	MeekMaxSessionStalenessMilliseconds *int
   264  
   265  	// MeekHTTPClientIOTimeoutMilliseconds specifies meek HTTP server I/O
   266  	// timeouts. The default is MEEK_DEFAULT_HTTP_CLIENT_IO_TIMEOUT.
   267  	MeekHTTPClientIOTimeoutMilliseconds *int
   268  
   269  	// MeekCachedResponseBufferSize is the size of a private,
   270  	// fixed-size buffer allocated for every meek client. The buffer
   271  	// is used to cache response payload, allowing the client to retry
   272  	// fetching when a network connection is interrupted. This retry
   273  	// makes the OSSH tunnel within meek resilient to interruptions
   274  	// at the HTTP TCP layer.
   275  	// Larger buffers increase resiliency to interruption, but consume
   276  	// more memory as buffers as never freed. The maximum size of a
   277  	// response payload is a function of client activity, network
   278  	// throughput and throttling.
   279  	// A default of 64K is used when MeekCachedResponseBufferSize is 0.
   280  	MeekCachedResponseBufferSize int
   281  
   282  	// MeekCachedResponsePoolBufferSize is the size of a fixed-size,
   283  	// shared buffer used to temporarily extend a private buffer when
   284  	// MeekCachedResponseBufferSize is insufficient. Shared buffers
   285  	// allow some clients to successfully retry longer response payloads
   286  	// without allocating large buffers for all clients.
   287  	// A default of 64K is used when MeekCachedResponsePoolBufferSize
   288  	// is 0.
   289  	MeekCachedResponsePoolBufferSize int
   290  
   291  	// MeekCachedResponsePoolBufferCount is the number of shared
   292  	// buffers. Shared buffers are allocated on first use and remain
   293  	// allocated, so shared buffer count * size is roughly the memory
   294  	// overhead of this facility.
   295  	// A default of 2048 is used when MeekCachedResponsePoolBufferCount
   296  	// is 0.
   297  	MeekCachedResponsePoolBufferCount int
   298  
   299  	// UDPInterceptUdpgwServerAddress specifies the network address of
   300  	// a udpgw server which clients may be port forwarding to. When
   301  	// specified, these TCP port forwards are intercepted and handled
   302  	// directly by this server, which parses the SSH channel using the
   303  	// udpgw protocol. Handling includes udpgw transparent DNS: tunneled
   304  	// UDP DNS packets are rerouted to the host's DNS server.
   305  	//
   306  	// The intercept is applied before the port forward destination is
   307  	// validated against SSH_DISALLOWED_PORT_FORWARD_HOSTS and
   308  	// AllowTCPPorts. So the intercept address may be any otherwise
   309  	// prohibited destination.
   310  	UDPInterceptUdpgwServerAddress string
   311  
   312  	// DNSResolverIPAddress specifies the IP address of a DNS server
   313  	// to be used when "/etc/resolv.conf" doesn't exist or fails to
   314  	// parse. When blank, "/etc/resolv.conf" must contain a usable
   315  	// "nameserver" entry.
   316  	DNSResolverIPAddress string
   317  
   318  	// LoadMonitorPeriodSeconds indicates how frequently to log server
   319  	// load information (number of connected clients per tunnel protocol,
   320  	// number of running goroutines, amount of memory allocated, etc.)
   321  	// The default, 0, disables load logging.
   322  	LoadMonitorPeriodSeconds int
   323  
   324  	// PeakUpstreamFailureRateMinimumSampleSize specifies the minimum number
   325  	// of samples (e.g., upstream port forward attempts) that are required
   326  	// before taking a failure rate snapshot which may be recorded as
   327  	// peak_dns_failure_rate/peak_tcp_port_forward_failure_rate.  The default
   328  	// is PEAK_UPSTREAM_FAILURE_RATE_SAMPLE_SIZE.
   329  	PeakUpstreamFailureRateMinimumSampleSize *int
   330  
   331  	// ProcessProfileOutputDirectory is the path of a directory to which
   332  	// process profiles will be written when signaled with SIGUSR2. The
   333  	// files are overwritten on each invocation. When set to the default
   334  	// value, blank, no profiles are written on SIGUSR2. Profiles include
   335  	// the default profiles here: https://golang.org/pkg/runtime/pprof/#Profile.
   336  	ProcessProfileOutputDirectory string
   337  
   338  	// ProcessBlockProfileDurationSeconds specifies the sample duration for
   339  	// "block" profiling. For the default, 0, no "block" profile is taken.
   340  	ProcessBlockProfileDurationSeconds int
   341  
   342  	// ProcessCPUProfileDurationSeconds specifies the sample duration for
   343  	// CPU profiling. For the default, 0, no CPU profile is taken.
   344  	ProcessCPUProfileDurationSeconds int
   345  
   346  	// TrafficRulesFilename is the path of a file containing a JSON-encoded
   347  	// TrafficRulesSet, the traffic rules to apply to Psiphon client tunnels.
   348  	TrafficRulesFilename string
   349  
   350  	// OSLConfigFilename is the path of a file containing a JSON-encoded
   351  	// OSL Config, the OSL schemes to apply to Psiphon client tunnels.
   352  	OSLConfigFilename string
   353  
   354  	// RunPacketTunnel specifies whether to run a packet tunnel.
   355  	RunPacketTunnel bool
   356  
   357  	// PacketTunnelEgressInterface specifies tun.ServerConfig.EgressInterface.
   358  	PacketTunnelEgressInterface string
   359  
   360  	// PacketTunnelEnableDNSFlowTracking sets
   361  	// tun.ServerConfig.EnableDNSFlowTracking.
   362  	PacketTunnelEnableDNSFlowTracking bool
   363  
   364  	// PacketTunnelDownstreamPacketQueueSize specifies
   365  	// tun.ServerConfig.DownStreamPacketQueueSize.
   366  	PacketTunnelDownstreamPacketQueueSize int
   367  
   368  	// PacketTunnelSessionIdleExpirySeconds specifies
   369  	// tun.ServerConfig.SessionIdleExpirySeconds.
   370  	PacketTunnelSessionIdleExpirySeconds int
   371  
   372  	// PacketTunnelSudoNetworkConfigCommands sets
   373  	// tun.ServerConfig.SudoNetworkConfigCommands.
   374  	PacketTunnelSudoNetworkConfigCommands bool
   375  
   376  	// RunPacketManipulator specifies whether to run a packet manipulator.
   377  	RunPacketManipulator bool
   378  
   379  	// MaxConcurrentSSHHandshakes specifies a limit on the number of concurrent
   380  	// SSH handshake negotiations. This is set to mitigate spikes in memory
   381  	// allocations and CPU usage associated with SSH handshakes when many clients
   382  	// attempt to connect concurrently. When a maximum limit is specified and
   383  	// reached, additional clients that establish TCP or meek connections will
   384  	// be disconnected after a short wait for the number of concurrent handshakes
   385  	// to drop below the limit.
   386  	// The default, 0 is no limit.
   387  	MaxConcurrentSSHHandshakes int
   388  
   389  	// PeriodicGarbageCollectionSeconds turns on periodic calls to
   390  	// debug.FreeOSMemory, every specified number of seconds, to force garbage
   391  	// collection and memory scavenging. Specify 0 to disable. The default is
   392  	// PERIODIC_GARBAGE_COLLECTION.
   393  	PeriodicGarbageCollectionSeconds *int
   394  
   395  	// StopEstablishTunnelsEstablishedClientThreshold sets the established client
   396  	// threshold for dumping profiles when SIGTSTP is signaled. When there are
   397  	// less than or equal to the threshold number of established clients,
   398  	// profiles are dumped to aid investigating unusual load limited states that
   399  	// occur when few clients are connected and load should be relatively low. A
   400  	// profile dump is attempted at most once per process lifetime, the first
   401  	// time the threshold is met. Disabled when < 0.
   402  	StopEstablishTunnelsEstablishedClientThreshold *int
   403  
   404  	// AccessControlVerificationKeyRing is the access control authorization
   405  	// verification key ring used to verify signed authorizations presented
   406  	// by clients. Verified, active (unexpired) access control types will be
   407  	// available for matching in the TrafficRulesFilter for the client via
   408  	// AuthorizedAccessTypes. All other authorizations are ignored.
   409  	AccessControlVerificationKeyRing accesscontrol.VerificationKeyRing
   410  
   411  	// TacticsConfigFilename is the path of a file containing a JSON-encoded
   412  	// tactics server configuration.
   413  	TacticsConfigFilename string
   414  
   415  	// BlocklistFilename is the path of a file containing a CSV-encoded
   416  	// blocklist configuration. See NewBlocklist for more file format
   417  	// documentation.
   418  	BlocklistFilename string
   419  
   420  	// BlocklistActive indicates whether to actively prevent blocklist hits in
   421  	// addition to logging events.
   422  	BlocklistActive bool
   423  
   424  	// AllowBogons disables port forward bogon checks. This should be used only
   425  	// for testing.
   426  	AllowBogons bool
   427  
   428  	// OwnEncodedServerEntries is a list of the server's own encoded server
   429  	// entries, idenfified by server entry tag. These values are used in the
   430  	// handshake API to update clients that don't yet have a signed copy of these
   431  	// server entries.
   432  	//
   433  	// For purposes of compartmentalization, each server receives only its own
   434  	// server entries here; and, besides the discovery server entries, in
   435  	// psinet.Database, necessary for the discovery feature, no other server
   436  	// entries are stored on a Psiphon server.
   437  	OwnEncodedServerEntries map[string]string
   438  
   439  	sshBeginHandshakeTimeout                       time.Duration
   440  	sshHandshakeTimeout                            time.Duration
   441  	peakUpstreamFailureRateMinimumSampleSize       int
   442  	periodicGarbageCollection                      time.Duration
   443  	stopEstablishTunnelsEstablishedClientThreshold int
   444  	dumpProfilesOnStopEstablishTunnelsDone         int32
   445  	frontingProviderID                             string
   446  	runningProtocols                               []string
   447  }
   448  
   449  // GetLogFileReopenConfig gets the reopen retries, and create/mode inputs for
   450  // rotate.NewRotatableFileWriter, which is used when writing to log files.
   451  //
   452  // By default, we expect the log files to be managed by logrotate, with
   453  // logrotate configured to re-create the next log file after rotation. As
   454  // described in the documentation for rotate.NewRotatableFileWriter, and as
   455  // observed in production, we occasionally need retries when attempting to
   456  // reopen the log file post-rotation; and we avoid conflicts, and spurious
   457  // re-rotations, by disabling file create in rotate.NewRotatableFileWriter. In
   458  // large scale production, incidents requiring retry are very rare, so the
   459  // retry delay is not expected to have a significant impact on performance.
   460  //
   461  // The defaults may be overriden in the Config.
   462  func (config *Config) GetLogFileReopenConfig() (int, bool, os.FileMode) {
   463  
   464  	retries := DEFAULT_LOG_FILE_REOPEN_RETRIES
   465  	if config.LogFileReopenRetries != nil {
   466  		retries = *config.LogFileReopenRetries
   467  	}
   468  	create := false
   469  	mode := os.FileMode(0)
   470  	if config.LogFileCreateMode != nil {
   471  		create = true
   472  		mode = os.FileMode(*config.LogFileCreateMode)
   473  	}
   474  	return retries, create, mode
   475  }
   476  
   477  // RunWebServer indicates whether to run a web server component.
   478  func (config *Config) RunWebServer() bool {
   479  	return config.WebServerPort > 0
   480  }
   481  
   482  // RunLoadMonitor indicates whether to monitor and log server load.
   483  func (config *Config) RunLoadMonitor() bool {
   484  	return config.LoadMonitorPeriodSeconds > 0
   485  }
   486  
   487  // RunPeriodicGarbageCollection indicates whether to run periodic garbage collection.
   488  func (config *Config) RunPeriodicGarbageCollection() bool {
   489  	return config.periodicGarbageCollection > 0
   490  }
   491  
   492  // DumpProfilesOnStopEstablishTunnels indicates whether dump profiles due to
   493  // an unexpectedly low number of established clients during high load.
   494  func (config *Config) DumpProfilesOnStopEstablishTunnels(establishedClientsCount int) bool {
   495  	if config.stopEstablishTunnelsEstablishedClientThreshold < 0 {
   496  		return false
   497  	}
   498  	if atomic.LoadInt32(&config.dumpProfilesOnStopEstablishTunnelsDone) != 0 {
   499  		return false
   500  	}
   501  	dump := (establishedClientsCount <= config.stopEstablishTunnelsEstablishedClientThreshold)
   502  	atomic.StoreInt32(&config.dumpProfilesOnStopEstablishTunnelsDone, 1)
   503  	return dump
   504  }
   505  
   506  // GetOwnEncodedServerEntry returns one of the server's own server entries, as
   507  // identified by the server entry tag.
   508  func (config *Config) GetOwnEncodedServerEntry(serverEntryTag string) (string, bool) {
   509  	serverEntry, ok := config.OwnEncodedServerEntries[serverEntryTag]
   510  	return serverEntry, ok
   511  }
   512  
   513  // GetFrontingProviderID returns the fronting provider ID associated with the
   514  // server's fronted protocol(s).
   515  func (config *Config) GetFrontingProviderID() string {
   516  	return config.frontingProviderID
   517  }
   518  
   519  // GetRunningProtocols returns the list of protcols this server is running.
   520  // The caller must not mutate the return value.
   521  func (config *Config) GetRunningProtocols() []string {
   522  	return config.runningProtocols
   523  }
   524  
   525  // LoadConfig loads and validates a JSON encoded server config.
   526  func LoadConfig(configJSON []byte) (*Config, error) {
   527  
   528  	var config Config
   529  	err := json.Unmarshal(configJSON, &config)
   530  	if err != nil {
   531  		return nil, errors.Trace(err)
   532  	}
   533  
   534  	if config.ServerIPAddress == "" {
   535  		return nil, errors.TraceNew("ServerIPAddress is required")
   536  	}
   537  
   538  	if config.WebServerPort > 0 && (config.WebServerSecret == "" || config.WebServerCertificate == "" ||
   539  		config.WebServerPrivateKey == "") {
   540  
   541  		return nil, errors.TraceNew(
   542  			"Web server requires WebServerSecret, WebServerCertificate, WebServerPrivateKey")
   543  	}
   544  
   545  	if config.WebServerPortForwardAddress != "" {
   546  		if err := validateNetworkAddress(config.WebServerPortForwardAddress, false); err != nil {
   547  			return nil, errors.TraceNew("WebServerPortForwardAddress is invalid")
   548  		}
   549  	}
   550  
   551  	if config.WebServerPortForwardRedirectAddress != "" {
   552  
   553  		if config.WebServerPortForwardAddress == "" {
   554  			return nil, errors.TraceNew(
   555  				"WebServerPortForwardRedirectAddress requires WebServerPortForwardAddress")
   556  		}
   557  
   558  		if err := validateNetworkAddress(config.WebServerPortForwardRedirectAddress, false); err != nil {
   559  			return nil, errors.TraceNew("WebServerPortForwardRedirectAddress is invalid")
   560  		}
   561  	}
   562  
   563  	for tunnelProtocol, _ := range config.TunnelProtocolPorts {
   564  		if !common.Contains(protocol.SupportedTunnelProtocols, tunnelProtocol) {
   565  			return nil, errors.Tracef("Unsupported tunnel protocol: %s", tunnelProtocol)
   566  		}
   567  		if protocol.TunnelProtocolUsesSSH(tunnelProtocol) ||
   568  			protocol.TunnelProtocolUsesObfuscatedSSH(tunnelProtocol) {
   569  			if config.SSHPrivateKey == "" || config.SSHServerVersion == "" ||
   570  				config.SSHUserName == "" || config.SSHPassword == "" {
   571  				return nil, errors.Tracef(
   572  					"Tunnel protocol %s requires SSHPrivateKey, SSHServerVersion, SSHUserName, SSHPassword",
   573  					tunnelProtocol)
   574  			}
   575  		}
   576  		if protocol.TunnelProtocolUsesObfuscatedSSH(tunnelProtocol) {
   577  			if config.ObfuscatedSSHKey == "" {
   578  				return nil, errors.Tracef(
   579  					"Tunnel protocol %s requires ObfuscatedSSHKey",
   580  					tunnelProtocol)
   581  			}
   582  		}
   583  		if protocol.TunnelProtocolUsesMeekHTTP(tunnelProtocol) ||
   584  			protocol.TunnelProtocolUsesMeekHTTPS(tunnelProtocol) {
   585  			if config.MeekCookieEncryptionPrivateKey == "" || config.MeekObfuscatedKey == "" {
   586  				return nil, errors.Tracef(
   587  					"Tunnel protocol %s requires MeekCookieEncryptionPrivateKey, MeekObfuscatedKey",
   588  					tunnelProtocol)
   589  			}
   590  		}
   591  	}
   592  
   593  	for tunnelProtocol, address := range config.TunnelProtocolPassthroughAddresses {
   594  		if !protocol.TunnelProtocolSupportsPassthrough(tunnelProtocol) {
   595  			return nil, errors.Tracef("Passthrough unsupported tunnel protocol: %s", tunnelProtocol)
   596  		}
   597  		if _, _, err := net.SplitHostPort(address); err != nil {
   598  			if err != nil {
   599  				return nil, errors.Tracef(
   600  					"Tunnel protocol %s passthrough address %s invalid: %s",
   601  					tunnelProtocol, address, err)
   602  			}
   603  		}
   604  	}
   605  
   606  	config.sshBeginHandshakeTimeout = SSH_BEGIN_HANDSHAKE_TIMEOUT
   607  	if config.SSHBeginHandshakeTimeoutMilliseconds != nil {
   608  		config.sshBeginHandshakeTimeout = time.Duration(*config.SSHBeginHandshakeTimeoutMilliseconds) * time.Millisecond
   609  	}
   610  
   611  	config.sshHandshakeTimeout = SSH_HANDSHAKE_TIMEOUT
   612  	if config.SSHHandshakeTimeoutMilliseconds != nil {
   613  		config.sshHandshakeTimeout = time.Duration(*config.SSHHandshakeTimeoutMilliseconds) * time.Millisecond
   614  	}
   615  
   616  	if config.ObfuscatedSSHKey != "" {
   617  		seed, err := protocol.DeriveSSHServerVersionPRNGSeed(config.ObfuscatedSSHKey)
   618  		if err != nil {
   619  			return nil, errors.Tracef(
   620  				"DeriveSSHServerVersionPRNGSeed failed: %s", err)
   621  		}
   622  
   623  		serverVersion := values.GetSSHServerVersion(seed)
   624  		if serverVersion != "" {
   625  			config.SSHServerVersion = serverVersion
   626  		}
   627  	}
   628  
   629  	if config.UDPInterceptUdpgwServerAddress != "" {
   630  		if err := validateNetworkAddress(config.UDPInterceptUdpgwServerAddress, true); err != nil {
   631  			return nil, errors.Tracef("UDPInterceptUdpgwServerAddress is invalid: %s", err)
   632  		}
   633  	}
   634  
   635  	if config.DNSResolverIPAddress != "" {
   636  		if net.ParseIP(config.DNSResolverIPAddress) == nil {
   637  			return nil, errors.Tracef("DNSResolverIPAddress is invalid")
   638  		}
   639  	}
   640  
   641  	config.peakUpstreamFailureRateMinimumSampleSize = PEAK_UPSTREAM_FAILURE_RATE_MINIMUM_SAMPLE_SIZE
   642  	if config.PeakUpstreamFailureRateMinimumSampleSize != nil {
   643  		config.peakUpstreamFailureRateMinimumSampleSize = *config.PeakUpstreamFailureRateMinimumSampleSize
   644  	}
   645  
   646  	config.periodicGarbageCollection = PERIODIC_GARBAGE_COLLECTION
   647  	if config.PeriodicGarbageCollectionSeconds != nil {
   648  		config.periodicGarbageCollection = time.Duration(*config.PeriodicGarbageCollectionSeconds) * time.Second
   649  	}
   650  
   651  	config.stopEstablishTunnelsEstablishedClientThreshold = STOP_ESTABLISH_TUNNELS_ESTABLISHED_CLIENT_THRESHOLD
   652  	if config.StopEstablishTunnelsEstablishedClientThreshold != nil {
   653  		config.stopEstablishTunnelsEstablishedClientThreshold = *config.StopEstablishTunnelsEstablishedClientThreshold
   654  	}
   655  
   656  	err = accesscontrol.ValidateVerificationKeyRing(&config.AccessControlVerificationKeyRing)
   657  	if err != nil {
   658  		return nil, errors.Tracef(
   659  			"AccessControlVerificationKeyRing is invalid: %s", err)
   660  	}
   661  
   662  	// Limitation: the following is a shortcut which extracts the server's
   663  	// fronting provider ID from the server's OwnEncodedServerEntries. This logic
   664  	// assumes a server has only one fronting provider. In principle, it's
   665  	// possible for server with multiple server entries to run multiple fronted
   666  	// protocols, each with a different fronting provider ID.
   667  	//
   668  	// TODO: add an explicit parameter mapping tunnel protocol ports to fronting
   669  	// provider IDs.
   670  
   671  	for _, encodedServerEntry := range config.OwnEncodedServerEntries {
   672  		serverEntry, err := protocol.DecodeServerEntry(encodedServerEntry, "", "")
   673  		if err != nil {
   674  			return nil, errors.Tracef(
   675  				"protocol.DecodeServerEntry failed: %s", err)
   676  		}
   677  		if config.frontingProviderID == "" {
   678  			config.frontingProviderID = serverEntry.FrontingProviderID
   679  		} else if config.frontingProviderID != serverEntry.FrontingProviderID {
   680  			return nil, errors.Tracef("unsupported multiple FrontingProviderID values")
   681  		}
   682  	}
   683  
   684  	config.runningProtocols = []string{}
   685  	for tunnelProtocol := range config.TunnelProtocolPorts {
   686  		config.runningProtocols = append(config.runningProtocols, tunnelProtocol)
   687  	}
   688  
   689  	return &config, nil
   690  }
   691  
   692  func validateNetworkAddress(address string, requireIPaddress bool) error {
   693  	host, portStr, err := net.SplitHostPort(address)
   694  	if err != nil {
   695  		return err
   696  	}
   697  	if requireIPaddress && net.ParseIP(host) == nil {
   698  		return errors.TraceNew("host must be an IP address")
   699  	}
   700  	port, err := strconv.Atoi(portStr)
   701  	if err != nil {
   702  		return err
   703  	}
   704  	if port < 0 || port > 65535 {
   705  		return errors.TraceNew("invalid port")
   706  	}
   707  	return nil
   708  }
   709  
   710  // GenerateConfigParams specifies customizations to be applied to
   711  // a generated server config.
   712  type GenerateConfigParams struct {
   713  	LogFilename                 string
   714  	SkipPanickingLogWriter      bool
   715  	LogLevel                    string
   716  	ServerIPAddress             string
   717  	WebServerPort               int
   718  	EnableSSHAPIRequests        bool
   719  	TunnelProtocolPorts         map[string]int
   720  	TrafficRulesConfigFilename  string
   721  	OSLConfigFilename           string
   722  	TacticsConfigFilename       string
   723  	TacticsRequestPublicKey     string
   724  	TacticsRequestObfuscatedKey string
   725  	Passthrough                 bool
   726  	LegacyPassthrough           bool
   727  	LimitQUICVersions           protocol.QUICVersions
   728  	EnableGQUIC                 bool
   729  }
   730  
   731  // GenerateConfig creates a new Psiphon server config. It returns JSON encoded
   732  // configs and a client-compatible "server entry" for the server. It generates
   733  // all necessary secrets and key material, which are emitted in the config
   734  // file and server entry as necessary.
   735  //
   736  // GenerateConfig uses sample values for many fields. The intention is for
   737  // generated configs to be used for testing or as examples for production
   738  // setup, not to generate production-ready configurations.
   739  //
   740  // When tactics key material is provided in GenerateConfigParams, tactics
   741  // capabilities are added for all meek protocols in TunnelProtocolPorts.
   742  func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, []byte, []byte, []byte, error) {
   743  
   744  	// Input validation
   745  
   746  	if net.ParseIP(params.ServerIPAddress) == nil {
   747  		return nil, nil, nil, nil, nil, errors.TraceNew("invalid IP address")
   748  	}
   749  
   750  	if len(params.TunnelProtocolPorts) == 0 {
   751  		return nil, nil, nil, nil, nil, errors.TraceNew("no tunnel protocols")
   752  	}
   753  
   754  	usedPort := make(map[int]bool)
   755  	if params.WebServerPort != 0 {
   756  		usedPort[params.WebServerPort] = true
   757  	}
   758  
   759  	usingMeek := false
   760  
   761  	for tunnelProtocol, port := range params.TunnelProtocolPorts {
   762  
   763  		if !common.Contains(protocol.SupportedTunnelProtocols, tunnelProtocol) {
   764  			return nil, nil, nil, nil, nil, errors.TraceNew("invalid tunnel protocol")
   765  		}
   766  
   767  		if usedPort[port] {
   768  			return nil, nil, nil, nil, nil, errors.TraceNew("duplicate listening port")
   769  		}
   770  		usedPort[port] = true
   771  
   772  		if protocol.TunnelProtocolUsesMeekHTTP(tunnelProtocol) ||
   773  			protocol.TunnelProtocolUsesMeekHTTPS(tunnelProtocol) {
   774  			usingMeek = true
   775  		}
   776  	}
   777  
   778  	// One test mode populates the tactics config file; this will generate
   779  	// keys. Another test mode passes in existing keys to be used in the
   780  	// server entry. Both the filename and existing keys cannot be passed in.
   781  	if (params.TacticsConfigFilename != "") &&
   782  		(params.TacticsRequestPublicKey != "" || params.TacticsRequestObfuscatedKey != "") {
   783  		return nil, nil, nil, nil, nil, errors.TraceNew("invalid tactics parameters")
   784  	}
   785  
   786  	// Web server config
   787  
   788  	var webServerSecret, webServerCertificate,
   789  		webServerPrivateKey, webServerPortForwardAddress string
   790  
   791  	if params.WebServerPort != 0 {
   792  		webServerSecretBytes, err := common.MakeSecureRandomBytes(WEB_SERVER_SECRET_BYTE_LENGTH)
   793  		if err != nil {
   794  			return nil, nil, nil, nil, nil, errors.Trace(err)
   795  		}
   796  		webServerSecret = hex.EncodeToString(webServerSecretBytes)
   797  
   798  		webServerCertificate, webServerPrivateKey, err = common.GenerateWebServerCertificate("")
   799  		if err != nil {
   800  			return nil, nil, nil, nil, nil, errors.Trace(err)
   801  		}
   802  
   803  		webServerPortForwardAddress = net.JoinHostPort(
   804  			params.ServerIPAddress, strconv.Itoa(params.WebServerPort))
   805  	}
   806  
   807  	// SSH config
   808  
   809  	rsaKey, err := rsa.GenerateKey(rand.Reader, SSH_RSA_HOST_KEY_BITS)
   810  	if err != nil {
   811  		return nil, nil, nil, nil, nil, errors.Trace(err)
   812  	}
   813  
   814  	sshPrivateKey := pem.EncodeToMemory(
   815  		&pem.Block{
   816  			Type:  "RSA PRIVATE KEY",
   817  			Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
   818  		},
   819  	)
   820  
   821  	signer, err := ssh.NewSignerFromKey(rsaKey)
   822  	if err != nil {
   823  		return nil, nil, nil, nil, nil, errors.Trace(err)
   824  	}
   825  
   826  	sshPublicKey := signer.PublicKey()
   827  
   828  	sshUserNameSuffixBytes, err := common.MakeSecureRandomBytes(SSH_USERNAME_SUFFIX_BYTE_LENGTH)
   829  	if err != nil {
   830  		return nil, nil, nil, nil, nil, errors.Trace(err)
   831  	}
   832  	sshUserNameSuffix := hex.EncodeToString(sshUserNameSuffixBytes)
   833  
   834  	sshUserName := "psiphon_" + sshUserNameSuffix
   835  
   836  	sshPasswordBytes, err := common.MakeSecureRandomBytes(SSH_PASSWORD_BYTE_LENGTH)
   837  	if err != nil {
   838  		return nil, nil, nil, nil, nil, errors.Trace(err)
   839  	}
   840  	sshPassword := hex.EncodeToString(sshPasswordBytes)
   841  
   842  	sshServerVersion := "SSH-2.0-Psiphon"
   843  
   844  	// Obfuscated SSH config
   845  
   846  	obfuscatedSSHKeyBytes, err := common.MakeSecureRandomBytes(SSH_OBFUSCATED_KEY_BYTE_LENGTH)
   847  	if err != nil {
   848  		return nil, nil, nil, nil, nil, errors.Trace(err)
   849  	}
   850  	obfuscatedSSHKey := hex.EncodeToString(obfuscatedSSHKeyBytes)
   851  
   852  	// Meek config
   853  
   854  	var meekCookieEncryptionPublicKey, meekCookieEncryptionPrivateKey, meekObfuscatedKey string
   855  
   856  	if usingMeek {
   857  		rawMeekCookieEncryptionPublicKey, rawMeekCookieEncryptionPrivateKey, err :=
   858  			box.GenerateKey(rand.Reader)
   859  		if err != nil {
   860  			return nil, nil, nil, nil, nil, errors.Trace(err)
   861  		}
   862  
   863  		meekCookieEncryptionPublicKey = base64.StdEncoding.EncodeToString(rawMeekCookieEncryptionPublicKey[:])
   864  		meekCookieEncryptionPrivateKey = base64.StdEncoding.EncodeToString(rawMeekCookieEncryptionPrivateKey[:])
   865  
   866  		meekObfuscatedKeyBytes, err := common.MakeSecureRandomBytes(SSH_OBFUSCATED_KEY_BYTE_LENGTH)
   867  		if err != nil {
   868  			return nil, nil, nil, nil, nil, errors.Trace(err)
   869  		}
   870  		meekObfuscatedKey = hex.EncodeToString(meekObfuscatedKeyBytes)
   871  	}
   872  
   873  	// Other config
   874  
   875  	discoveryValueHMACKeyBytes, err := common.MakeSecureRandomBytes(DISCOVERY_VALUE_KEY_BYTE_LENGTH)
   876  	if err != nil {
   877  		return nil, nil, nil, nil, nil, errors.Trace(err)
   878  	}
   879  	discoveryValueHMACKey := base64.StdEncoding.EncodeToString(discoveryValueHMACKeyBytes)
   880  
   881  	// Assemble configs and server entry
   882  
   883  	// Note: this config is intended for either testing or as an illustrative
   884  	// example or template and is not intended for production deployment.
   885  
   886  	logLevel := params.LogLevel
   887  	if logLevel == "" {
   888  		logLevel = "info"
   889  	}
   890  
   891  	// For testing, set the Psiphon server to create its log files; we do not
   892  	// expect tests to necessarily run under log managers, such as logrotate.
   893  	createMode := 0666
   894  
   895  	config := &Config{
   896  		LogLevel:                       logLevel,
   897  		LogFilename:                    params.LogFilename,
   898  		LogFileCreateMode:              &createMode,
   899  		SkipPanickingLogWriter:         params.SkipPanickingLogWriter,
   900  		GeoIPDatabaseFilenames:         nil,
   901  		HostID:                         "example-host-id",
   902  		ServerIPAddress:                params.ServerIPAddress,
   903  		DiscoveryValueHMACKey:          discoveryValueHMACKey,
   904  		WebServerPort:                  params.WebServerPort,
   905  		WebServerSecret:                webServerSecret,
   906  		WebServerCertificate:           webServerCertificate,
   907  		WebServerPrivateKey:            webServerPrivateKey,
   908  		WebServerPortForwardAddress:    webServerPortForwardAddress,
   909  		SSHPrivateKey:                  string(sshPrivateKey),
   910  		SSHServerVersion:               sshServerVersion,
   911  		SSHUserName:                    sshUserName,
   912  		SSHPassword:                    sshPassword,
   913  		ObfuscatedSSHKey:               obfuscatedSSHKey,
   914  		TunnelProtocolPorts:            params.TunnelProtocolPorts,
   915  		DNSResolverIPAddress:           "8.8.8.8",
   916  		UDPInterceptUdpgwServerAddress: "127.0.0.1:7300",
   917  		MeekCookieEncryptionPrivateKey: meekCookieEncryptionPrivateKey,
   918  		MeekObfuscatedKey:              meekObfuscatedKey,
   919  		MeekProhibitedHeaders:          nil,
   920  		MeekProxyForwardedForHeaders:   []string{"X-Forwarded-For"},
   921  		LoadMonitorPeriodSeconds:       300,
   922  		TrafficRulesFilename:           params.TrafficRulesConfigFilename,
   923  		OSLConfigFilename:              params.OSLConfigFilename,
   924  		TacticsConfigFilename:          params.TacticsConfigFilename,
   925  		LegacyPassthrough:              params.LegacyPassthrough,
   926  		EnableGQUIC:                    params.EnableGQUIC,
   927  	}
   928  
   929  	encodedConfig, err := json.MarshalIndent(config, "\n", "    ")
   930  	if err != nil {
   931  		return nil, nil, nil, nil, nil, errors.Trace(err)
   932  	}
   933  
   934  	intPtr := func(i int) *int {
   935  		return &i
   936  	}
   937  
   938  	trafficRulesSet := &TrafficRulesSet{
   939  		DefaultRules: TrafficRules{
   940  			RateLimits: RateLimits{
   941  				ReadUnthrottledBytes:  new(int64),
   942  				ReadBytesPerSecond:    new(int64),
   943  				WriteUnthrottledBytes: new(int64),
   944  				WriteBytesPerSecond:   new(int64),
   945  			},
   946  			IdleTCPPortForwardTimeoutMilliseconds: intPtr(DEFAULT_IDLE_TCP_PORT_FORWARD_TIMEOUT_MILLISECONDS),
   947  			IdleUDPPortForwardTimeoutMilliseconds: intPtr(DEFAULT_IDLE_UDP_PORT_FORWARD_TIMEOUT_MILLISECONDS),
   948  			MaxTCPPortForwardCount:                intPtr(DEFAULT_MAX_TCP_PORT_FORWARD_COUNT),
   949  			MaxUDPPortForwardCount:                intPtr(DEFAULT_MAX_UDP_PORT_FORWARD_COUNT),
   950  			AllowTCPPorts:                         nil,
   951  			AllowUDPPorts:                         nil,
   952  		},
   953  	}
   954  
   955  	encodedTrafficRulesSet, err := json.MarshalIndent(trafficRulesSet, "\n", "    ")
   956  	if err != nil {
   957  		return nil, nil, nil, nil, nil, errors.Trace(err)
   958  	}
   959  
   960  	encodedOSLConfig, err := json.MarshalIndent(&osl.Config{}, "\n", "    ")
   961  	if err != nil {
   962  		return nil, nil, nil, nil, nil, errors.Trace(err)
   963  	}
   964  
   965  	tacticsRequestPublicKey := params.TacticsRequestPublicKey
   966  	tacticsRequestObfuscatedKey := params.TacticsRequestObfuscatedKey
   967  	var tacticsRequestPrivateKey string
   968  	var encodedTacticsConfig []byte
   969  
   970  	if params.TacticsConfigFilename != "" {
   971  
   972  		tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, err =
   973  			tactics.GenerateKeys()
   974  		if err != nil {
   975  			return nil, nil, nil, nil, nil, errors.Trace(err)
   976  		}
   977  
   978  		decodedTacticsRequestPublicKey, err := base64.StdEncoding.DecodeString(tacticsRequestPublicKey)
   979  		if err != nil {
   980  			return nil, nil, nil, nil, nil, errors.Trace(err)
   981  		}
   982  
   983  		decodedTacticsRequestPrivateKey, err := base64.StdEncoding.DecodeString(tacticsRequestPrivateKey)
   984  		if err != nil {
   985  			return nil, nil, nil, nil, nil, errors.Trace(err)
   986  		}
   987  
   988  		decodedTacticsRequestObfuscatedKey, err := base64.StdEncoding.DecodeString(tacticsRequestObfuscatedKey)
   989  		if err != nil {
   990  			return nil, nil, nil, nil, nil, errors.Trace(err)
   991  		}
   992  
   993  		tacticsConfig := &tactics.Server{
   994  			RequestPublicKey:     decodedTacticsRequestPublicKey,
   995  			RequestPrivateKey:    decodedTacticsRequestPrivateKey,
   996  			RequestObfuscatedKey: decodedTacticsRequestObfuscatedKey,
   997  			DefaultTactics: tactics.Tactics{
   998  				TTL:         "1m",
   999  				Probability: 1.0,
  1000  			},
  1001  		}
  1002  
  1003  		encodedTacticsConfig, err = json.MarshalIndent(tacticsConfig, "\n", "    ")
  1004  		if err != nil {
  1005  			return nil, nil, nil, nil, nil, errors.Trace(err)
  1006  		}
  1007  	}
  1008  
  1009  	capabilities := []string{}
  1010  
  1011  	if params.EnableSSHAPIRequests {
  1012  		capabilities = append(capabilities, protocol.CAPABILITY_SSH_API_REQUESTS)
  1013  	}
  1014  
  1015  	if params.WebServerPort != 0 {
  1016  		capabilities = append(capabilities, protocol.CAPABILITY_UNTUNNELED_WEB_API_REQUESTS)
  1017  	}
  1018  
  1019  	for tunnelProtocol := range params.TunnelProtocolPorts {
  1020  
  1021  		capability := protocol.GetCapability(tunnelProtocol)
  1022  
  1023  		if params.Passthrough && protocol.TunnelProtocolSupportsPassthrough(tunnelProtocol) {
  1024  			if !params.LegacyPassthrough {
  1025  				capability += "-PASSTHROUGH-v2"
  1026  			} else {
  1027  				capability += "-PASSTHROUGH"
  1028  			}
  1029  		}
  1030  
  1031  		if tunnelProtocol == protocol.TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH && !params.EnableGQUIC {
  1032  			capability += "v1"
  1033  		}
  1034  
  1035  		capabilities = append(capabilities, capability)
  1036  
  1037  		if params.TacticsRequestPublicKey != "" && params.TacticsRequestObfuscatedKey != "" &&
  1038  			protocol.TunnelProtocolUsesMeek(tunnelProtocol) {
  1039  
  1040  			capabilities = append(capabilities, protocol.GetTacticsCapability(tunnelProtocol))
  1041  		}
  1042  	}
  1043  
  1044  	sshPort := params.TunnelProtocolPorts[protocol.TUNNEL_PROTOCOL_SSH]
  1045  	obfuscatedSSHPort := params.TunnelProtocolPorts[protocol.TUNNEL_PROTOCOL_OBFUSCATED_SSH]
  1046  	obfuscatedSSHQUICPort := params.TunnelProtocolPorts[protocol.TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH]
  1047  
  1048  	// Meek port limitations
  1049  	// - fronted meek protocols are hard-wired in the client to be port 443 or 80.
  1050  	// - only one other meek port may be specified.
  1051  	meekPort := params.TunnelProtocolPorts[protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK]
  1052  	if meekPort == 0 {
  1053  		meekPort = params.TunnelProtocolPorts[protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_HTTPS]
  1054  	}
  1055  	if meekPort == 0 {
  1056  		meekPort = params.TunnelProtocolPorts[protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET]
  1057  	}
  1058  
  1059  	// Note: fronting params are a stub; this server entry will exercise
  1060  	// client and server fronting code paths, but not actually traverse
  1061  	// a fronting hop.
  1062  
  1063  	serverEntryWebServerPort := ""
  1064  	strippedWebServerCertificate := ""
  1065  
  1066  	if params.WebServerPort != 0 {
  1067  		serverEntryWebServerPort = fmt.Sprintf("%d", params.WebServerPort)
  1068  
  1069  		// Server entry format omits the BEGIN/END lines and newlines
  1070  		lines := strings.Split(webServerCertificate, "\n")
  1071  		strippedWebServerCertificate = strings.Join(lines[1:len(lines)-2], "")
  1072  	}
  1073  
  1074  	serverEntry := &protocol.ServerEntry{
  1075  		IpAddress:                     params.ServerIPAddress,
  1076  		WebServerPort:                 serverEntryWebServerPort,
  1077  		WebServerSecret:               webServerSecret,
  1078  		WebServerCertificate:          strippedWebServerCertificate,
  1079  		SshPort:                       sshPort,
  1080  		SshUsername:                   sshUserName,
  1081  		SshPassword:                   sshPassword,
  1082  		SshHostKey:                    base64.RawStdEncoding.EncodeToString(sshPublicKey.Marshal()),
  1083  		SshObfuscatedPort:             obfuscatedSSHPort,
  1084  		SshObfuscatedQUICPort:         obfuscatedSSHQUICPort,
  1085  		LimitQUICVersions:             params.LimitQUICVersions,
  1086  		SshObfuscatedKey:              obfuscatedSSHKey,
  1087  		Capabilities:                  capabilities,
  1088  		Region:                        "US",
  1089  		MeekServerPort:                meekPort,
  1090  		MeekCookieEncryptionPublicKey: meekCookieEncryptionPublicKey,
  1091  		MeekObfuscatedKey:             meekObfuscatedKey,
  1092  		MeekFrontingHosts:             []string{params.ServerIPAddress},
  1093  		MeekFrontingAddresses:         []string{params.ServerIPAddress},
  1094  		MeekFrontingDisableSNI:        false,
  1095  		TacticsRequestPublicKey:       tacticsRequestPublicKey,
  1096  		TacticsRequestObfuscatedKey:   tacticsRequestObfuscatedKey,
  1097  		ConfigurationVersion:          1,
  1098  	}
  1099  
  1100  	encodedServerEntry, err := protocol.EncodeServerEntry(serverEntry)
  1101  	if err != nil {
  1102  		return nil, nil, nil, nil, nil, errors.Trace(err)
  1103  	}
  1104  
  1105  	return encodedConfig, encodedTrafficRulesSet, encodedOSLConfig, encodedTacticsConfig, []byte(encodedServerEntry), nil
  1106  }