Release Warp Client 2018.3.0
This commit is contained in:
parent
0cfee43d41
commit
96dd6cc3a0
|
@ -5,6 +5,7 @@ package main
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
cli "gopkg.in/urfave/cli.v2"
|
cli "gopkg.in/urfave/cli.v2"
|
||||||
)
|
)
|
||||||
|
@ -180,16 +181,20 @@ func installLinuxService(c *cli.Context) error {
|
||||||
}
|
}
|
||||||
templateArgs := ServiceTemplateArgs{Path: etPath}
|
templateArgs := ServiceTemplateArgs{Path: etPath}
|
||||||
|
|
||||||
if err = copyCredentials(serviceConfigDir); err != nil {
|
defaultConfigDir := filepath.Dir(c.String("config"))
|
||||||
fmt.Fprintf(os.Stderr, "Failed to copy user configuration: %v\n", err)
|
defaultConfigFile := filepath.Base(c.String("config"))
|
||||||
fmt.Fprintf(os.Stderr, "Before running the service, ensure that %s contains two files, %s and %s",
|
if err = copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile); err != nil {
|
||||||
serviceConfigDir, credentialFile, configFile)
|
Log.WithError(err).Infof("Failed to copy user configuration. Before running the service, ensure that %s contains two files, %s and %s",
|
||||||
|
serviceConfigDir, credentialFile, defaultConfigFiles[0])
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case isSystemd():
|
case isSystemd():
|
||||||
|
Log.Infof("Using Systemd")
|
||||||
return installSystemd(&templateArgs)
|
return installSystemd(&templateArgs)
|
||||||
default:
|
default:
|
||||||
|
Log.Infof("Using Sysv")
|
||||||
return installSysv(&templateArgs)
|
return installSysv(&templateArgs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -198,24 +203,30 @@ func installSystemd(templateArgs *ServiceTemplateArgs) error {
|
||||||
for _, serviceTemplate := range systemdTemplates {
|
for _, serviceTemplate := range systemdTemplates {
|
||||||
err := serviceTemplate.Generate(templateArgs)
|
err := serviceTemplate.Generate(templateArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error generating service template")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err := runCommand("systemctl", "enable", "cloudflare-warp.service"); err != nil {
|
if err := runCommand("systemctl", "enable", "cloudflare-warp.service"); err != nil {
|
||||||
|
Log.WithError(err).Infof("systemctl enable cloudflare-warp.service error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := runCommand("systemctl", "start", "cloudflare-warp-update.timer"); err != nil {
|
if err := runCommand("systemctl", "start", "cloudflare-warp-update.timer"); err != nil {
|
||||||
|
Log.WithError(err).Infof("systemctl start cloudflare-warp-update.timer error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Log.Infof("systemctl daemon-reload")
|
||||||
return runCommand("systemctl", "daemon-reload")
|
return runCommand("systemctl", "daemon-reload")
|
||||||
}
|
}
|
||||||
|
|
||||||
func installSysv(templateArgs *ServiceTemplateArgs) error {
|
func installSysv(templateArgs *ServiceTemplateArgs) error {
|
||||||
confPath, err := sysvTemplate.ResolvePath()
|
confPath, err := sysvTemplate.ResolvePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error resolving system path")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := sysvTemplate.Generate(templateArgs); err != nil {
|
if err := sysvTemplate.Generate(templateArgs); err != nil {
|
||||||
|
Log.WithError(err).Infof("error generating system template")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, i := range [...]string{"2", "3", "4", "5"} {
|
for _, i := range [...]string{"2", "3", "4", "5"} {
|
||||||
|
@ -234,29 +245,36 @@ func installSysv(templateArgs *ServiceTemplateArgs) error {
|
||||||
func uninstallLinuxService(c *cli.Context) error {
|
func uninstallLinuxService(c *cli.Context) error {
|
||||||
switch {
|
switch {
|
||||||
case isSystemd():
|
case isSystemd():
|
||||||
|
Log.Infof("Using Systemd")
|
||||||
return uninstallSystemd()
|
return uninstallSystemd()
|
||||||
default:
|
default:
|
||||||
|
Log.Infof("Using Sysv")
|
||||||
return uninstallSysv()
|
return uninstallSysv()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func uninstallSystemd() error {
|
func uninstallSystemd() error {
|
||||||
if err := runCommand("systemctl", "disable", "cloudflare-warp.service"); err != nil {
|
if err := runCommand("systemctl", "disable", "cloudflare-warp.service"); err != nil {
|
||||||
|
Log.WithError(err).Infof("systemctl disable cloudflare-warp.service error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := runCommand("systemctl", "stop", "cloudflare-warp-update.timer"); err != nil {
|
if err := runCommand("systemctl", "stop", "cloudflare-warp-update.timer"); err != nil {
|
||||||
|
Log.WithError(err).Infof("systemctl stop cloudflare-warp-update.timer error")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, serviceTemplate := range systemdTemplates {
|
for _, serviceTemplate := range systemdTemplates {
|
||||||
if err := serviceTemplate.Remove(); err != nil {
|
if err := serviceTemplate.Remove(); err != nil {
|
||||||
|
Log.WithError(err).Infof("error removing service template")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log.Infof("Successfully uninstall cloudflare-warp service")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func uninstallSysv() error {
|
func uninstallSysv() error {
|
||||||
if err := sysvTemplate.Remove(); err != nil {
|
if err := sysvTemplate.Remove(); err != nil {
|
||||||
|
Log.WithError(err).Infof("error removing service template")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
for _, i := range [...]string{"2", "3", "4", "5"} {
|
for _, i := range [...]string{"2", "3", "4", "5"} {
|
||||||
|
@ -269,5 +287,6 @@ func uninstallSysv() error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log.Infof("Successfully uninstall cloudflare-warp service")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ const baseCertStoreURL = "https://login.cloudflarewarp.com"
|
||||||
const clientTimeout = time.Minute * 20
|
const clientTimeout = time.Minute * 20
|
||||||
|
|
||||||
func login(c *cli.Context) error {
|
func login(c *cli.Context) error {
|
||||||
configPath, err := homedir.Expand(defaultConfigDir)
|
configPath, err := homedir.Expand(defaultConfigDirs[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import (
|
||||||
cli "gopkg.in/urfave/cli.v2"
|
cli "gopkg.in/urfave/cli.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const launchAgentIdentifier = "com.cloudflare.warp"
|
||||||
|
|
||||||
func runApp(app *cli.App) {
|
func runApp(app *cli.App) {
|
||||||
app.Commands = append(app.Commands, &cli.Command{
|
app.Commands = append(app.Commands, &cli.Command{
|
||||||
Name: "service",
|
Name: "service",
|
||||||
|
@ -31,16 +33,20 @@ func runApp(app *cli.App) {
|
||||||
|
|
||||||
var launchdTemplate = ServiceTemplate{
|
var launchdTemplate = ServiceTemplate{
|
||||||
Path: "~/Library/LaunchAgents/com.cloudflare.warp.plist",
|
Path: "~/Library/LaunchAgents/com.cloudflare.warp.plist",
|
||||||
Content: `<?xml version="1.0" encoding="UTF-8"?>
|
Content: fmt.Sprintf(`<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>Label</key>
|
<key>Label</key>
|
||||||
<string>com.cloudflare.warp</string>
|
<string>%s</string>
|
||||||
<key>Program</key>
|
<key>Program</key>
|
||||||
<string>{{ .Path }}</string>
|
<string>{{ .Path }}</string>
|
||||||
<key>RunAtLoad</key>
|
<key>RunAtLoad</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>StandardOutPath</key>
|
||||||
|
<string>/tmp/%s.out.log</string>
|
||||||
|
<key>StandardErrorPath</key>
|
||||||
|
<string>/tmp/%s.err.log</string>
|
||||||
<key>KeepAlive</key>
|
<key>KeepAlive</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>NetworkState</key>
|
<key>NetworkState</key>
|
||||||
|
@ -49,34 +55,43 @@ var launchdTemplate = ServiceTemplate{
|
||||||
<key>ThrottleInterval</key>
|
<key>ThrottleInterval</key>
|
||||||
<integer>20</integer>
|
<integer>20</integer>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>`,
|
</plist>`, launchAgentIdentifier, launchAgentIdentifier, launchAgentIdentifier),
|
||||||
}
|
}
|
||||||
|
|
||||||
func installLaunchd(c *cli.Context) error {
|
func installLaunchd(c *cli.Context) error {
|
||||||
|
Log.Infof("Installing Cloudflare Warp as an user launch agent")
|
||||||
etPath, err := os.Executable()
|
etPath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error determining executable path")
|
||||||
return fmt.Errorf("error determining executable path: %v", err)
|
return fmt.Errorf("error determining executable path: %v", err)
|
||||||
}
|
}
|
||||||
templateArgs := ServiceTemplateArgs{Path: etPath}
|
templateArgs := ServiceTemplateArgs{Path: etPath}
|
||||||
err = launchdTemplate.Generate(&templateArgs)
|
err = launchdTemplate.Generate(&templateArgs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error generating launchd template")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
plistPath, err := launchdTemplate.ResolvePath()
|
plistPath, err := launchdTemplate.ResolvePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error resolving launchd template path")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Log.Infof("Outputs are logged in %s and %s", fmt.Sprintf("/tmp/%s.out.log", launchAgentIdentifier), fmt.Sprintf("/tmp/%s.err.log", launchAgentIdentifier))
|
||||||
return runCommand("launchctl", "load", plistPath)
|
return runCommand("launchctl", "load", plistPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func uninstallLaunchd(c *cli.Context) error {
|
func uninstallLaunchd(c *cli.Context) error {
|
||||||
|
Log.Infof("Uninstalling Cloudflare Warp as an user launch agent")
|
||||||
plistPath, err := launchdTemplate.ResolvePath()
|
plistPath, err := launchdTemplate.ResolvePath()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error resolving launchd template path")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = runCommand("launchctl", "unload", plistPath)
|
err = runCommand("launchctl", "unload", plistPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error unloading")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
Log.Infof("Outputs are logged in %s and %s", fmt.Sprintf("/tmp/%s.out.log", launchAgentIdentifier), fmt.Sprintf("/tmp/%s.err.log", launchAgentIdentifier))
|
||||||
return launchdTemplate.Remove()
|
return launchdTemplate.Remove()
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,14 +36,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b:3e8827f6f9f740738eb11138f7bebb68@sentry.io/189878"
|
const sentryDSN = "https://56a9c9fa5c364ab28f34b14f35ea0f1b:3e8827f6f9f740738eb11138f7bebb68@sentry.io/189878"
|
||||||
const defaultConfigDir = "~/.cloudflare-warp"
|
|
||||||
const credentialFile = "cert.pem"
|
const credentialFile = "cert.pem"
|
||||||
const configFile = "config.yml"
|
const quickStartUrl = "https://developers.cloudflare.com/warp/quickstart/quickstart/"
|
||||||
|
|
||||||
var listeners = gracenet.Net{}
|
var listeners = gracenet.Net{}
|
||||||
var Version = "DEV"
|
var Version = "DEV"
|
||||||
var BuildTime = "unknown"
|
var BuildTime = "unknown"
|
||||||
var Log *logrus.Logger
|
var Log *logrus.Logger
|
||||||
|
var defaultConfigFiles = []string{"config.yml", "config.yaml"}
|
||||||
|
|
||||||
|
// Windows default config dir was ~/cloudflare-warp in documentation, let's keep it compatible
|
||||||
|
var defaultConfigDirs = []string{"~/.cloudflare-warp", "~/cloudflare-warp"}
|
||||||
|
|
||||||
// Shutdown channel used by the app. When closed, app must terminate.
|
// Shutdown channel used by the app. When closed, app must terminate.
|
||||||
// May be closed by the Windows service runner.
|
// May be closed by the Windows service runner.
|
||||||
|
@ -86,6 +89,7 @@ WARNING:
|
||||||
&cli.StringFlag{
|
&cli.StringFlag{
|
||||||
Name: "config",
|
Name: "config",
|
||||||
Usage: "Specifies a config file in YAML format.",
|
Usage: "Specifies a config file in YAML format.",
|
||||||
|
Value: findDefaultConfigPath(),
|
||||||
},
|
},
|
||||||
altsrc.NewDurationFlag(&cli.DurationFlag{
|
altsrc.NewDurationFlag(&cli.DurationFlag{
|
||||||
Name: "autoupdate-freq",
|
Name: "autoupdate-freq",
|
||||||
|
@ -98,9 +102,9 @@ WARNING:
|
||||||
Value: false,
|
Value: false,
|
||||||
}),
|
}),
|
||||||
altsrc.NewBoolFlag(&cli.BoolFlag{
|
altsrc.NewBoolFlag(&cli.BoolFlag{
|
||||||
Name: "is-autoupdated",
|
Name: "is-autoupdated",
|
||||||
Usage: "Signal the new process that Warp client has been autoupdated",
|
Usage: "Signal the new process that Warp client has been autoupdated",
|
||||||
Value: false,
|
Value: false,
|
||||||
Hidden: true,
|
Hidden: true,
|
||||||
}),
|
}),
|
||||||
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
|
altsrc.NewStringSliceFlag(&cli.StringSliceFlag{
|
||||||
|
@ -119,7 +123,7 @@ WARNING:
|
||||||
Name: "origincert",
|
Name: "origincert",
|
||||||
Usage: "Path to the certificate generated for your origin when you run cloudflare-warp login.",
|
Usage: "Path to the certificate generated for your origin when you run cloudflare-warp login.",
|
||||||
EnvVars: []string{"TUNNEL_ORIGIN_CERT"},
|
EnvVars: []string{"TUNNEL_ORIGIN_CERT"},
|
||||||
Value: filepath.Join(defaultConfigDir, credentialFile),
|
Value: findDefaultOriginCertPath(),
|
||||||
}),
|
}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
Name: "url",
|
Name: "url",
|
||||||
|
@ -132,6 +136,11 @@ WARNING:
|
||||||
Usage: "Set a hostname on a Cloudflare zone to route traffic through this tunnel.",
|
Usage: "Set a hostname on a Cloudflare zone to route traffic through this tunnel.",
|
||||||
EnvVars: []string{"TUNNEL_HOSTNAME"},
|
EnvVars: []string{"TUNNEL_HOSTNAME"},
|
||||||
}),
|
}),
|
||||||
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
|
Name: "origin-server-name",
|
||||||
|
Usage: "Hostname on the origin server certificate.",
|
||||||
|
EnvVars: []string{"TUNNEL_ORIGIN_SERVER_NAME"},
|
||||||
|
}),
|
||||||
altsrc.NewStringFlag(&cli.StringFlag{
|
altsrc.NewStringFlag(&cli.StringFlag{
|
||||||
Name: "id",
|
Name: "id",
|
||||||
Usage: "A unique identifier used to tie connections to this tunnel instance.",
|
Usage: "A unique identifier used to tie connections to this tunnel instance.",
|
||||||
|
@ -267,9 +276,15 @@ WARNING:
|
||||||
Log = logrus.New()
|
Log = logrus.New()
|
||||||
inputSource, err := findInputSourceContext(context)
|
inputSource, err := findInputSourceContext(context)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("Cannot load configuration from %s", context.String("config"))
|
||||||
return err
|
return err
|
||||||
} else if inputSource != nil {
|
} else if inputSource != nil {
|
||||||
return altsrc.ApplyInputSourceValues(context, inputSource, app.Flags)
|
err := altsrc.ApplyInputSourceValues(context, inputSource, app.Flags)
|
||||||
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("Cannot apply configuration from %s", context.String("config"))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
Log.Infof("Applied configuration from %s", context.String("config"))
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -323,6 +338,7 @@ func startServer(c *cli.Context) {
|
||||||
// c.NumFlags() == 0 && c.NArg() == 0. For warp to work, the user needs to at
|
// c.NumFlags() == 0 && c.NArg() == 0. For warp to work, the user needs to at
|
||||||
// least provide a hostname.
|
// least provide a hostname.
|
||||||
if c.NumFlags() == 0 && c.NArg() == 0 && os.Getenv("TUNNEL_HOSTNAME") == "" {
|
if c.NumFlags() == 0 && c.NArg() == 0 && os.Getenv("TUNNEL_HOSTNAME") == "" {
|
||||||
|
Log.Infof("No arguments were provided. You need to at least specify the hostname for this tunnel. See %s", quickStartUrl)
|
||||||
cli.ShowAppHelp(c)
|
cli.ShowAppHelp(c)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -356,7 +372,6 @@ func startServer(c *cli.Context) {
|
||||||
hostname, err := validation.ValidateHostname(c.String("hostname"))
|
hostname, err := validation.ValidateHostname(c.String("hostname"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Log.WithError(err).Fatal("Invalid hostname")
|
Log.WithError(err).Fatal("Invalid hostname")
|
||||||
|
|
||||||
}
|
}
|
||||||
clientID := c.String("id")
|
clientID := c.String("id")
|
||||||
if !c.IsSet("id") {
|
if !c.IsSet("id") {
|
||||||
|
@ -401,6 +416,9 @@ func startServer(c *cli.Context) {
|
||||||
Log.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
|
Log.WithError(err).Fatalf("Cannot resolve path %s", c.String("origincert"))
|
||||||
}
|
}
|
||||||
ok, err := fileExists(originCertPath)
|
ok, err := fileExists(originCertPath)
|
||||||
|
if err != nil {
|
||||||
|
Log.Fatalf("Cannot check if origin cert exists at path %s", c.String("origincert"))
|
||||||
|
}
|
||||||
if !ok {
|
if !ok {
|
||||||
Log.Fatalf(`Cannot find a valid certificate for your origin at the path:
|
Log.Fatalf(`Cannot find a valid certificate for your origin at the path:
|
||||||
|
|
||||||
|
@ -432,6 +450,11 @@ If you don't have a certificate signed by Cloudflare, run the command:
|
||||||
ExpectContinueTimeout: 1 * time.Second,
|
ExpectContinueTimeout: 1 * time.Second,
|
||||||
TLSClientConfig: &tls.Config{RootCAs: tlsconfig.LoadOriginCertsPool()},
|
TLSClientConfig: &tls.Config{RootCAs: tlsconfig.LoadOriginCertsPool()},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !c.IsSet("hello-world") && c.IsSet("origin-server-name") {
|
||||||
|
httpTransport.TLSClientConfig.ServerName = c.String("origin-server-name")
|
||||||
|
}
|
||||||
|
|
||||||
tunnelConfig := &origin.TunnelConfig{
|
tunnelConfig := &origin.TunnelConfig{
|
||||||
EdgeAddrs: c.StringSlice("edge"),
|
EdgeAddrs: c.StringSlice("edge"),
|
||||||
OriginUrl: url,
|
OriginUrl: url,
|
||||||
|
@ -562,25 +585,41 @@ func fileExists(path string) (bool, error) {
|
||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func findInputSourceContext(context *cli.Context) (altsrc.InputSourceContext, error) {
|
// returns the first path that contains a cert.pem file. If none of the defaultConfigDirs
|
||||||
if context.IsSet("config") {
|
// (differs by OS for legacy reasons) contains a cert.pem file, return empty string
|
||||||
return altsrc.NewYamlSourceFromFile(context.String("config"))
|
func findDefaultOriginCertPath() string {
|
||||||
}
|
for _, defaultConfigDir := range defaultConfigDirs {
|
||||||
dirPath, err := homedir.Expand(defaultConfigDir)
|
originCertPath, _ := homedir.Expand(filepath.Join(defaultConfigDir, credentialFile))
|
||||||
if err != nil {
|
if ok, _ := fileExists(originCertPath); ok {
|
||||||
return nil, nil
|
return originCertPath
|
||||||
}
|
|
||||||
for _, path := range []string{
|
|
||||||
filepath.Join(dirPath, "/config.yml"),
|
|
||||||
filepath.Join(dirPath, "/config.yaml"),
|
|
||||||
} {
|
|
||||||
ok, err := fileExists(path)
|
|
||||||
if ok {
|
|
||||||
return altsrc.NewYamlSourceFromFile(path)
|
|
||||||
} else if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// returns the firt path that contains a config file. If none of the combination of
|
||||||
|
// defaultConfigDirs (differs by OS for legacy reasons) and defaultConfigFiles
|
||||||
|
// contains a config file, return empty string
|
||||||
|
func findDefaultConfigPath() string {
|
||||||
|
for _, configDir := range defaultConfigDirs {
|
||||||
|
for _, configFile := range defaultConfigFiles {
|
||||||
|
dirPath, err := homedir.Expand(configDir)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
path := filepath.Join(dirPath, configFile)
|
||||||
|
if ok, _ := fileExists(path); ok {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func findInputSourceContext(context *cli.Context) (altsrc.InputSourceContext, error) {
|
||||||
|
if context.String("config") != "" {
|
||||||
|
return altsrc.NewYamlSourceFromFile(context.String("config"))
|
||||||
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,22 +658,26 @@ func validateUrl(c *cli.Context) (string, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func initLogFile(c *cli.Context, protoLogger *logrus.Logger) error {
|
func initLogFile(c *cli.Context, protoLogger *logrus.Logger) error {
|
||||||
fileMode := os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_TRUNC
|
filePath, err := homedir.Expand(c.String("logfile"))
|
||||||
|
if err != nil {
|
||||||
|
return errors.Wrap(err, "Cannot resolve logfile path")
|
||||||
|
}
|
||||||
|
|
||||||
|
fileMode := os.O_WRONLY | os.O_APPEND | os.O_CREATE | os.O_TRUNC
|
||||||
// do not truncate log file if the client has been autoupdated
|
// do not truncate log file if the client has been autoupdated
|
||||||
if c.Bool("is-autoupdated") {
|
if c.Bool("is-autoupdated") {
|
||||||
fileMode = os.O_WRONLY|os.O_APPEND|os.O_CREATE
|
fileMode = os.O_WRONLY | os.O_APPEND | os.O_CREATE
|
||||||
}
|
}
|
||||||
f, err := os.OpenFile(c.String("logfile"), fileMode, 0664)
|
f, err := os.OpenFile(filePath, fileMode, 0664)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors.Wrap(err, fmt.Sprintf("Cannot open file %s", c.String("logfile")))
|
errors.Wrap(err, fmt.Sprintf("Cannot open file %s", filePath))
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
pathMap := lfshook.PathMap{
|
pathMap := lfshook.PathMap{
|
||||||
logrus.InfoLevel: c.String("logfile"),
|
logrus.InfoLevel: filePath,
|
||||||
logrus.ErrorLevel: c.String("logfile"),
|
logrus.ErrorLevel: filePath,
|
||||||
logrus.FatalLevel: c.String("logfile"),
|
logrus.FatalLevel: filePath,
|
||||||
logrus.PanicLevel: c.String("logfile"),
|
logrus.PanicLevel: filePath,
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
|
Log.Hooks.Add(lfshook.NewHook(pathMap, &logrus.JSONFormatter{}))
|
||||||
|
|
|
@ -73,18 +73,21 @@ func runCommand(command string, args ...string) error {
|
||||||
cmd := exec.Command(command, args...)
|
cmd := exec.Command(command, args...)
|
||||||
stderr, err := cmd.StderrPipe()
|
stderr, err := cmd.StderrPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error getting stderr pipe")
|
||||||
return fmt.Errorf("error getting stderr pipe: %v", err)
|
return fmt.Errorf("error getting stderr pipe: %v", err)
|
||||||
}
|
}
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("error starting %s", command)
|
||||||
return fmt.Errorf("error starting %s: %v", command, err)
|
return fmt.Errorf("error starting %s: %v", command, err)
|
||||||
}
|
}
|
||||||
commandErr, _ := ioutil.ReadAll(stderr)
|
commandErr, _ := ioutil.ReadAll(stderr)
|
||||||
if len(commandErr) > 0 {
|
if len(commandErr) > 0 {
|
||||||
fmt.Fprintf(os.Stderr, "%s: %s", command, commandErr)
|
Log.Errorf("%s: %s", command, commandErr)
|
||||||
}
|
}
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("%s returned error", command)
|
||||||
return fmt.Errorf("%s returned with error: %v", command, err)
|
return fmt.Errorf("%s returned with error: %v", command, err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -116,9 +119,8 @@ func openFile(path string, create bool) (file *os.File, exists bool, err error)
|
||||||
return file, false, err
|
return file, false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyCertificate(configDir string) error {
|
func copyCertificate(srcConfigDir, destConfigDir string) error {
|
||||||
// Copy certificate
|
destCredentialPath := filepath.Join(destConfigDir, credentialFile)
|
||||||
destCredentialPath := filepath.Join(configDir, credentialFile)
|
|
||||||
destFile, exists, err := openFile(destCredentialPath, true)
|
destFile, exists, err := openFile(destCredentialPath, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -128,13 +130,14 @@ func copyCertificate(configDir string) error {
|
||||||
}
|
}
|
||||||
defer destFile.Close()
|
defer destFile.Close()
|
||||||
|
|
||||||
srcCredentialPath := filepath.Join(defaultConfigDir, credentialFile)
|
srcCredentialPath := filepath.Join(srcConfigDir, credentialFile)
|
||||||
srcFile, _, err := openFile(srcCredentialPath, false)
|
srcFile, _, err := openFile(srcCredentialPath, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer srcFile.Close()
|
defer srcFile.Close()
|
||||||
|
|
||||||
|
// Copy certificate
|
||||||
_, err = io.Copy(destFile, srcFile)
|
_, err = io.Copy(destFile, srcFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to copy %s to %s: %v", srcCredentialPath, destCredentialPath, err)
|
return fmt.Errorf("unable to copy %s to %s: %v", srcCredentialPath, destCredentialPath, err)
|
||||||
|
@ -143,19 +146,20 @@ func copyCertificate(configDir string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyCredentials(configDir string) error {
|
func copyCredentials(serviceConfigDir, defaultConfigDir, defaultConfigFile string) error {
|
||||||
if err := ensureConfigDirExists(configDir); err != nil {
|
if err := ensureConfigDirExists(serviceConfigDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := copyCertificate(configDir); err != nil {
|
if err := copyCertificate(defaultConfigDir, serviceConfigDir); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy or create config
|
// Copy or create config
|
||||||
destConfigPath := filepath.Join(configDir, configFile)
|
destConfigPath := filepath.Join(serviceConfigDir, defaultConfigFile)
|
||||||
destFile, exists, err := openFile(destConfigPath, true)
|
destFile, exists, err := openFile(destConfigPath, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("cannot open %s", destConfigPath)
|
||||||
return err
|
return err
|
||||||
} else if exists {
|
} else if exists {
|
||||||
// config already exists, do nothing
|
// config already exists, do nothing
|
||||||
|
@ -163,7 +167,7 @@ func copyCredentials(configDir string) error {
|
||||||
}
|
}
|
||||||
defer destFile.Close()
|
defer destFile.Close()
|
||||||
|
|
||||||
srcConfigPath := filepath.Join(defaultConfigDir, configFile)
|
srcConfigPath := filepath.Join(defaultConfigDir, defaultConfigFile)
|
||||||
srcFile, _, err := openFile(srcConfigPath, false)
|
srcFile, _, err := openFile(srcConfigPath, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Your service needs a config file that at least specifies the hostname option.")
|
fmt.Println("Your service needs a config file that at least specifies the hostname option.")
|
||||||
|
@ -181,7 +185,7 @@ func copyCredentials(configDir string) error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err)
|
return fmt.Errorf("unable to copy %s to %s: %v", srcConfigPath, destConfigPath, err)
|
||||||
}
|
}
|
||||||
fmt.Printf("Copied %s to %s", srcConfigPath, destConfigPath)
|
Log.Infof("Copied %s to %s", srcConfigPath, destConfigPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -9,7 +9,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
log "github.com/sirupsen/logrus"
|
|
||||||
cli "gopkg.in/urfave/cli.v2"
|
cli "gopkg.in/urfave/cli.v2"
|
||||||
|
|
||||||
"golang.org/x/sys/windows/svc"
|
"golang.org/x/sys/windows/svc"
|
||||||
|
@ -42,7 +41,7 @@ func runApp(app *cli.App) {
|
||||||
|
|
||||||
isIntSess, err := svc.IsAnInteractiveSession()
|
isIntSess, err := svc.IsAnInteractiveSession()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
|
Log.Fatalf("failed to determine if we are running in an interactive session: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if isIntSess {
|
if isIntSess {
|
||||||
|
@ -52,11 +51,14 @@ func runApp(app *cli.App) {
|
||||||
|
|
||||||
elog, err := eventlog.Open(windowsServiceName)
|
elog, err := eventlog.Open(windowsServiceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("Cannot open event log for %s", windowsServiceName)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer elog.Close()
|
defer elog.Close()
|
||||||
|
|
||||||
elog.Info(1, fmt.Sprintf("%s service starting", windowsServiceName))
|
elog.Info(1, fmt.Sprintf("%s service starting", windowsServiceName))
|
||||||
|
// Run executes service name by calling windowsService which is a Handler
|
||||||
|
// interface that implements Execute method
|
||||||
err = svc.Run(windowsServiceName, &windowsService{app: app, elog: elog})
|
err = svc.Run(windowsServiceName, &windowsService{app: app, elog: elog})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
elog.Error(1, fmt.Sprintf("%s service failed: %v", windowsServiceName, err))
|
elog.Error(1, fmt.Sprintf("%s service failed: %v", windowsServiceName, err))
|
||||||
|
@ -70,10 +72,12 @@ type windowsService struct {
|
||||||
elog *eventlog.Log
|
elog *eventlog.Log
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// called by the package code at the start of the service
|
||||||
func (s *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
func (s *windowsService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (ssec bool, errno uint32) {
|
||||||
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
|
||||||
changes <- svc.Status{State: svc.StartPending}
|
changes <- svc.Status{State: svc.StartPending}
|
||||||
go s.app.Run(args)
|
go s.app.Run(args)
|
||||||
|
|
||||||
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
|
||||||
loop:
|
loop:
|
||||||
for {
|
for {
|
||||||
|
@ -81,8 +85,13 @@ loop:
|
||||||
case c := <-r:
|
case c := <-r:
|
||||||
switch c.Cmd {
|
switch c.Cmd {
|
||||||
case svc.Interrogate:
|
case svc.Interrogate:
|
||||||
|
s.elog.Info(1, fmt.Sprintf("control request 1 #%d", c))
|
||||||
changes <- c.CurrentStatus
|
changes <- c.CurrentStatus
|
||||||
case svc.Stop, svc.Shutdown:
|
case svc.Stop:
|
||||||
|
s.elog.Info(1, "received stop control request")
|
||||||
|
break loop
|
||||||
|
case svc.Shutdown:
|
||||||
|
s.elog.Info(1, "received shutdown control request")
|
||||||
break loop
|
break loop
|
||||||
default:
|
default:
|
||||||
s.elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
|
s.elog.Error(1, fmt.Sprintf("unexpected control request #%d", c))
|
||||||
|
@ -95,50 +104,62 @@ loop:
|
||||||
}
|
}
|
||||||
|
|
||||||
func installWindowsService(c *cli.Context) error {
|
func installWindowsService(c *cli.Context) error {
|
||||||
|
Log.Infof("Installing Cloudflare Warp Windows service")
|
||||||
exepath, err := os.Executable()
|
exepath, err := os.Executable()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.Infof("Cannot find path name that start the process")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
m, err := mgr.Connect()
|
m, err := mgr.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.WithError(err).Infof("Cannot establish a connection to the service control manager")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer m.Disconnect()
|
defer m.Disconnect()
|
||||||
s, err := m.OpenService(windowsServiceName)
|
s, err := m.OpenService(windowsServiceName)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
s.Close()
|
s.Close()
|
||||||
|
Log.Errorf("service %s already exists", windowsServiceName)
|
||||||
return fmt.Errorf("service %s already exists", windowsServiceName)
|
return fmt.Errorf("service %s already exists", windowsServiceName)
|
||||||
}
|
}
|
||||||
s, err = m.CreateService(windowsServiceName, exepath, mgr.Config{DisplayName: windowsServiceDescription}, "is", "auto-started")
|
config := mgr.Config{StartType: mgr.StartAutomatic, DisplayName: windowsServiceDescription}
|
||||||
|
s, err = m.CreateService(windowsServiceName, exepath, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.Infof("Cannot install service %s", windowsServiceName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
|
err = eventlog.InstallAsEventCreate(windowsServiceName, eventlog.Error|eventlog.Warning|eventlog.Info)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
s.Delete()
|
s.Delete()
|
||||||
|
Log.WithError(err).Infof("Cannot install event logger")
|
||||||
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
|
return fmt.Errorf("SetupEventLogSource() failed: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func uninstallWindowsService(c *cli.Context) error {
|
func uninstallWindowsService(c *cli.Context) error {
|
||||||
|
Log.Infof("Uninstalling Cloudflare Warp Windows Service")
|
||||||
m, err := mgr.Connect()
|
m, err := mgr.Connect()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.Infof("Cannot establish a connection to the service control manager")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
defer m.Disconnect()
|
defer m.Disconnect()
|
||||||
s, err := m.OpenService(windowsServiceName)
|
s, err := m.OpenService(windowsServiceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.Infof("service %s is not installed", windowsServiceName)
|
||||||
return fmt.Errorf("service %s is not installed", windowsServiceName)
|
return fmt.Errorf("service %s is not installed", windowsServiceName)
|
||||||
}
|
}
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
err = s.Delete()
|
err = s.Delete()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.Errorf("Cannot delete service %s", windowsServiceName)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
err = eventlog.Remove(windowsServiceName)
|
err = eventlog.Remove(windowsServiceName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
Log.Infof("Cannot remove event logger")
|
||||||
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
|
return fmt.Errorf("RemoveEventLogSource() failed: %s", err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -20,7 +20,7 @@ func IsWebSocketUpgrade(req *http.Request) bool {
|
||||||
|
|
||||||
// ClientConnect creates a WebSocket client connection for provided request. Caller is responsible for closing.
|
// ClientConnect creates a WebSocket client connection for provided request. Caller is responsible for closing.
|
||||||
func ClientConnect(req *http.Request, tlsClientConfig *tls.Config) (*websocket.Conn, *http.Response, error) {
|
func ClientConnect(req *http.Request, tlsClientConfig *tls.Config) (*websocket.Conn, *http.Response, error) {
|
||||||
req.URL.Scheme = "wss"
|
req.URL.Scheme = changeRequestScheme(req)
|
||||||
d := &websocket.Dialer{TLSClientConfig: tlsClientConfig}
|
d := &websocket.Dialer{TLSClientConfig: tlsClientConfig}
|
||||||
conn, response, err := d.Dial(req.URL.String(), nil)
|
conn, response, err := d.Dial(req.URL.String(), nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -75,3 +75,16 @@ func sha1Base64(str string) string {
|
||||||
func generateAcceptKey(req *http.Request) string {
|
func generateAcceptKey(req *http.Request) string {
|
||||||
return sha1Base64(req.Header.Get("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
return sha1Base64(req.Header.Get("Sec-WebSocket-Key") + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// changeRequestScheme is needed as the gorilla websocket library requires the ws scheme.
|
||||||
|
// (even though it changes it back to http/https, but ¯\_(ツ)_/¯.)
|
||||||
|
func changeRequestScheme(req *http.Request) string {
|
||||||
|
switch req.URL.Scheme {
|
||||||
|
case "https":
|
||||||
|
return "wss"
|
||||||
|
case "http":
|
||||||
|
return "ws"
|
||||||
|
default:
|
||||||
|
return req.URL.Scheme
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue