This commit is contained in:
Adam Ierymenko 2019-09-24 16:44:29 -07:00
parent e3d47e588a
commit e5bd230fb0
No known key found for this signature in database
GPG Key ID: C8877CF2D7A5D7F3
15 changed files with 197 additions and 42 deletions

View File

@ -14,5 +14,5 @@
package cli
// AddRoot CLI command
func AddRoot(args []string) {
func AddRoot(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Join CLI command
func Join(args []string) {
func Join(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Leave CLI command
func Leave(args []string) {
func Leave(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Networks CLI command
func Networks(args []string) {
func Networks(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Peers CLI command
func Peers(args []string) {
func Peers(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// RemoveRoot CLI command
func RemoveRoot(args []string) {
func RemoveRoot(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Roots CLI command
func Roots(args []string) {
func Roots(basePath, authToken string, args []string) {
}

View File

@ -13,6 +13,17 @@
package cli
// Service is "zerotier service ..."
func Service(args []string) {
/*
func nodeStart() {
osSignalChannel := make(chan os.Signal, 2)
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2)
go func() {
<-osSignalChannel
}()
}
*/
// Service is "zerotier service ..."
func Service(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Set CLI command
func Set(args []string) {
func Set(basePath, authToken string, args []string) {
}

View File

@ -14,5 +14,5 @@
package cli
// Show CLI command
func Show(args []string) {
func Show(basePath, authToken string, args []string) {
}

View File

@ -13,6 +13,37 @@
package cli
import (
"encoding/json"
"fmt"
"net/http"
"os"
"zerotier/pkg/zerotier"
)
// Status shows service status info
func Status(args []string) {
func Status(basePath, authToken string, args []string, jsonOutput bool) {
var status zerotier.APIStatus
statusCode, err := zerotier.APIGet(basePath, zerotier.APISocketName, authToken, "/status", &status)
if err != nil {
fmt.Printf("FATAL: API response code %d: %s\n", statusCode, err.Error())
os.Exit(1)
return
}
if statusCode != http.StatusOK {
if statusCode == http.StatusUnauthorized {
fmt.Printf("FATAL: API response code %d: unauthorized (authorization token incorrect)\n", statusCode)
}
fmt.Printf("FATAL: API response code %d\n", statusCode)
os.Exit(1)
return
}
if jsonOutput {
j, _ := json.MarshalIndent(&status, "", " ")
fmt.Println(string(j))
} else {
}
os.Exit(0)
}

View File

@ -16,15 +16,18 @@ package main
import (
"flag"
"fmt"
"io/ioutil"
"os"
"path"
"runtime"
"strings"
"zerotier/cmd/zerotier/cli"
"zerotier/pkg/zerotier"
)
var copyrightText = fmt.Sprintf(`ZeroTier Network Virtualization Service Version %d.%d.%d
(c)2019 ZeroTier, Inc.
Licensed under the ZeroTier BSL (see LICENSE.txt)`,
zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
Licensed under the ZeroTier BSL (see LICENSE.txt)`, zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
func printHelp() {
fmt.Println(copyrightText + `
@ -78,20 +81,41 @@ used to explicitly specify a location.
`)
}
/*
func nodeStart() {
osSignalChannel := make(chan os.Signal, 2)
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2)
go func() {
<-osSignalChannel
}()
func readAuthToken(basePath string) string {
data, _ := ioutil.ReadFile(path.Join(basePath, "authtoken.secret"))
if len(data) > 0 {
return string(data)
}
userHome, _ := os.UserHomeDir()
if len(userHome) > 0 {
if runtime.GOOS == "darwin" {
data, _ = ioutil.ReadFile(userHome + "/Library/Application Support/ZeroTier/authtoken.secret")
if len(data) > 0 {
return string(data)
}
data, _ = ioutil.ReadFile(userHome + "/Library/Application Support/ZeroTier/One/authtoken.secret")
if len(data) > 0 {
return string(data)
}
}
data, _ = ioutil.ReadFile(path.Join(userHome, ".zerotierauth"))
if len(data) > 0 {
return string(data)
}
data, _ = ioutil.ReadFile(path.Join(userHome, ".zeroTierOneAuthToken"))
if len(data) > 0 {
return string(data)
}
}
return ""
}
*/
func main() {
globalOpts := flag.NewFlagSet("global", flag.ContinueOnError)
hflag := globalOpts.Bool("h", false, "") // support -h to be canonical with other Unix utilities
jflag := globalOpts.Bool("j", false, "")
pflag := globalOpts.String("p", "", "")
tflag := globalOpts.String("t", "", "")
err := globalOpts.Parse(os.Args[1:])
if err != nil {
printHelp()
@ -109,6 +133,22 @@ func main() {
cmdArgs = args[1:]
}
basePath := zerotier.PlatformDefaultHomePath
if len(*pflag) > 0 {
basePath = *pflag
}
var authToken string
if len(*tflag) > 0 {
authToken = *tflag
} else {
authToken = readAuthToken(basePath)
}
if len(authToken) == 0 {
fmt.Println("FATAL: unable to read API authorization token from service path or user home ('sudo' may be needed)")
os.Exit(1)
}
authToken = strings.TrimSpace(authToken)
switch args[0] {
case "help":
printHelp()
@ -117,27 +157,27 @@ func main() {
fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
os.Exit(0)
case "service":
cli.Service(cmdArgs)
cli.Service(basePath, authToken, cmdArgs)
case "status":
cli.Status(cmdArgs)
cli.Status(basePath, authToken, cmdArgs, *jflag)
case "peers":
cli.Peers(cmdArgs)
cli.Peers(basePath, authToken, cmdArgs)
case "roots":
cli.Roots(cmdArgs)
cli.Roots(basePath, authToken, cmdArgs)
case "addroot":
cli.AddRoot(cmdArgs)
cli.AddRoot(basePath, authToken, cmdArgs)
case "removeroot":
cli.RemoveRoot(cmdArgs)
cli.RemoveRoot(basePath, authToken, cmdArgs)
case "networks":
cli.Networks(cmdArgs)
cli.Networks(basePath, authToken, cmdArgs)
case "join":
cli.Join(cmdArgs)
cli.Join(basePath, authToken, cmdArgs)
case "leave":
cli.Leave(cmdArgs)
cli.Leave(basePath, authToken, cmdArgs)
case "show":
cli.Show(cmdArgs)
cli.Show(basePath, authToken, cmdArgs)
case "set":
cli.Set(cmdArgs)
cli.Set(basePath, authToken, cmdArgs)
}
printHelp()

View File

@ -14,6 +14,7 @@
package zerotier
import (
"bytes"
secrand "crypto/rand"
"encoding/json"
"fmt"
@ -27,7 +28,58 @@ import (
acl "github.com/hectane/go-acl"
)
type apiStatus struct {
// APISocketName is the default socket name for accessing the API
const APISocketName = "apisocket"
// APIGet makes a query to the API via a Unix domain or windows pipe socket
func APIGet(basePath, socketName, authToken, queryPath string, obj interface{}) (int, error) {
client, err := createNamedSocketHTTPClient(basePath, socketName)
if err != nil {
return http.StatusTeapot, err
}
req, err := http.NewRequest("GET", "http://socket"+queryPath, nil)
if err != nil {
return http.StatusTeapot, err
}
req.Header.Add("Authorization", "bearer "+authToken)
resp, err := client.Do(req)
if err != nil {
return http.StatusTeapot, err
}
err = json.NewDecoder(resp.Body).Decode(obj)
return resp.StatusCode, err
}
// APIPost posts a JSON object to the API via a Unix domain or windows pipe socket and reads a response
func APIPost(basePath, socketName, authToken, queryPath string, post, result interface{}) (int, error) {
client, err := createNamedSocketHTTPClient(basePath, socketName)
if err != nil {
return http.StatusTeapot, err
}
var data []byte
if post != nil {
data, err = json.Marshal(post)
if err != nil {
return http.StatusTeapot, err
}
} else {
data = []byte("null")
}
req, err := http.NewRequest("POST", "http://socket"+queryPath, bytes.NewReader(data))
if err != nil {
return http.StatusTeapot, err
}
req.Header.Add("Authorization", "bearer "+authToken)
resp, err := client.Do(req)
if err != nil {
return http.StatusTeapot, err
}
err = json.NewDecoder(resp.Body).Decode(result)
return resp.StatusCode, err
}
// APIStatus is the object returned by API status inquiries
type APIStatus struct {
Address Address
Clock int64
Config LocalConfig
@ -42,7 +94,8 @@ type apiStatus struct {
VersionBuild int
}
type apiNetwork struct {
// APINetwork is the object returned by API network inquiries
type APINetwork struct {
Config *NetworkConfig
Settings *NetworkLocalSettings
MulticastSubscriptions []*MulticastGroup
@ -89,12 +142,12 @@ func apiReadObj(out http.ResponseWriter, req *http.Request, dest interface{}) (e
}
func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool {
ah := req.Header.Get("X-ZT1-Auth")
if len(ah) > 0 && strings.TrimSpace(ah) == token {
ah := req.Header.Get("Authorization")
if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
return true
}
ah = req.Header.Get("Authorization")
if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
ah = req.Header.Get("X-ZT1-Auth")
if len(ah) > 0 && strings.TrimSpace(ah) == token {
return true
}
apiSendObj(out, req, http.StatusUnauthorized, nil)
@ -134,7 +187,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, error) {
}
apiSetStandardHeaders(out)
if req.Method == http.MethodGet || req.Method == http.MethodHead {
apiSendObj(out, req, http.StatusOK, &apiStatus{
apiSendObj(out, req, http.StatusOK, &APIStatus{
Address: node.Address(),
Clock: TimeMs(),
Config: node.LocalConfig(),
@ -268,7 +321,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, error) {
}
})
listener, err := createNamedSocketListener(basePath, "apisocket")
listener, err := createNamedSocketListener(basePath, APISocketName)
if err != nil {
return nil, err
}

View File

@ -21,6 +21,9 @@ import (
"unsafe"
)
// ZeroTierLogoChar is the unicode character that is ZeroTier's logo
const ZeroTierLogoChar = "⏁"
var base32StdLowerCase = base32.NewEncoding("abcdefghijklmnopqrstuvwxyz234567").WithPadding(base32.NoPadding)
// TimeMs returns the time in milliseconds since epoch.

View File

@ -16,9 +16,12 @@
package zerotier
import (
"context"
"net"
"net/http"
"os"
"path"
"time"
)
func createNamedSocketListener(basePath, name string) (net.Listener, error) {
@ -26,3 +29,17 @@ func createNamedSocketListener(basePath, name string) (net.Listener, error) {
os.Remove(apiSockPath)
return net.Listen("unix", apiSockPath)
}
func createNamedSocketHTTPClient(basePath, name string) (*http.Client, error) {
apiSockPath := path.Join(basePath, name)
return &http.Client{
Timeout: 10 * time.Second,
Transport: &http.Transport{
DialContext: func(_ context.Context, _, _ string) (net.Conn, error) {
return net.Dial("unix", apiSockPath)
},
DisableKeepAlives: true,
DisableCompression: true,
},
}, nil
}