diff --git a/README.md b/README.md index 1600eff..d7dbb68 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,7 @@ CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -mod vendor -ldflags "-H window ``` The binary can be built with `VENDOR_ID` and `CLIENT_SECRET` built into the binary. +You can also change the `serviceName` and `serviceDescription` at build time. See `examples/run-as-client.sh`. ### Configure diff --git a/cmd/telebit/telebit.go b/cmd/telebit/telebit.go index 59df3da..3aea85c 100644 --- a/cmd/telebit/telebit.go +++ b/cmd/telebit/telebit.go @@ -22,6 +22,7 @@ import ( "git.rootprojects.org/root/telebit/dbg" tbDns01 "git.rootprojects.org/root/telebit/internal/dns01" "git.rootprojects.org/root/telebit/internal/http01" + "git.rootprojects.org/root/telebit/internal/service" "git.rootprojects.org/root/telebit/iplist" "git.rootprojects.org/root/telebit/mgmt" "git.rootprojects.org/root/telebit/mgmt/authstore" @@ -61,8 +62,15 @@ var ( GitVersion = "v0.0.0-pre0+0000000" // GitTimestamp refers to the timestamp of the most recent commit GitTimestamp = "0000-00-00T00:00:00+0000" + + // serviceName is the service name + serviceName = "telebit" + + // serviceDesc + serviceDesc = "securely relay traffic through telebit.io" ) +// Forward describes how to route a network connection type Forward struct { scheme string pattern string @@ -81,6 +89,23 @@ var VendorID string var ClientSecret string func main() { + if len(os.Args) >= 2 { + if "version" == strings.TrimLeft(os.Args[1], "-") { + fmt.Printf("telebit %s (%s) %s\n", GitVersion, GitRev[:7], GitTimestamp) + os.Exit(exitOk) + return + } + } + + if len(os.Args) >= 2 { + if "install" == os.Args[1] { + if err := service.Install(); nil != err { + fmt.Fprintf(os.Stderr, "%v", err) + } + return + } + } + var domains []string var forwards []Forward var portForwards []Forward @@ -126,13 +151,6 @@ func main() { ) } - if len(os.Args) >= 2 { - if "version" == os.Args[1] { - fmt.Printf("telebit %s %s %s\n", GitVersion, GitRev[:7], GitTimestamp) - os.Exit(exitOk) - } - } - if len(*acmeDirectory) > 0 { if *acmeStaging { fmt.Fprintf(os.Stderr, "pick either acme-directory or acme-staging\n") @@ -829,19 +847,6 @@ func getACMEProvider(acmeRelay, token *string) (challenge.Provider, error) { return provider, nil } -type ACMEProvider struct { - BaseURL string - provider challenge.Provider -} - -func (p *ACMEProvider) Present(domain, token, keyAuth string) error { - return p.provider.Present(domain, token, keyAuth) -} - -func (p *ACMEProvider) CleanUp(domain, token, keyAuth string) error { - return p.provider.CleanUp(domain, token, keyAuth) -} - // newDuckDNSProvider is for the sake of demoing the tunnel func newDuckDNSProvider(token string) (*duckdns.DNSProvider, error) { config := duckdns.NewDefaultConfig() diff --git a/examples/run-as-client.sh b/examples/run-as-client.sh index 38a0237..9baebd1 100644 --- a/examples/run-as-client.sh +++ b/examples/run-as-client.sh @@ -9,7 +9,7 @@ source .env VENDOR_ID="${VENDOR_ID:-"${VENDOR_ID:-"test-id"}"}" CLIENT_SECRET="${CLIENT_SECRET:-}" #go build -mod=vendor -o ./telebit \ -# -ldflags="-X 'main.VendorID=$VENDOR_ID' -X 'main.ClientSecret=$CLIENT_SECRET'" \ +# -ldflags="-X 'main.VendorID=$VENDOR_ID' -X 'main.ClientSecret=$CLIENT_SECRET' -X 'main.serviceName=telebit' -X 'main.serviceDesc=securely tunnel through telebit.io'" \ # cmd/telebit/*.go go build -mod=vendor -o telebit \ cmd/telebit/*.go diff --git a/go.mod b/go.mod index f708664..9906396 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect github.com/shurcooL/vfsgen v0.0.0-20181202132449-6a9ea43bcacd github.com/stretchr/testify v1.6.1 - golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect + golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae golang.org/x/text v0.3.3 // indirect golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f // indirect gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect diff --git a/internal/service/launchctl_darwin.go b/internal/service/launchctl_darwin.go new file mode 100644 index 0000000..dc784d6 --- /dev/null +++ b/internal/service/launchctl_darwin.go @@ -0,0 +1,10 @@ +package service + +import ( + "errors" +) + +// Install ensures a systemd service is active +func Install() error { + return errors.New("'install' not supported for system services on this platform") +} diff --git a/internal/service/other.go b/internal/service/other.go new file mode 100644 index 0000000..d7ad0b4 --- /dev/null +++ b/internal/service/other.go @@ -0,0 +1,12 @@ +// +build !darwin,!linux,!windows + +package service + +import ( + "errors" +) + +// Install ensures a windows service is active +func Install() error { + return errors.New("not supported for system services on this platform") +} diff --git a/internal/service/svc_windows.go b/internal/service/svc_windows.go new file mode 100644 index 0000000..131120c --- /dev/null +++ b/internal/service/svc_windows.go @@ -0,0 +1,79 @@ +package service + +import ( + "errors" + "fmt" + "os" + "path/filepath" + + "golang.org/x/sys/windows/svc/eventlog" + "golang.org/x/sys/windows/svc/mgr" +) + +// Install ensures a windows service is active +func Install(name, desc string) error { + exepath, err := getExecPath(os.Args[0]) + if err != nil { + return err + } + + return installService(name, desc, exepath) +} + +func getExecPath(exepath string) (string, error) { + p, err := filepath.Abs(exepath) + if err != nil { + return "", err + } + + fi, err := os.Stat(p) + if err == nil { + if fi.Mode().IsRegular() { + return p, nil + } + } + + if 0 == len(filepath.Ext(p)) { + var err error + p += ".exe" + fi, err = os.Stat(p) + if nil != err { + return err + } + } + + if !fi.Mode().IsRegular() { + // this should never happen + return "", errors.New("not a regular file") + } + + return p, nil +} + +func install(name, desc, exepath string) error { + m, err := mgr.Connect() + if nil != err { + return err + } + defer m.Disconnect() + + s, err := m.OpenService(name) + if nil == err { + s.Close() + return nil + } + + s, err = m.CreateService(name, exepath, mgr.Config{DisplayName: desc}, "is", "auto-started") + if err != nil { + return err + } + defer s.Close() + + err = eventlog.InstallAsEventCreate(name, eventlog.Error|eventlog.Warning|eventlog.Info) + if nil != err { + s.Delete() + return fmt.Errorf("could not install system service: %v", err) + } + + return nil +} diff --git a/internal/service/systemd_linux.go b/internal/service/systemd_linux.go new file mode 100644 index 0000000..dc784d6 --- /dev/null +++ b/internal/service/systemd_linux.go @@ -0,0 +1,10 @@ +package service + +import ( + "errors" +) + +// Install ensures a systemd service is active +func Install() error { + return errors.New("'install' not supported for system services on this platform") +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 5f62dda..0a2b060 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -162,6 +162,9 @@ golang.org/x/sys/internal/unsafeheader golang.org/x/sys/unix golang.org/x/sys/windows golang.org/x/sys/windows/registry +golang.org/x/sys/windows/svc +golang.org/x/sys/windows/svc/eventlog +golang.org/x/sys/windows/svc/mgr # golang.org/x/text v0.3.3 ## explicit golang.org/x/text/secure/bidirule