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 package cli
// AddRoot CLI command // AddRoot CLI command
func AddRoot(args []string) { func AddRoot(basePath, authToken string, args []string) {
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -13,6 +13,17 @@
package cli 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 package cli
// Set CLI command // Set CLI command
func Set(args []string) { func Set(basePath, authToken string, args []string) {
} }

View File

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

View File

@ -13,6 +13,37 @@
package cli package cli
import (
"encoding/json"
"fmt"
"net/http"
"os"
"zerotier/pkg/zerotier"
)
// Status shows service status info // 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 ( import (
"flag" "flag"
"fmt" "fmt"
"io/ioutil"
"os" "os"
"path"
"runtime"
"strings"
"zerotier/cmd/zerotier/cli" "zerotier/cmd/zerotier/cli"
"zerotier/pkg/zerotier" "zerotier/pkg/zerotier"
) )
var copyrightText = fmt.Sprintf(`ZeroTier Network Virtualization Service Version %d.%d.%d var copyrightText = fmt.Sprintf(`ZeroTier Network Virtualization Service Version %d.%d.%d
(c)2019 ZeroTier, Inc. (c)2019 ZeroTier, Inc.
Licensed under the ZeroTier BSL (see LICENSE.txt)`, Licensed under the ZeroTier BSL (see LICENSE.txt)`, zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
func printHelp() { func printHelp() {
fmt.Println(copyrightText + ` fmt.Println(copyrightText + `
@ -78,20 +81,41 @@ used to explicitly specify a location.
`) `)
} }
/* func readAuthToken(basePath string) string {
func nodeStart() { data, _ := ioutil.ReadFile(path.Join(basePath, "authtoken.secret"))
osSignalChannel := make(chan os.Signal, 2) if len(data) > 0 {
signal.Notify(osSignalChannel, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT, syscall.SIGBUS) return string(data)
signal.Ignore(syscall.SIGUSR1, syscall.SIGUSR2) }
go func() { userHome, _ := os.UserHomeDir()
<-osSignalChannel 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() { func main() {
globalOpts := flag.NewFlagSet("global", flag.ContinueOnError) globalOpts := flag.NewFlagSet("global", flag.ContinueOnError)
hflag := globalOpts.Bool("h", false, "") // support -h to be canonical with other Unix utilities 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:]) err := globalOpts.Parse(os.Args[1:])
if err != nil { if err != nil {
printHelp() printHelp()
@ -109,6 +133,22 @@ func main() {
cmdArgs = args[1:] 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] { switch args[0] {
case "help": case "help":
printHelp() printHelp()
@ -117,27 +157,27 @@ func main() {
fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision) fmt.Printf("%d.%d.%d\n", zerotier.CoreVersionMajor, zerotier.CoreVersionMinor, zerotier.CoreVersionRevision)
os.Exit(0) os.Exit(0)
case "service": case "service":
cli.Service(cmdArgs) cli.Service(basePath, authToken, cmdArgs)
case "status": case "status":
cli.Status(cmdArgs) cli.Status(basePath, authToken, cmdArgs, *jflag)
case "peers": case "peers":
cli.Peers(cmdArgs) cli.Peers(basePath, authToken, cmdArgs)
case "roots": case "roots":
cli.Roots(cmdArgs) cli.Roots(basePath, authToken, cmdArgs)
case "addroot": case "addroot":
cli.AddRoot(cmdArgs) cli.AddRoot(basePath, authToken, cmdArgs)
case "removeroot": case "removeroot":
cli.RemoveRoot(cmdArgs) cli.RemoveRoot(basePath, authToken, cmdArgs)
case "networks": case "networks":
cli.Networks(cmdArgs) cli.Networks(basePath, authToken, cmdArgs)
case "join": case "join":
cli.Join(cmdArgs) cli.Join(basePath, authToken, cmdArgs)
case "leave": case "leave":
cli.Leave(cmdArgs) cli.Leave(basePath, authToken, cmdArgs)
case "show": case "show":
cli.Show(cmdArgs) cli.Show(basePath, authToken, cmdArgs)
case "set": case "set":
cli.Set(cmdArgs) cli.Set(basePath, authToken, cmdArgs)
} }
printHelp() printHelp()

View File

@ -14,6 +14,7 @@
package zerotier package zerotier
import ( import (
"bytes"
secrand "crypto/rand" secrand "crypto/rand"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -27,7 +28,58 @@ import (
acl "github.com/hectane/go-acl" 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 Address Address
Clock int64 Clock int64
Config LocalConfig Config LocalConfig
@ -42,7 +94,8 @@ type apiStatus struct {
VersionBuild int VersionBuild int
} }
type apiNetwork struct { // APINetwork is the object returned by API network inquiries
type APINetwork struct {
Config *NetworkConfig Config *NetworkConfig
Settings *NetworkLocalSettings Settings *NetworkLocalSettings
MulticastSubscriptions []*MulticastGroup 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 { func apiCheckAuth(out http.ResponseWriter, req *http.Request, token string) bool {
ah := req.Header.Get("X-ZT1-Auth") ah := req.Header.Get("Authorization")
if len(ah) > 0 && strings.TrimSpace(ah) == token { if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) {
return true return true
} }
ah = req.Header.Get("Authorization") ah = req.Header.Get("X-ZT1-Auth")
if len(ah) > 0 && strings.TrimSpace(ah) == ("bearer "+token) { if len(ah) > 0 && strings.TrimSpace(ah) == token {
return true return true
} }
apiSendObj(out, req, http.StatusUnauthorized, nil) apiSendObj(out, req, http.StatusUnauthorized, nil)
@ -134,7 +187,7 @@ func createAPIServer(basePath string, node *Node) (*http.Server, error) {
} }
apiSetStandardHeaders(out) apiSetStandardHeaders(out)
if req.Method == http.MethodGet || req.Method == http.MethodHead { if req.Method == http.MethodGet || req.Method == http.MethodHead {
apiSendObj(out, req, http.StatusOK, &apiStatus{ apiSendObj(out, req, http.StatusOK, &APIStatus{
Address: node.Address(), Address: node.Address(),
Clock: TimeMs(), Clock: TimeMs(),
Config: node.LocalConfig(), 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 { if err != nil {
return nil, err return nil, err
} }

View File

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

View File

@ -16,9 +16,12 @@
package zerotier package zerotier
import ( import (
"context"
"net" "net"
"net/http"
"os" "os"
"path" "path"
"time"
) )
func createNamedSocketListener(basePath, name string) (net.Listener, error) { func createNamedSocketListener(basePath, name string) (net.Listener, error) {
@ -26,3 +29,17 @@ func createNamedSocketListener(basePath, name string) (net.Listener, error) {
os.Remove(apiSockPath) os.Remove(apiSockPath)
return net.Listen("unix", 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
}