config.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  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. package server
  20. import (
  21. "crypto/rand"
  22. "crypto/rsa"
  23. "crypto/x509"
  24. "encoding/base64"
  25. "encoding/json"
  26. "encoding/pem"
  27. "errors"
  28. "fmt"
  29. "math/big"
  30. "net"
  31. "strconv"
  32. "strings"
  33. "time"
  34. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
  35. "golang.org/x/crypto/ssh"
  36. )
  37. const (
  38. SERVER_CONFIG_FILENAME = "psiphon-server.config"
  39. SERVER_ENTRY_FILENAME = "serverEntry.dat"
  40. DEFAULT_LOG_LEVEL = "info"
  41. DEFAULT_SYSLOG_TAG = "psiphon-server"
  42. DEFAULT_GEO_IP_DATABASE_FILENAME = "GeoLite2-City.mmdb"
  43. DEFAULT_SERVER_IP_ADDRESS = "127.0.0.1"
  44. WEB_SERVER_SECRET_BYTE_LENGTH = 32
  45. WEB_SERVER_CERTIFICATE_RSA_KEY_BITS = 2048
  46. WEB_SERVER_CERTIFICATE_VALIDITY_PERIOD = 10 * 365 * 24 * time.Hour // approx. 10 years
  47. DEFAULT_WEB_SERVER_PORT = 8000
  48. WEB_SERVER_READ_TIMEOUT = 10 * time.Second
  49. WEB_SERVER_WRITE_TIMEOUT = 10 * time.Second
  50. SSH_USERNAME_SUFFIX_BYTE_LENGTH = 8
  51. SSH_PASSWORD_BYTE_LENGTH = 32
  52. SSH_RSA_HOST_KEY_BITS = 2048
  53. DEFAULT_SSH_SERVER_PORT = 2222
  54. SSH_HANDSHAKE_TIMEOUT = 30 * time.Second
  55. SSH_CONNECTION_READ_DEADLINE = 5 * time.Minute
  56. SSH_TCP_PORT_FORWARD_DIAL_TIMEOUT = 30 * time.Second
  57. SSH_OBFUSCATED_KEY_BYTE_LENGTH = 32
  58. DEFAULT_OBFUSCATED_SSH_SERVER_PORT = 3333
  59. REDIS_POOL_MAX_IDLE = 50
  60. REDIS_POOL_MAX_ACTIVE = 1000
  61. REDIS_POOL_IDLE_TIMEOUT = 5 * time.Minute
  62. )
  63. // TODO: break config into sections (sub-structs)
  64. // Config specifies the configuration and behavior of a Psiphon
  65. // server.
  66. type Config struct {
  67. // LogLevel specifies the log level. Valid values are:
  68. // panic, fatal, error, warn, info, debug
  69. LogLevel string
  70. // SyslogFacility specifies the syslog facility to log to.
  71. // When set, the local syslog service is used for message
  72. // logging.
  73. // Valid values include: "user", "local0", "local1", etc.
  74. SyslogFacility string
  75. // SyslogTag specifies an optional tag for syslog log
  76. // messages. The default tag is "psiphon-server". The
  77. // fail2ban logs, if enabled, also use this tag.
  78. SyslogTag string
  79. // Fail2BanFormat is a string format specifier for the
  80. // log message format to use for fail2ban integration for
  81. // blocking abusive clients by source IP address.
  82. // When set, logs with this format are made to the AUTH
  83. // facility with INFO severity in the local syslog server
  84. // if clients fail to authenticate.
  85. // The client's IP address is included with the log message.
  86. // An example format specifier, which should be compatible
  87. // with default SSH fail2ban configuration, is
  88. // "Authentication failure for psiphon-client from %s".
  89. Fail2BanFormat string
  90. // DiscoveryValueHMACKey is the network-wide secret value
  91. // used to determine a unique discovery strategy.
  92. DiscoveryValueHMACKey string
  93. // GeoIPDatabaseFilename is the path of the GeoIP2/GeoLite2
  94. // MaxMind database file. when blank, no GeoIP lookups are
  95. // performed.
  96. GeoIPDatabaseFilename string
  97. // ServerIPAddress is the public IP address of the server.
  98. ServerIPAddress string
  99. // WebServerPort is the listening port of the web server.
  100. // When <= 0, no web server component is run.
  101. WebServerPort int
  102. // WebServerSecret is the unique secret value that the client
  103. // must supply to make requests to the web server.
  104. WebServerSecret string
  105. // WebServerCertificate is the certificate the client uses to
  106. // authenticate the web server.
  107. WebServerCertificate string
  108. // WebServerPrivateKey is the private key the web server uses to
  109. // authenticate itself to clients.
  110. WebServerPrivateKey string
  111. // SSHServerPort is the listening port of the SSH server.
  112. // When <= 0, no SSH server component is run.
  113. SSHServerPort int
  114. // SSHPrivateKey is the SSH host key. The same key is used for
  115. // both the SSH and Obfuscated SSH servers.
  116. SSHPrivateKey string
  117. // SSHServerVersion is the server version presented in the
  118. // identification string. The same value is used for both SSH
  119. // and Obfuscated SSH servers.
  120. SSHServerVersion string
  121. // SSHUserName is the SSH user name to be presented by the
  122. // the tunnel-core client. The same value is used for both SSH
  123. // and Obfuscated SSH servers.
  124. SSHUserName string
  125. // SSHPassword is the SSH password to be presented by the
  126. // the tunnel-core client. The same value is used for both SSH
  127. // and Obfuscated SSH servers.
  128. SSHPassword string
  129. // ObfuscatedSSHServerPort is the listening port of the Obfuscated SSH server.
  130. // When <= 0, no Obfuscated SSH server component is run.
  131. ObfuscatedSSHServerPort int
  132. // ObfuscatedSSHKey is the secret key for use in the Obfuscated
  133. // SSH protocol.
  134. ObfuscatedSSHKey string
  135. // RedisServerAddress is the TCP address of a redis server. When
  136. // set, redis is used to store per-session GeoIP information.
  137. RedisServerAddress string
  138. // DefaultTrafficRules specifies the traffic rules to be used when
  139. // no regional-specific rules are set.
  140. DefaultTrafficRules TrafficRules
  141. // RegionalTrafficRules specifies the traffic rules for particular
  142. // client regions (countries) as determined by GeoIP lookup of the
  143. // client IP address. The key for each regional traffic rule entry
  144. // is one or more space delimited ISO 3166-1 alpha-2 country codes.
  145. RegionalTrafficRules map[string]TrafficRules
  146. // DNSServerAddress specifies the network address of a DNS server
  147. // to which DNS UDP packets will be forwarded to. When set, any
  148. // tunneled DNS UDP packets will be re-routed to this destination.
  149. DNSServerAddress string
  150. // UdpgwServerAddress specifies the network address of a udpgw
  151. // server which clients may be port forwarding to. When specified,
  152. // these TCP port forwards are intercepted and handled directly
  153. // by this server, which parses the SSH channel using the udpgw
  154. // protocol.
  155. UdpgwServerAddress string
  156. // LoadMonitorPeriodSeconds indicates how frequently to log server
  157. // load information (number of connected clients per tunnel protocol,
  158. // number of running goroutines, amount of memory allocated).
  159. // The default, 0, disables load logging.
  160. LoadMonitorPeriodSeconds int
  161. }
  162. // TrafficRules specify the limits placed on client traffic.
  163. type TrafficRules struct {
  164. // LimitDownstreamBytesPerSecond specifies a rate limit for
  165. // downstream data transfer between a single client and the
  166. // server.
  167. // The default, 0, is no rate limit.
  168. LimitDownstreamBytesPerSecond int
  169. // LimitUpstreamBytesPerSecond specifies a rate limit for
  170. // upstream data transfer between a single client and the
  171. // server.
  172. // The default, 0, is no rate limit.
  173. LimitUpstreamBytesPerSecond int
  174. // IdlePortForwardTimeoutMilliseconds is the timeout period
  175. // after which idle (no bytes flowing in either direction)
  176. // SSH client port forwards are preemptively closed.
  177. // The default, 0, is no idle timeout.
  178. IdlePortForwardTimeoutMilliseconds int
  179. // MaxTCPPortForwardCount is the maximum number of TCP port
  180. // forwards each client may have open concurrently.
  181. // The default, 0, is no maximum.
  182. MaxTCPPortForwardCount int
  183. // MaxUDPPortForwardCount is the maximum number of UDP port
  184. // forwards each client may have open concurrently.
  185. // The default, 0, is no maximum.
  186. MaxUDPPortForwardCount int
  187. // AllowTCPPorts specifies a whitelist of TCP ports that
  188. // are permitted for port forwarding. When set, only ports
  189. // in the list are accessible to clients.
  190. AllowTCPPorts []int
  191. // AllowUDPPorts specifies a whitelist of UDP ports that
  192. // are permitted for port forwarding. When set, only ports
  193. // in the list are accessible to clients.
  194. AllowUDPPorts []int
  195. // DenyTCPPorts specifies a blacklist of TCP ports that
  196. // are not permitted for port forwarding. When set, the
  197. // ports in the list are inaccessible to clients.
  198. DenyTCPPorts []int
  199. // DenyUDPPorts specifies a blacklist of UDP ports that
  200. // are not permitted for port forwarding. When set, the
  201. // ports in the list are inaccessible to clients.
  202. DenyUDPPorts []int
  203. }
  204. // RunWebServer indicates whether to run a web server component.
  205. func (config *Config) RunWebServer() bool {
  206. return config.WebServerPort > 0
  207. }
  208. // RunSSHServer indicates whether to run an SSH server component.
  209. func (config *Config) RunSSHServer() bool {
  210. return config.SSHServerPort > 0
  211. }
  212. // RunObfuscatedSSHServer indicates whether to run an Obfuscated SSH server component.
  213. func (config *Config) RunObfuscatedSSHServer() bool {
  214. return config.ObfuscatedSSHServerPort > 0
  215. }
  216. // RunLoadMonitor indicates whether to monitor and log server load.
  217. func (config *Config) RunLoadMonitor() bool {
  218. return config.LoadMonitorPeriodSeconds > 0
  219. }
  220. // UseRedis indicates whether to store per-session GeoIP information in
  221. // redis. This is for integration with the legacy psi_web component.
  222. func (config *Config) UseRedis() bool {
  223. return config.RedisServerAddress != ""
  224. }
  225. // UseFail2Ban indicates whether to log client IP addresses, in authentication
  226. // failure cases, to the local syslog service AUTH facility for use by fail2ban.
  227. func (config *Config) UseFail2Ban() bool {
  228. return config.Fail2BanFormat != ""
  229. }
  230. // GetTrafficRules looks up the traffic rules for the specified country. If there
  231. // are no RegionalTrafficRules for the country, DefaultTrafficRules are returned.
  232. func (config *Config) GetTrafficRules(targetCountryCode string) TrafficRules {
  233. // TODO: faster lookup?
  234. for countryCodes, trafficRules := range config.RegionalTrafficRules {
  235. for _, countryCode := range strings.Split(countryCodes, " ") {
  236. if countryCode == targetCountryCode {
  237. return trafficRules
  238. }
  239. }
  240. }
  241. return config.DefaultTrafficRules
  242. }
  243. // LoadConfig loads and validates a JSON encoded server config. If more than one
  244. // JSON config is specified, then all are loaded and values are merged together,
  245. // in order. Multiple configs allows for use cases like storing static, server-specific
  246. // values in a base config while also deploying network-wide throttling settings
  247. // in a secondary file that can be paved over on all server hosts.
  248. func LoadConfig(configJSONs [][]byte) (*Config, error) {
  249. // Note: default values are set in GenerateConfig
  250. var config Config
  251. for _, configJSON := range configJSONs {
  252. err := json.Unmarshal(configJSON, &config)
  253. if err != nil {
  254. return nil, psiphon.ContextError(err)
  255. }
  256. }
  257. if config.Fail2BanFormat != "" && strings.Count(config.Fail2BanFormat, "%s") != 1 {
  258. return nil, errors.New("Fail2BanFormat must have one '%%s' placeholder")
  259. }
  260. if config.ServerIPAddress == "" {
  261. return nil, errors.New("ServerIPAddress is missing from config file")
  262. }
  263. if config.WebServerPort > 0 && (config.WebServerSecret == "" || config.WebServerCertificate == "" ||
  264. config.WebServerPrivateKey == "") {
  265. return nil, errors.New(
  266. "Web server requires WebServerSecret, WebServerCertificate, WebServerPrivateKey")
  267. }
  268. if config.SSHServerPort > 0 && (config.SSHPrivateKey == "" || config.SSHServerVersion == "" ||
  269. config.SSHUserName == "" || config.SSHPassword == "") {
  270. return nil, errors.New(
  271. "SSH server requires SSHPrivateKey, SSHServerVersion, SSHUserName, SSHPassword")
  272. }
  273. if config.ObfuscatedSSHServerPort > 0 && (config.SSHPrivateKey == "" || config.SSHServerVersion == "" ||
  274. config.SSHUserName == "" || config.SSHPassword == "" || config.ObfuscatedSSHKey == "") {
  275. return nil, errors.New(
  276. "Obfuscated SSH server requires SSHPrivateKey, SSHServerVersion, SSHUserName, SSHPassword, ObfuscatedSSHKey")
  277. }
  278. validateNetworkAddress := func(address string) error {
  279. host, port, err := net.SplitHostPort(address)
  280. if err == nil && net.ParseIP(host) == nil {
  281. err = errors.New("Host must be an IP address")
  282. }
  283. if err == nil {
  284. _, err = strconv.Atoi(port)
  285. }
  286. return err
  287. }
  288. if config.DNSServerAddress != "" {
  289. if err := validateNetworkAddress(config.DNSServerAddress); err != nil {
  290. return nil, fmt.Errorf("DNSServerAddress is invalid: %s", err)
  291. }
  292. }
  293. if config.UdpgwServerAddress != "" {
  294. if err := validateNetworkAddress(config.UdpgwServerAddress); err != nil {
  295. return nil, fmt.Errorf("UdpgwServerAddress is invalid: %s", err)
  296. }
  297. }
  298. return &config, nil
  299. }
  300. // GenerateConfigParams specifies customizations to be applied to
  301. // a generated server config.
  302. type GenerateConfigParams struct {
  303. // ServerIPAddress is the public IP address of the server.
  304. ServerIPAddress string
  305. // ServerNetworkInterface specifies a network interface to
  306. // use to determine the ServerIPAddress automatically. When
  307. // set, ServerIPAddress is ignored.
  308. ServerNetworkInterface string
  309. // WebServerPort is the listening port of the web server.
  310. // When <= 0, no web server component is run.
  311. WebServerPort int
  312. // SSHServerPort is the listening port of the SSH server.
  313. // When <= 0, no SSH server component is run.
  314. SSHServerPort int
  315. // ObfuscatedSSHServerPort is the listening port of the Obfuscated SSH server.
  316. // When <= 0, no Obfuscated SSH server component is run.
  317. ObfuscatedSSHServerPort int
  318. }
  319. // GenerateConfig create a new Psiphon server config. It returns a JSON
  320. // encoded config and a client-compatible "server entry" for the server. It
  321. // generates all necessary secrets and key material, which are emitted in
  322. // the config file and server entry as necessary.
  323. func GenerateConfig(params *GenerateConfigParams) ([]byte, []byte, error) {
  324. serverIPaddress := params.ServerIPAddress
  325. if serverIPaddress == "" {
  326. serverIPaddress = DEFAULT_SERVER_IP_ADDRESS
  327. }
  328. if params.ServerNetworkInterface != "" {
  329. var err error
  330. serverIPaddress, err = psiphon.GetInterfaceIPAddress(params.ServerNetworkInterface)
  331. if err != nil {
  332. return nil, nil, psiphon.ContextError(err)
  333. }
  334. }
  335. // Web server config
  336. webServerPort := params.WebServerPort
  337. if webServerPort == 0 {
  338. webServerPort = DEFAULT_WEB_SERVER_PORT
  339. }
  340. webServerSecret, err := psiphon.MakeRandomString(WEB_SERVER_SECRET_BYTE_LENGTH)
  341. if err != nil {
  342. return nil, nil, psiphon.ContextError(err)
  343. }
  344. webServerCertificate, webServerPrivateKey, err := generateWebServerCertificate()
  345. if err != nil {
  346. return nil, nil, psiphon.ContextError(err)
  347. }
  348. // SSH config
  349. sshServerPort := params.SSHServerPort
  350. if sshServerPort == 0 {
  351. sshServerPort = DEFAULT_SSH_SERVER_PORT
  352. }
  353. // TODO: use other key types: anti-fingerprint by varying params
  354. rsaKey, err := rsa.GenerateKey(rand.Reader, SSH_RSA_HOST_KEY_BITS)
  355. if err != nil {
  356. return nil, nil, psiphon.ContextError(err)
  357. }
  358. sshPrivateKey := pem.EncodeToMemory(
  359. &pem.Block{
  360. Type: "RSA PRIVATE KEY",
  361. Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
  362. },
  363. )
  364. signer, err := ssh.NewSignerFromKey(rsaKey)
  365. if err != nil {
  366. return nil, nil, psiphon.ContextError(err)
  367. }
  368. sshPublicKey := signer.PublicKey()
  369. sshUserNameSuffix, err := psiphon.MakeRandomString(SSH_USERNAME_SUFFIX_BYTE_LENGTH)
  370. if err != nil {
  371. return nil, nil, psiphon.ContextError(err)
  372. }
  373. sshUserName := "psiphon_" + sshUserNameSuffix
  374. sshPassword, err := psiphon.MakeRandomString(SSH_PASSWORD_BYTE_LENGTH)
  375. if err != nil {
  376. return nil, nil, psiphon.ContextError(err)
  377. }
  378. // TODO: vary version string for anti-fingerprint
  379. sshServerVersion := "SSH-2.0-Psiphon"
  380. // Obfuscated SSH config
  381. obfuscatedSSHServerPort := params.ObfuscatedSSHServerPort
  382. if obfuscatedSSHServerPort == 0 {
  383. obfuscatedSSHServerPort = DEFAULT_OBFUSCATED_SSH_SERVER_PORT
  384. }
  385. obfuscatedSSHKey, err := psiphon.MakeRandomString(SSH_OBFUSCATED_KEY_BYTE_LENGTH)
  386. if err != nil {
  387. return nil, nil, psiphon.ContextError(err)
  388. }
  389. // Assemble config and server entry
  390. config := &Config{
  391. LogLevel: DEFAULT_LOG_LEVEL,
  392. SyslogFacility: "",
  393. SyslogTag: DEFAULT_SYSLOG_TAG,
  394. Fail2BanFormat: "",
  395. DiscoveryValueHMACKey: "",
  396. GeoIPDatabaseFilename: DEFAULT_GEO_IP_DATABASE_FILENAME,
  397. ServerIPAddress: serverIPaddress,
  398. WebServerPort: webServerPort,
  399. WebServerSecret: webServerSecret,
  400. WebServerCertificate: webServerCertificate,
  401. WebServerPrivateKey: webServerPrivateKey,
  402. SSHPrivateKey: string(sshPrivateKey),
  403. SSHServerVersion: sshServerVersion,
  404. SSHUserName: sshUserName,
  405. SSHPassword: sshPassword,
  406. SSHServerPort: sshServerPort,
  407. ObfuscatedSSHKey: obfuscatedSSHKey,
  408. ObfuscatedSSHServerPort: obfuscatedSSHServerPort,
  409. RedisServerAddress: "",
  410. }
  411. encodedConfig, err := json.MarshalIndent(config, "\n", " ")
  412. if err != nil {
  413. return nil, nil, psiphon.ContextError(err)
  414. }
  415. // Server entry format omits the BEGIN/END lines and newlines
  416. lines := strings.Split(webServerCertificate, "\n")
  417. strippedWebServerCertificate := strings.Join(lines[1:len(lines)-2], "")
  418. capabilities := []string{
  419. psiphon.GetCapability(psiphon.TUNNEL_PROTOCOL_SSH),
  420. psiphon.GetCapability(psiphon.TUNNEL_PROTOCOL_OBFUSCATED_SSH),
  421. }
  422. serverEntry := &psiphon.ServerEntry{
  423. IpAddress: serverIPaddress,
  424. WebServerPort: fmt.Sprintf("%d", webServerPort),
  425. WebServerSecret: webServerSecret,
  426. WebServerCertificate: strippedWebServerCertificate,
  427. SshPort: sshServerPort,
  428. SshUsername: sshUserName,
  429. SshPassword: sshPassword,
  430. SshHostKey: base64.RawStdEncoding.EncodeToString(sshPublicKey.Marshal()),
  431. SshObfuscatedPort: obfuscatedSSHServerPort,
  432. SshObfuscatedKey: obfuscatedSSHKey,
  433. Capabilities: capabilities,
  434. Region: "US",
  435. }
  436. encodedServerEntry, err := psiphon.EncodeServerEntry(serverEntry)
  437. if err != nil {
  438. return nil, nil, psiphon.ContextError(err)
  439. }
  440. return encodedConfig, []byte(encodedServerEntry), nil
  441. }
  442. func generateWebServerCertificate() (string, string, error) {
  443. // Based on https://golang.org/src/crypto/tls/generate_cert.go
  444. // TODO: use other key types: anti-fingerprint by varying params
  445. rsaKey, err := rsa.GenerateKey(rand.Reader, WEB_SERVER_CERTIFICATE_RSA_KEY_BITS)
  446. if err != nil {
  447. return "", "", psiphon.ContextError(err)
  448. }
  449. notBefore := time.Now()
  450. notAfter := notBefore.Add(WEB_SERVER_CERTIFICATE_VALIDITY_PERIOD)
  451. // TODO: psi_ops_install sets serial number to 0?
  452. // TODO: psi_ops_install sets RSA exponent to 3, digest type to 'sha1', and version to 2?
  453. serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
  454. serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
  455. if err != nil {
  456. return "", "", psiphon.ContextError(err)
  457. }
  458. template := x509.Certificate{
  459. // TODO: psi_ops_install leaves subject blank?
  460. /*
  461. Subject: pkix.Name{
  462. Organization: []string{""},
  463. },
  464. IPAddresses: ...
  465. */
  466. SerialNumber: serialNumber,
  467. NotBefore: notBefore,
  468. NotAfter: notAfter,
  469. KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
  470. ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
  471. BasicConstraintsValid: true,
  472. IsCA: true,
  473. }
  474. derCert, err := x509.CreateCertificate(rand.Reader, &template, &template, rsaKey.Public(), rsaKey)
  475. if err != nil {
  476. return "", "", psiphon.ContextError(err)
  477. }
  478. webServerCertificate := pem.EncodeToMemory(
  479. &pem.Block{
  480. Type: "CERTIFICATE",
  481. Bytes: derCert,
  482. },
  483. )
  484. webServerPrivateKey := pem.EncodeToMemory(
  485. &pem.Block{
  486. Type: "RSA PRIVATE KEY",
  487. Bytes: x509.MarshalPKCS1PrivateKey(rsaKey),
  488. },
  489. )
  490. return string(webServerCertificate), string(webServerPrivateKey), nil
  491. }