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 }