Release 2017.11.1

This commit is contained in:
Chris Branch 2017-11-07 15:17:19 +00:00
parent 82cb539fbe
commit ff67bd23f2
10 changed files with 537 additions and 223 deletions

View File

@ -4,7 +4,27 @@ import (
"crypto/x509" "crypto/x509"
) )
const cloudflareRootCA = `-----BEGIN CERTIFICATE----- // TODO: remove the Origin CA root certs when migrated to Authenticated Origin Pull certs
const cloudflareRootCA = `
Issuer: C=US, ST=California, L=San Francisco, O=CloudFlare, Inc., OU=CloudFlare Origin SSL ECC Certificate Authority
-----BEGIN CERTIFICATE-----
MIICiDCCAi6gAwIBAgIUXZP3MWb8MKwBE1Qbawsp1sfA/Y4wCgYIKoZIzj0EAwIw
gY8xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQHEw1T
YW4gRnJhbmNpc2NvMRkwFwYDVQQKExBDbG91ZEZsYXJlLCBJbmMuMTgwNgYDVQQL
Ey9DbG91ZEZsYXJlIE9yaWdpbiBTU0wgRUNDIENlcnRpZmljYXRlIEF1dGhvcml0
eTAeFw0xNjAyMjIxODI0MDBaFw0yMTAyMjIwMDI0MDBaMIGPMQswCQYDVQQGEwJV
UzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZyYW5jaXNjbzEZ
MBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjE4MDYGA1UECxMvQ2xvdWRGbGFyZSBP
cmlnaW4gU1NMIEVDQyBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwWTATBgcqhkjOPQIB
BggqhkjOPQMBBwNCAASR+sGALuaGshnUbcxKry+0LEXZ4NY6JUAtSeA6g87K3jaA
xpIg9G50PokpfWkhbarLfpcZu0UAoYy2su0EhN7wo2YwZDAOBgNVHQ8BAf8EBAMC
AQYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4EFgQUhTBdOypw1O3VkmcH/es5
tBoOOKcwHwYDVR0jBBgwFoAUhTBdOypw1O3VkmcH/es5tBoOOKcwCgYIKoZIzj0E
AwIDSAAwRQIgEiIEHQr5UKma50D1WRMJBUSgjg24U8n8E2mfw/8UPz0CIQCr5V/e
mcifak4CQsr+DH4pn5SJD7JxtCG3YGswW8QZsw==
-----END CERTIFICATE-----
Issuer: C=US, O=CloudFlare, Inc., OU=CloudFlare Origin SSL Certificate Authority, L=San Francisco, ST=California
-----BEGIN CERTIFICATE-----
MIID/DCCAuagAwIBAgIID+rOSdTGfGcwCwYJKoZIhvcNAQELMIGLMQswCQYDVQQG MIID/DCCAuagAwIBAgIID+rOSdTGfGcwCwYJKoZIhvcNAQELMIGLMQswCQYDVQQG
EwJVUzEZMBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjE0MDIGA1UECxMrQ2xvdWRG EwJVUzEZMBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjE0MDIGA1UECxMrQ2xvdWRG
bGFyZSBPcmlnaW4gU1NMIENlcnRpZmljYXRlIEF1dGhvcml0eTEWMBQGA1UEBxMN bGFyZSBPcmlnaW4gU1NMIENlcnRpZmljYXRlIEF1dGhvcml0eTEWMBQGA1UEBxMN
@ -27,6 +47,42 @@ QGgDl6gRmb8aDwk7Q92BPvek5nMzaWlP82ixavvYI+okoSY8pwdcVKobx6rWzMWz
ZEC9M6H3F0dDYE23XcCFIdgNSAmmGyXPBstOe0aAJXwJTxOEPn36VWr0PKIQJy5Y ZEC9M6H3F0dDYE23XcCFIdgNSAmmGyXPBstOe0aAJXwJTxOEPn36VWr0PKIQJy5Y
4o1wpMpqCOIwWc8J9REV/REzN6Z1LXImdUgXIXOwrz56gKUJzPejtBQyIGj0mveX 4o1wpMpqCOIwWc8J9REV/REzN6Z1LXImdUgXIXOwrz56gKUJzPejtBQyIGj0mveX
Fu6q54beR89jDc+oABmOgg== Fu6q54beR89jDc+oABmOgg==
-----END CERTIFICATE-----
Issuer: C=US, O=CloudFlare, Inc., OU=Origin Pull, L=San Francisco, ST=California, CN=origin-pull.cloudflare.net
-----BEGIN CERTIFICATE-----
MIIGBjCCA/CgAwIBAgIIV5G6lVbCLmEwCwYJKoZIhvcNAQENMIGQMQswCQYDVQQG
EwJVUzEZMBcGA1UEChMQQ2xvdWRGbGFyZSwgSW5jLjEUMBIGA1UECxMLT3JpZ2lu
IFB1bGwxFjAUBgNVBAcTDVNhbiBGcmFuY2lzY28xEzARBgNVBAgTCkNhbGlmb3Ju
aWExIzAhBgNVBAMTGm9yaWdpbi1wdWxsLmNsb3VkZmxhcmUubmV0MB4XDTE1MDEx
MzAyNDc1M1oXDTIwMDExMjAyNTI1M1owgZAxCzAJBgNVBAYTAlVTMRkwFwYDVQQK
ExBDbG91ZEZsYXJlLCBJbmMuMRQwEgYDVQQLEwtPcmlnaW4gUHVsbDEWMBQGA1UE
BxMNU2FuIEZyYW5jaXNjbzETMBEGA1UECBMKQ2FsaWZvcm5pYTEjMCEGA1UEAxMa
b3JpZ2luLXB1bGwuY2xvdWRmbGFyZS5uZXQwggIiMA0GCSqGSIb3DQEBAQUAA4IC
DwAwggIKAoICAQDdsts6I2H5dGyn4adACQRXlfo0KmwsN7B5rxD8C5qgy6spyONr
WV0ecvdeGQfWa8Gy/yuTuOnsXfy7oyZ1dm93c3Mea7YkM7KNMc5Y6m520E9tHooc
f1qxeDpGSsnWc7HWibFgD7qZQx+T+yfNqt63vPI0HYBOYao6hWd3JQhu5caAcIS2
ms5tzSSZVH83ZPe6Lkb5xRgLl3eXEFcfI2DjnlOtLFqpjHuEB3Tr6agfdWyaGEEi
lRY1IB3k6TfLTaSiX2/SyJ96bp92wvTSjR7USjDV9ypf7AD6u6vwJZ3bwNisNw5L
ptph0FBnc1R6nDoHmvQRoyytoe0rl/d801i9Nru/fXa+l5K2nf1koR3IX440Z2i9
+Z4iVA69NmCbT4MVjm7K3zlOtwfI7i1KYVv+ATo4ycgBuZfY9f/2lBhIv7BHuZal
b9D+/EK8aMUfjDF4icEGm+RQfExv2nOpkR4BfQppF/dLmkYfjgtO1403X0ihkT6T
PYQdmYS6Jf53/KpqC3aA+R7zg2birtvprinlR14MNvwOsDOzsK4p8WYsgZOR4Qr2
gAx+z2aVOs/87+TVOR0r14irQsxbg7uP2X4t+EXx13glHxwG+CnzUVycDLMVGvuG
aUgF9hukZxlOZnrl6VOf1fg0Caf3uvV8smOkVw6DMsGhBZSJVwao0UQNqQIDAQAB
o2YwZDAOBgNVHQ8BAf8EBAMCAAYwEgYDVR0TAQH/BAgwBgEB/wIBAjAdBgNVHQ4E
FgQUQ1lLK2mLgOERM2pXzVc42p59xeswHwYDVR0jBBgwFoAUQ1lLK2mLgOERM2pX
zVc42p59xeswCwYJKoZIhvcNAQENA4ICAQDKDQM1qPRVP/4Gltz0D6OU6xezFBKr
LWtDoA1qW2F7pkiYawCP9MrDPDJsHy7dx+xw3bBZxOsK5PA/T7p1dqpEl6i8F692
g//EuYOifLYw3ySPe3LRNhvPl/1f6Sn862VhPvLa8aQAAwR9e/CZvlY3fj+6G5ik
3it7fikmKUsVnugNOkjmwI3hZqXfJNc7AtHDFw0mEOV0dSeAPTo95N9cxBbm9PKv
qAEmTEXp2trQ/RjJ/AomJyfA1BQjsD0j++DI3a9/BbDwWmr1lJciKxiNKaa0BRLB
dKMrYQD+PkPNCgEuojT+paLKRrMyFUzHSG1doYm46NE9/WARTh3sFUp1B7HZSBqA
kHleoB/vQ/mDuW9C3/8Jk2uRUdZxR+LoNZItuOjU8oTy6zpN1+GgSj7bHjiy9rfA
F+ehdrz+IOh80WIiqs763PGoaYUyzxLvVowLWNoxVVoc9G+PqFKqD988XlipHVB6
Bz+1CD4D/bWrs3cC9+kk/jFmrrAymZlkFX8tDb5aXASSLJjUjcptci9SKqtI2h0J
wUGkD7+bQAr+7vr8/R+CBmNMe7csE8NeEX6lVMF7Dh0a1YKQa6hUN18bBuYgTMuT
QzMmZpRpIBB321ZBlcnlxiTJvWxvbCPHKHj20VwwAz7LONF59s84ZsOqfoBv8gKM
s0s5dsq5zpLeaw==
-----END CERTIFICATE-----` -----END CERTIFICATE-----`
func GetCloudflareRootCA() *x509.CertPool { func GetCloudflareRootCA() *x509.CertPool {

View File

@ -4,23 +4,21 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"html/template" "html/template"
"io/ioutil"
"net" "net"
"net/http" "net/http"
"os" "os"
"strings"
"github.com/pkg/errors" "github.com/pkg/errors"
log "github.com/Sirupsen/logrus" log "github.com/Sirupsen/logrus"
"github.com/cloudflare/cloudflare-warp/origin"
tunnelpogs "github.com/cloudflare/cloudflare-warp/tunnelrpc/pogs"
cli "gopkg.in/urfave/cli.v2" cli "gopkg.in/urfave/cli.v2"
) )
type templateData struct { type templateData struct {
ServerName string ServerName string
Request *http.Request Request *http.Request
Tags []tunnelpogs.Tag Body string
} }
const defaultServerName = "the Cloudflare Warp test server" const defaultServerName = "the Cloudflare Warp test server"
@ -42,7 +40,7 @@ const indexTemplate = `
</style> </style>
</head> </head>
<body class="sans-serif black"> <body class="sans-serif black">
<div class="bt bw2 b--orange bg-white pb6"> <div class="bt bw2 b--orange bg-white pb6">
<div class="mw7 center ph4 pt3"> <div class="mw7 center ph4 pt3">
<svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 109 40.5" class="mw4"> <svg id="Layer_2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 109 40.5" class="mw4">
<path class="st0" d="M98.6 14.2L93 12.9l-1-.4-25.7.2v12.4l32.3.1z"/> <path class="st0" d="M98.6 14.2L93 12.9l-1-.4-25.7.2v12.4l32.3.1z"/>
@ -56,21 +54,30 @@ const indexTemplate = `
running an encrypted, virtual tunnel from your laptop or server to running an encrypted, virtual tunnel from your laptop or server to
Cloudflare's edge network. Cloudflare's edge network.
</p> </p>
<p class="b f5 mt5 fw6">Ready for the next step?</p> <p class="b f5 mt5 fw6">Ready for the next step?</p>
<a <a
class="fw6 link white bg-blue ph4 pv2 br1 dib f5 link-hover" class="fw6 link white bg-blue ph4 pv2 br1 dib f5 link-hover"
style="border-bottom: 1px solid #1f679e" style="border-bottom: 1px solid #1f679e"
href="https://warp.cloudflare.com"> href="https://warp.cloudflare.com">
Get started here Get started here
</a> </a>
{{if .Tags}} <section> <section>
<h4 class="f6 fw4 pt5 mb2">Connection</h4> <h4 class="f6 fw4 pt5 mb2">Request</h4>
<dl class="bl bw2 b--orange ph3 pt3 pb2 bg-light-gray f7 code overflow-x-auto mw-100"> <dl class="bl bw2 b--orange ph3 pt3 pb2 bg-light-gray f7 code overflow-x-auto mw-100">
{{range .Tags}} <dt class="ttu mb1">{{.Name}}</dt> <dd class="ml0 mb3 f5">Method: {{.Request.Method}}</dd>
<dd class="ml0 mb3 f5">{{.Value}}</dd> <dd class="ml0 mb3 f5">Protocol: {{.Request.Proto}}</dd>
{{end}} </dl> <dd class="ml0 mb3 f5">Request URL: {{.Request.URL}}</dd>
<dd class="ml0 mb3 f5">Transfer encoding: {{.Request.TransferEncoding}}</dd>
<dd class="ml0 mb3 f5">Host: {{.Request.Host}}</dd>
<dd class="ml0 mb3 f5">Remote address: {{.Request.RemoteAddr}}</dd>
<dd class="ml0 mb3 f5">Request URI: {{.Request.RequestURI}}</dd>
{{range $key, $value := .Request.Header}}
<dd class="ml0 mb3 f5">Header: {{$key}}, Value: {{$value}}</dd>
{{end}}
<dd class="ml0 mb3 f5">Body: {{.Body}}</dd>
</dl>
</section> </section>
{{end}} </div> </div>
</div> </div>
</body> </body>
</html> </html>
@ -127,10 +134,17 @@ func (s *HelloWorldServer) ListenAndServe(address string) error {
func (s *HelloWorldServer) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (s *HelloWorldServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
log.WithField("client", r.RemoteAddr).Infof("%s %s %s", r.Method, r.URL, r.Proto) log.WithField("client", r.RemoteAddr).Infof("%s %s %s", r.Method, r.URL, r.Proto)
var buffer bytes.Buffer var buffer bytes.Buffer
err := s.responseTemplate.Execute(&buffer, &templateData{ var body string
rawBody, err := ioutil.ReadAll(r.Body)
if err == nil {
body = string(rawBody)
} else {
body = ""
}
err = s.responseTemplate.Execute(&buffer, &templateData{
ServerName: s.serverName, ServerName: s.serverName,
Request: r, Request: r,
Tags: tagsFromHeaders(r.Header), Body: body,
}) })
if err != nil { if err != nil {
w.WriteHeader(http.StatusInternalServerError) w.WriteHeader(http.StatusInternalServerError)
@ -139,17 +153,3 @@ func (s *HelloWorldServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
buffer.WriteTo(w) buffer.WriteTo(w)
} }
} }
func tagsFromHeaders(header http.Header) []tunnelpogs.Tag {
var tags []tunnelpogs.Tag
for headerName, headerValues := range header {
trimmed := strings.TrimPrefix(headerName, origin.TagHeaderNamePrefix)
if trimmed == headerName {
continue
}
for _, value := range headerValues {
tags = append(tags, tunnelpogs.Tag{Name: trimmed, Value: value})
}
}
return tags
}

View File

@ -29,6 +29,8 @@ func runApp(app *cli.App) {
app.Run(os.Args) app.Run(os.Args)
} }
const serviceConfigDir = "/etc/cloudflare-warp"
var systemdTemplates = []ServiceTemplate{ var systemdTemplates = []ServiceTemplate{
{ {
Path: "/etc/systemd/system/cloudflare-warp.service", Path: "/etc/systemd/system/cloudflare-warp.service",
@ -39,8 +41,7 @@ After=network.target
[Service] [Service]
TimeoutStartSec=0 TimeoutStartSec=0
Type=notify Type=notify
ExecStart={{ .Path }} --config /etc/cloudflare-warp.yml --autoupdate 0s ExecStart={{ .Path }} --config /etc/cloudflare-warp/config.yml --origincert /etc/cloudflare-warp/cert.pem --autoupdate 0s
User=nobody
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
@ -86,7 +87,7 @@ var sysvTemplate = ServiceTemplate{
# Short-Description: Cloudflare Warp # Short-Description: Cloudflare Warp
# Description: Cloudflare Warp agent # Description: Cloudflare Warp agent
### END INIT INFO ### END INIT INFO
cmd="{{.Path}} --config /etc/cloudflare-warp.yml --pidfile /var/run/$name.pid" cmd="{{.Path}} --config /etc/cloudflare-warp/config.yml --origincert /etc/cloudflare-warp/cert.pem --pidfile /var/run/$name.pid"
name=$(basename $(readlink -f $0)) name=$(basename $(readlink -f $0))
pid_file="/var/run/$name.pid" pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log" stdout_log="/var/log/$name.log"
@ -177,6 +178,12 @@ func installLinuxService(c *cli.Context) error {
} }
templateArgs := ServiceTemplateArgs{Path: etPath} templateArgs := ServiceTemplateArgs{Path: etPath}
if err = copyCredentials(serviceConfigDir); err != nil {
fmt.Fprintf(os.Stderr, "Failed to copy user configuration: %v\n", err)
fmt.Fprintf(os.Stderr, "Before running the service, ensure that %s contains two files, %s and %s",
serviceConfigDir, credentialFile, configFile)
}
switch { switch {
case isSystemd(): case isSystemd():
return installSystemd(&templateArgs) return installSystemd(&templateArgs)

View File

@ -1,31 +1,195 @@
package main package main
import ( import (
"crypto/rand"
"encoding/base32"
"fmt" "fmt"
"io"
"net/http"
"net/url"
"os" "os"
"os/exec"
"path/filepath"
"runtime"
"syscall" "syscall"
"time"
log "github.com/Sirupsen/logrus"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
cli "gopkg.in/urfave/cli.v2" cli "gopkg.in/urfave/cli.v2"
) )
const baseLoginURL = "https://www.cloudflare.com/a/warp"
const baseCertStoreURL = "https://login.cloudflarewarp.com"
const clientTimeout = time.Minute * 20
func login(c *cli.Context) error { func login(c *cli.Context) error {
path, err := homedir.Expand(defaultConfigPath) configPath, err := homedir.Expand(defaultConfigDir)
if err != nil { if err != nil {
return err return err
} }
ok, err := fileExists(configPath)
if !ok && err == nil {
// create config directory if doesn't already exist
err = os.Mkdir(configPath, 0700)
}
if err != nil {
return err
}
path := filepath.Join(configPath, credentialFile)
fileInfo, err := os.Stat(path) fileInfo, err := os.Stat(path)
if err == nil && fileInfo.Size() > 0 { if err == nil && fileInfo.Size() > 0 {
fmt.Fprintf(os.Stderr, `You have an existing config file at %s which login would overwrite. fmt.Fprintf(os.Stderr, `You have an existing certificate at %s which login would overwrite.
If this is intentional, please move or delete that file then run this command again. If this is intentional, please move or delete that file then run this command again.
`, defaultConfigPath) `, path)
return nil return nil
} }
if err != nil && err.(*os.PathError).Err != syscall.ENOENT { if err != nil && err.(*os.PathError).Err != syscall.ENOENT {
return err return err
} }
fmt.Fprintln(os.Stderr, "Please visit https://www.cloudflare.com/a/warp to obtain a certificate.") // for local debugging
baseURL := baseCertStoreURL
if c.IsSet("url") {
baseURL = c.String("url")
}
// Generate a random post URL
certURL := baseURL + generateRandomPath()
loginURL, err := url.Parse(baseLoginURL)
if err != nil {
// shouldn't happen, URL is hardcoded
return err
}
loginURL.RawQuery = "callback=" + url.QueryEscape(certURL)
err = open(loginURL.String())
if err != nil {
fmt.Fprintf(os.Stderr, `Please open the following URL and log in with your Cloudflare account:
%s
Leave cloudflare-warp running to install the certificate automatically.
`, loginURL.String())
} else {
fmt.Fprintf(os.Stderr, `A browser window should have opened at the following URL:
%s
If the browser failed to open, open it yourself and visit the URL above.
`, loginURL.String())
}
if download(certURL, path) {
fmt.Fprintf(os.Stderr, `You have successfully logged in.
If you wish to copy your credentials to a server, they have been saved to:
%s
`, path)
} else {
fmt.Fprintf(os.Stderr, `Failed to write the certificate due to the following error:
%v
Your browser will download the certificate instead. You will have to manually
copy it to the following path:
%s
`, err, path)
}
return nil return nil
} }
// generateRandomPath generates a random URL to associate with the certificate.
func generateRandomPath() string {
randomBytes := make([]byte, 40)
_, err := rand.Read(randomBytes)
if err != nil {
panic(err)
}
return "/" + base32.StdEncoding.EncodeToString(randomBytes)
}
// open opens the specified URL in the default browser of the user.
func open(url string) error {
var cmd string
var args []string
switch runtime.GOOS {
case "windows":
cmd = "cmd"
args = []string{"/c", "start"}
case "darwin":
cmd = "open"
default: // "linux", "freebsd", "openbsd", "netbsd"
cmd = "xdg-open"
}
args = append(args, url)
return exec.Command(cmd, args...).Start()
}
func download(certURL, filePath string) bool {
client := &http.Client{Timeout: clientTimeout}
// attempt a (long-running) certificate get
for i := 0; i < 20; i++ {
ok, err := tryDownload(client, certURL, filePath)
if ok {
putSuccess(client, certURL)
return true
}
if err != nil {
log.WithError(err).Error("Error fetching certificate")
return false
}
}
return false
}
func tryDownload(client *http.Client, certURL, filePath string) (ok bool, err error) {
resp, err := client.Get(certURL)
if err != nil {
return false, err
}
defer resp.Body.Close()
if resp.StatusCode == 404 {
return false, nil
}
if resp.StatusCode != 200 {
return false, fmt.Errorf("Unexpected HTTP error code %d", resp.StatusCode)
}
if resp.Header.Get("Content-Type") != "application/x-pem-file" {
return false, fmt.Errorf("Unexpected content type %s", resp.Header.Get("Content-Type"))
}
// write response
file, err := os.Create(filePath)
if err != nil {
return false, err
}
defer file.Close()
written, err := io.Copy(file, resp.Body)
switch {
case err != nil:
return false, err
case resp.ContentLength != written && resp.ContentLength != -1:
return false, fmt.Errorf("Short read (%d bytes) from server while writing certificate", written)
default:
return true, nil
}
}
func putSuccess(client *http.Client, certURL string) {
// indicate success to the relay server
req, err := http.NewRequest("PUT", certURL+"/ok", nil)
if err != nil {
log.WithError(err).Error("HTTP request error")
return
}
resp, err := client.Do(req)
if err != nil {
log.WithError(err).Error("HTTP error")
return
}
resp.Body.Close()
if resp.StatusCode != 200 {
log.Errorf("Unexpected HTTP error code %d", resp.StatusCode)
}
}

View File

@ -4,10 +4,12 @@ import (
"crypto/tls" "crypto/tls"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"io/ioutil"
"math/rand" "math/rand"
"net" "net"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"sync" "sync"
"syscall" "syscall"
"time" "time"
@ -31,7 +33,9 @@ import (
) )
const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b:3e8827f6f9f740738eb11138f7bebb68@sentry.io/189878" const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b:3e8827f6f9f740738eb11138f7bebb68@sentry.io/189878"
const defaultConfigPath = "~/.cloudflare-warp.yml" const defaultConfigDir = "~/.cloudflare-warp"
const credentialFile = "cert.pem"
const configFile = "config.yml"
var listeners = gracenet.Net{} var listeners = gracenet.Net{}
var Version = "DEV" var Version = "DEV"
@ -71,10 +75,15 @@ WARNING:
Usage: "Specifies a config file in YAML format.", Usage: "Specifies a config file in YAML format.",
}, },
altsrc.NewDurationFlag(&cli.DurationFlag{ altsrc.NewDurationFlag(&cli.DurationFlag{
Name: "autoupdate", Name: "autoupdate-freq",
Usage: "Periodically check for updates, restarting the server with the new version.", Usage: "Autoupdate frequency. Default is 24h.",
Value: time.Hour * 24, Value: time.Hour * 24,
}), }),
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "no-autoupdate",
Usage: "Disable periodic check for updates, restarting the server with the new version.",
Value: false,
}),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "edge", Name: "edge",
Value: "cftunnel.com:7844", Value: "cftunnel.com:7844",
@ -88,6 +97,12 @@ WARNING:
EnvVars: []string{"TUNNEL_CACERT"}, EnvVars: []string{"TUNNEL_CACERT"},
Hidden: true, Hidden: true,
}), }),
altsrc.NewStringFlag(&cli.StringFlag{
Name: "origincert",
Usage: "Path to the certificate generated for your origin when you run cloudflare-warp login.",
EnvVars: []string{"ORIGIN_CERT"},
Value: filepath.Join(defaultConfigDir, credentialFile),
}),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "url", Name: "url",
Value: "http://localhost:8080", Value: "http://localhost:8080",
@ -112,18 +127,21 @@ WARNING:
}), }),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "api-key", Name: "api-key",
Usage: "A Cloudflare API key. Required(can be in the config file) unless you are only running the hello command or login command.", Usage: "This parameter has been deprecated since version 2017.10.1.",
EnvVars: []string{"TUNNEL_API_KEY"}, EnvVars: []string{"TUNNEL_API_KEY"},
Hidden: true,
}), }),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "api-email", Name: "api-email",
Usage: "The Cloudflare user's email address associated with the API key. Required(can be in the config file) unless you are only running the hello command or login command.", Usage: "This parameter has been deprecated since version 2017.10.1.",
EnvVars: []string{"TUNNEL_API_EMAIL"}, EnvVars: []string{"TUNNEL_API_EMAIL"},
Hidden: true,
}), }),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "api-ca-key", Name: "api-ca-key",
Usage: "The Origin CA service key associated with the user. Required(can be in the config file) unless you are only running the hello command or login command.", Usage: "This parameter has been deprecated since version 2017.10.1.",
EnvVars: []string{"TUNNEL_API_CA_KEY"}, EnvVars: []string{"TUNNEL_API_CA_KEY"},
Hidden: true,
}), }),
altsrc.NewStringFlag(&cli.StringFlag{ altsrc.NewStringFlag(&cli.StringFlag{
Name: "metrics", Name: "metrics",
@ -160,12 +178,6 @@ WARNING:
Usage: "Maximum number of retries for connection/protocol errors.", Usage: "Maximum number of retries for connection/protocol errors.",
EnvVars: []string{"TUNNEL_RETRIES"}, EnvVars: []string{"TUNNEL_RETRIES"},
}), }),
altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "debug",
Value: false,
Usage: "Enable HTTP requests to the autogenerated cftunnel.com domain.",
EnvVars: []string{"TUNNEL_DEBUG"},
}),
altsrc.NewBoolFlag(&cli.BoolFlag{ altsrc.NewBoolFlag(&cli.BoolFlag{
Name: "hello-world", Name: "hello-world",
Usage: "Run Hello World Server", Usage: "Run Hello World Server",
@ -207,6 +219,12 @@ WARNING:
Action: login, Action: login,
Usage: "Generate a configuration file with your login details", Usage: "Generate a configuration file with your login details",
ArgsUsage: " ", ArgsUsage: " ",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "url",
Hidden: true,
},
},
}, },
&cli.Command{ &cli.Command{
Name: "hello", Name: "hello",
@ -239,6 +257,7 @@ func startServer(c *cli.Context) {
if err != nil { if err != nil {
log.WithError(err).Fatal("Unknown logging level specified") log.WithError(err).Fatal("Unknown logging level specified")
} }
log.SetLevel(logLevel) log.SetLevel(logLevel)
hostname, err := validation.ValidateHostname(c.String("hostname")) hostname, err := validation.ValidateHostname(c.String("hostname"))
if err != nil { if err != nil {
@ -260,7 +279,6 @@ func startServer(c *cli.Context) {
if c.IsSet("hello-world") { if c.IsSet("hello-world") {
wg.Add(1) wg.Add(1)
listener, err := findAvailablePort() listener, err := findAvailablePort()
if err != nil { if err != nil {
listener.Close() listener.Close()
log.WithError(err).Fatal("Cannot start Hello World Server") log.WithError(err).Fatal("Cannot start Hello World Server")
@ -268,32 +286,50 @@ func startServer(c *cli.Context) {
go func() { go func() {
startHelloWorldServer(listener, shutdownC) startHelloWorldServer(listener, shutdownC)
wg.Done() wg.Done()
listener.Close()
}() }()
c.Set("url", "http://"+listener.Addr().String()) c.Set("url", "http://"+listener.Addr().String())
log.Infof("Starting Hello World Server at %s", c.String("url")) log.Infof("Starting Hello World Server at %s", c.String("url"))
} }
url, err := validateUrl(c) url, err := validateUrl(c)
if err != nil { if err != nil {
log.WithError(err).Fatal("Error validating url") log.WithError(err).Fatal("Error validating url")
} }
// User must have api-key, api-email and api-ca-key
if !c.IsSet("api-key") {
log.Fatal("You need to give us your api-key either via the --api-key option or put it in the configuration file. You will also need to give us your api-email and api-ca-key.")
}
if !c.IsSet("api-email") {
log.Fatal("You need to give us your api-email either via the --api-email option or put it in the configuration file. You will also need to give us your api-ca-key.")
}
if !c.IsSet("api-ca-key") {
log.Fatal("You need to give us your api-ca-key either via the --api-ca-key option or put it in the configuration file.")
}
log.Infof("Proxying tunnel requests to %s", url) log.Infof("Proxying tunnel requests to %s", url)
// Fail if the user provided an old authentication method
if c.IsSet("api-key") || c.IsSet("api-email") || c.IsSet("api-ca-key") {
log.Fatal("You don't need to give us your api-key anymore. Please use the new log in method. Just run cloudflare-warp login")
}
// Check that the user has acquired a certificate using the log in command
originCertPath, err := homedir.Expand(c.String("origincert"))
if err != nil {
log.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
}
ok, err := fileExists(originCertPath)
if !ok {
log.Fatalf(`Cannot find a valid certificate for your origin at the path:
%s
If the path above is wrong, specify the path with the -origincert option.
If you don't have a certificate signed by Cloudflare, run the command:
%s login
`, originCertPath, os.Args[0])
}
// Easier to send the certificate as []byte via RPC than decoding it at this point
originCert, err := ioutil.ReadFile(originCertPath)
if err != nil {
log.WithError(err).Fatalf("Cannot read %s to load origin certificate", originCertPath)
}
tunnelConfig := &origin.TunnelConfig{ tunnelConfig := &origin.TunnelConfig{
EdgeAddr: c.String("edge"), EdgeAddr: c.String("edge"),
OriginUrl: url, OriginUrl: url,
Hostname: hostname, Hostname: hostname,
APIKey: c.String("api-key"), OriginCert: originCert,
APIEmail: c.String("api-email"),
APICAKey: c.String("api-ca-key"),
TlsConfig: &tls.Config{}, TlsConfig: &tls.Config{},
Retries: c.Uint("retries"), Retries: c.Uint("retries"),
HeartbeatInterval: c.Duration("heartbeat-interval"), HeartbeatInterval: c.Duration("heartbeat-interval"),
@ -302,7 +338,6 @@ func startServer(c *cli.Context) {
ReportedVersion: Version, ReportedVersion: Version,
LBPool: c.String("lb-pool"), LBPool: c.String("lb-pool"),
Tags: tags, Tags: tags,
AccessInternalIP: c.Bool("debug"),
ConnectedSignal: h2mux.NewSignal(), ConnectedSignal: h2mux.NewSignal(),
} }
@ -329,7 +364,10 @@ func startServer(c *cli.Context) {
wg.Done() wg.Done()
}() }()
go autoupdate(c.Duration("autoupdate"), shutdownC) if !c.Bool("no-autoupdate") {
log.Infof("Autoupdate frequency is set to %v", c.Duration("autoupdate-freq"))
go autoupdate(c.Duration("autoupdate-period"), shutdownC)
}
err = WaitForSignal(errC, shutdownC) err = WaitForSignal(errC, shutdownC)
if err != nil { if err != nil {
@ -413,21 +451,14 @@ func findInputSourceContext(context *cli.Context) (altsrc.InputSourceContext, er
if context.IsSet("config") { if context.IsSet("config") {
return altsrc.NewYamlSourceFromFile(context.String("config")) return altsrc.NewYamlSourceFromFile(context.String("config"))
} }
for _, tryPath := range []string{ dirPath, err := homedir.Expand(defaultConfigDir)
defaultConfigPath, if err != nil {
"~/.cloudflare-warp.yaml", return nil, nil
"~/cloudflare-warp.yaml", }
"~/cloudflare-warp.yml", for _, path := range []string{
"~/.et.yaml", filepath.Join(dirPath, "/config.yml"),
"~/et.yml", filepath.Join(dirPath, "/config.yaml"),
"~/et.yaml",
"~/.cftunnel.yaml", // for existing users
"~/cftunnel.yaml",
} { } {
path, err := homedir.Expand(tryPath)
if err != nil {
continue
}
ok, err := fileExists(path) ok, err := fileExists(path)
if ok { if ok {
return altsrc.NewYamlSourceFromFile(path) return altsrc.NewYamlSourceFromFile(path)

View File

@ -1,11 +1,14 @@
package main package main
import ( import (
"bufio"
"bytes" "bytes"
"fmt" "fmt"
"io"
"io/ioutil" "io/ioutil"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"text/template" "text/template"
homedir "github.com/mitchellh/go-homedir" homedir "github.com/mitchellh/go-homedir"
@ -78,7 +81,7 @@ func runCommand(command string, args ...string) error {
} }
commandErr, _ := ioutil.ReadAll(stderr) commandErr, _ := ioutil.ReadAll(stderr)
if len(commandErr) > 0 { if len(commandErr) > 0 {
return fmt.Errorf("%s error: %s", command, commandErr) fmt.Fprintf(os.Stderr, "%s: %s", command, commandErr)
} }
err = cmd.Wait() err = cmd.Wait()
if err != nil { if err != nil {
@ -86,3 +89,100 @@ func runCommand(command string, args ...string) error {
} }
return nil return nil
} }
func ensureConfigDirExists(configDir string) error {
ok, err := fileExists(configDir)
if !ok && err == nil {
err = os.Mkdir(configDir, 0700)
}
return err
}
// openFile opens the file at path. If create is set and the file exists, returns nil, true, nil
func openFile(path string, create bool) (file *os.File, exists bool, err error) {
expandedPath, err := homedir.Expand(path)
if err != nil {
return nil, false, err
}
if create {
fileInfo, err := os.Stat(expandedPath)
if err == nil && fileInfo.Size() > 0 {
return nil, true, nil
}
file, err = os.OpenFile(expandedPath, os.O_RDWR|os.O_CREATE, 0600)
} else {
file, err = os.Open(expandedPath)
}
return file, false, err
}
func copyCertificate(configDir string) error {
// Copy certificate
destCredentialPath := filepath.Join(configDir, credentialFile)
destFile, exists, err := openFile(destCredentialPath, true)
if err != nil {
return err
} else if exists {
// credentials already exist, do nothing
return nil
}
defer destFile.Close()
srcCredentialPath := filepath.Join(defaultConfigDir, credentialFile)
srcFile, _, err := openFile(srcCredentialPath, false)
if err != nil {
return err
}
defer srcFile.Close()
_, err = io.Copy(destFile, srcFile)
if err != nil {
return fmt.Errorf("unable to copy %s to %s: %v", srcCredentialPath, destCredentialPath, err)
}
return nil
}
func copyCredentials(configDir string) error {
if err := ensureConfigDirExists(configDir); err != nil {
return err
}
if err := copyCertificate(configDir); err != nil {
return err
}
// Copy or create config
destConfigPath := filepath.Join(configDir, configFile)
destFile, exists, err := openFile(destConfigPath, true)
if err != nil {
return err
} else if exists {
// config already exists, do nothing
return nil
}
defer destFile.Close()
srcConfigPath := filepath.Join(defaultConfigDir, configFile)
srcFile, _, err := openFile(srcConfigPath, false)
if err != nil {
fmt.Println("Your service needs a config file that at least specifies the hostname option.")
fmt.Println("Type in a hostname now, or leave it blank and create the config file later.")
fmt.Print("Hostname: ")
reader := bufio.NewReader(os.Stdin)
input, _ := reader.ReadString('\n')
if input == "" {
return err
}
fmt.Fprintf(destFile, "hostname: %s\n", input)
} else {
defer srcFile.Close()
_, err = io.Copy(destFile, srcFile)
if err != nil {
return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err)
}
fmt.Printf("Copied %s to %s", srcConfigPath, destConfigPath)
}
return nil
}

View File

@ -34,9 +34,7 @@ type TunnelConfig struct {
EdgeAddr string EdgeAddr string
OriginUrl string OriginUrl string
Hostname string Hostname string
APIKey string OriginCert []byte
APIEmail string
APICAKey string
TlsConfig *tls.Config TlsConfig *tls.Config
Retries uint Retries uint
HeartbeatInterval time.Duration HeartbeatInterval time.Duration
@ -45,7 +43,6 @@ type TunnelConfig struct {
ReportedVersion string ReportedVersion string
LBPool string LBPool string
Tags []tunnelpogs.Tag Tags []tunnelpogs.Tag
AccessInternalIP bool
ConnectedSignal h2mux.Signal ConnectedSignal h2mux.Signal
} }
@ -78,7 +75,6 @@ func (c *TunnelConfig) RegistrationOptions() *tunnelpogs.RegistrationOptions {
ExistingTunnelPolicy: policy, ExistingTunnelPolicy: policy,
PoolID: c.LBPool, PoolID: c.LBPool,
Tags: c.Tags, Tags: c.Tags,
ExposeInternalHostname: c.AccessInternalIP,
} }
} }
@ -146,15 +142,16 @@ func ServeTunnel(
return err, true return err, true
} }
if registerErr != nil { if registerErr != nil {
raven.CaptureError(registerErr, nil)
// Don't retry on errors like entitlement failure or version too old // Don't retry on errors like entitlement failure or version too old
if e, ok := registerErr.(printableRegisterTunnelError); ok { if e, ok := registerErr.(printableRegisterTunnelError); ok {
log.WithError(e).Error("Cannot register") log.Error(e)
if e.permanent { if e.permanent {
return nil, false return nil, false
} }
return e.cause, true return e.cause, true
} }
// Only log errors to Sentry that may have been caused by the client side, to reduce dupes
raven.CaptureError(registerErr, nil)
log.Error("Cannot register") log.Error("Cannot register")
return err, true return err, true
} }
@ -202,7 +199,7 @@ func RegisterTunnel(ctx context.Context, muxer *h2mux.Muxer, config *TunnelConfi
}) })
registration, err := ts.RegisterTunnel( registration, err := ts.RegisterTunnel(
ctx, ctx,
&tunnelpogs.Authentication{Key: config.APIKey, Email: config.APIEmail, OriginCAKey: config.APICAKey}, config.OriginCert,
config.Hostname, config.Hostname,
config.RegistrationOptions(), config.RegistrationOptions(),
) )
@ -220,9 +217,9 @@ func RegisterTunnel(ctx context.Context, muxer *h2mux.Muxer, config *TunnelConfi
permanent: registration.PermanentFailure, permanent: registration.PermanentFailure,
} }
} }
for _, url := range registration.Urls {
log.Infof("Registered at %s", url) log.Infof("Registered at %s", registration.Url)
}
for _, logLine := range registration.LogLines { for _, logLine := range registration.LogLines {
log.Infof(logLine) log.Infof(logLine)
} }

View File

@ -27,7 +27,7 @@ func UnmarshalAuthentication(s tunnelrpc.Authentication) (*Authentication, error
type TunnelRegistration struct { type TunnelRegistration struct {
Err string Err string
Urls []string Url string
LogLines []string LogLines []string
PermanentFailure bool PermanentFailure bool
} }
@ -48,7 +48,6 @@ type RegistrationOptions struct {
OS string `capnp:"os"` OS string `capnp:"os"`
ExistingTunnelPolicy tunnelrpc.ExistingTunnelPolicy ExistingTunnelPolicy tunnelrpc.ExistingTunnelPolicy
PoolID string `capnp:"poolId"` PoolID string `capnp:"poolId"`
ExposeInternalHostname bool
Tags []Tag Tags []Tag
} }
@ -82,7 +81,7 @@ func UnmarshalServerInfo(s tunnelrpc.ServerInfo) (*ServerInfo, error) {
} }
type TunnelServer interface { type TunnelServer interface {
RegisterTunnel(ctx context.Context, auth *Authentication, hostname string, options *RegistrationOptions) (*TunnelRegistration, error) RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) (*TunnelRegistration, error)
GetServerInfo(ctx context.Context) (*ServerInfo, error) GetServerInfo(ctx context.Context) (*ServerInfo, error)
} }
@ -95,11 +94,7 @@ type TunnelServer_PogsImpl struct {
} }
func (i TunnelServer_PogsImpl) RegisterTunnel(p tunnelrpc.TunnelServer_registerTunnel) error { func (i TunnelServer_PogsImpl) RegisterTunnel(p tunnelrpc.TunnelServer_registerTunnel) error {
authentication, err := p.Params.Auth() originCert, err := p.Params.OriginCert()
if err != nil {
return err
}
pogsAuthentication, err := UnmarshalAuthentication(authentication)
if err != nil { if err != nil {
return err return err
} }
@ -116,7 +111,7 @@ func (i TunnelServer_PogsImpl) RegisterTunnel(p tunnelrpc.TunnelServer_registerT
return err return err
} }
server.Ack(p.Options) server.Ack(p.Options)
registration, err := i.impl.RegisterTunnel(p.Ctx, pogsAuthentication, hostname, pogsOptions) registration, err := i.impl.RegisterTunnel(p.Ctx, originCert, hostname, pogsOptions)
if err != nil { if err != nil {
return err return err
} }
@ -149,14 +144,10 @@ func (c TunnelServer_PogsClient) Close() error {
return c.Conn.Close() return c.Conn.Close()
} }
func (c TunnelServer_PogsClient) RegisterTunnel(ctx context.Context, auth *Authentication, hostname string, options *RegistrationOptions) (*TunnelRegistration, error) { func (c TunnelServer_PogsClient) RegisterTunnel(ctx context.Context, originCert []byte, hostname string, options *RegistrationOptions) (*TunnelRegistration, error) {
client := tunnelrpc.TunnelServer{Client: c.Client} client := tunnelrpc.TunnelServer{Client: c.Client}
promise := client.RegisterTunnel(ctx, func(p tunnelrpc.TunnelServer_registerTunnel_Params) error { promise := client.RegisterTunnel(ctx, func(p tunnelrpc.TunnelServer_registerTunnel_Params) error {
authentication, err := p.NewAuth() err := p.SetOriginCert(originCert)
if err != nil {
return err
}
err = MarshalAuthentication(authentication, auth)
if err != nil { if err != nil {
return err return err
} }

View File

@ -11,8 +11,8 @@ struct Authentication {
struct TunnelRegistration { struct TunnelRegistration {
err @0 :Text; err @0 :Text;
# A list of URLs that the tunnel is accessible from. # the url to access the tunnel
urls @1 :List(Text); url @1 :Text;
# Used to inform the client of actions taken. # Used to inform the client of actions taken.
logLines @2 :List(Text); logLines @2 :List(Text);
# In case of error, whether the client should attempt to reconnect. # In case of error, whether the client should attempt to reconnect.
@ -29,10 +29,8 @@ struct RegistrationOptions {
existingTunnelPolicy @3 :ExistingTunnelPolicy; existingTunnelPolicy @3 :ExistingTunnelPolicy;
# If using the balancing policy, identifies the LB pool to use. # If using the balancing policy, identifies the LB pool to use.
poolId @4 :Text; poolId @4 :Text;
# Prevents the tunnel from being accessed at <subdomain>.cftunnel.com
exposeInternalHostname @5 :Bool;
# Client-defined tags to associate with the tunnel # Client-defined tags to associate with the tunnel
tags @6 :List(Tag); tags @5 :List(Tag);
} }
struct Tag { struct Tag {
@ -51,6 +49,6 @@ struct ServerInfo {
} }
interface TunnelServer { interface TunnelServer {
registerTunnel @0 (auth :Authentication, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration); registerTunnel @0 (originCert :Data, hostname :Text, options :RegistrationOptions) -> (result :TunnelRegistration);
getServerInfo @1 () -> (result :ServerInfo); getServerInfo @1 () -> (result :ServerInfo);
} }

View File

@ -157,29 +157,23 @@ func (s TunnelRegistration) SetErr(v string) error {
return s.Struct.SetText(0, v) return s.Struct.SetText(0, v)
} }
func (s TunnelRegistration) Urls() (capnp.TextList, error) { func (s TunnelRegistration) Url() (string, error) {
p, err := s.Struct.Ptr(1) p, err := s.Struct.Ptr(1)
return capnp.TextList{List: p.List()}, err return p.Text(), err
} }
func (s TunnelRegistration) HasUrls() bool { func (s TunnelRegistration) HasUrl() bool {
p, err := s.Struct.Ptr(1) p, err := s.Struct.Ptr(1)
return p.IsValid() || err != nil return p.IsValid() || err != nil
} }
func (s TunnelRegistration) SetUrls(v capnp.TextList) error { func (s TunnelRegistration) UrlBytes() ([]byte, error) {
return s.Struct.SetPtr(1, v.List.ToPtr()) p, err := s.Struct.Ptr(1)
return p.TextBytes(), err
} }
// NewUrls sets the urls field to a newly func (s TunnelRegistration) SetUrl(v string) error {
// allocated capnp.TextList, preferring placement in s's segment. return s.Struct.SetText(1, v)
func (s TunnelRegistration) NewUrls(n int32) (capnp.TextList, error) {
l, err := capnp.NewTextList(s.Struct.Segment(), n)
if err != nil {
return capnp.TextList{}, err
}
err = s.Struct.SetPtr(1, l.List.ToPtr())
return l, err
} }
func (s TunnelRegistration) LogLines() (capnp.TextList, error) { func (s TunnelRegistration) LogLines() (capnp.TextList, error) {
@ -349,14 +343,6 @@ func (s RegistrationOptions) SetPoolId(v string) error {
return s.Struct.SetText(3, v) return s.Struct.SetText(3, v)
} }
func (s RegistrationOptions) ExposeInternalHostname() bool {
return s.Struct.Bit(16)
}
func (s RegistrationOptions) SetExposeInternalHostname(v bool) {
s.Struct.SetBit(16, v)
}
func (s RegistrationOptions) Tags() (Tag_List, error) { func (s RegistrationOptions) Tags() (Tag_List, error) {
p, err := s.Struct.Ptr(4) p, err := s.Struct.Ptr(4)
return Tag_List{List: p.List()}, err return Tag_List{List: p.List()}, err
@ -750,29 +736,18 @@ func (s TunnelServer_registerTunnel_Params) String() string {
return str return str
} }
func (s TunnelServer_registerTunnel_Params) Auth() (Authentication, error) { func (s TunnelServer_registerTunnel_Params) OriginCert() ([]byte, error) {
p, err := s.Struct.Ptr(0) p, err := s.Struct.Ptr(0)
return Authentication{Struct: p.Struct()}, err return []byte(p.Data()), err
} }
func (s TunnelServer_registerTunnel_Params) HasAuth() bool { func (s TunnelServer_registerTunnel_Params) HasOriginCert() bool {
p, err := s.Struct.Ptr(0) p, err := s.Struct.Ptr(0)
return p.IsValid() || err != nil return p.IsValid() || err != nil
} }
func (s TunnelServer_registerTunnel_Params) SetAuth(v Authentication) error { func (s TunnelServer_registerTunnel_Params) SetOriginCert(v []byte) error {
return s.Struct.SetPtr(0, v.Struct.ToPtr()) return s.Struct.SetData(0, v)
}
// NewAuth sets the auth field to a newly
// allocated Authentication struct, preferring placement in s's segment.
func (s TunnelServer_registerTunnel_Params) NewAuth() (Authentication, error) {
ss, err := NewAuthentication(s.Struct.Segment())
if err != nil {
return Authentication{}, err
}
err = s.Struct.SetPtr(0, ss.Struct.ToPtr())
return ss, err
} }
func (s TunnelServer_registerTunnel_Params) Hostname() (string, error) { func (s TunnelServer_registerTunnel_Params) Hostname() (string, error) {
@ -844,10 +819,6 @@ func (p TunnelServer_registerTunnel_Params_Promise) Struct() (TunnelServer_regis
return TunnelServer_registerTunnel_Params{s}, err return TunnelServer_registerTunnel_Params{s}, err
} }
func (p TunnelServer_registerTunnel_Params_Promise) Auth() Authentication_Promise {
return Authentication_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
}
func (p TunnelServer_registerTunnel_Params_Promise) Options() RegistrationOptions_Promise { func (p TunnelServer_registerTunnel_Params_Promise) Options() RegistrationOptions_Promise {
return RegistrationOptions_Promise{Pipeline: p.Pipeline.GetPipeline(2)} return RegistrationOptions_Promise{Pipeline: p.Pipeline.GetPipeline(2)}
} }
@ -1060,74 +1031,73 @@ func (p TunnelServer_getServerInfo_Results_Promise) Result() ServerInfo_Promise
return ServerInfo_Promise{Pipeline: p.Pipeline.GetPipeline(0)} return ServerInfo_Promise{Pipeline: p.Pipeline.GetPipeline(0)}
} }
const schema_db8274f9144abc7e = "x\xda\x94To\x88\x14e\x18\x7f~\xef\xbb3\xabp" + const schema_db8274f9144abc7e = "x\xda\x9cT_h\x1c\xd5\x1b\xfd\xce\xbd3\xbb-$" +
"\xb6;\xcc\x0aut\x08b\x90\x82\xe6e\x86\x99\xb4\xe7" + "\xbf\xcd0\x1bh\x17B\xa0\xe4\x87\xb6\xd0?\xb1*5" +
"\xa5\xe6^\xa7\xb7\xaf]a\xe9\x07\xc7\xbd\xf7\xf6\xc6f" + "\x167\x89m%1m\xf6\xc6Vk\x9b\x87N7\xb7" +
"g\xb6\x99\xd9\xcb\x8b\xd4\x92 \x0c2R\xfb\xd2\x87\xc8" + "\x9b\x89\xb33\xeb\xccl\xb4\x85\xb4Z\"R\xc1\xa2\x96" +
"\xfbf`%\x14E\x18\x9c\xd0\x1f\xc1\"\x02\x0b\xad\xeb" + "\x82\x05\x11\x15\x0b*E\xfb\xa0h\xa0\x0f\xf5E\x91\"" +
"C\x98\x04RH\xd2\x87\x0cb\xe2\x9d\xbd\xd9\x99\xee\x94" + "\xfa\xa0\x82\xd8\x17-E,J!\xf8\xe2\xd3\xc8\x9d\xcd" +
"\xf0\xdb\xfb\xe7y\x7f\xef\xef\xf9=\xcf\xf3[9\xc1\xfa" + "\xec\x8c)\xa6\xd2\xb7;w\xbe?\xe7\x9c\xfb}g\xcb" +
"X\xafv\x8fF$\xd6iz\xb4\xae\xf1\xcd\xe4\xfdo" + "\xd3l\x90\xf5\xeb\x9bu\"\xb1]\xcfE\xdb\xeb\xdf\xbc" +
"\x9c{\x89\x8c\"\x8b\xf6\x9f\x1e(]\x0f\x0f\xfeH\x84" + "s\xff\xd9+\xf3d\x94Xt\xfc\xd2h\xf1\xaf\xf0\xe4" +
"U\xfb\xd82\x98\xaf\xb2<\x91y\x88\x0d\x11\xa2\x85\x15" + "OD\xd8:\xc7\x8e\xc1|\x95\xe5\x89\xcc\x97\xd98!" +
"LO\xf5\xe6>\"\xa3\x07D\x1a\xcf\x13\xad:\xce\xde" + "\xea\x1e\xc1\xd5\xcb\xfd\xda\xa7d\xdc\x05\"\x9d\xe7\x89\xb6" +
"\x04\xc1<\xc5\xde#D=\xbf\xf7/p\xaf\x1e\x9c\"" + "\x9eg7@0\x17\xd8G\x84\xa8\xe7\x8f\xe1N\xf7\xe6" +
"\xa3\x88\x14*\x0e4\xb7\xf0\xbf\xcd'\xe3\xd5\xe3\\\xc5" + "\xc9\xcbd\x94\x90\x96j\x05>\xc9Ga\xd6\xd5\xd1\xb4" +
"\x0e\xec8zD\xbb|\xf4K\x12Ed\x835\x85\xfa" + "\xb9\x0a\x1e=x\xe65\xfd\xfa\x99/I\x94\x90\x8d\xd6" +
"\x07_\x0c\x139\xb5\xfc\x87\xbf\x06B\xd4\xfd\xfe\x83\xef" + "U\xb4\xae\xf90\xd7j\xea\xd8\xad=\x01BT\xba\xf8" +
"\xf6\x8f\\<7\x0b:fwB\x9b4O\xa9w\xe6" + "\xe0\x87\xc3S?^YV;\x867\xa7/\x9a\xa7T" +
"I\xedYB\xc4.[w\xbc\xf0\xfdC\xd3m\x9e1" + "\x9e\xf9\x82\xfe\x0c!b\xd7\xad\xb5\xcf\xfd\xf0\xd0\xd5\x16" +
"\xca|\xfd\x08(\x17m}b\xc7\x9e\xf9\xfb.]\x9a" + "\xd0\xb8\xca\xcf\xfa/ -\xda\xf3\xf8\xc1\x99\xd5s\xd7" +
"\xc9\x00\xea\xea\xba\x16g0_/\x13\xa2\xd5\xbb\xd6\xcb" + "\xae-Q\x80\xfa\xf5\xbd\x1eS\xf8M/\x13\xa2\xfb\x0e" +
"\x9dk\xb6_!\xa3\xc8\xb3b\x98K\xf5+\xe6j]" + "\x0d\xc9\xc9m\xfbo\x90Q\xe2\xffPcun\x00\xe6" +
"\xfd\xd1\xab\xbfl\x1eR\xab\xe8\xf0\xfe\x0dC\x0f,>" + "\xda\x9cj\xd2\x9d{\xd1\xac\xabSt\xfa\xf8\x8e\xf1\x07" +
"s-\x8b\xf6\x8c>\xa9\xd0^\x8c\xd1F\xd7\xfc\xf6\xc8" + "\xd6}\xbe\x98-\xb7/\xb7\xa8\xca\xd99U\xee\xc8\xb6" +
"]\x87\xbf\xb86\x8b\xb4\x0a4\x8f\xeb?\x98'c\xc0" + "\xdf\x1f\xf9\xff\xe9/\x16\x97\xa1\x8e\x03O\xe56\xc0<" +
"\x13*\xf6\xea\xa6\xb7\xcew\x17\xba\xff\x9c\xa5F\xac\xf1" + "\x17W<\xab\x82o\xeez\xf3\xbbR\xa1\xf4\xe72=" +
"\xd7z7\xcc\x9f\xe2\xd8\x8b\xfa\xaf\xb4<\x0a[\xae+" + "b\xf5\x16r30\xbf\x8ec\xbf\xca\xfdJ\x1b\xa3\xb0" +
"\x1d\xbf\xc9k+jV\xd3m\xae\xdd\xb8\xd7\x0eB\xdb" + "\xe9\xba\xd2\xf1\x1bZusr\xacn\xaaZ\x0d\xb71" +
"\xad\x0f\xc7\x17U\xaf\xe0\xd8\xb5\x89* \xba\xc0\x88\x8c" + "\xb0\xf3Y;\x08m\xb7\xb67\xbe/W<\xc7\xae\x1e" +
"\x9e\xb5D\x80\xb1\xf0)\"0\xc3\xe8'*\xdbu\xd7" + "\xad\x00\xa2\x03\x8c\xc8\xe8\x19 \x02\x8c\xee\x03D`\x86" +
"\xf3e4b\x075\xcfu%\xf1Zx`\xb7\xe5X" + "1LT\xb6k\xae\xe7\xcbh\xca\x0e\xaa\x9e\xebJ\xe2" +
"nMv\xe0\xb5\x04\xbe\x0d\xfb\x98\xf4\xc7\xa5\xbf\xc2\x97" + "\xd5\xf0\xc4a\xcb\xb1\xdc\xaal7\xca\xdd\xda\xa8\xd5\xe0" +
"u;\x08\xa5\xdf>\\R\xb5|\x8b7\x02\xd1\xc5s" + "1\xe9\xcfJ\x7f\x93/kv\x10J\xbfu\xd9W\xb1" +
"D9\x10\x19\x1b\x97\x11\x89>\x0e1\xc8`\x00%\xa8" + "\x0a\xbeU\x0fD\x07\xd7\x884\x10\x19;\x0f\x10\x89\x1d" +
"\xc3\xca\x00\x91\xd8\xcc!\x86\x19\x0c\xc6J1/\xd1O" + "\x1c\xa2\xc2`\x00E\xa8\xcb\xdd\xa3Db\x8cC\xecg" +
"$\x069\xc4v\x86\x82\xd5\x0a\xc7PL{\x88\x80\"" + "0\x18+\xc6\x08\xf7\x0d\x13\x89\x0a\x87\x98d\x88<\xdf" +
"!\x1a\xf3\x82\xd0\xb5\x1a\x92\x88\xd0E\x0c]\x84\x03^" + "\xae\xd9\xee\xc3\x92\xb8\x1f\xa2\x93\x18:\x09\xd1\xb4\x17\x84" +
"3\xb4=7@1\xed\xa2\x99\xe8\x84:K\xa8\xafo" + "\xaeU\x97D\x84\x0eb\xe8 \x9c\xf0\x1a\xa1\xed\xb9\x01" +
"\x85c\xd2\x0d\xedr\xcdRobMR\xa6\x8bo\xc4" + "\xba\xd2\xc9\"\xa0\x8b\xb0\x92VC\xcdpZ\xba\xa1]" +
"\xf4^\"\xb1\x81CT3L\xb7\xecN\x99\xe6\x9f\x96" + "\xb5T2Q,S\x0ay\x1d\x91\x18\xe4\x10c\x19\xc8" +
"\x13\x09\x95E\xb2a\xd9N\xb2\x8b<\xdf\xae\xdb\xee\xc3" + "#\xf7dx$\x90w\x1fNy\xe4\x9f\x92G\x13T" +
"\xeb)\xffh\x1a3\xb7\\\xdbb\x09\xfd\x98\xd1P3" + "\xbd\xb2n\xd9N\xf2\x95\x90\x19\xa2\xfc\xa3i\xccJ\xf8" +
"\xb4\xf3\x9e\x1b(fwv\x98}\xa8\xe4\xfa\x80CL" + "&bU\xfd\x18\xddx\xa37f\xa80\xaeic<" +
"e\x98}\xaa\xe4\xfa\x98C|\x96av\xa6\x9bH\x9c" + "\xa7\x14|\x9dC\xbc\x9b\xc1\xf8\xb6R\xf0\x0d\x0e\xf1^" +
"\xe6\x10g\x19\xc0K\xe0D\xc6\xe7\xef\x10\x89\xb3\x1c\xe2" + "\x06\xe3\xf9\x12\x91x\x8bC\\`\x00/\x82\x13\x19\xef" +
"<\x83\x91\xe3%\xe4\x88\x8co\xd7\x12\x89\xaf8\xc4\x05" + "\x7f@$.p\x88\xcf\x18\x0c\x8d\x17\xa1\x11\x19\x9f\x0c" +
"\x06C+\x96\xa0\x11\x19\xdf}B$.p\x88_\x18" + "\x10\x89\x8b\x1c\xe2\x12\x83\xa1kE\xe8D\xc6\xc2\x06\"" +
"\x0c=W\x82Nd\xfc\xac\x0a8\xcd!\xfeb\x88j" + "\xf11\x87\xf8\x96!\xaa:\xb6t\xc3\x91\xa9\xac\xfe\xb3" +
"\x8e-\xdd\xb02\x92\xd5\x7f\\\xfa\x81\xed\xb9\xc9\x9e{" + "\xd2\x0fl\xcfM\xbe\xb9\x17\xb4\x09\xca\xa5\x89Dk8" +
"A'W9\xd3\x89\xf8O+\xa2\x90\xda\x0c\x01\x05B" + "*^A\x8d$\x0a\xa9\xf7\x10P \x94\x1b\x9e\xe7\x8c" +
"\xb9\xe9yNe$\xf3\xae\xe9\x05\xb2\xe2\"\x94\xbek" + "L%y\x85\xd0\xaa\x05\xf8\x1f\xa1\xc2\x81\xae\xd4\x01\x08" +
"9\x9b\xbdr\xbb\xec\x001\x80P\x08\xadz\x80\xdb\x08" + "\xea\xb2-\x1b[.[oc`\xafUS2\xadj" +
"U\x0e\x14S; \xa8\xc3\x8e\xc4H$\xce\x0f[u" + "\xcb\xb4^\xc1\xef\xe3\x10[22mTOy7\x87" +
"%\xe9\xbc\x8e\xa4KUVK8\xc4\xca\x8c\xa4\xcbU" + "\xb8\x97\xa1\xa0\xe6\xa9\xfdl\xb3\x96\xd3\x94\xb7<\xd0\xed" +
"\xb1\xef\xe6\x10\xf71\x14\xe2\xff\x92\xc2\x8e[NK\xce" + "v\xa0&\xc3\xd6i\xc4=\xe2\xf5U,?o\xd5\x83" +
")\xe1\x8dG\xa2.\xc3\xf6\xaa\xe2\x8ez\xf1D4\x10" + ";\xcc\x9e\x90A\xa1\xe9\x84\x81\xd0\xda\x1c:\xd5\xbb\xac" +
"\xdc\xd2\x9bm2h9<\x0cD\xae\xc3w\x81\xaa\xd7" + "\xe2\x10E\x86\xb2/\x83\xa6\x13\xa2+\xb5\x98e\xd3\xce" +
"<\x0eQb(\xfb\xea>D1\xb5\x94\x9b5|\xf2" + "\xff\xad]\xb9\xd5\xa5\xa5\x8fN\xd4\xf6u$vf\xf4" +
"IAa\xb7\x15\xd0\x88:\xde\x8d\xc4\xb4\x8c\xde\xe7\x88" + "\x1f#f\xac\xcf#\xb5R$\xcei\xf4\xf8\xc4\x8c\xee" +
"\x19K\xf3H\xfd\x12\x89=\x1a=>1ca>J" + "|\x94,<\x95[e\x07\x11%\x0c\xa87\xe60\x88" +
"f\x9d\xcam\xd8>D\x09oZ\x143\xefC\x15\xb8" + "\x0ap\xa7\x062!{\x83\xff\xc2?q\xcd\xdb\xb3o" +
"5\xc7P\xb9\xe6\x9d\xff\xcf5\xb1\xc4\x9be\x9a\xc8\xc7" + "\xf5)(d\x8a{\xa6\xee\x0c\x91\xe8\xe0\x10k\x18\"" +
"G=\x95g\x06m\x0f\x91\xe8\xe2\x10\xb73D\x8e\xd7" + "\xc7[\xf2\x82\xc2\x9e\xcc@\xac\xb4\xa3-\xc0\xc9\xa6\x16" +
"\x9e|*l\xcd\x94w\xeeL\xb6\xc9\xa5\x93\xc9\xdbf" + "T\xb2\xaa\xdf\xd5\xaeo)\x1b\x99\xe4\x10\xd3\x99\xd9\x93" +
"Q\xec\xa0Z\xca,vr\x88\xb1L\xffH\xd5T\xbb" + "\xea\xf2\x10\x87p2+j\xabe\x9e\xe6\x10\xf3\xe9\x8a" +
"8\xc4\xf3\x99\x91\x9cP\xc3\xbb\x97C\x1cKG\xf2\xf5" + ">\xff\x12\x91\x98\xe7\x10\xaf0\xe4\xa5\xef'\x90\xf2M" +
"W\x88\xc41\x0e\xf16C^\xfa~B\xa4\xd0\xf2\x9d" + "?5\x16\xc7\xab\x8d\xd9\xae\x0c\xd4B.-\x8c\xfa\xa5" +
"N_\xab3\xd5\xcd\x8eW\x1f\xb4]\x19\xa8\x99\x9bu" + "\xd6\xa4!\xfd\xba\xe5J\x17\xe1.\xcbv\x9a\xbeT\x83" +
"\xd5\x94~\xc3r\xa5\x8bp\x93e;-_\xaaFh" + "B\x0c \xfc\x1d\x00\x00\xff\xff\xfa.\x1fg"
"\x8f\xc8\xbf\x01\x00\x00\xff\xff\x80\xf4\x060"
func init() { func init() {
schemas.Register(schema_db8274f9144abc7e, schemas.Register(schema_db8274f9144abc7e,