diff --git a/Dockerfile b/Dockerfile index 746b6364..056030d0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -34,4 +34,3 @@ EXPOSE 8081 EXPOSE 50051 CMD ["./app"] - diff --git a/Dockerfile-netclient b/Dockerfile-netclient new file mode 100644 index 00000000..c8fa9233 --- /dev/null +++ b/Dockerfile-netclient @@ -0,0 +1,26 @@ +#first stage - builder + +FROM golang:latest as builder + +COPY . /app + +WORKDIR /app/netclient + +ENV GO111MODULE=auto + +RUN CGO_ENABLED=0 GOOS=linux go build -o netclient main.go + +#second stage + +FROM debian:latest + +RUN apt-get update && apt-get -y install systemd procps + +WORKDIR /root/ + +COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ + +COPY --from=builder /app/netclient/netclient . + +CMD ["./netclient"] + diff --git a/compose/docker-compose.nodns.yml b/compose/docker-compose.nodns.yml index d05d99bb..ce116ac1 100644 --- a/compose/docker-compose.nodns.yml +++ b/compose/docker-compose.nodns.yml @@ -20,7 +20,7 @@ services: container_name: netmaker depends_on: - mongodb - image: gravitl/netmaker:v0.5 + image: gravitl/netmaker:v0.5.7 volumes: - ./:/local - /etc/netclient:/etc/netclient @@ -37,6 +37,7 @@ services: network_mode: host environment: DNS_MODE: "off" + SERVER_HOST: "192.168.50.149" netmaker-ui: container_name: netmaker-ui depends_on: @@ -47,7 +48,7 @@ services: ports: - "80:80" environment: - BACKEND_URL: "http://HOST_IP:8081" + BACKEND_URL: "http://192.168.50.149:8081" volumes: mongovol: {} dnsconfig: {} diff --git a/config/config.go b/config/config.go index 5fc99133..d7b66d91 100644 --- a/config/config.go +++ b/config/config.go @@ -6,22 +6,23 @@ package config import ( - "os" - "fmt" - "log" - "gopkg.in/yaml.v3" + "fmt" + "log" + "os" + + "gopkg.in/yaml.v3" ) //setting dev by default func getEnv() string { - env := os.Getenv("NETMAKER_ENV") + env := os.Getenv("NETMAKER_ENV") - if len(env) == 0 { - return "dev" - } + if len(env) == 0 { + return "dev" + } - return env + return env } // Config : application config stored as global variable @@ -29,76 +30,77 @@ var Config *EnvironmentConfig // EnvironmentConfig : type EnvironmentConfig struct { - Server ServerConfig `yaml:"server"` - MongoConn MongoConnConfig `yaml:"mongoconn"` - WG WG `yaml:"wg"` + Server ServerConfig `yaml:"server"` + MongoConn MongoConnConfig `yaml:"mongoconn"` + WG WG `yaml:"wg"` } // ServerConfig : type ServerConfig struct { - APIConnString string `yaml:"apiconn"` - APIHost string `yaml:"apihost"` - APIPort string `yaml:"apiport"` - GRPCConnString string `yaml:"grpcconn"` - GRPCHost string `yaml:"grpchost"` - GRPCPort string `yaml:"grpcport"` - GRPCSecure string `yaml:"grpcsecure"` - DefaultNodeLimit int32 `yaml:"defaultnodelimit"` - MasterKey string `yaml:"masterkey"` - AllowedOrigin string `yaml:"allowedorigin"` - RestBackend string `yaml:"restbackend"` - AgentBackend string `yaml:"agentbackend"` - ClientMode string `yaml:"clientmode"` - DNSMode string `yaml:"dnsmode"` - DisableRemoteIPCheck string `yaml:"disableremoteipcheck"` - DisableDefaultNet string `yaml:"disabledefaultnet"` - GRPCSSL string `yaml:"grpcssl"` + CoreDNSAddr string `yaml:"corednsaddr"` + APIConnString string `yaml:"apiconn"` + APIHost string `yaml:"apihost"` + APIPort string `yaml:"apiport"` + GRPCConnString string `yaml:"grpcconn"` + GRPCHost string `yaml:"grpchost"` + GRPCPort string `yaml:"grpcport"` + GRPCSecure string `yaml:"grpcsecure"` + DefaultNodeLimit int32 `yaml:"defaultnodelimit"` + MasterKey string `yaml:"masterkey"` + AllowedOrigin string `yaml:"allowedorigin"` + RestBackend string `yaml:"restbackend"` + AgentBackend string `yaml:"agentbackend"` + ClientMode string `yaml:"clientmode"` + DNSMode string `yaml:"dnsmode"` + DisableRemoteIPCheck string `yaml:"disableremoteipcheck"` + DisableDefaultNet string `yaml:"disabledefaultnet"` + GRPCSSL string `yaml:"grpcssl"` + Verbosity int32 `yaml:"verbosity"` } type WG struct { - RegisterKeyRequired string `yaml:"keyrequired"` - GRPCWireGuard string `yaml:"grpcwg"` - GRPCWGInterface string `yaml:"grpciface"` - GRPCWGAddress string `yaml:"grpcaddr"` - GRPCWGAddressRange string `yaml:"grpcaddrrange"` - GRPCWGPort string `yaml:"grpcport"` - GRPCWGPubKey string `yaml:"pubkey"` - GRPCWGPrivKey string `yaml:"privkey"` + RegisterKeyRequired string `yaml:"keyrequired"` + GRPCWireGuard string `yaml:"grpcwg"` + GRPCWGInterface string `yaml:"grpciface"` + GRPCWGAddress string `yaml:"grpcaddr"` + GRPCWGAddressRange string `yaml:"grpcaddrrange"` + GRPCWGPort string `yaml:"grpcport"` + GRPCWGPubKey string `yaml:"pubkey"` + GRPCWGPrivKey string `yaml:"privkey"` } type MongoConnConfig struct { - User string `yaml:"user"` - Pass string `yaml:"pass"` - Host string `yaml:"host"` - Port string `yaml:"port"` - Opts string `yaml:"opts"` + User string `yaml:"user"` + Pass string `yaml:"pass"` + Host string `yaml:"host"` + Port string `yaml:"port"` + Opts string `yaml:"opts"` } - //reading in the env file func readConfig() *EnvironmentConfig { - file := fmt.Sprintf("config/environments/%s.yaml", getEnv()) - f, err := os.Open(file) - var cfg EnvironmentConfig - if err != nil { - //log.Fatal(err) - //os.Exit(2) - //log.Println("Unable to open config file at config/environments/" + getEnv()) - //log.Println("Will proceed with defaults or enironment variables (no config file).") - return &cfg - } - defer f.Close() + file := fmt.Sprintf("config/environments/%s.yaml", getEnv()) + f, err := os.Open(file) + var cfg EnvironmentConfig + if err != nil { + //log.Fatal(err) + //os.Exit(2) + //log.Println("Unable to open config file at config/environments/" + getEnv()) + //log.Println("Will proceed with defaults or enironment variables (no config file).") + return &cfg + } + defer f.Close() - decoder := yaml.NewDecoder(f) - err = decoder.Decode(&cfg) - if err != nil { - log.Fatal(err) - os.Exit(2) - } - return &cfg + decoder := yaml.NewDecoder(f) + err = decoder.Decode(&cfg) + if err != nil { + log.Fatal(err) + os.Exit(2) + } + return &cfg } func init() { - Config = readConfig() + Config = readConfig() } diff --git a/config/dnsconfig/Corefile b/config/dnsconfig/Corefile index de95397e..c1dbc014 100644 --- a/config/dnsconfig/Corefile +++ b/config/dnsconfig/Corefile @@ -1,4 +1,4 @@ -default comms { +default k8scom tester textx comms { reload 15s hosts /root/dnsconfig/netmaker.hosts { fallthrough diff --git a/config/dnsconfig/netmaker.hosts b/config/dnsconfig/netmaker.hosts index 9babe44e..e69de29b 100644 --- a/config/dnsconfig/netmaker.hosts +++ b/config/dnsconfig/netmaker.hosts @@ -1 +0,0 @@ -10.10.10.1 netmaker.default diff --git a/controllers/networkHttpController.go b/controllers/networkHttpController.go index b50d3ace..ba2bce89 100644 --- a/controllers/networkHttpController.go +++ b/controllers/networkHttpController.go @@ -9,6 +9,7 @@ import ( "net/http" "strings" "time" + "github.com/go-playground/validator/v10" "github.com/gorilla/mux" "github.com/gravitl/netmaker/functions" @@ -20,8 +21,11 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) +const ALL_NETWORK_ACCESS = "THIS_USER_HAS_ALL" +const NO_NETWORKS_PRESENT = "THIS_USER_HAS_NONE" + func networkHandlers(r *mux.Router) { - r.HandleFunc("/api/networks", securityCheck(true, http.HandlerFunc(getNetworks))).Methods("GET") + r.HandleFunc("/api/networks", securityCheck(false, http.HandlerFunc(getNetworks))).Methods("GET") r.HandleFunc("/api/networks", securityCheck(true, http.HandlerFunc(createNetwork))).Methods("POST") r.HandleFunc("/api/networks/{networkname}", securityCheck(false, http.HandlerFunc(getNetwork))).Methods("GET") r.HandleFunc("/api/networks/{networkname}", securityCheck(false, http.HandlerFunc(updateNetwork))).Methods("PUT") @@ -30,7 +34,7 @@ func networkHandlers(r *mux.Router) { r.HandleFunc("/api/networks/{networkname}/keyupdate", securityCheck(false, http.HandlerFunc(keyUpdate))).Methods("POST") r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(createAccessKey))).Methods("POST") r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(getAccessKeys))).Methods("GET") - r.HandleFunc("/api/networks/{networkname}/signuptoken", securityCheck(false, http.HandlerFunc(getSignupToken))).Methods("GET") + r.HandleFunc("/api/networks/{networkname}/signuptoken", securityCheck(false, http.HandlerFunc(getSignupToken))).Methods("GET") r.HandleFunc("/api/networks/{networkname}/keys/{name}", securityCheck(false, http.HandlerFunc(deleteAccessKey))).Methods("DELETE") } @@ -45,7 +49,7 @@ func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { var params = mux.Vars(r) bearerToken := r.Header.Get("Authorization") - err := SecurityCheck(reqAdmin, params["networkname"], bearerToken) + err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken) if err != nil { if strings.Contains(err.Error(), "does not exist") { errorResponse.Code = http.StatusNotFound @@ -54,18 +58,26 @@ func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc { returnErrorResponse(w, r, errorResponse) return } + networksJson, err := json.Marshal(&networks) + if err != nil { + errorResponse.Message = err.Error() + returnErrorResponse(w, r, errorResponse) + return + } + r.Header.Set("user", username) + r.Header.Set("networks", string(networksJson)) next.ServeHTTP(w, r) } } -func SecurityCheck(reqAdmin bool, netname, token string) error { - hasnetwork := netname != "" +func SecurityCheck(reqAdmin bool, netname, token string) (error, []string, string) { + networkexists, err := functions.NetworkExists(netname) if err != nil { - return err + return err, nil, "" } - if hasnetwork && !networkexists { - return errors.New("This network does not exist") + if netname != "" && !networkexists { + return errors.New("This network does not exist"), nil, "" } var hasBearer = true @@ -77,23 +89,30 @@ func SecurityCheck(reqAdmin bool, netname, token string) error { } else { authToken = tokenSplit[1] } + userNetworks := []string{} //all endpoints here require master so not as complicated - if !hasBearer || !authenticateMaster(authToken) { - _, networks, isadmin, err := functions.VerifyUserToken(authToken) + isMasterAuthenticated := authenticateMaster(authToken) + username := "" + if !hasBearer || !isMasterAuthenticated { + userName, networks, isadmin, err := functions.VerifyUserToken(authToken) + username = userName if err != nil { - return errors.New("Error verifying user token") + return errors.New("Error verifying user token"), nil, username } if !isadmin && reqAdmin { - return errors.New("You are unauthorized to access this endpoint") - } else if !isadmin && netname != ""{ - if !functions.SliceContains(networks, netname){ - return errors.New("You are unauthorized to access this endpoint") - } - } else if !isadmin { - return errors.New("You are unauthorized to access this endpoint") + return errors.New("You are unauthorized to access this endpoint"), nil, username } + userNetworks = networks + if isadmin { + userNetworks = []string{ALL_NETWORK_ACCESS} + } + } else if isMasterAuthenticated { + userNetworks = []string{ALL_NETWORK_ACCESS} } - return nil + if len(userNetworks) == 0 { + userNetworks = append(userNetworks, NO_NETWORKS_PRESENT) + } + return nil, userNetworks, username } //Consider a more secure way of setting master key @@ -107,16 +126,33 @@ func authenticateMaster(tokenString string) bool { //simple get all networks function func getNetworks(w http.ResponseWriter, r *http.Request) { - allnetworks, err := functions.ListNetworks() - if err != nil { - returnErrorResponse(w, r, formatError(err, "internal")) + headerNetworks := r.Header.Get("networks") + networksSlice := []string{} + marshalErr := json.Unmarshal([]byte(headerNetworks), &networksSlice) + if marshalErr != nil { + returnErrorResponse(w, r, formatError(marshalErr, "internal")) return } + allnetworks := []models.Network{} + err := errors.New("Networks Error") + if networksSlice[0] == ALL_NETWORK_ACCESS { + allnetworks, err = functions.ListNetworks() + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + } else { + for _, network := range networksSlice { + netObject, parentErr := functions.GetParentNetwork(network) + if parentErr == nil { + allnetworks = append(allnetworks, netObject) + } + } + } networks := RemoveComms(allnetworks) - + functions.PrintUserLog(r.Header.Get("user"), "fetched networks.", 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(networks) - return } func RemoveComms(networks []models.Network) []models.Network { @@ -137,13 +173,13 @@ func RemoveComms(networks []models.Network) []models.Network { func ValidateNetworkUpdate(network models.NetworkUpdate) error { v := validator.New() - _ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool { - if fl.Field().String() == "" { + _ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool { + if fl.Field().String() == "" { return true } - inCharSet := functions.NameInNetworkCharSet(fl.Field().String()) - return inCharSet - }) + inCharSet := functions.NameInNetworkCharSet(fl.Field().String()) + return inCharSet + }) // _ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool { // isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String()) @@ -231,6 +267,7 @@ func getNetwork(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "fetched network "+netname, 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(network) } @@ -257,6 +294,7 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "updated key on network "+netname, 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(network) } @@ -329,7 +367,8 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var params = mux.Vars(r) var network models.Network - network, err := functions.GetParentNetwork(params["networkname"]) + netname := params["networkname"] + network, err := functions.GetParentNetwork(netname) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return @@ -359,45 +398,47 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) { return } + functions.PrintUserLog(r.Header.Get("user"), "updated network "+netname, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(returnednetwork) } func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - var network models.Network - network, err := functions.GetParentNetwork(params["networkname"]) - if err != nil { - returnErrorResponse(w, r, formatError(err, "internal")) - return - } - - var networkChange models.NetworkUpdate - - _ = json.NewDecoder(r.Body).Decode(&networkChange) - - collection := mongoconn.Client.Database("netmaker").Collection("networks") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - filter := bson.M{"netid": network.NetID} - - if networkChange.NodeLimit !=0 { - update := bson.D{ - {"$set", bson.D{ - {"nodelimit", networkChange.NodeLimit}, - }}, - } - err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network) - defer cancel() - if err != nil { - returnErrorResponse(w, r, formatError(err, "badrequest")) - return - } + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + var network models.Network + netname := params["networkname"] + network, err := functions.GetParentNetwork(netname) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(network) -} + var networkChange models.NetworkUpdate + + _ = json.NewDecoder(r.Body).Decode(&networkChange) + + collection := mongoconn.Client.Database("netmaker").Collection("networks") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + filter := bson.M{"netid": network.NetID} + + if networkChange.NodeLimit != 0 { + update := bson.D{ + {"$set", bson.D{ + {"nodelimit", networkChange.NodeLimit}, + }}, + } + err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&network) + defer cancel() + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + } + functions.PrintUserLog(r.Header.Get("user"), "updated network node limit on, "+netname, 1) + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(network) +} func UpdateNetwork(networkChange models.NetworkUpdate, network models.Network) (models.Network, error) { //NOTE: Network.NetID is intentionally NOT editable. It acts as a static ID for the network. @@ -528,12 +569,13 @@ func deleteNetwork(w http.ResponseWriter, r *http.Request) { if err != nil { errtype := "badrequest" - if strings.Contains(err.Error(), "Node check failed"){ + if strings.Contains(err.Error(), "Node check failed") { errtype = "forbidden" } returnErrorResponse(w, r, formatError(err, errtype)) return } + functions.PrintUserLog(r.Header.Get("user"), "deleted network "+network, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(count) } @@ -585,6 +627,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "badrequest")) return } + functions.PrintUserLog(r.Header.Get("user"), "created network "+network.NetID, 1) w.WriteHeader(http.StatusOK) //json.NewEncoder(w).Encode(result) } @@ -633,7 +676,8 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) var accesskey models.AccessKey //start here - network, err := functions.GetParentNetwork(params["networkname"]) + netname := params["networkname"] + network, err := functions.GetParentNetwork(netname) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return @@ -648,6 +692,7 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "badrequest")) return } + functions.PrintUserLog(r.Header.Get("user"), "created access key "+netname, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(key) //w.Write([]byte(accesskey.AccessString)) @@ -666,10 +711,10 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models accesskey.Uses = 1 } - checkkeys, err := GetKeys(network.NetID) - if err != nil { - return models.AccessKey{}, errors.New("could not retrieve network keys") - } + checkkeys, err := GetKeys(network.NetID) + if err != nil { + return models.AccessKey{}, errors.New("could not retrieve network keys") + } for _, key := range checkkeys { if key.Name == accesskey.Name { @@ -685,10 +730,11 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models netID := network.NetID - var accessToken models.AccessToken - s := servercfg.GetServerConfig() - w := servercfg.GetWGConfig() + var accessToken models.AccessToken + s := servercfg.GetServerConfig() + w := servercfg.GetWGConfig() servervals := models.ServerConfig{ + CoreDNSAddr: s.CoreDNSAddr, APIConnString: s.APIConnString, APIHost: s.APIHost, APIPort: s.APIPort, @@ -696,27 +742,27 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models GRPCHost: s.GRPCHost, GRPCPort: s.GRPCPort, GRPCSSL: s.GRPCSSL, - } + } wgvals := models.WG{ - GRPCWireGuard: w.GRPCWireGuard, - GRPCWGAddress: w.GRPCWGAddress, - GRPCWGPort: w.GRPCWGPort, - GRPCWGPubKey: w.GRPCWGPubKey, - GRPCWGEndpoint: s.APIHost, - } + GRPCWireGuard: w.GRPCWireGuard, + GRPCWGAddress: w.GRPCWGAddress, + GRPCWGPort: w.GRPCWGPort, + GRPCWGPubKey: w.GRPCWGPubKey, + GRPCWGEndpoint: s.APIHost, + } - accessToken.ServerConfig = servervals - accessToken.WG = wgvals + accessToken.ServerConfig = servervals + accessToken.WG = wgvals accessToken.ClientConfig.Network = netID accessToken.ClientConfig.Key = accesskey.Value accessToken.ClientConfig.LocalRange = privAddr - tokenjson, err := json.Marshal(accessToken) - if err != nil { - return accesskey, err - } + tokenjson, err := json.Marshal(accessToken) + if err != nil { + return accesskey, err + } - accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) + accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) //validate accesskey v := validator.New() @@ -752,52 +798,51 @@ func GetSignupToken(netID string) (models.AccessKey, error) { var accesskey models.AccessKey var accessToken models.AccessToken - s := servercfg.GetServerConfig() - w := servercfg.GetWGConfig() - servervals := models.ServerConfig{ - APIConnString: s.APIConnString, - APIHost: s.APIHost, - APIPort: s.APIPort, - GRPCConnString: s.GRPCConnString, - GRPCHost: s.GRPCHost, - GRPCPort: s.GRPCPort, - GRPCSSL: s.GRPCSSL, - } - wgvals := models.WG{ - GRPCWireGuard: w.GRPCWireGuard, - GRPCWGAddress: w.GRPCWGAddress, - GRPCWGPort: w.GRPCWGPort, - GRPCWGPubKey: w.GRPCWGPubKey, - GRPCWGEndpoint: s.APIHost, - } + s := servercfg.GetServerConfig() + w := servercfg.GetWGConfig() + servervals := models.ServerConfig{ + APIConnString: s.APIConnString, + APIHost: s.APIHost, + APIPort: s.APIPort, + GRPCConnString: s.GRPCConnString, + GRPCHost: s.GRPCHost, + GRPCPort: s.GRPCPort, + GRPCSSL: s.GRPCSSL, + } + wgvals := models.WG{ + GRPCWireGuard: w.GRPCWireGuard, + GRPCWGAddress: w.GRPCWGAddress, + GRPCWGPort: w.GRPCWGPort, + GRPCWGPubKey: w.GRPCWGPubKey, + GRPCWGEndpoint: s.APIHost, + } - accessToken.ServerConfig = servervals - accessToken.WG = wgvals + accessToken.ServerConfig = servervals + accessToken.WG = wgvals tokenjson, err := json.Marshal(accessToken) - if err != nil { - return accesskey, err - } + if err != nil { + return accesskey, err + } - accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) - return accesskey, nil + accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) + return accesskey, nil } func getSignupToken(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - netID := params["networkname"] + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + netID := params["networkname"] token, err := GetSignupToken(netID) - if err != nil { - returnErrorResponse(w, r, formatError(err, "internal")) - return - } - w.WriteHeader(http.StatusOK) - json.NewEncoder(w).Encode(token) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + functions.PrintUserLog(r.Header.Get("user"), "got signup token "+netID, 2) + w.WriteHeader(http.StatusOK) + json.NewEncoder(w).Encode(token) } - - //pretty simple get func getAccessKeys(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") @@ -808,6 +853,7 @@ func getAccessKeys(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "fetched access keys on network "+network, 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(keys) } @@ -836,6 +882,7 @@ func deleteAccessKey(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "badrequest")) return } + functions.PrintUserLog(r.Header.Get("user"), "deleted access key "+keyname+" on network "+netname, 1) w.WriteHeader(http.StatusOK) } func DeleteKey(keyname, netname string) error { diff --git a/controllers/nodeHttpController.go b/controllers/nodeHttpController.go index d2e7defa..126eefaa 100644 --- a/controllers/nodeHttpController.go +++ b/controllers/nodeHttpController.go @@ -5,10 +5,11 @@ import ( "encoding/json" "errors" "fmt" + "log" "net/http" "strings" "time" - "log" + "github.com/gorilla/mux" "github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/models" @@ -184,13 +185,13 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha //B: the token corresponds to a mac address, and if so, which one //TODO: There's probably a better way of dealing with the "master token"/master password. Plz Halp. - var isAuthorized = false + var isAuthorized = false var macaddress = "" - _, networks, isadmin, errN := functions.VerifyUserToken(authToken) + username, networks, isadmin, errN := functions.VerifyUserToken(authToken) isnetadmin := isadmin if errN == nil && isadmin { - macaddress = "mastermac" - isAuthorized = true + macaddress = "mastermac" + isAuthorized = true } else { mac, _, err := functions.VerifyToken(authToken) if err != nil { @@ -202,11 +203,11 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha } macaddress = mac } - if !isadmin && params["network"] != ""{ - if functions.SliceContains(networks, params["network"]){ - isnetadmin = true - } - } + if !isadmin && params["network"] != "" { + if functions.SliceContains(networks, params["network"]) { + isnetadmin = true + } + } //The mastermac (login with masterkey from config) can do everything!! May be dangerous. if macaddress == "mastermac" { isAuthorized = true @@ -223,20 +224,20 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha if isnetadmin { isAuthorized = true } else { - node, err := functions.GetNodeByMacAddress(params["network"], macaddress) - if err != nil { - errorResponse = models.ErrorResponse{ - Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.", + node, err := functions.GetNodeByMacAddress(params["network"], macaddress) + if err != nil { + errorResponse = models.ErrorResponse{ + Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.", + } + returnErrorResponse(w, r, errorResponse) + return } - returnErrorResponse(w, r, errorResponse) - return - } - isAuthorized = (node.Network == params["network"]) + isAuthorized = (node.Network == params["network"]) } case "node": - if isnetadmin { - isAuthorized = true - } else { + if isnetadmin { + isAuthorized = true + } else { isAuthorized = (macaddress == params["macaddress"]) } case "master": @@ -253,6 +254,10 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha return } else { //If authorized, this function passes along it's request and output to the appropriate route function. + if username == "" { + username = "(user not found)" + } + r.Header.Set("user", username) next.ServeHTTP(w, r) } } @@ -266,13 +271,15 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) { var nodes []models.Node var params = mux.Vars(r) - nodes, err := GetNetworkNodes(params["network"]) + networkName := params["network"] + nodes, err := GetNetworkNodes(networkName) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return } //Returns all the nodes in JSON format + functions.PrintUserLog(r.Header.Get("user"), "fetched nodes on network"+networkName, 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(nodes) } @@ -319,6 +326,7 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) { return } //Return all the nodes in JSON format + functions.PrintUserLog(r.Header.Get("user"), "fetched nodes", 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(nodes) } @@ -391,6 +399,7 @@ func getNode(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "fetched node "+params["macaddress"], 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(node) } @@ -409,6 +418,7 @@ func getLastModified(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "called last modified", 2) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(network.NodesLastModified) } @@ -503,6 +513,7 @@ func createNode(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "created new node "+node.Name+" on network "+node.Network, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(node) } @@ -517,7 +528,7 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } - fmt.Println("Node " + node.Name + " uncordoned.") + functions.PrintUserLog(r.Header.Get("user"), "uncordoned node "+node.Name, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode("SUCCESS") } @@ -563,6 +574,7 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "created egress gateway on node "+gateway.NodeID+" on network "+gateway.NetID, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(node) } @@ -594,13 +606,13 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro nodechange.PostUp = node.PostUp } } - if node.PostDown != "" { - if !strings.Contains(node.PostDown, nodechange.PostDown) { - nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown - } else { - nodechange.PostDown = node.PostDown - } - } + if node.PostDown != "" { + if !strings.Contains(node.PostDown, nodechange.PostDown) { + nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown + } else { + nodechange.PostDown = node.PostDown + } + } collection := mongoconn.Client.Database("netmaker").Collection("nodes") ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) // Create filter @@ -637,7 +649,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro func ValidateEgressGateway(gateway models.EgressGatewayRequest) error { var err error //isIp := functions.IsIpCIDR(gateway.RangeString) - empty := len(gateway.Ranges)==0 + empty := len(gateway.Ranges) == 0 if empty { err = errors.New("IP Ranges Cannot Be Empty") } @@ -651,11 +663,14 @@ func ValidateEgressGateway(gateway models.EgressGatewayRequest) error { func deleteEgressGateway(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var params = mux.Vars(r) - node, err := DeleteEgressGateway(params["network"], params["macaddress"]) + nodeMac := params["macaddress"] + netid := params["network"] + node, err := DeleteEgressGateway(netid, nodeMac) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "delete egress gateway "+nodeMac+" on network "+netid, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(node) } @@ -705,15 +720,19 @@ func DeleteEgressGateway(network, macaddress string) (models.Node, error) { } return node, nil } + // == INGRESS == func createIngressGateway(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) w.Header().Set("Content-Type", "application/json") - node, err := CreateIngressGateway(params["network"], params["macaddress"]) + nodeMac := params["macaddress"] + netid := params["network"] + node, err := CreateIngressGateway(netid, nodeMac) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "created ingress gateway on node "+nodeMac+" on network "+netid, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(node) } @@ -721,77 +740,79 @@ func createIngressGateway(w http.ResponseWriter, r *http.Request) { func CreateIngressGateway(netid string, macaddress string) (models.Node, error) { node, err := functions.GetNodeByMacAddress(netid, macaddress) - if err != nil { - return models.Node{}, err - } + if err != nil { + return models.Node{}, err + } - network, err := functions.GetParentNetwork(netid) - if err != nil { + network, err := functions.GetParentNetwork(netid) + if err != nil { log.Println("Could not find network.") - return models.Node{}, err - } - var nodechange models.Node + return models.Node{}, err + } + var nodechange models.Node nodechange.IngressGatewayRange = network.AddressRange - nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + node.Interface + " -j MASQUERADE" - nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE" - if node.PostUp != "" { - if !strings.Contains(node.PostUp, nodechange.PostUp) { - nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp - } else { - nodechange.PostUp = node.PostUp - } - } - if node.PostDown != "" { - if !strings.Contains(node.PostDown, nodechange.PostDown) { - nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown - } else { - nodechange.PostDown = node.PostDown - } - } + nodechange.PostUp = "iptables -A FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -A POSTROUTING -o " + node.Interface + " -j MASQUERADE" + nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE" + if node.PostUp != "" { + if !strings.Contains(node.PostUp, nodechange.PostUp) { + nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp + } else { + nodechange.PostUp = node.PostUp + } + } + if node.PostDown != "" { + if !strings.Contains(node.PostDown, nodechange.PostDown) { + nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown + } else { + nodechange.PostDown = node.PostDown + } + } collection := mongoconn.Client.Database("netmaker").Collection("nodes") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - // Create filter - filter := bson.M{"macaddress": macaddress, "network": netid} - node.SetLastModified() - // prepare update model. - update := bson.D{ - {"$set", bson.D{ - {"postup", nodechange.PostUp}, - {"postdown", nodechange.PostDown}, - {"isingressgateway", true}, - {"ingressgatewayrange", nodechange.IngressGatewayRange}, - {"lastmodified", node.LastModified}, - }}, - } - var nodeupdate models.Node - err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate) - defer cancel() - if err != nil { + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Create filter + filter := bson.M{"macaddress": macaddress, "network": netid} + node.SetLastModified() + // prepare update model. + update := bson.D{ + {"$set", bson.D{ + {"postup", nodechange.PostUp}, + {"postdown", nodechange.PostDown}, + {"isingressgateway", true}, + {"ingressgatewayrange", nodechange.IngressGatewayRange}, + {"lastmodified", node.LastModified}, + }}, + } + var nodeupdate models.Node + err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate) + defer cancel() + if err != nil { log.Println("error updating node to gateway") - return models.Node{}, err - } - err = SetNetworkNodesLastModified(netid) - if err != nil { - return node, err - } - //Get updated values to return - node, err = functions.GetNodeByMacAddress(netid, macaddress) - if err != nil { + return models.Node{}, err + } + err = SetNetworkNodesLastModified(netid) + if err != nil { + return node, err + } + //Get updated values to return + node, err = functions.GetNodeByMacAddress(netid, macaddress) + if err != nil { log.Println("error finding node after update") - return node, err - } - return node, nil + return node, err + } + return node, nil } func deleteIngressGateway(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") var params = mux.Vars(r) - node, err := DeleteIngressGateway(params["network"], params["macaddress"]) + nodeMac := params["macaddress"] + node, err := DeleteIngressGateway(params["network"], nodeMac) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return } + functions.PrintUserLog(r.Header.Get("user"), "deleted ingress gateway"+nodeMac, 1) w.WriteHeader(http.StatusOK) json.NewEncoder(w).Encode(node) } diff --git a/controllers/userHttpController.go b/controllers/userHttpController.go index 1cd23b7f..9e3ac487 100644 --- a/controllers/userHttpController.go +++ b/controllers/userHttpController.go @@ -30,7 +30,7 @@ func userHandlers(r *mux.Router) { r.HandleFunc("/api/users/{username}", authorizeUserAdm(http.HandlerFunc(createUser))).Methods("POST") r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(deleteUser))).Methods("DELETE") r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(getUser))).Methods("GET") - r.HandleFunc("/api/users", authorizeUserAdm(http.HandlerFunc(getUsers))).Methods("GET") + r.HandleFunc("/api/users", authorizeUserAdm(http.HandlerFunc(getUsers))).Methods("GET") } //Node authenticates using its password and retrieves a JWT for authorization. @@ -63,12 +63,13 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { return } + username := authRequest.UserName var successResponse = models.SuccessResponse{ Code: http.StatusOK, - Message: "W1R3: Device " + authRequest.UserName + " Authorized", + Message: "W1R3: Device " + username + " Authorized", Response: models.SuccessfulUserLoginResponse{ AuthToken: jwt, - UserName: authRequest.UserName, + UserName: username, }, } //Send back the JWT @@ -78,6 +79,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) { returnErrorResponse(response, request, errorResponse) return } + functions.PrintUserLog(username, "was authenticated", 2) response.Header().Set("Content-Type", "application/json") response.Write(successJSONResponse) } @@ -112,7 +114,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { } //Create a new JWT for the node - tokenString, _ := functions.CreateUserJWT(authRequest.UserName, result.Networks, result.IsAdmin) + tokenString, _ := functions.CreateUserJWT(authRequest.UserName, result.Networks, result.IsAdmin) return tokenString, nil } @@ -126,36 +128,39 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) { func authorizeUser(next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) + var params = mux.Vars(r) //get the auth token bearerToken := r.Header.Get("Authorization") - err := ValidateUserToken(bearerToken, params["username"], false) + username := params["username"] + err := ValidateUserToken(bearerToken, username, false) if err != nil { returnErrorResponse(w, r, formatError(err, "unauthorized")) return } + r.Header.Set("user", username) next.ServeHTTP(w, r) } } func authorizeUserAdm(next http.Handler) http.HandlerFunc { - return func(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) + return func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) - //get the auth token - bearerToken := r.Header.Get("Authorization") - err := ValidateUserToken(bearerToken, params["username"], true) - if err != nil { - returnErrorResponse(w, r, formatError(err, "unauthorized")) - return - } - next.ServeHTTP(w, r) - } + //get the auth token + bearerToken := r.Header.Get("Authorization") + username := params["username"] + err := ValidateUserToken(bearerToken, username, true) + if err != nil { + returnErrorResponse(w, r, formatError(err, "unauthorized")) + return + } + r.Header.Set("user", username) + next.ServeHTTP(w, r) + } } - func ValidateUserToken(token string, user string, adminonly bool) error { var tokenSplit = strings.Split(token, " ") @@ -241,79 +246,74 @@ func GetUser(username string) (models.User, error) { func GetUsers() ([]models.User, error) { - var users []models.User + var users []models.User - collection := mongoconn.Client.Database("netmaker").Collection("users") + collection := mongoconn.Client.Database("netmaker").Collection("users") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) + cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) - if err != nil { - return users, err - } + if err != nil { + return users, err + } - defer cancel() + defer cancel() - for cur.Next(context.TODO()) { + for cur.Next(context.TODO()) { - var user models.User - err := cur.Decode(&user) - if err != nil { - return users, err - } + var user models.User + err := cur.Decode(&user) + if err != nil { + return users, err + } - // add network our array - users = append(users, user) - } + // add network our array + users = append(users, user) + } - if err := cur.Err(); err != nil { - return users, err - } + if err := cur.Err(); err != nil { + return users, err + } - return users, err + return users, err } - //Get an individual node. Nothin fancy here folks. func getUser(w http.ResponseWriter, r *http.Request) { // set header. w.Header().Set("Content-Type", "application/json") var params = mux.Vars(r) + usernameFetched := params["username"] + user, err := GetUser(usernameFetched) - user, err := GetUser(params["username"]) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + functions.PrintUserLog(r.Header.Get("user"), "fetched user "+usernameFetched, 2) + json.NewEncoder(w).Encode(user) +} + +//Get an individual node. Nothin fancy here folks. +func getUsers(w http.ResponseWriter, r *http.Request) { + // set header. + w.Header().Set("Content-Type", "application/json") + + users, err := GetUsers() if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return } - json.NewEncoder(w).Encode(user) + functions.PrintUserLog(r.Header.Get("user"), "fetched users", 2) + json.NewEncoder(w).Encode(users) } -//Get an individual node. Nothin fancy here folks. -func getUsers(w http.ResponseWriter, r *http.Request) { - // set header. - w.Header().Set("Content-Type", "application/json") - - users, err := GetUsers() - - if err != nil { - returnErrorResponse(w, r, formatError(err, "internal")) - return - } - - json.NewEncoder(w).Encode(users) -} - - func CreateUser(user models.User) (models.User, error) { - hasadmin, err := HasAdmin() - if hasadmin && user.IsAdmin { - return models.User{}, errors.New("Admin already Exists") - } - err = ValidateUser("create", user) + err := ValidateUser("create", user) if err != nil { return models.User{}, err } @@ -326,7 +326,7 @@ func CreateUser(user models.User) (models.User, error) { //set password to encrypted password user.Password = string(hash) - tokenString, _ := functions.CreateUserJWT(user.UserName,user.Networks, user.IsAdmin) + tokenString, _ := functions.CreateUserJWT(user.UserName, user.Networks, user.IsAdmin) if tokenString == "" { //returnErrorResponse(w, r, errorResponse) @@ -350,35 +350,34 @@ func createAdmin(w http.ResponseWriter, r *http.Request) { var admin models.User //get node from body of request _ = json.NewDecoder(r.Body).Decode(&admin) - admin.IsAdmin = true + admin.IsAdmin = true admin, err := CreateUser(admin) if err != nil { returnErrorResponse(w, r, formatError(err, "badrequest")) return } - + functions.PrintUserLog(admin.UserName, "was made a new admin", 1) json.NewEncoder(w).Encode(admin) } func createUser(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") + w.Header().Set("Content-Type", "application/json") - var user models.User - //get node from body of request - _ = json.NewDecoder(r.Body).Decode(&user) + var user models.User + //get node from body of request + _ = json.NewDecoder(r.Body).Decode(&user) - user, err := CreateUser(user) + user, err := CreateUser(user) - if err != nil { - returnErrorResponse(w, r, formatError(err, "badrequest")) - return - } - - json.NewEncoder(w).Encode(user) + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + functions.PrintUserLog(user.UserName, "was created", 1) + json.NewEncoder(w).Encode(user) } - func UpdateUser(userchange models.User, user models.User) (models.User, error) { err := ValidateUser("update", userchange) @@ -391,9 +390,9 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) { if userchange.UserName != "" { user.UserName = userchange.UserName } - if len(userchange.Networks) > 0 { - user.Networks = userchange.Networks - } + if len(userchange.Networks) > 0 { + user.Networks = userchange.Networks + } if userchange.Password != "" { //encrypt that password so we never see it again hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5) @@ -445,7 +444,8 @@ func updateUser(w http.ResponseWriter, r *http.Request) { var params = mux.Vars(r) var user models.User //start here - user, err := GetUser(params["username"]) + username := params["username"] + user, err := GetUser(username) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) return @@ -463,32 +463,35 @@ func updateUser(w http.ResponseWriter, r *http.Request) { returnErrorResponse(w, r, formatError(err, "badrequest")) return } + functions.PrintUserLog(username, "was updated", 1) json.NewEncoder(w).Encode(user) } func updateUserAdm(w http.ResponseWriter, r *http.Request) { - w.Header().Set("Content-Type", "application/json") - var params = mux.Vars(r) - var user models.User - //start here - user, err := GetUser(params["username"]) - if err != nil { - returnErrorResponse(w, r, formatError(err, "internal")) - return - } - var userchange models.User - // we decode our body request params - err = json.NewDecoder(r.Body).Decode(&userchange) - if err != nil { - returnErrorResponse(w, r, formatError(err, "internal")) - return - } + w.Header().Set("Content-Type", "application/json") + var params = mux.Vars(r) + var user models.User + //start here + username := params["username"] + user, err := GetUser(username) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } + var userchange models.User + // we decode our body request params + err = json.NewDecoder(r.Body).Decode(&userchange) + if err != nil { + returnErrorResponse(w, r, formatError(err, "internal")) + return + } user, err = UpdateUser(userchange, user) - if err != nil { - returnErrorResponse(w, r, formatError(err, "badrequest")) - return - } - json.NewEncoder(w).Encode(user) + if err != nil { + returnErrorResponse(w, r, formatError(err, "badrequest")) + return + } + functions.PrintUserLog(username, "was updated (admin)", 1) + json.NewEncoder(w).Encode(user) } func DeleteUser(user string) (bool, error) { @@ -521,7 +524,8 @@ func deleteUser(w http.ResponseWriter, r *http.Request) { // get params var params = mux.Vars(r) - success, err := DeleteUser(params["username"]) + username := params["username"] + success, err := DeleteUser(username) if err != nil { returnErrorResponse(w, r, formatError(err, "internal")) @@ -531,6 +535,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) { return } + functions.PrintUserLog(username, "was deleted", 1) json.NewEncoder(w).Encode(params["username"] + " deleted.") } diff --git a/docs/_build/doctrees/architecture.doctree b/docs/_build/doctrees/architecture.doctree index 094656d8..b6bcc7cb 100644 Binary files a/docs/_build/doctrees/architecture.doctree and b/docs/_build/doctrees/architecture.doctree differ diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle index 7ca3b72f..878bbef9 100644 Binary files a/docs/_build/doctrees/environment.pickle and b/docs/_build/doctrees/environment.pickle differ diff --git a/docs/_build/doctrees/external-clients.doctree b/docs/_build/doctrees/external-clients.doctree index 6411828f..3d36ea9f 100644 Binary files a/docs/_build/doctrees/external-clients.doctree and b/docs/_build/doctrees/external-clients.doctree differ diff --git a/docs/_build/doctrees/quick-start.doctree b/docs/_build/doctrees/quick-start.doctree index b36bbc1e..09d180f8 100644 Binary files a/docs/_build/doctrees/quick-start.doctree and b/docs/_build/doctrees/quick-start.doctree differ diff --git a/docs/_build/doctrees/server-installation.doctree b/docs/_build/doctrees/server-installation.doctree index 71e8ee69..de024009 100644 Binary files a/docs/_build/doctrees/server-installation.doctree and b/docs/_build/doctrees/server-installation.doctree differ diff --git a/docs/_build/html/_images/exclient1.png b/docs/_build/html/_images/exclient1.png new file mode 100644 index 00000000..8a490358 Binary files /dev/null and b/docs/_build/html/_images/exclient1.png differ diff --git a/docs/_build/html/_images/exclient2.png b/docs/_build/html/_images/exclient2.png new file mode 100644 index 00000000..a0dd9ab9 Binary files /dev/null and b/docs/_build/html/_images/exclient2.png differ diff --git a/docs/_build/html/_images/exclient3.png b/docs/_build/html/_images/exclient3.png new file mode 100644 index 00000000..f4c52545 Binary files /dev/null and b/docs/_build/html/_images/exclient3.png differ diff --git a/docs/_build/html/_images/exclient4.png b/docs/_build/html/_images/exclient4.png new file mode 100644 index 00000000..355065b1 Binary files /dev/null and b/docs/_build/html/_images/exclient4.png differ diff --git a/docs/_build/html/_sources/architecture.rst.txt b/docs/_build/html/_sources/architecture.rst.txt index 37374f94..6e0a4c32 100644 --- a/docs/_build/html/_sources/architecture.rst.txt +++ b/docs/_build/html/_sources/architecture.rst.txt @@ -63,8 +63,7 @@ SystemD SystemD is a system service manager for a wide array of Linux operating systems. Not all Linux distributions have adopted systemd, but, for better or worse, it has become a fairly common standard in the Linux world. That said, any non-Linux operating system will not have systemd, and many Linux/Unix distributionshave alternative system service managers. -Netmaker's netclient, the agent which controls networking on all nodes, relies heavily on systemd as of version 0.3. This reliance is being reduced but is currently a core dependency, causing most of the limitations and incompatibilities. As Netmaker evolves, systemd will become just one of the possible service management options, allowing the netclient to be run on a wider array of devices. - +Netmaker's netclient, the agent which controls networking on all nodes, can be run as a CLI or as a system daemon. It runs as a daemon by default, and this requires systemd. As Netmaker evolves, systemd will become just one of the possible service management options, allowing the netclient to be run on a wider array of devices. However, for the time being, the netclient should be run "unmanaged" (netclient join -daemon=off) on systems that do not run systemd, and some other method can be used like a cron job or custom script. Components =========== @@ -88,15 +87,17 @@ The Netmaker server interacts with (as of v0.3) a MongoDB instance, which holds Netclient ---------------- -The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker `GitHub Repository `_. The binary, by itself, can be compiled for most systems. However, this binary is designed to manage a certain number of Operating Systems. As of version 0.3, it requires systemd in order to manage the host system appropriately. It may be installable, and it may even make the machine a part of the mesh network, but it will not function in entirely (see Compatible Systems for more info) without systemd. +The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker `GitHub Repository `_. The binary, by itself, can be compiled for most systems. However, this binary is designed to manage a certain number of Operating Systems. As of version 0.5, the netclient can be run as a system daemon on linux distributions with systemd, or as an "unmanaged" client on distributions without systemd. -The netclient is installed via a simple bash script, which pulls the latest binary and runs install command. +The netclient is installed via a simple bash script, which pulls the latest binary and runs 'register' and 'join' commands. -The install command registers the machine with the Netmaker server using sensible defaults, which can be overridden with a config file or environment variables. Assuming the netclient has a valid key (or the network allows manual node signup), it will be registered in the Netmaker network, which will return configuration details about how to set up the local network. +The 'register' command adds a WireGuard tunnel directly to the netmaker server, for all subsequent communication. -The netclient then sets itself up in systemd, and configures WireGuard. At this point it should be part of the network. +The 'join' command attempts to add the machine to the Netmaker network using sensible defaults, which can be overridden with a config file or environment variables. Assuming the netclient has a valid key (or the network allows manual node signup), it will be registered into the Netmaker network, and will be returned necessary configuration details for how to set up its local network. -On a periodic basis (systemd timer), the netclient performs a "check in." It will authenticate with the server, and check to see if anything has changed in the network. It will also post changes about its own local configuration if there. If there has been a change, the server will return new configurations and the netclient will reconfigure the network. +The netclient then sets up the systemd daemon (if running in daemon mode), and configures WireGuard. At this point it should be part of the network. + +If running in daemon mode, on a periodic basis (systemd timer), the netclient performs a "check in." It will authenticate with the server, and check to see if anything has changed in the network. It will also post changes about its own local configuration if there. If there has been a change, the server will return new configurations and the netclient will reconfigure the network. If not running in daemon mode, it is up to the operator to perform check ins (netclient checkin -n < network name >). The check in process is what allows Netmaker to create dynamic mesh networks. As nodes are added to, removed from, and modified on the network, other nodes are notified, and make appropriate changes. @@ -104,7 +105,7 @@ The check in process is what allows Netmaker to create dynamic mesh networks. As MongoDB -------- -As of v0.3, Netmaker uses MongoDB as its database, and interacts with a MongoDB instance to store and retrieve information about nodes, networks, and users. Netmaker is rapidly evolving, and MongoDB provides a flexible database structure that accelerates development. However, MongoDB is also the heaviest component of Netmaker (high cpu/memory consumption), and is set to be replaced by a lighter-weight, SQL-based database in the future. +As of v0.5, Netmaker uses MongoDB as its database, and interacts with a MongoDB instance to store and retrieve information about nodes, networks, and users. Netmaker is rapidly evolving, and MongoDB provides a flexible database structure that accelerates development. However, MongoDB is also the heaviest component of Netmaker (high cpu/memory consumption), and is set to be replaced by a lighter-weight, SQL-based database in the future. Netmaker UI --------------- @@ -121,6 +122,16 @@ v0.3 introduced the concept of private DNS management for nodes. This requires a Worth considering is that CoreDNS requires port 53 on the Netmaker host system, which may cause conflicts depending on your operating system. This is explained in the :doc:`Server Installation <./server-installation>` guide. +External Client +---------------- + +The external client is simply a manually configured WireGuard connection to your network, which Netmaker helps to manage. + +Most machines can run WireGuard. It is fairly simple to set up a WireGuard connection to a single endpoint. It is setting up mesh networks and other topologies like site-to-site which becomes complicated. + +Netmaker v0.5 introduces the "external client" to handle any devices which are not currently compatible with the netclient, including Windows, iPhone, Android, and Mac. Over time, this list will be eliminated and there may not even be a need for the external client. + +External clients hook into a Netmaker network via an "Ingress Gateway," which is configured for a given node and allows traffic to flow into the network. Technical Process ==================== @@ -132,22 +143,25 @@ Below is a high level, step-by-step overview of the flow of communications withi 3. Both of the above requests are routed to the server via an API call from the front end 4. Admin runs the netclient install script on any given node (machine). 5. Netclient decodes key, which contains the GRPC server location and port -6. Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers -7. Netclient reaches out to GRPC server with this information, authenticating via access key. -8. Netmaker server verifies information and creates the node, setting default values for any missing information. -9. Timestamp is set for the network (see #16). -10. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network. -11. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard -12. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the network). -13. Netmaker sends back peers list, including current known configurations of all nodes in network. -14. Netclient configures WireGuard with this information. At this point, the node is fully configured as a part of the network and should be able to reach the other nodes via private address. -15. Netclient begins daemon (system timer) to run check in's with the server. It awaits changes, reporting local changes, and retrieving changes from any other nodes in the network. -16. Other netclients on the network, upon checking in with the Netmaker server, will see that the timestamp has updated, and they will retrieve a new peers list, completing the update cycle. +6. Netclient uses information to register and set up WireGuard tunnel to GRPC server +7. Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers +8. Netclient reaches out to GRPC server with this information, authenticating via access key. +9. Netmaker server verifies information and creates the node, setting default values for any missing information. +10. Timestamp is set for the network (see #16). +11. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network. +12. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard +13. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the network). +14. Netmaker sends back peers list, including current known configurations of all nodes in network. +15. Netclient configures WireGuard with this information. At this point, the node is fully configured as a part of the network and should be able to reach the other nodes via private address. +16. Netclient begins daemon (system timer) to run check in's with the server. It awaits changes, reporting local changes, and retrieving changes from any other nodes in the network. +17. Other netclients on the network, upon checking in with the Netmaker server, will see that the timestamp has updated, and they will retrieve a new peers list, completing the update cycle. Compatible Systems for Netclient ================================== +To manage a node manually, the Netclient can be compiled and run for most linux distibutions, with a prerequisite of WireGuard. + To manage a node automatically, the Netmaker client (netclient) requires **systemd-based linux.** Compatible systems include: - Fedora - Ubuntu @@ -173,4 +187,3 @@ Install limitations mostly include platform-specific limitations, such as needin - **Double NAT**: Netmaker is currently unable to route traffic for devices behind a "double NAT". - **CGNAT**: Netmaker is currently unable to route traffic for for devices behind a "carrier-grade NAT". -- **Windows/iPhone/Android**: To reiterate the systemd limitation, Netmaker is not currently configured to support "end user" devices such as Windows desktops and phones generally. In v0.4, Netmaker will introduce external device gateways to allow this traffic (and many other sorts of devices). diff --git a/docs/_build/html/_sources/external-clients.rst.txt b/docs/_build/html/_sources/external-clients.rst.txt index 58379c53..66eddcae 100644 --- a/docs/_build/html/_sources/external-clients.rst.txt +++ b/docs/_build/html/_sources/external-clients.rst.txt @@ -17,3 +17,43 @@ By using this method, you can hook any machine into a netmaker network that can It is recommended to run the netclient where compatible, but for all other cases, a machine can be configured as an external client. Important to note, an external client is not **reachable** by the network, meaning the client can establish connections to other machines, but those machines cannot independently establish a connection back. The External Client method should only be used in use cases where one wishes to access resource runnin on the virtual network, and **not** for use cases where one wishes to make a resource accessible on the network. For that, use netclient. + +Configuring an Ingress Gateway +================================== + +External Clients must attach to an Ingress Gateway. By default, your network will not have an ingress gateway. To configure an ingress gateway, you can use any node in your network, but it should have a public IP address (not behind a NAT). Your Netmaker server can be an ingress gateway and makes for a good default choice if you are unsure of which node to select. + +.. image:: images/exclient1.png + :width: 80% + :alt: Gateway + :align: center + +Adding Clients to a Gateway +============================= + +Once you have configured a node as a gateway, you can then add clients to that gateway. Clients will be able to access other nodes in the network just as the gateway node does. + +.. image:: images/exclient2.png + :width: 80% + :alt: Gateway + :align: center + +After creating a client, you can edit the name to something more logical. + +.. image:: images/exclient3.png + :width: 80% + :alt: Gateway + :align: center + +Then, you can either download the configuration file directly, or scan the QR code from your phone (assuming you have the WireGuard app installed). It will accept the configuration just as it would accept a typical WireGuard configuration file. + +.. image:: images/exclient4.png + :width: 80% + :alt: Gateway + :align: center + +Example config file: + +.. literalinclude:: ./examplecode/myclient.conf + +Your client should now be able to access the network! A client can be invalidated at any time by simply deleting it from the UI. diff --git a/docs/_build/html/_sources/quick-start.rst.txt b/docs/_build/html/_sources/quick-start.rst.txt index 66b5d140..689dd28d 100644 --- a/docs/_build/html/_sources/quick-start.rst.txt +++ b/docs/_build/html/_sources/quick-start.rst.txt @@ -63,7 +63,7 @@ You will use this command to install the netclient on your nodes. There are thre * The **Access Key** value is the secret string that will allow your node to authenticate with the Netmaker network. This can be used with existing netclient installations where additional configurations (such as setting the server IP manually) may be required. This is not typical. E.g. ``netclient -c install -k -s 1.2.3.4 -p 50052`` * The **Access Token** value is a base64 encoded string that contains the server IP and grpc port, as well as the access key. This is decoded by the netclient and can be used with existing netclient installations like this: ``netclient -c install -t ``. You should use this method for adding a network to a node that is already on a network. For instance, Node A is in the **mynet** network and now you are adding it to **default**. -* The **install command** value is a curl command that can be run on Linux systems. It is a simple script that downloads the netclient binary and runs the install command all in one. +* The **install command** value is a curl command that can be run on Linux systems. It is a simple script that downloads the netclient binary and runs the install command all in one. However, this script is tailored for Secure GRPC Mode and contains an additional (unnecessary) command: **netclient register -k keyvalue**. This command will not work without secure GRPC enabled and will return a 500 error. Networks can also be enabled to allow nodes to sign up without keys at all. In this scenario, nodes enter a "pending state" and are not permitted to join the network until an admin approves them. @@ -130,7 +130,7 @@ Uninstalling the netclient 1. To remove your nodes from the default network, run the following on each node: ``sudo netclient leave -n default`` 2. To remove the netclient entirely from each node, run ``sudo rm -rf /etc/netclient`` (after running the first step) -Uninstralling Netmaker +Uninstalling Netmaker =========================== To uninstall Netmaker from the server, simply run ``docker-compose down`` or ``docker-compose down --volumes`` to remove the docker volumes for a future installation. diff --git a/docs/_build/html/architecture.html b/docs/_build/html/architecture.html index 849a89f3..a82883ec 100644 --- a/docs/_build/html/architecture.html +++ b/docs/_build/html/architecture.html @@ -273,6 +273,8 @@
  • Netmaker UI
  • CoreDNS +
  • +
  • External Client
  • @@ -785,6 +787,8 @@
  • Netmaker UI
  • CoreDNS +
  • +
  • External Client
  • @@ -840,7 +844,7 @@

    SystemD

    SystemD is a system service manager for a wide array of Linux operating systems. Not all Linux distributions have adopted systemd, but, for better or worse, it has become a fairly common standard in the Linux world. That said, any non-Linux operating system will not have systemd, and many Linux/Unix distributionshave alternative system service managers.

    -

    Netmaker’s netclient, the agent which controls networking on all nodes, relies heavily on systemd as of version 0.3. This reliance is being reduced but is currently a core dependency, causing most of the limitations and incompatibilities. As Netmaker evolves, systemd will become just one of the possible service management options, allowing the netclient to be run on a wider array of devices.

    +

    Netmaker’s netclient, the agent which controls networking on all nodes, can be run as a CLI or as a system daemon. It runs as a daemon by default, and this requires systemd. As Netmaker evolves, systemd will become just one of the possible service management options, allowing the netclient to be run on a wider array of devices. However, for the time being, the netclient should be run “unmanaged” (netclient join -daemon=off) on systems that do not run systemd, and some other method can be used like a cron job or custom script.

    @@ -856,16 +860,17 @@

    Netclient

    -

    The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker GitHub Repository. The binary, by itself, can be compiled for most systems. However, this binary is designed to manage a certain number of Operating Systems. As of version 0.3, it requires systemd in order to manage the host system appropriately. It may be installable, and it may even make the machine a part of the mesh network, but it will not function in entirely (see Compatible Systems for more info) without systemd.

    -

    The netclient is installed via a simple bash script, which pulls the latest binary and runs install command.

    -

    The install command registers the machine with the Netmaker server using sensible defaults, which can be overridden with a config file or environment variables. Assuming the netclient has a valid key (or the network allows manual node signup), it will be registered in the Netmaker network, which will return configuration details about how to set up the local network.

    -

    The netclient then sets itself up in systemd, and configures WireGuard. At this point it should be part of the network.

    -

    On a periodic basis (systemd timer), the netclient performs a “check in.” It will authenticate with the server, and check to see if anything has changed in the network. It will also post changes about its own local configuration if there. If there has been a change, the server will return new configurations and the netclient will reconfigure the network.

    +

    The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker GitHub Repository. The binary, by itself, can be compiled for most systems. However, this binary is designed to manage a certain number of Operating Systems. As of version 0.5, the netclient can be run as a system daemon on linux distributions with systemd, or as an “unmanaged” client on distributions without systemd.

    +

    The netclient is installed via a simple bash script, which pulls the latest binary and runs ‘register’ and ‘join’ commands.

    +

    The ‘register’ command adds a WireGuard tunnel directly to the netmaker server, for all subsequent communication.

    +

    The ‘join’ command attempts to add the machine to the Netmaker network using sensible defaults, which can be overridden with a config file or environment variables. Assuming the netclient has a valid key (or the network allows manual node signup), it will be registered into the Netmaker network, and will be returned necessary configuration details for how to set up its local network.

    +

    The netclient then sets up the systemd daemon (if running in daemon mode), and configures WireGuard. At this point it should be part of the network.

    +

    If running in daemon mode, on a periodic basis (systemd timer), the netclient performs a “check in.” It will authenticate with the server, and check to see if anything has changed in the network. It will also post changes about its own local configuration if there. If there has been a change, the server will return new configurations and the netclient will reconfigure the network. If not running in daemon mode, it is up to the operator to perform check ins (netclient checkin -n < network name >).

    The check in process is what allows Netmaker to create dynamic mesh networks. As nodes are added to, removed from, and modified on the network, other nodes are notified, and make appropriate changes.

    MongoDB

    -

    As of v0.3, Netmaker uses MongoDB as its database, and interacts with a MongoDB instance to store and retrieve information about nodes, networks, and users. Netmaker is rapidly evolving, and MongoDB provides a flexible database structure that accelerates development. However, MongoDB is also the heaviest component of Netmaker (high cpu/memory consumption), and is set to be replaced by a lighter-weight, SQL-based database in the future.

    +

    As of v0.5, Netmaker uses MongoDB as its database, and interacts with a MongoDB instance to store and retrieve information about nodes, networks, and users. Netmaker is rapidly evolving, and MongoDB provides a flexible database structure that accelerates development. However, MongoDB is also the heaviest component of Netmaker (high cpu/memory consumption), and is set to be replaced by a lighter-weight, SQL-based database in the future.

    Netmaker UI

    @@ -878,6 +883,13 @@

    Worth considering is that CoreDNS requires port 53 on the Netmaker host system, which may cause conflicts depending on your operating system. This is explained in the Server Installation guide.

    +

    External Client

    +

    The external client is simply a manually configured WireGuard connection to your network, which Netmaker helps to manage.

    +

    Most machines can run WireGuard. It is fairly simple to set up a WireGuard connection to a single endpoint. It is setting up mesh networks and other topologies like site-to-site which becomes complicated.

    +

    Netmaker v0.5 introduces the “external client” to handle any devices which are not currently compatible with the netclient, including Windows, iPhone, Android, and Mac. Over time, this list will be eliminated and there may not even be a need for the external client.

    +

    External clients hook into a Netmaker network via an “Ingress Gateway,” which is configured for a given node and allows traffic to flow into the network.

    + +

    Technical Process

    Below is a high level, step-by-step overview of the flow of communications within Netmaker (assuming Netmaker has already been installed):

    @@ -887,6 +899,7 @@
  • Both of the above requests are routed to the server via an API call from the front end

  • Admin runs the netclient install script on any given node (machine).

  • Netclient decodes key, which contains the GRPC server location and port

  • +
  • Netclient uses information to register and set up WireGuard tunnel to GRPC server

  • Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers

  • Netclient reaches out to GRPC server with this information, authenticating via access key.

  • Netmaker server verifies information and creates the node, setting default values for any missing information.

  • @@ -902,6 +915,7 @@

    Compatible Systems for Netclient

    +

    To manage a node manually, the Netclient can be compiled and run for most linux distibutions, with a prerequisite of WireGuard.

    To manage a node automatically, the Netmaker client (netclient) requires systemd-based linux. Compatible systems include:
    • Fedora

    • @@ -931,7 +945,6 @@
      • Double NAT: Netmaker is currently unable to route traffic for devices behind a “double NAT”.

      • CGNAT: Netmaker is currently unable to route traffic for for devices behind a “carrier-grade NAT”.

      • -
      • Windows/iPhone/Android: To reiterate the systemd limitation, Netmaker is not currently configured to support “end user” devices such as Windows desktops and phones generally. In v0.4, Netmaker will introduce external device gateways to allow this traffic (and many other sorts of devices).

      diff --git a/docs/_build/html/external-clients.html b/docs/_build/html/external-clients.html index 485f2b56..8b3b0fbe 100644 --- a/docs/_build/html/external-clients.html +++ b/docs/_build/html/external-clients.html @@ -333,7 +333,7 @@
    • - Uninstralling Netmaker + Uninstalling Netmaker
    @@ -459,6 +459,10 @@
  • External Clients
  • @@ -471,6 +475,20 @@ Introduction + +
  • + + + Configuring an Ingress Gateway + + +
  • +
  • + + + Adding Clients to a Gateway + +
  • @@ -729,6 +747,10 @@
  • External Clients
  • @@ -759,6 +781,34 @@

    Important to note, an external client is not reachable by the network, meaning the client can establish connections to other machines, but those machines cannot independently establish a connection back. The External Client method should only be used in use cases where one wishes to access resource runnin on the virtual network, and not for use cases where one wishes to make a resource accessible on the network. For that, use netclient.

    +

    Configuring an Ingress Gateway

    +

    External Clients must attach to an Ingress Gateway. By default, your network will not have an ingress gateway. To configure an ingress gateway, you can use any node in your network, but it should have a public IP address (not behind a NAT). Your Netmaker server can be an ingress gateway and makes for a good default choice if you are unsure of which node to select.

    +Gateway + + +

    Adding Clients to a Gateway

    +

    Once you have configured a node as a gateway, you can then add clients to that gateway. Clients will be able to access other nodes in the network just as the gateway node does.

    +Gateway +

    After creating a client, you can edit the name to something more logical.

    +Gateway +

    Then, you can either download the configuration file directly, or scan the QR code from your phone (assuming you have the WireGuard app installed). It will accept the configuration just as it would accept a typical WireGuard configuration file.

    +Gateway +

    Example config file:

    +
    [Interface]
    +Address = 10.7.11.5/32
    +PrivateKey = EJf6Yy51M/YDaZgedRpuxMmrqul35WfjmHvRZR1rQ0U=
    +
    +[Peer]
    +PublicKey = m/RPuMVsbpgQ+RkxlgK2mG+dDFlzqn+ua2zJt8Wn7GA=
    +AllowedIPs = 10.7.11.0/24
    +Endpoint = 3.236.60.247:51822
    +PersistentKeepalive = 20
    +
    +
    +
    +

    Your client should now be able to access the network! A client can be invalidated at any time by simply deleting it from the UI.

    + + diff --git a/docs/_build/html/genindex.html b/docs/_build/html/genindex.html index fafa065a..b74b5cbe 100644 --- a/docs/_build/html/genindex.html +++ b/docs/_build/html/genindex.html @@ -331,7 +331,7 @@
  • - Uninstralling Netmaker + Uninstalling Netmaker
  • @@ -453,6 +453,20 @@ Introduction + +
  • + + + Configuring an Ingress Gateway + + +
  • +
  • + + + Adding Clients to a Gateway + +
  • diff --git a/docs/_build/html/index.html b/docs/_build/html/index.html index f7ce4f12..f02424da 100644 --- a/docs/_build/html/index.html +++ b/docs/_build/html/index.html @@ -332,7 +332,7 @@
  • - Uninstralling Netmaker + Uninstalling Netmaker
  • @@ -454,6 +454,20 @@ Introduction + +
  • + + + Configuring an Ingress Gateway + + +
  • +
  • + + + Adding Clients to a Gateway + +
  • @@ -797,7 +811,7 @@
  • Deploy Nodes
  • Manage Nodes
  • Uninstalling the netclient
  • -
  • Uninstralling Netmaker
  • +
  • Uninstalling Netmaker
  • @@ -845,6 +859,8 @@ diff --git a/docs/_build/html/quick-start.html b/docs/_build/html/quick-start.html index cbd7b37b..b212340c 100644 --- a/docs/_build/html/quick-start.html +++ b/docs/_build/html/quick-start.html @@ -308,7 +308,7 @@
  • Uninstalling the netclient
  • -
  • Uninstralling Netmaker +
  • Uninstalling Netmaker
  • @@ -367,7 +367,7 @@
  • - Uninstralling Netmaker + Uninstalling Netmaker
  • @@ -764,7 +764,7 @@
  • Uninstalling the netclient
  • -
  • Uninstralling Netmaker +
  • Uninstalling Netmaker
  • @@ -822,14 +822,15 @@
  • Click ADD NEW ACCESS KEY

  • Give it a name (ex: “mykey”) and a number of uses (ex: 25)

  • Click CREATE KEY (Important: Do not click out of the following screen until you have saved your key details. It will appear only once.)

  • -
  • Copy the bottom command under “Your agent install command with access token” and save it somewhere locally. E.x: curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.3/scripts/netclient-install.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -

  • +
  • Copy the bottom command under “Your agent install command with access token” and save it somewhere locally. E.x: curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -. A change is required here. Change netclient-install.sh in this command to netclient-install.slim.sh, EX:

  • +

    curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.slim.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -

    Access Key Screen

    You will use this command to install the netclient on your nodes. There are three different values for three different scenarios:

    • The Access Key value is the secret string that will allow your node to authenticate with the Netmaker network. This can be used with existing netclient installations where additional configurations (such as setting the server IP manually) may be required. This is not typical. E.g. netclient -c install -k <access key> -s 1.2.3.4 -p 50052

    • The Access Token value is a base64 encoded string that contains the server IP and grpc port, as well as the access key. This is decoded by the netclient and can be used with existing netclient installations like this: netclient -c install -t <access token>. You should use this method for adding a network to a node that is already on a network. For instance, Node A is in the mynet network and now you are adding it to default.

    • -
    • The install command value is a curl command that can be run on Linux systems. It is a simple script that downloads the netclient binary and runs the install command all in one.

    • +
    • The install command value is a curl command that can be run on Linux systems. It is a simple script that downloads the netclient binary and runs the install command all in one. However, this script is tailored for Secure GRPC Mode and contains an additional (unnecessary) command: netclient register -k keyvalue. This command will not work without secure GRPC enabled and will return a 500 error.

    Networks can also be enabled to allow nodes to sign up without keys at all. In this scenario, nodes enter a “pending state” and are not permitted to join the network until an admin approves them.

    @@ -848,7 +849,7 @@
      -
    1. Run the install command, Ex: curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -

    2. +
    3. Run the install command, Ex: curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.slim.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -

    You should get output similar to the below. The netclient retrieves local settings, submits them to the server for processing, and retrieves updated settings. Then it sets the local network configuration. For more information about this process, see the client installation documentation. If this process failed and you do not see your node in the console (see below), then reference the troubleshooting documentation.

    Output from Netclient Install @@ -873,7 +874,7 @@ -

    Uninstralling Netmaker

    +

    Uninstalling Netmaker

    To uninstall Netmaker from the server, simply run docker-compose down or docker-compose down --volumes to remove the docker volumes for a future installation.

    diff --git a/docs/_build/html/search.html b/docs/_build/html/search.html index 7fcb080f..e34bb47a 100644 --- a/docs/_build/html/search.html +++ b/docs/_build/html/search.html @@ -337,7 +337,7 @@
  • - Uninstralling Netmaker + Uninstalling Netmaker
  • @@ -459,6 +459,20 @@ Introduction + +
  • + + + Configuring an Ingress Gateway + + +
  • +
  • + + + Adding Clients to a Gateway + +
  • diff --git a/docs/_build/html/searchindex.js b/docs/_build/html/searchindex.js index 003f9ebe..c9289c2c 100644 --- a/docs/_build/html/searchindex.js +++ b/docs/_build/html/searchindex.js @@ -1 +1 @@ -Search.setIndex({docnames:["about","api","architecture","client-installation","conduct","contribute","external-clients","index","license","quick-start","server-installation","support","troubleshoot","usage"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["about.rst","api.rst","architecture.rst","client-installation.rst","conduct.rst","contribute.rst","external-clients.rst","index.rst","license.rst","quick-start.rst","server-installation.rst","support.rst","troubleshoot.rst","usage.rst"],objects:{},objnames:{},objtypes:{},terms:{"0afehuytvin":3,"100":1,"101":[3,10],"127":10,"168":3,"170":3,"172":1,"192":3,"200":1,"27017":10,"50051":[2,3,10],"50052":9,"50555":10,"51821":3,"534":10,"5qktbtgsvb45y3qyrmwft":3,"6400":1,"8081":[1,2,3,10],"abstract":2,"break":3,"case":[1,2,3,6,7,9,10,11,13],"default":[1,2,3,9,10],"function":[1,2,3,10,11],"import":[2,6,9,10],"long":[0,9,11],"new":[2,3,9,11],"public":[2,3,4,8],"return":2,"short":3,"static":[2,10],"switch":[10,11],"true":[3,10],"try":11,"var":[1,10],"while":[2,10,11],AWS:[0,11],Being:4,DNS:[2,7,9,13],For:[2,3,6,9,10],IPs:2,Not:[2,11],That:[2,11],The:[0,1,2,3,4,6,9,10,11],Then:[9,10],There:[0,1,3,9,10,11],These:[2,3,9,10,11],Use:[3,7],Used:[3,10],Useful:10,Using:[0,1,4,7,9],Will:[3,10],With:[2,10,11],aa3bvg0rnitirxdx:1,abil:11,abl:[1,2,10,11],abou:9,about:[2,3,9,11],abov:[2,9,10,11],absent:3,abus:4,acceler:2,accept:[3,4],access:[0,2,3,6,7,9,10,11,13],accesskei:[1,3],accomplish:11,account:[4,11],achiev:[1,2,11],across:[0,3],act:[2,4,6,10],action:[1,3,4],actual:2,adapt:4,add:[0,1,2,3,9,10],added:[2,3,9,10],adding:9,addit:[2,3,9,10,11],addnetwork:1,addr:3,address:[2,3,4,9,10],addressipv6:3,addressrang:[1,9],adequ:10,adm:1,admin:[0,1,2,3,9,10],adopt:2,advanc:[1,4,9,10],after:[3,9,11],against:[3,10],age:4,agent:[0,2,3,7,9,10],agent_backend:10,agentbackend:10,aggreg:2,agre:11,alex:11,algo:11,align:4,all:[0,1,2,3,4,6,7,8,9,10,11],allow:[1,2,3,6,9,10,13],allowedip:3,allowedorigin:10,alreadi:[2,3,9,10],also:[0,1,2,9,10,11],altern:[2,10],alwai:10,android:2,ani:[0,2,3,4,6,9,10],anoth:[2,11],anyth:2,anywai:11,aorijqalrik3ajflaqrdajhkr:1,apach:[2,10],api:[2,3,9,10],api_port:10,apiaddress:3,apihost:10,apiport:10,apiserv:3,app:11,appear:[4,9],appli:4,applic:1,appoint:4,appropri:[2,4,6,10],approv:[1,3,9,11],apt:10,arbitrari:0,arch:2,architectur:[9,10],argument:3,arm:3,arrai:2,artifact:8,ask:3,asset:10,assum:[2,10],attack:4,attent:4,attribut:7,authent:[2,3,9],author:1,authsourc:10,autom:0,automat:[2,3,6,10,11],avail:[3,4,8,10,11],awai:2,await:2,back:[2,6,10],backend:10,backend_url:10,background:2,backup:11,badpassword:3,balanc:11,ban:4,bare:2,base64:9,base:[2,3,9,10,11],bash:2,basi:[2,11],basic:7,bearer:1,becaus:[0,2,10],becom:[2,10,11],been:[2,3],befor:[2,10,11],begin:2,behavior:4,behind:2,being:[2,11],believ:11,below:[1,2,9,10,13],best:[4,11],better:2,between:[0,2,10],bewar:3,beyond:[10,11],bin:10,binari:[2,3,9,10,11],bind:10,bit:[10,11,13],blank:[3,10],block:10,bodi:4,both:[2,4,10,11],bottom:9,bring:11,brows:11,browser:9,bug:7,build:7,built:2,bunch:11,busi:11,button:10,call:[0,2,3,7,10],callabl:3,can:[0,1,2,3,6,8,9,10,11,13],cannot:[3,6],cap_add:10,car:11,carrier:2,caus:[2,10],caution:[],center:0,cento:2,certain:[2,11],certif:10,cgnat:2,cgroup:10,challeng:10,chang:[0,1,2,3,6,9,10,11],check:[1,2,3,9,10],checkin:[1,3],choos:3,chose:11,chosen:[2,3],circumst:[3,4],clarifi:4,clear:11,click:[2,9,10],client:[0,2,9,11],client_mod:10,clientmod:10,close:11,cloud:[0,13],cluster:[10,13],code:[2,3,8],com:[3,4,9,10,11],come:[2,7,10],comm:[3,10],command:[2,3,9,10],comment:[4,10],commit:4,common:[2,7],commun:[2,3,4,7,10,11,13],compat:[6,7,9,11],compil:[2,3],complaint:4,complet:2,complex:2,complic:2,compon:[7,10],compos:[7,9],comput:[0,2,3],concept:7,concern:11,conf:10,confidenti:4,config:[1,2,6,11],configur:[0,1,2,6,7,9,11],conflict:[2,9,10],conflift:[],connect:[0,2,3,6,9,10,13],consequ:10,consid:[2,4],consider:2,consist:2,consol:9,construct:4,consum:[3,10],consumpt:[2,11],contact:[4,7],contain:[2,9,10],container_nam:10,content:1,contrast:2,contribut:4,contributor:4,control:[2,9,10],copi:[9,10],core:[0,7,11],coredn:7,corefil:10,coreo:2,corpor:11,correct:4,cors_allowed_origin:10,could:[0,4],coven:4,cover:[7,10],cpu:2,creat:[0,1,2,3,4,7,10,13],createadmin:1,creategatewai:1,createus:10,creation:0,credenti:10,critic:4,cross:13,cryptocurr:0,curl:[9,10],current:[2,3,10],custom:[7,10],customiz:10,cycl:[1,2],daemon:2,data:[0,2,10],databas:[2,11],date:1,dbadminanydatabas:10,dbu:10,debian:[2,10],decis:7,decod:[2,3,9],deem:4,defaultkeepal:9,defin:4,delet:[1,9],deletegatewai:1,depend:[2,3,10],depends_on:10,deploi:[2,7,10],deploy:[2,10],derogatori:4,deserv:11,design:[2,3,6,7,11],desktop:[2,3,6],detail:[1,2,3,4,7,9,10],determin:[4,10,11],dev:10,develop:[2,11],devic:[0,2,3,7,9,11],diabl:10,diagram:2,differ:[0,2,4,9,10,11,13],directli:[0,1,2,10],directori:10,disabl:[2,4],disable_remote_ip_check:10,disableremoteipcheck:10,discord:11,discuss:10,displai:9,displaynam:1,distribut:2,distributionshav:2,dns:[2,3,10],dns_mode:10,dnsconfig:10,dnsmode:10,dnsstublisten:10,doc:[9,10,11],docker:[2,7,9],document:[0,2,3,9,10],doe:[2,6,7,10],doing:0,domain:10,don:[2,3,11,13],doubl:2,down:[0,9,11],download:[9,10],dual:[3,7],duplic:3,dure:3,dynam:[0,2,7,11],each:[0,2,3,9,10],easi:10,easier:[2,10,11],easiest:2,easili:[0,10],echo:9,econom:11,edit:4,effect:9,effici:2,egress:3,either:[2,11],electron:4,elev:10,els:11,email:11,empathi:4,enabl:[2,9],encod:9,encompass:0,encount:[2,11],encrypt:[0,2,3,10],end:[2,10,11],endpoint:[1,2,3,6,10],enforc:7,enhanc:7,enough:9,ensur:[10,11],enter:9,entir:[2,9],entireti:2,env:[1,10],environ:[0,1,2,4,10],equal:10,equival:10,escal:10,especi:10,establish:6,etc:[3,9,10],eth0:1,ethnic:4,evalu:11,even:[0,2,10],event:4,eventu:11,everi:[2,3,9,11],everyon:4,everyth:2,evolv:2,examin:9,exampl:[2,3,4,7],except:10,execut:11,exist:[0,2,9,10],expand:11,expect:[4,7,10,11],experi:4,explain:[2,7,10],explicit:4,explicitli:10,expos:2,express:4,expressvpn:11,extens:2,extern:[2,3,9,11],face:4,fact:0,fail:9,fair:4,fairli:2,faith:4,fals:[3,10],familiar:[0,2,11],faq:7,fast:[2,7,10],faster:[0,2,3],featur:[7,9],fedora:2,feiszli:11,few:[3,10,11],field:9,figur:13,file:[2,6,11],filenam:1,financi:11,find:[2,13],fine:0,firewal:10,firmli:11,first:[2,3,9,11,13],fit:13,flag:10,flat:0,flexibl:[0,2,11],flow:2,focu:11,focus:4,folder:2,follow:[4,9,10,11],fork:7,form:3,forward:[3,6],foster:4,found:[0,2,8,9],free:4,freeli:8,from:[0,1,2,3,4,9,10,11,13],front:[2,10],full:[0,2],fulli:[0,2,10,11],fundament:10,further:4,futur:[2,9,10],gain:10,gatewai:[1,2,3,6,10,11,13],gender:4,gener:[1,2,3,6,10,11],generate_config_j:10,get:[1,3,7,9,10,13],github:[2,3,7,10,11],githubusercont:[9,10],give:[2,7,9,10],given:[0,2,9,10],global:3,goe:0,going:9,golang:2,good:[4,11,13],googl:10,grab:3,gracefulli:4,grade:2,gravitl:[3,4,9,10,11],grpc:[2,3,9],grpc_port:10,grpcaddr:10,grpcaddress:3,grpcaddrrang:10,grpcendpoint:10,grpchost:10,grpcifac:10,grpcport:10,grpcserver:3,grpcwg:10,guid:[0,2,9,10],guidelin:11,hack:11,hand:7,handl:0,harass:4,hard:11,harm:4,has:[0,2,3,10,11,13],hasadmin:1,have:[0,2,3,4,9,10,11,13],header:1,health:11,heart:3,heavi:2,heaviest:2,heavili:2,help:[1,2,3,7,10,11,13],here:[2,4,8,9,10,11,13],high:2,highli:3,hold:[0,2,3],home:[0,2,3],hook:6,host:[2,3,9,10,11],host_ip:[9,10],hous:1,how:[2,3,7,10],howev:[1,2,3,9,10,11],html:10,http:[1,3,9,10,11],http_port:10,hub:[0,2,10],idea:11,ident:4,identifi:3,ignor:3,imag:10,imageri:4,immedi:11,impact:[2,11],inappropri:4,incid:4,includ:[2,4,6,7,10,11],inclus:4,incompat:2,incorrect:[3,10],increas:[2,10],inde:11,independ:6,individu:[4,10],industri:2,info:[2,4,10,11],inform:[2,3,4,9],ingress:[3,6,11],initi:[3,11],insert:[9,10],instal:[0,2],instanc:[1,2,3,4,9,10],instead:[0,3,6,9,10],instruct:[9,10],insult:4,intact:3,intend:3,interact:[0,2,3],interest:[4,11],interfac:[1,3,10],internet:[0,2,11],intro:13,introduc:[2,11],introduct:7,invalid:9,investig:4,invis:2,iot:[0,2],ipforward:3,iphon:2,ipsec:2,iptabl:3,ipv4:3,ipv6:[3,7,10],isdualstack:3,isingressgatewai:3,isloc:3,issu:[4,7,10],iter:11,its:[0,2,3,4,6,9,10],itself:[2,3],join:[3,9,10],journalctl:10,json:1,just:[0,2,3,9,10,11],jwt:[1,3],keep:[1,10],keepal:3,kei:[2,3,10],kernel:[0,2,10,11],keynam:1,keyrequir:10,keyupd:1,know:[10,11,13],known:2,kubernet:[0,7],lack:10,lan:3,languag:4,laptop1:1,laptop:6,larg:2,larger:10,last:1,lastmodifi:1,latenc:2,later:[2,10],latest:[2,3,6,10],layer:0,layout:2,lead:11,leadership:4,learn:9,least:0,leav:[3,9],left:10,less:2,let:[0,11,13],level:[2,4,10],light:10,lighter:2,lightweight:2,like:[0,2,3,9,10],likewis:10,limit:[7,10,11],line:2,link:[7,10],linux:[2,3,7,9,11],list:[0,2,3,10],listenport:1,littl:[2,11],load:[2,10,11],local:[2,3,7,9,10],localaddress:[1,3],localhost:1,localrang:3,locat:[0,2,3,10],login:[9,10],look:11,lot:[2,11],lxc:10,mac:[3,10],macaddress:[1,3],machin:[0,2,3,6,9,10],made:[0,2],mai:[1,2,3,4,9,10,11],mail:4,main:[],mainfram:[],maintain:[4,11],make:[0,2,4,6,9,10,11],man:6,manag:[0,2,6,7,10,13],mandatori:10,mani:[0,2,10,11,13],manual:[2,3,9,11],map:10,master:[9,10],master_kei:10,masterkei:[1,10],match:[3,10],mean:[2,3,6,10,11],media:4,member:[4,13],memori:2,mesh:[0,6,11,13],meshclient:1,metal:2,method:[1,2,3,6,9],mgmt:1,middl:6,might:[0,2,10],mind:10,mint:2,minu:10,miss:2,mix:3,mode:[2,7,9],model:[0,2],modif:10,modifi:[1,2,3,9,10,11],monet:11,mongo:10,mongo_admin:10,mongo_host:10,mongo_initdb_root_password:10,mongo_initdb_root_usernam:10,mongo_opt:10,mongo_pass:10,mongo_port:10,mongoadmin:10,mongoconn:10,mongopass:10,mongovol:10,more:[0,1,2,7,9,10,11],most:[1,2,3,10,11],mostli:[2,3],mount:10,move:11,much:[0,2,11],mullvad:11,mullvadvpn:11,multipl:[0,9,10],must:[1,2,3,9,10],mykei:[1,9],mynet:9,name:[1,3,9,10],nameserv:[2,3,10],nat:2,nation:4,navig:9,nebula:0,necessari:[3,4,9,10],need:[0,1,2,3,9,10,11],neighborhood:0,neither:2,net_admin:10,netclient:[0,6,7,10],netclient_accesskei:3,netclient_accesstoken:3,netclient_address:3,netclient_addressipv6:3,netclient_api_serv:3,netclient_daemon:3,netclient_dn:3,netclient_endpoint:3,netclient_grpc_serv:3,netclient_interfac:3,netclient_ipforward:3,netclient_is_dualstack:3,netclient_is_loc:3,netclient_keepal:3,netclient_localaddress:3,netclient_localrang:3,netclient_macaddress:3,netclient_nam:3,netclient_network:3,netclient_o:3,netclient_password:3,netclient_port:3,netclient_postdown:3,netclient_postup:3,netclient_privatekei:3,netclient_publickei:3,netclient_roam:3,netconfig:3,netid:1,netmak:[1,3,6,8,10],network:[0,6,7,9,10,11],network_mod:10,next:[3,9],nginx:[2,10],noclient:10,node:[3,7,10,11],nodn:10,non:[2,3,10],none:0,noonewillguessthi:1,nordvpn:11,note:[1,3,6,7],notifi:2,now:[0,9,10,11],number:[2,9,10,11],oblig:4,obtain:[1,3],ode:3,off:[2,3,10],offens:4,offic:[0,13],offici:[1,4,9],offlin:4,onc:9,one:[2,3,6,9,10],onli:[1,2,3,6,9,11],onlin:4,onto:10,open:[2,4,10,11],openvpn:2,oper:[2,3,10],operatingsystem:3,opt:10,option:[2,3,7,11],order:[2,11],orient:4,origin:10,osi:11,other:[0,2,3,4,6,9,10,11],otherwis:[0,3,4],our:[7,10,11],out:[0,2,9,10,11,13],outlin:[2,10],output:[3,9],outsid:7,over:[0,2,10,11],overlai:[0,7],overrid:10,overridden:[2,10],overview:[2,7,13],overwhelm:13,own:2,pace:11,page:2,pai:11,pair:[2,10],pane:9,part:[2,3,10],partial:2,particip:4,particular:[2,3],pass:[2,3,10],password:[1,3,9,10],path:[1,10,11],patient:11,peer:[2,3,13],pend:[1,9],peopl:11,per:11,perform:[1,2,3,10],period:2,perman:4,permiss:[4,9],permit:9,persistenkeepal:9,persistentkeepal:3,person:4,perspect:[0,2],phase:11,phone:[2,3,6,11],physic:4,pick:[2,9],pictur:2,pidof:9,ping:9,pivpn:11,place:[3,10],plaintext:3,plan:[10,11],platform:[2,7],pleas:[3,9,11],pledg:7,plu:3,point:[2,11],polici:4,polit:4,popul:10,port:[2,3,9,10],posit:4,possibl:[2,7,9],post:[1,2,3,4],postchang:3,postdown:3,postup:3,practic:2,pre:[9,10],preced:2,prefer:10,prereqisit:7,prerequisit:[7,10],present:9,previou:2,primari:10,prior:10,privaci:11,privat:[2,4,7,9,10,11,13],privatekei:[3,10],privileg:10,privkei:[3,10],probabl:[0,11],problem:[10,11],proce:[],process:[7,9,10,11],product:[10,11],profession:4,project:[1,4,7,11],proof:0,properli:3,properti:3,provid:[0,2,10],pubkei:[3,10],publickei:[1,3,10],publish:[4,8],pull:[2,3,6],push:[3,6],put:1,pwd:10,q9cog7c9qjnoxygvri:3,quarantin:3,quick:10,quickli:[7,9],race:[4,11],rang:[3,9,10],rangestr:1,rapid:11,rapidli:2,raspian:2,raw:[3,9,10],reach:[0,2,3,6],reachabl:[2,3,6,9,10],reactj:2,read:10,readwriteanydatabas:10,reallysecret:1,reason:[2,4,10,11],reccommend:10,recent:2,reciev:[1,2],recommend:[1,3,6,10],reconfigur:[2,3],reduc:2,refer:[2,9],regard:[2,4],regardless:4,regist:[2,3],registr:3,regular:11,reiter:2,reject:4,rel:2,relai:[2,6,10,11],releas:[2,3,10],reli:[0,2,3],relianc:2,religion:4,remot:[0,3,10],remov:[1,2,4,9,10],removenetwork:1,repeat:9,repercuss:4,replac:[2,3,10],report:[2,4],repositori:[2,8],repres:4,represent:4,request:[1,2,7,10],requir:[1,2,3,9,10],resolv:[2,3,10],resolvectl:[3,10],resourc:[6,7,11],respect:4,respons:[2,7],rest:10,rest_backend:10,restart:10,restbackend:10,restrict:[10,11],result:4,retriev:[0,1,2,3,9,11],revert:3,review:4,rhel:2,right:[0,4,11],rncjjbsaa3hzuhrk5hpyxm:3,road:11,roam:3,role:10,root:[9,10],rout:[0,2],router:2,rule:3,run:[0,1,2,3,6,7,9,10,11],runnin:6,runtim:10,said:[2,11],same:[0,3,9,10],sampl:0,save:9,scenario:[9,10],schema:11,scope:7,screen:9,script:[2,3,9,10],second:9,secret:[1,3,9],secretkei:10,section:[3,9,10],secur:[0,1,2,3,7,9],securebetween:[],sed:[9,10],see:[1,2,3,9,10,11],select:[9,13],self:11,send:[2,3,10],sens:[2,11],sensibl:[0,2,9],separ:[2,4,10,13],serv:[2,10],server:[0,3,6,8,9,11],server_api_host:10,server_grpc_host:10,server_grpc_wg_address:10,server_grpc_wg_address_rang:10,server_grpc_wg_interfac:10,server_grpc_wg_keyrequir:10,server_grpc_wg_port:10,server_grpc_wg_privkei:10,server_grpc_wg_pubkei:10,server_grpc_wireguard:10,server_host:10,server_http_host:10,servic:[2,3,6,10,11],set:[1,2,3,4,9,10,11,13],setup:[0,2,7],sever:[2,10],sexual:4,sfl:[9,10],share:10,ship:9,should:[0,1,2,3,6,7,9,10,11],show:[3,4,9,10],side:[8,10],sign:[2,3,9,10],signific:2,signigif:2,signup:[2,3],similar:[0,9,10,11],simpl:[2,3,9,10],simplest:[2,3],simpli:[2,3,9],simultan:0,sinc:10,singl:[1,10,11],site:[0,7,10],situat:2,size:4,skynet:1,slim:9,slow:0,small:[2,11],smartgui:1,social:4,solut:2,solv:10,some:[1,2,3,10,11,13],someon:11,someth:[9,11],somewher:9,soon:[7,10],sort:[0,2],sourc:[2,3,7,8,11],space:4,special:[2,3,9,10],specif:[2,3,4],specifi:[3,10],speed:[0,2],split:3,spoke:[0,2,10],sponsor:11,spread:0,sql:2,ssh:9,sspl:8,stabil:11,stack:[3,7],stai:3,stake:0,standard:[2,7,9,10],start:[10,13],startup:[1,9,10],state:9,statement:7,statu:10,step:[2,3,9,10],still:[0,10,11],stock:11,stop:10,store:[2,10],string:9,structur:2,submit:[7,9],subnet:2,subspac:11,subspacecloud:11,substanti:[0,10],success:2,sudo:[3,9,10],support:[2,10],sure:[10,11],surfshark:11,suse:2,swagger:7,sys:10,sys_modul:10,system:[7,9],system_bus_socket:10,systemctl:10,systemd:[3,9,10,11],tab:9,tailscal:0,take:[0,1,2,4,9,10],talk:[0,2],team:[4,11],technic:[0,7,11],technolog:2,tell:[0,2,3],temporari:4,temporarili:4,term:11,terrain:11,test:[7,9,10],than:[2,11],thei:[0,2,4,10,11],them:[2,9,10,11],themselv:2,thhe:10,thi:[0,1,2,3,4,6,7,8,9,10,11],thing:[0,2,10,11],think:[11,13],thmpvlcykonxi:3,those:[0,2,3,6,10],though:[10,11],thought:11,thousand:0,threaten:4,three:9,through:2,ticket:11,time:[1,2,3,9,11,13],timer:2,timestamp:2,token:[3,9],tool:[0,3],top:2,topic:13,topolog:2,total:10,toward:4,traffic:[0,2,6,10],trailofbit:11,transact:2,treat:[2,3],tricki:2,troll:4,troubleshoot:[1,9],tunnel:[0,2],tunnelbear:11,turn:[2,10],tutori:7,two:[0,1,2,10],type:1,typic:[0,2,9],ubuntu:[2,10],udp:10,ultim:11,unabl:2,unaccept:4,uncom:10,uncordon:1,under:[3,8,9,10],underlai:[0,13],underli:10,understand:10,uninstal:7,uninstral:7,uniqu:3,unix:[2,3,11],unless:[3,10],unmanag:11,unmesh:11,unnecessari:11,unregist:3,unregistr:3,unset:[3,9,10],unsupport:11,unten:11,until:[3,9,11],unwelcom:4,unzip:10,updat:[1,2,9,11],upon:[2,3],url:10,usag:[3,7,10,13],use:[0,1,2,3,4,6,7,9,10,11,13],used:[1,2,3,6,9,10],useful:10,user:[2,3,9,10,11],useradminanydatabas:10,usernam:[1,9,10],uses:[0,1,2,6,9,10],using:[0,1,2,3,4,6,7,10,11,13],usr:10,usual:3,util:3,valid:2,valu:[1,2,3,9,10],vari:10,variabl:[2,3],variou:[3,7,10],vehicl:11,veri:[0,2,3,11],verifi:2,versa:11,version:[2,4,8,10],via:[1,2,4,6,10,11,13],vice:11,view:[9,11],viewpoint:4,vim:10,virtual:[0,3,6,7,11,13],visibl:9,vm3ow4thatogiwnsla3thsl3894th:9,vne197vmradjodkb1zsuja:3,volum:[9,10],vpc:[0,11],vpn:[2,13],wai:[10,11],want:[0,2,9,10,11],warn:10,web:[10,11],webserv:2,websit:2,week:11,weight:[2,10],welcom:[4,11],well:[2,3,7,9,11],wgaddress6:3,wgaddress:3,wget:[9,10],what:[2,4,6,7],whatev:[3,10],when:[2,3,4,10],where:[2,6,7,9,10,13],wherev:0,whether:[0,3,10],which:[0,2,3,4,6,8,9,10,11],who:4,why:[2,3,7],wide:2,wider:2,wiki:4,window:[2,3,10,11],wiregard:10,wireguard:[0,3,6,7,9,10,11],wish:[6,10],within:[2,3,4],without:[1,2,3,4,7,9,11],won:11,work:[2,7,11],workstat:9,world:[0,2],wors:2,worth:2,would:[0,3,9,10,11],write:[2,10],ws2:3,www:10,x86:3,yaml:[1,10],yes:[3,10],yml:[9,10],you:[0,1,2,3,6,7,9,10,13],your:[0,1,2,3,9,10,13],your_pass:1,your_password:1,your_secret_kei:1,zeroti:0,zip:10,zrb9vfhk8a:11},titles:["About","API Reference","Architecture","Client Installation","Code of Conduct","Contribute","External Clients","Welcome to the Netmaker Documentation","License","Quick Start","Server Installation","Support","Troubleshooting","Using Netmaker"],titleterms:{"case":0,Adding:3,DNS:[3,10],Use:0,Using:13,Will:11,about:[0,7],access:1,agent:12,ani:11,annot:10,api:[1,7],architectur:[2,7],attribut:4,authent:1,basic:13,better:11,bug:11,build:5,call:1,cli:3,client:[3,6,7,10],code:[4,5,7],common:12,compat:[2,3,10],compon:2,compos:10,concept:2,conduct:[4,7],config:[3,10],configur:[3,10],contact:11,contribut:[5,7],core:2,coredn:[2,10,12],creat:9,curl:1,daemon:3,deploi:9,descript:10,disabl:10,docker:10,document:[1,7],doe:[0,11],dual:13,enabl:10,enforc:4,enhanc:5,exampl:1,extern:[6,7,13],faq:11,featur:[10,11],file:[1,3,10],fork:5,format:1,full:10,grpc:10,guid:7,how:[0,11],instal:[3,7,9,10,11],introduct:[3,6,9],ipv6:13,issu:[5,11,12],kei:[1,9],kubernet:[10,13],licens:[7,8,11],like:11,limit:2,linux:10,local:13,log:3,make:3,manag:[1,3,9],manual:[],mesh:2,mode:[3,10],mongodb:[2,10,11],netclient:[2,3,9,11],netmak:[0,2,7,9,11,13],network:[1,2,3,13],node:[1,2,9,13],nordnpn:11,note:10,offer:11,onli:10,oper:11,option:10,our:4,paid:11,pledg:4,prereqisit:10,prerequisit:[3,9],privat:3,process:2,quick:[7,9],refer:[1,3,7,10],remov:3,request:11,respons:4,scope:4,secur:10,server:[1,2,7,10,12],setup:[9,10],site:13,slim:10,smaller:11,sql:11,sspl:11,stack:13,standard:4,start:[7,9],submit:5,support:[7,11],system:[2,3,10,11],systemd:2,technic:2,test:5,token:[],troubleshoot:[3,7,12],tutori:13,uninstal:[3,9],uninstral:9,updat:3,usag:1,user:1,variabl:10,video:13,view:3,vpn:11,welcom:7,what:0,why:11,wireguard:2,without:10,work:0,written:13,you:11}}) \ No newline at end of file +Search.setIndex({docnames:["about","api","architecture","client-installation","conduct","contribute","external-clients","index","license","quick-start","server-installation","support","troubleshoot","usage"],envversion:{"sphinx.domains.c":2,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":3,"sphinx.domains.index":1,"sphinx.domains.javascript":2,"sphinx.domains.math":2,"sphinx.domains.python":2,"sphinx.domains.rst":2,"sphinx.domains.std":2,"sphinx.ext.intersphinx":1,sphinx:56},filenames:["about.rst","api.rst","architecture.rst","client-installation.rst","conduct.rst","contribute.rst","external-clients.rst","index.rst","license.rst","quick-start.rst","server-installation.rst","support.rst","troubleshoot.rst","usage.rst"],objects:{},objnames:{},objtypes:{},terms:{"0afehuytvin":3,"100":1,"101":[3,10],"127":10,"168":3,"170":3,"172":1,"192":3,"200":1,"236":6,"247":6,"27017":10,"500":9,"50051":[2,3,10],"50052":9,"50555":10,"51821":3,"51822":6,"534":10,"5qktbtgsvb45y3qyrmwft":3,"6400":1,"8081":[1,2,3,10],"abstract":2,"break":3,"case":[1,2,3,6,7,9,10,11,13],"default":[1,2,3,6,9,10],"function":[1,3,10,11],"import":[2,6,9,10],"long":[0,9,11],"new":[2,3,9,11],"public":[2,3,4,6,8],"return":[2,9],"short":3,"static":[2,10],"switch":[10,11],"true":[3,10],"try":11,"var":[1,10],"while":[2,10,11],AWS:[0,11],Adding:7,Being:4,DNS:[2,7,9,13],For:[2,3,6,9,10],IPs:2,Not:[2,11],That:[2,11],The:[0,1,2,3,4,6,9,10,11],Then:[6,9,10],There:[0,1,3,9,10,11],These:[2,3,9,10,11],Use:[3,7],Used:[3,10],Useful:10,Using:[0,1,4,7,9],Will:[3,10],With:[2,10,11],aa3bvg0rnitirxdx:1,abil:11,abl:[1,2,6,10,11],abou:9,about:[2,3,9,11],abov:[2,9,10,11],absent:3,abus:4,acceler:2,accept:[3,4,6],access:[0,2,3,6,7,9,10,11,13],accesskei:[1,3],accomplish:11,account:[4,11],achiev:[1,2,11],across:[0,3],act:[2,4,6,10],action:[1,3,4],actual:2,adapt:4,add:[0,1,2,3,6,9,10],added:[2,3,9,10],adding:9,addit:[2,3,9,10,11],addnetwork:1,addr:3,address:[2,3,4,6,9,10],addressipv6:3,addressrang:[1,9],adequ:10,adm:1,admin:[0,1,2,3,9,10],adopt:2,advanc:[1,4,9,10],after:[3,6,9,11],against:[3,10],age:4,agent:[0,2,3,7,9,10],agent_backend:10,agentbackend:10,aggreg:2,agre:11,alex:11,algo:11,align:4,all:[0,1,2,3,4,6,7,8,9,10,11],allow:[1,2,3,6,9,10,13],allowedip:[3,6],allowedorigin:10,alreadi:[2,3,9,10],also:[0,1,2,9,10,11],altern:[2,10],alwai:10,android:2,ani:[0,2,3,4,6,9,10],anoth:[2,11],anyth:2,anywai:11,aorijqalrik3ajflaqrdajhkr:1,apach:[2,10],api:[2,3,9,10],api_port:10,apiaddress:3,apihost:10,apiport:10,apiserv:3,app:[6,11],appear:[4,9],appli:4,applic:1,appoint:4,appropri:[2,4,6,10],approv:[1,3,9,11],apt:10,arbitrari:0,arch:2,architectur:[9,10],argument:3,arm:3,arrai:2,artifact:8,ask:3,asset:10,assum:[2,6,10],attach:6,attack:4,attempt:2,attent:4,attribut:7,authent:[2,3,9],author:1,authsourc:10,autom:0,automat:[2,3,6,10,11],avail:[3,4,8,10,11],awai:2,await:2,back:[2,6,10],backend:10,backend_url:10,background:2,backup:11,badpassword:3,balanc:11,ban:4,bare:2,base64:9,base:[2,3,9,10,11],bash:2,basi:[2,11],basic:7,bearer:1,becaus:[0,2,10],becom:[2,10,11],been:[2,3],befor:[2,10,11],begin:2,behavior:4,behind:[2,6],being:[2,11],believ:11,below:[1,2,9,10,13],best:[4,11],better:2,between:[0,2,10],bewar:3,beyond:[10,11],bin:10,binari:[2,3,9,10,11],bind:10,bit:[10,11,13],blank:[3,10],block:10,bodi:4,both:[2,4,10,11],bottom:9,bring:11,brows:11,browser:9,bug:7,build:7,built:2,bunch:11,busi:11,button:10,call:[0,2,3,7,10],callabl:3,can:[0,1,2,3,6,8,9,10,11,13],cannot:[3,6],cap_add:10,car:11,carrier:2,caus:[2,10],caution:[],center:0,cento:2,certain:[2,11],certif:10,cgnat:2,cgroup:10,challeng:10,chang:[0,1,2,3,6,9,10,11],check:[1,2,3,9,10],checkin:[1,2,3],choic:6,choos:3,chose:11,chosen:[2,3],circumst:[3,4],clarifi:4,clear:11,cli:2,click:[2,9,10],client:[0,9,11],client_mod:10,clientmod:10,close:11,cloud:[0,13],cluster:[10,13],code:[2,3,6,8],com:[3,4,9,10,11],come:[2,7,10],comm:[3,10],command:[2,3,9,10],comment:[4,10],commit:4,common:[2,7],commun:[2,3,4,7,10,11,13],compat:[6,7,9,11],compil:[2,3],complaint:4,complet:2,complex:2,complic:2,compon:[7,10],compos:[7,9],comput:[0,2,3],concept:7,concern:11,conf:10,confidenti:4,config:[1,2,6,11],configur:[0,1,2,7,9,11],conflict:[2,9,10],conflift:[],connect:[0,2,3,6,9,10,13],consequ:10,consid:[2,4],consider:2,consist:2,consol:9,construct:4,consum:[3,10],consumpt:[2,11],contact:[4,7],contain:[2,9,10],container_nam:10,content:1,contrast:2,contribut:4,contributor:4,control:[2,9,10],copi:[9,10],core:[0,7,11],coredn:7,corefil:10,coreo:2,corpor:11,correct:4,cors_allowed_origin:10,could:[0,4],coven:4,cover:[7,10],cpu:2,creat:[0,1,2,3,4,6,7,10,13],createadmin:1,creategatewai:1,createus:10,creation:0,credenti:10,critic:4,cron:2,cross:13,cryptocurr:0,curl:[9,10],current:[2,3,10],custom:[2,7,10],customiz:10,cycl:[1,2],daemon:2,data:[0,2,10],databas:[2,11],date:1,dbadminanydatabas:10,dbu:10,ddflzqn:6,debian:[2,10],decis:7,decod:[2,3,9],deem:4,defaultkeepal:9,defin:4,delet:[1,6,9],deletegatewai:1,depend:[2,3,10],depends_on:10,deploi:[2,7,10],deploy:[2,10],derogatori:4,deserv:11,design:[2,3,6,7,11],desktop:[2,3,6],detail:[1,2,3,4,7,9,10],determin:[4,10,11],dev:10,develop:[2,11],devic:[0,2,3,7,9,11],diabl:10,diagram:2,differ:[0,2,4,9,10,11,13],directli:[0,1,2,6,10],directori:10,disabl:[2,4],disable_remote_ip_check:10,disableremoteipcheck:10,discord:11,discuss:10,displai:9,displaynam:1,distibut:2,distribut:2,distributionshav:2,dns:[2,3,10],dns_mode:10,dnsconfig:10,dnsmode:10,dnsstublisten:10,doc:[9,10,11],docker:[2,7,9],document:[0,2,3,9,10],doe:[2,6,7,10],doing:0,domain:10,don:[2,3,11,13],doubl:2,down:[0,9,11],download:[6,9,10],dual:[3,7],duplic:3,dure:3,dynam:[0,2,7,11],each:[0,2,3,9,10],easi:10,easier:[2,10,11],easiest:2,easili:[0,10],echo:9,econom:11,edit:[4,6],effect:9,effici:2,egress:3,either:[2,6,11],ejf6yy51m:6,electron:4,elev:10,elimin:2,els:11,email:11,empathi:4,enabl:[2,9],encod:9,encompass:0,encount:[2,11],encrypt:[0,2,3,10],end:[2,10,11],endpoint:[1,2,3,6,10],enforc:7,enhanc:7,enough:9,ensur:[10,11],enter:9,entir:9,entireti:2,env:[1,10],environ:[0,1,2,4,10],equal:10,equival:10,error:9,escal:10,especi:10,establish:6,etc:[3,9,10],eth0:1,ethnic:4,evalu:11,even:[0,2,10],event:4,eventu:11,everi:[2,3,9,11],everyon:4,everyth:2,evolv:2,examin:9,exampl:[2,3,4,6,7],except:10,execut:11,exist:[0,2,9,10],expand:11,expect:[4,7,10,11],experi:4,explain:[2,7,10],explicit:4,explicitli:10,expos:2,express:4,expressvpn:11,extens:2,extern:[3,9,11],face:4,fact:0,fail:9,fair:4,fairli:2,faith:4,fals:[3,10],familiar:[0,2,11],faq:7,fast:[2,7,10],faster:[0,2,3],featur:[7,9],fedora:2,feiszli:11,few:[3,10,11],field:9,figur:13,file:[2,6,11],filenam:1,financi:11,find:[2,13],fine:0,firewal:10,firmli:11,first:[2,3,9,11,13],fit:13,flag:10,flat:0,flexibl:[0,2,11],flow:2,focu:11,focus:4,folder:2,follow:[4,9,10,11],fork:7,form:3,forward:[3,6],foster:4,found:[0,2,8,9],free:4,freeli:8,from:[0,1,2,3,4,6,9,10,11,13],front:[2,10],full:[0,2],fulli:[0,2,10,11],fundament:10,further:4,futur:[2,9,10],gain:10,gatewai:[1,2,3,7,10,11,13],gender:4,gener:[1,2,3,6,10,11],generate_config_j:10,get:[1,3,7,9,10,13],github:[2,3,7,10,11],githubusercont:[9,10],give:[2,7,9,10],given:[0,2,9,10],global:3,goe:0,going:9,golang:2,good:[4,6,11,13],googl:10,grab:3,gracefulli:4,grade:2,gravitl:[3,4,9,10,11],grpc:[2,3,9],grpc_port:10,grpcaddr:10,grpcaddress:3,grpcaddrrang:10,grpcendpoint:10,grpchost:10,grpcifac:10,grpcport:10,grpcserver:3,grpcwg:10,guid:[0,2,9,10],guidelin:11,hack:11,hand:7,handl:[0,2],harass:4,hard:11,harm:4,has:[0,2,3,10,11,13],hasadmin:1,have:[0,2,3,4,6,9,10,11,13],header:1,health:11,heart:3,heavi:2,heaviest:2,heavili:[],help:[1,2,3,7,10,11,13],here:[2,4,8,9,10,11,13],high:2,highli:3,hold:[0,2,3],home:[0,2,3],hook:[2,6],host:[2,3,9,10,11],host_ip:[9,10],hous:1,how:[2,3,7,10],howev:[1,2,3,9,10,11],html:10,http:[1,3,9,10,11],http_port:10,hub:[0,2,10],idea:11,ident:4,identifi:3,ignor:3,imag:10,imageri:4,immedi:11,impact:[2,11],inappropri:4,incid:4,includ:[2,4,6,7,10,11],inclus:4,incompat:[],incorrect:[3,10],increas:[2,10],inde:11,independ:6,individu:[4,10],industri:2,info:[2,4,10,11],inform:[2,3,4,9],ingress:[2,3,7,11],initi:[3,11],ins:2,insert:[9,10],instal:[0,2,6],instanc:[1,2,3,4,9,10],instead:[0,3,6,9,10],instruct:[9,10],insult:4,intact:3,intend:3,interact:[0,2,3],interest:[4,11],interfac:[1,3,6,10],internet:[0,2,11],intro:13,introduc:[2,11],introduct:7,invalid:[6,9],investig:4,invis:2,iot:[0,2],ipforward:3,iphon:2,ipsec:2,iptabl:3,ipv4:3,ipv6:[3,7,10],isdualstack:3,isingressgatewai:3,isloc:3,issu:[4,7,10],iter:11,its:[0,2,3,4,6,9,10],itself:[2,3],job:2,join:[2,3,9,10],journalctl:10,json:1,just:[0,2,3,6,9,10,11],jwt:[1,3],keep:[1,10],keepal:3,kei:[2,3,10],kernel:[0,2,10,11],keynam:1,keyrequir:10,keyupd:1,keyvalu:9,know:[10,11,13],known:2,kubernet:[0,7],lack:10,lan:3,languag:4,laptop1:1,laptop:6,larg:2,larger:10,last:1,lastmodifi:1,latenc:2,later:[2,10],latest:[2,3,6,10],layer:0,layout:2,lead:11,leadership:4,learn:9,least:0,leav:[3,9],left:10,less:2,let:[0,11,13],level:[2,4,10],light:10,lighter:2,lightweight:2,like:[0,2,3,9,10],likewis:10,limit:[7,10,11],line:2,link:[7,10],linux:[2,3,7,9,11],list:[0,2,3,10],listenport:1,littl:[2,11],load:[2,10,11],local:[2,3,7,9,10],localaddress:[1,3],localhost:1,localrang:3,locat:[0,2,3,10],logic:6,login:[9,10],look:11,lot:[2,11],lxc:10,mac:[2,3,10],macaddress:[1,3],machin:[0,2,3,6,9,10],made:[0,2],mai:[1,2,3,4,9,10,11],mail:4,main:[],mainfram:[],maintain:[4,11],make:[0,2,4,6,9,10,11],man:6,manag:[0,2,6,7,10,13],mandatori:10,mani:[0,2,10,11,13],manual:[2,3,9,11],map:10,master:[9,10],master_kei:10,masterkei:[1,10],match:[3,10],mean:[2,3,6,10,11],media:4,member:[4,13],memori:2,mesh:[0,6,11,13],meshclient:1,metal:2,method:[1,2,3,6,9],mgmt:1,middl:6,might:[0,2,10],mind:10,mint:2,minu:10,miss:2,mix:3,mode:[2,7,9],model:[0,2],modif:10,modifi:[1,2,3,9,10,11],monet:11,mongo:10,mongo_admin:10,mongo_host:10,mongo_initdb_root_password:10,mongo_initdb_root_usernam:10,mongo_opt:10,mongo_pass:10,mongo_port:10,mongoadmin:10,mongoconn:10,mongopass:10,mongovol:10,more:[0,1,2,6,7,9,10,11],most:[1,2,3,10,11],mostli:[2,3],mount:10,move:11,much:[0,2,11],mullvad:11,mullvadvpn:11,multipl:[0,9,10],must:[1,2,3,6,9,10],mykei:[1,9],mynet:9,name:[1,2,3,6,9,10],nameserv:[2,3,10],nat:[2,6],nation:4,navig:9,nebula:0,necessari:[2,3,4,9,10],need:[0,1,2,3,9,10,11],neighborhood:0,neither:2,net_admin:10,netclient:[0,6,7,10],netclient_accesskei:3,netclient_accesstoken:3,netclient_address:3,netclient_addressipv6:3,netclient_api_serv:3,netclient_daemon:3,netclient_dn:3,netclient_endpoint:3,netclient_grpc_serv:3,netclient_interfac:3,netclient_ipforward:3,netclient_is_dualstack:3,netclient_is_loc:3,netclient_keepal:3,netclient_localaddress:3,netclient_localrang:3,netclient_macaddress:3,netclient_nam:3,netclient_network:3,netclient_o:3,netclient_password:3,netclient_port:3,netclient_postdown:3,netclient_postup:3,netclient_privatekei:3,netclient_publickei:3,netclient_roam:3,netconfig:3,netid:1,netmak:[1,3,6,8,10],network:[0,6,7,9,10,11],network_mod:10,next:[3,9],nginx:[2,10],noclient:10,node:[3,6,7,10,11],nodn:10,non:[2,3,10],none:0,noonewillguessthi:1,nordvpn:11,note:[1,3,6,7],notifi:2,now:[0,6,9,10,11],number:[2,9,10,11],oblig:4,obtain:[1,3],ode:3,off:[2,3,10],offens:4,offic:[0,13],offici:[1,4,9],offlin:4,onc:[6,9],one:[2,3,6,9,10],onli:[1,2,3,6,9,11],onlin:4,onto:10,open:[2,4,10,11],openvpn:2,oper:[2,3,10],operatingsystem:3,opt:10,option:[2,3,7,11],order:11,orient:4,origin:10,osi:11,other:[0,2,3,4,6,9,10,11],otherwis:[0,3,4],our:[7,10,11],out:[0,2,9,10,11,13],outlin:[2,10],output:[3,9],outsid:7,over:[0,2,10,11],overlai:[0,7],overrid:10,overridden:[2,10],overview:[2,7,13],overwhelm:13,own:2,pace:11,page:2,pai:11,pair:[2,10],pane:9,part:[2,3,10],partial:2,particip:4,particular:[2,3],pass:[2,3,10],password:[1,3,9,10],path:[1,10,11],patient:11,peer:[2,3,6,13],pend:[1,9],peopl:11,per:11,perform:[1,2,3,10],period:2,perman:4,permiss:[4,9],permit:9,persistenkeepal:9,persistentkeepal:[3,6],person:4,perspect:[0,2],phase:11,phone:[3,6,11],physic:4,pick:[2,9],pictur:2,pidof:9,ping:9,pivpn:11,place:[3,10],plaintext:3,plan:[10,11],platform:[2,7],pleas:[3,9,11],pledg:7,plu:3,point:[2,11],polici:4,polit:4,popul:10,port:[2,3,9,10],posit:4,possibl:[2,7,9],post:[1,2,3,4],postchang:3,postdown:3,postup:3,practic:2,pre:[9,10],preced:2,prefer:10,prereqisit:7,prerequisit:[2,7,10],present:9,previou:2,primari:10,prior:10,privaci:11,privat:[2,4,7,9,10,11,13],privatekei:[3,6,10],privileg:10,privkei:[3,10],probabl:[0,11],problem:[10,11],proce:[],process:[7,9,10,11],product:[10,11],profession:4,project:[1,4,7,11],proof:0,properli:3,properti:3,provid:[0,2,10],pubkei:[3,10],publickei:[1,3,6,10],publish:[4,8],pull:[2,3,6],push:[3,6],put:1,pwd:10,q9cog7c9qjnoxygvri:3,quarantin:3,quick:10,quickli:[7,9],race:[4,11],rang:[3,9,10],rangestr:1,rapid:11,rapidli:2,raspian:2,raw:[3,9,10],reach:[0,2,3,6],reachabl:[2,3,6,9,10],reactj:2,read:10,readwriteanydatabas:10,reallysecret:1,reason:[2,4,10,11],reccommend:10,recent:2,reciev:[1,2],recommend:[1,3,6,10],reconfigur:[2,3],reduc:[],refer:[2,9],regard:[2,4],regardless:4,regist:[2,3,9],registr:3,regular:11,reiter:[],reject:4,rel:2,relai:[2,6,10,11],releas:[2,3,10],reli:[0,3],relianc:[],religion:4,remot:[0,3,10],remov:[1,2,4,9,10],removenetwork:1,repeat:9,repercuss:4,replac:[2,3,10],report:[2,4],repositori:[2,8],repres:4,represent:4,request:[1,2,7,10],requir:[1,2,3,9,10],resolv:[2,3,10],resolvectl:[3,10],resourc:[6,7,11],respect:4,respons:[2,7],rest:10,rest_backend:10,restart:10,restbackend:10,restrict:[10,11],result:4,retriev:[0,1,2,3,9,11],revert:3,review:4,rhel:2,right:[0,4,11],rkxlgk2mg:6,rncjjbsaa3hzuhrk5hpyxm:3,road:11,roam:3,role:10,root:[9,10],rout:[0,2],router:2,rpumvsbpgq:6,rule:3,run:[0,1,2,3,6,7,9,10,11],runnin:6,runtim:10,said:[2,11],same:[0,3,9,10],sampl:0,save:9,scan:6,scenario:[9,10],schema:11,scope:7,screen:9,script:[2,3,9,10],second:9,secret:[1,3,9],secretkei:10,section:[3,9,10],secur:[0,1,2,3,7,9],securebetween:[],sed:[9,10],see:[1,2,3,9,10,11],select:[6,9,13],self:11,send:[2,3,10],sens:[2,11],sensibl:[0,2,9],separ:[2,4,10,13],serv:[2,10],server:[0,3,6,8,9,11],server_api_host:10,server_grpc_host:10,server_grpc_wg_address:10,server_grpc_wg_address_rang:10,server_grpc_wg_interfac:10,server_grpc_wg_keyrequir:10,server_grpc_wg_port:10,server_grpc_wg_privkei:10,server_grpc_wg_pubkei:10,server_grpc_wireguard:10,server_host:10,server_http_host:10,servic:[2,3,6,10,11],set:[1,2,3,4,9,10,11,13],setup:[0,2,7],sever:[2,10],sexual:4,sfl:[9,10],share:10,ship:9,should:[0,1,2,3,6,7,9,10,11],show:[3,4,9,10],side:[8,10],sign:[2,3,9,10],signific:2,signigif:2,signup:[2,3],similar:[0,9,10,11],simpl:[2,3,9,10],simplest:[2,3],simpli:[2,3,6,9],simultan:0,sinc:10,singl:[1,2,10,11],site:[0,2,7,10],situat:2,size:4,skynet:1,slim:9,slow:0,small:[2,11],smartgui:1,social:4,solut:2,solv:10,some:[1,2,3,10,11,13],someon:11,someth:[6,9,11],somewher:9,soon:[7,10],sort:0,sourc:[2,3,7,8,11],space:4,special:[2,3,9,10],specif:[2,3,4],specifi:[3,10],speed:[0,2],split:3,spoke:[0,2,10],sponsor:11,spread:0,sql:2,ssh:9,sspl:8,stabil:11,stack:[3,7],stai:3,stake:0,standard:[2,7,9,10],start:[10,13],startup:[1,9,10],state:9,statement:7,statu:10,step:[2,3,9,10],still:[0,10,11],stock:11,stop:10,store:[2,10],string:9,structur:2,submit:[7,9],subnet:2,subsequ:2,subspac:11,subspacecloud:11,substanti:[0,10],success:2,sudo:[3,9,10],support:[2,10],sure:[10,11],surfshark:11,suse:2,swagger:7,sys:10,sys_modul:10,system:[7,9],system_bus_socket:10,systemctl:10,systemd:[3,9,10,11],tab:9,tailor:9,tailscal:0,take:[0,1,2,4,9,10],talk:[0,2],team:[4,11],technic:[0,7,11],technolog:2,tell:[0,2,3],temporari:4,temporarili:4,term:11,terrain:11,test:[7,9,10],than:[2,11],thei:[0,2,4,10,11],them:[2,9,10,11],themselv:2,thhe:10,thi:[0,1,2,3,4,6,7,8,9,10,11],thing:[0,2,10,11],think:[11,13],thmpvlcykonxi:3,those:[0,2,3,6,10],though:[10,11],thought:11,thousand:0,threaten:4,three:9,through:2,ticket:11,time:[1,2,3,6,9,11,13],timer:2,timestamp:2,token:[3,9],tool:[0,3],top:2,topic:13,topolog:2,total:10,toward:4,traffic:[0,2,6,10],trailofbit:11,transact:2,treat:[2,3],tricki:2,troll:4,troubleshoot:[1,9],tunnel:[0,2],tunnelbear:11,turn:[2,10],tutori:7,two:[0,1,2,10],type:1,typic:[0,2,6,9],ua2zjt8wn7ga:6,ubuntu:[2,10],udp:10,ultim:11,unabl:2,unaccept:4,uncom:10,uncordon:1,under:[3,8,9,10],underlai:[0,13],underli:10,understand:10,uninstal:7,uninstral:[],uniqu:3,unix:[2,3,11],unless:[3,10],unmanag:[2,11],unmesh:11,unnecessari:[9,11],unregist:3,unregistr:3,unset:[3,9,10],unsupport:11,unsur:6,unten:11,until:[3,9,11],unwelcom:4,unzip:10,updat:[1,2,9,11],upon:[2,3],url:10,usag:[3,7,10,13],use:[0,1,2,3,4,6,7,9,10,11,13],used:[1,2,3,6,9,10],useful:10,user:[2,3,9,10,11],useradminanydatabas:10,usernam:[1,9,10],uses:[0,1,2,6,9,10],using:[0,1,2,3,4,6,7,10,11,13],usr:10,usual:3,util:3,valid:2,valu:[1,2,3,9,10],vari:10,variabl:[2,3],variou:[3,7,10],vehicl:11,veri:[0,2,3,11],verifi:2,versa:11,version:[2,4,8,10],via:[1,2,4,6,10,11,13],vice:11,view:[9,11],viewpoint:4,vim:10,virtual:[0,3,6,7,11,13],visibl:9,vm3ow4thatogiwnsla3thsl3894th:9,vne197vmradjodkb1zsuja:3,volum:[9,10],vpc:[0,11],vpn:[2,13],wai:[10,11],want:[0,2,9,10,11],warn:10,web:[10,11],webserv:2,websit:2,week:11,weight:[2,10],welcom:[4,11],well:[2,3,7,9,11],wgaddress6:3,wgaddress:3,wget:[9,10],what:[2,4,6,7],whatev:[3,10],when:[2,3,4,10],where:[2,6,7,9,10,13],wherev:0,whether:[0,3,10],which:[0,2,3,4,6,8,9,10,11],who:4,why:[2,3,7],wide:2,wider:2,wiki:4,window:[2,3,10,11],wiregard:10,wireguard:[0,3,6,7,9,10,11],wish:[6,10],within:[2,3,4],without:[1,2,3,4,7,9,11],won:11,work:[2,7,9,11],workstat:9,world:[0,2],wors:2,worth:2,would:[0,3,6,9,10,11],write:[2,10],ws2:3,www:10,x86:3,yaml:[1,10],ydazgedrpuxmmrqul35wfjmhvrzr1rq0u:6,yes:[3,10],yml:[9,10],you:[0,1,2,3,6,7,9,10,13],your:[0,1,2,3,6,9,10,13],your_pass:1,your_password:1,your_secret_kei:1,zeroti:0,zip:10,zrb9vfhk8a:11},titles:["About","API Reference","Architecture","Client Installation","Code of Conduct","Contribute","External Clients","Welcome to the Netmaker Documentation","License","Quick Start","Server Installation","Support","Troubleshooting","Using Netmaker"],titleterms:{"case":0,Adding:[3,6],DNS:[3,10],Use:0,Using:13,Will:11,about:[0,7],access:1,agent:12,ani:11,annot:10,api:[1,7],architectur:[2,7],attribut:4,authent:1,basic:13,better:11,bug:11,build:5,call:1,cli:3,client:[2,3,6,7,10],code:[4,5,7],common:12,compat:[2,3,10],compon:2,compos:10,concept:2,conduct:[4,7],config:[3,10],configur:[3,6,10],contact:11,contribut:[5,7],core:2,coredn:[2,10,12],creat:9,curl:1,daemon:3,deploi:9,descript:10,disabl:10,docker:10,document:[1,7],doe:[0,11],dual:13,enabl:10,enforc:4,enhanc:5,exampl:1,extern:[2,6,7,13],faq:11,featur:[10,11],file:[1,3,10],fork:5,format:1,full:10,gatewai:6,grpc:10,guid:7,how:[0,11],ingress:6,instal:[3,7,9,10,11],introduct:[3,6,9],ipv6:13,issu:[5,11,12],kei:[1,9],kubernet:[10,13],licens:[7,8,11],like:11,limit:2,linux:10,local:13,log:3,make:3,manag:[1,3,9],manual:[],mesh:2,mode:[3,10],mongodb:[2,10,11],netclient:[2,3,9,11],netmak:[0,2,7,9,11,13],network:[1,2,3,13],node:[1,2,9,13],nordnpn:11,note:10,offer:11,onli:10,oper:11,option:10,our:4,paid:11,pledg:4,prereqisit:10,prerequisit:[3,9],privat:3,process:2,quick:[7,9],refer:[1,3,7,10],remov:3,request:11,respons:4,scope:4,secur:10,server:[1,2,7,10,12],setup:[9,10],site:13,slim:10,smaller:11,sql:11,sspl:11,stack:13,standard:4,start:[7,9],submit:5,support:[7,11],system:[2,3,10,11],systemd:2,technic:2,test:5,token:[],troubleshoot:[3,7,12],tutori:13,uninstal:[3,9],uninstral:[],updat:3,usag:1,user:1,variabl:10,video:13,view:3,vpn:11,welcom:7,what:0,why:11,wireguard:2,without:10,work:0,written:13,you:11}}) \ No newline at end of file diff --git a/docs/_build/html/server-installation.html b/docs/_build/html/server-installation.html index 4ff1effc..0ed97d7b 100644 --- a/docs/_build/html/server-installation.html +++ b/docs/_build/html/server-installation.html @@ -1009,7 +1009,7 @@ container_name: netmaker depends_on: - mongodb - image: gravitl/netmaker:v0.3 + image: gravitl/netmaker:v0.5 volumes: # Volume mounts necessary for CLIENT_MODE to control netclient, wireguard, and networking on host (except dnsconfig, which is where dns config files are stored for use by CoreDNS) - ./:/local - /etc/netclient:/etc/netclient @@ -1053,7 +1053,7 @@ container_name: netmaker-ui depends_on: - netmaker - image: gravitl/netmaker-ui:v0.3 + image: gravitl/netmaker-ui:v0.5 links: - "netmaker:api" ports: diff --git a/docs/architecture.rst b/docs/architecture.rst index 37374f94..6e0a4c32 100644 --- a/docs/architecture.rst +++ b/docs/architecture.rst @@ -63,8 +63,7 @@ SystemD SystemD is a system service manager for a wide array of Linux operating systems. Not all Linux distributions have adopted systemd, but, for better or worse, it has become a fairly common standard in the Linux world. That said, any non-Linux operating system will not have systemd, and many Linux/Unix distributionshave alternative system service managers. -Netmaker's netclient, the agent which controls networking on all nodes, relies heavily on systemd as of version 0.3. This reliance is being reduced but is currently a core dependency, causing most of the limitations and incompatibilities. As Netmaker evolves, systemd will become just one of the possible service management options, allowing the netclient to be run on a wider array of devices. - +Netmaker's netclient, the agent which controls networking on all nodes, can be run as a CLI or as a system daemon. It runs as a daemon by default, and this requires systemd. As Netmaker evolves, systemd will become just one of the possible service management options, allowing the netclient to be run on a wider array of devices. However, for the time being, the netclient should be run "unmanaged" (netclient join -daemon=off) on systems that do not run systemd, and some other method can be used like a cron job or custom script. Components =========== @@ -88,15 +87,17 @@ The Netmaker server interacts with (as of v0.3) a MongoDB instance, which holds Netclient ---------------- -The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker `GitHub Repository `_. The binary, by itself, can be compiled for most systems. However, this binary is designed to manage a certain number of Operating Systems. As of version 0.3, it requires systemd in order to manage the host system appropriately. It may be installable, and it may even make the machine a part of the mesh network, but it will not function in entirely (see Compatible Systems for more info) without systemd. +The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker `GitHub Repository `_. The binary, by itself, can be compiled for most systems. However, this binary is designed to manage a certain number of Operating Systems. As of version 0.5, the netclient can be run as a system daemon on linux distributions with systemd, or as an "unmanaged" client on distributions without systemd. -The netclient is installed via a simple bash script, which pulls the latest binary and runs install command. +The netclient is installed via a simple bash script, which pulls the latest binary and runs 'register' and 'join' commands. -The install command registers the machine with the Netmaker server using sensible defaults, which can be overridden with a config file or environment variables. Assuming the netclient has a valid key (or the network allows manual node signup), it will be registered in the Netmaker network, which will return configuration details about how to set up the local network. +The 'register' command adds a WireGuard tunnel directly to the netmaker server, for all subsequent communication. -The netclient then sets itself up in systemd, and configures WireGuard. At this point it should be part of the network. +The 'join' command attempts to add the machine to the Netmaker network using sensible defaults, which can be overridden with a config file or environment variables. Assuming the netclient has a valid key (or the network allows manual node signup), it will be registered into the Netmaker network, and will be returned necessary configuration details for how to set up its local network. -On a periodic basis (systemd timer), the netclient performs a "check in." It will authenticate with the server, and check to see if anything has changed in the network. It will also post changes about its own local configuration if there. If there has been a change, the server will return new configurations and the netclient will reconfigure the network. +The netclient then sets up the systemd daemon (if running in daemon mode), and configures WireGuard. At this point it should be part of the network. + +If running in daemon mode, on a periodic basis (systemd timer), the netclient performs a "check in." It will authenticate with the server, and check to see if anything has changed in the network. It will also post changes about its own local configuration if there. If there has been a change, the server will return new configurations and the netclient will reconfigure the network. If not running in daemon mode, it is up to the operator to perform check ins (netclient checkin -n < network name >). The check in process is what allows Netmaker to create dynamic mesh networks. As nodes are added to, removed from, and modified on the network, other nodes are notified, and make appropriate changes. @@ -104,7 +105,7 @@ The check in process is what allows Netmaker to create dynamic mesh networks. As MongoDB -------- -As of v0.3, Netmaker uses MongoDB as its database, and interacts with a MongoDB instance to store and retrieve information about nodes, networks, and users. Netmaker is rapidly evolving, and MongoDB provides a flexible database structure that accelerates development. However, MongoDB is also the heaviest component of Netmaker (high cpu/memory consumption), and is set to be replaced by a lighter-weight, SQL-based database in the future. +As of v0.5, Netmaker uses MongoDB as its database, and interacts with a MongoDB instance to store and retrieve information about nodes, networks, and users. Netmaker is rapidly evolving, and MongoDB provides a flexible database structure that accelerates development. However, MongoDB is also the heaviest component of Netmaker (high cpu/memory consumption), and is set to be replaced by a lighter-weight, SQL-based database in the future. Netmaker UI --------------- @@ -121,6 +122,16 @@ v0.3 introduced the concept of private DNS management for nodes. This requires a Worth considering is that CoreDNS requires port 53 on the Netmaker host system, which may cause conflicts depending on your operating system. This is explained in the :doc:`Server Installation <./server-installation>` guide. +External Client +---------------- + +The external client is simply a manually configured WireGuard connection to your network, which Netmaker helps to manage. + +Most machines can run WireGuard. It is fairly simple to set up a WireGuard connection to a single endpoint. It is setting up mesh networks and other topologies like site-to-site which becomes complicated. + +Netmaker v0.5 introduces the "external client" to handle any devices which are not currently compatible with the netclient, including Windows, iPhone, Android, and Mac. Over time, this list will be eliminated and there may not even be a need for the external client. + +External clients hook into a Netmaker network via an "Ingress Gateway," which is configured for a given node and allows traffic to flow into the network. Technical Process ==================== @@ -132,22 +143,25 @@ Below is a high level, step-by-step overview of the flow of communications withi 3. Both of the above requests are routed to the server via an API call from the front end 4. Admin runs the netclient install script on any given node (machine). 5. Netclient decodes key, which contains the GRPC server location and port -6. Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers -7. Netclient reaches out to GRPC server with this information, authenticating via access key. -8. Netmaker server verifies information and creates the node, setting default values for any missing information. -9. Timestamp is set for the network (see #16). -10. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network. -11. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard -12. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the network). -13. Netmaker sends back peers list, including current known configurations of all nodes in network. -14. Netclient configures WireGuard with this information. At this point, the node is fully configured as a part of the network and should be able to reach the other nodes via private address. -15. Netclient begins daemon (system timer) to run check in's with the server. It awaits changes, reporting local changes, and retrieving changes from any other nodes in the network. -16. Other netclients on the network, upon checking in with the Netmaker server, will see that the timestamp has updated, and they will retrieve a new peers list, completing the update cycle. +6. Netclient uses information to register and set up WireGuard tunnel to GRPC server +7. Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers +8. Netclient reaches out to GRPC server with this information, authenticating via access key. +9. Netmaker server verifies information and creates the node, setting default values for any missing information. +10. Timestamp is set for the network (see #16). +11. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network. +12. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard +13. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the network). +14. Netmaker sends back peers list, including current known configurations of all nodes in network. +15. Netclient configures WireGuard with this information. At this point, the node is fully configured as a part of the network and should be able to reach the other nodes via private address. +16. Netclient begins daemon (system timer) to run check in's with the server. It awaits changes, reporting local changes, and retrieving changes from any other nodes in the network. +17. Other netclients on the network, upon checking in with the Netmaker server, will see that the timestamp has updated, and they will retrieve a new peers list, completing the update cycle. Compatible Systems for Netclient ================================== +To manage a node manually, the Netclient can be compiled and run for most linux distibutions, with a prerequisite of WireGuard. + To manage a node automatically, the Netmaker client (netclient) requires **systemd-based linux.** Compatible systems include: - Fedora - Ubuntu @@ -173,4 +187,3 @@ Install limitations mostly include platform-specific limitations, such as needin - **Double NAT**: Netmaker is currently unable to route traffic for devices behind a "double NAT". - **CGNAT**: Netmaker is currently unable to route traffic for for devices behind a "carrier-grade NAT". -- **Windows/iPhone/Android**: To reiterate the systemd limitation, Netmaker is not currently configured to support "end user" devices such as Windows desktops and phones generally. In v0.4, Netmaker will introduce external device gateways to allow this traffic (and many other sorts of devices). diff --git a/docs/examplecode/myclient.conf b/docs/examplecode/myclient.conf new file mode 100644 index 00000000..614e94f8 --- /dev/null +++ b/docs/examplecode/myclient.conf @@ -0,0 +1,10 @@ +[Interface] +Address = 10.7.11.5/32 +PrivateKey = EJf6Yy51M/YDaZgedRpuxMmrqul35WfjmHvRZR1rQ0U= + +[Peer] +PublicKey = m/RPuMVsbpgQ+RkxlgK2mG+dDFlzqn+ua2zJt8Wn7GA= +AllowedIPs = 10.7.11.0/24 +Endpoint = 3.236.60.247:51822 +PersistentKeepalive = 20 + diff --git a/docs/external-clients.rst b/docs/external-clients.rst index 58379c53..66eddcae 100644 --- a/docs/external-clients.rst +++ b/docs/external-clients.rst @@ -17,3 +17,43 @@ By using this method, you can hook any machine into a netmaker network that can It is recommended to run the netclient where compatible, but for all other cases, a machine can be configured as an external client. Important to note, an external client is not **reachable** by the network, meaning the client can establish connections to other machines, but those machines cannot independently establish a connection back. The External Client method should only be used in use cases where one wishes to access resource runnin on the virtual network, and **not** for use cases where one wishes to make a resource accessible on the network. For that, use netclient. + +Configuring an Ingress Gateway +================================== + +External Clients must attach to an Ingress Gateway. By default, your network will not have an ingress gateway. To configure an ingress gateway, you can use any node in your network, but it should have a public IP address (not behind a NAT). Your Netmaker server can be an ingress gateway and makes for a good default choice if you are unsure of which node to select. + +.. image:: images/exclient1.png + :width: 80% + :alt: Gateway + :align: center + +Adding Clients to a Gateway +============================= + +Once you have configured a node as a gateway, you can then add clients to that gateway. Clients will be able to access other nodes in the network just as the gateway node does. + +.. image:: images/exclient2.png + :width: 80% + :alt: Gateway + :align: center + +After creating a client, you can edit the name to something more logical. + +.. image:: images/exclient3.png + :width: 80% + :alt: Gateway + :align: center + +Then, you can either download the configuration file directly, or scan the QR code from your phone (assuming you have the WireGuard app installed). It will accept the configuration just as it would accept a typical WireGuard configuration file. + +.. image:: images/exclient4.png + :width: 80% + :alt: Gateway + :align: center + +Example config file: + +.. literalinclude:: ./examplecode/myclient.conf + +Your client should now be able to access the network! A client can be invalidated at any time by simply deleting it from the UI. diff --git a/docs/images/exclient1.png b/docs/images/exclient1.png new file mode 100644 index 00000000..8a490358 Binary files /dev/null and b/docs/images/exclient1.png differ diff --git a/docs/images/exclient2.png b/docs/images/exclient2.png new file mode 100644 index 00000000..a0dd9ab9 Binary files /dev/null and b/docs/images/exclient2.png differ diff --git a/docs/images/exclient3.png b/docs/images/exclient3.png new file mode 100644 index 00000000..f4c52545 Binary files /dev/null and b/docs/images/exclient3.png differ diff --git a/docs/images/exclient4.png b/docs/images/exclient4.png new file mode 100644 index 00000000..355065b1 Binary files /dev/null and b/docs/images/exclient4.png differ diff --git a/docs/quick-start.rst b/docs/quick-start.rst index 94596811..b77f1270 100644 --- a/docs/quick-start.rst +++ b/docs/quick-start.rst @@ -52,7 +52,10 @@ Create Key #. Click ADD NEW ACCESS KEY #. Give it a name (ex: "mykey") and a number of uses (ex: 25) #. Click CREATE KEY (**Important:** Do not click out of the following screen until you have saved your key details. It will appear only once.) -#. Copy the bottom command under "Your agent install command with access token" and save it somewhere locally. E.x: ``curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.3/scripts/netclient-install.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -`` +#. Copy the bottom command under "Your agent install command with access token" and save it somewhere locally. E.x: ``curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -``. **A change is required here. Change netclient-install.sh in this command to netclient-install.slim.sh, EX:** + + +``curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.slim.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -`` .. image:: images/access-key.png :width: 80% @@ -63,7 +66,12 @@ You will use this command to install the netclient on your nodes. There are thre * The **Access Key** value is the secret string that will allow your node to authenticate with the Netmaker network. This can be used with existing netclient installations where additional configurations (such as setting the server IP manually) may be required. This is not typical. E.g. ``netclient -c install -k -s 1.2.3.4 -p 50052`` * The **Access Token** value is a base64 encoded string that contains the server IP and grpc port, as well as the access key. This is decoded by the netclient and can be used with existing netclient installations like this: ``netclient -c install -t ``. You should use this method for adding a network to a node that is already on a network. For instance, Node A is in the **mynet** network and now you are adding it to **default**. +<<<<<<< HEAD +======= +* The **install command** value is a curl command that can be run on Linux systems. It is a simple script that downloads the netclient binary and runs the install command all in one. However, this script is tailored for Secure GRPC Mode and contains an additional (unnecessary) command: **netclient register -k keyvalue**. This command will not work without secure GRPC enabled and will return a 500 error. + +>>>>>>> c360eb1878a4fe89538235ab240da6f6890934a1 Networks can also be enabled to allow nodes to sign up without keys at all. In this scenario, nodes enter a "pending state" and are not permitted to join the network until an admin approves them. Deploy Nodes @@ -76,7 +84,7 @@ Deploy Nodes * ``which wg`` (should show wg binary present) * ``pidof systemd && echo "systemd found" || echo "systemd not found"`` -4. Run the install command, Ex: ``curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -`` +4. Run the install command, Ex: ``curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.slim.sh | KEY=vm3ow4thatogiwnsla3thsl3894ths sh -`` You should get output similar to the below. The netclient retrieves local settings, submits them to the server for processing, and retrieves updated settings. Then it sets the local network configuration. For more information about this process, see the :doc:`client installation <./client-installation>` documentation. If this process failed and you do not see your node in the console (see below), then reference the :doc:`troubleshooting <./troubleshoot>` documentation. @@ -129,7 +137,7 @@ Uninstalling the netclient 1. To remove your nodes from the default network, run the following on each node: ``sudo netclient leave -n default`` 2. To remove the netclient entirely from each node, run ``sudo rm -rf /etc/netclient`` (after running the first step) -Uninstralling Netmaker +Uninstalling Netmaker =========================== To uninstall Netmaker from the server, simply run ``docker-compose down`` or ``docker-compose down --volumes`` to remove the docker volumes for a future installation. diff --git a/functions/helpers.go b/functions/helpers.go index 989e2734..389daf36 100644 --- a/functions/helpers.go +++ b/functions/helpers.go @@ -7,6 +7,7 @@ package functions import ( "context" "encoding/base64" + "encoding/json" "errors" "fmt" "log" @@ -14,6 +15,7 @@ import ( "net" "strings" "time" + "github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/mongoconn" "github.com/gravitl/netmaker/servercfg" @@ -23,17 +25,24 @@ import ( "go.mongodb.org/mongo-driver/mongo/options" ) +func PrintUserLog(username string, message string, loglevel int) { + log.SetFlags(log.Flags() &^ (log.Llongfile | log.Lshortfile)) + if int32(loglevel) <= servercfg.GetVerbose() && servercfg.GetVerbose() != 0 { + log.Println(username, message) + } +} + //Takes in an arbitrary field and value for field and checks to see if any other //node has that value for the same field within the network func SliceContains(slice []string, item string) bool { - set := make(map[string]struct{}, len(slice)) - for _, s := range slice { - set[s] = struct{}{} - } + set := make(map[string]struct{}, len(slice)) + for _, s := range slice { + set[s] = struct{}{} + } - _, ok := set[item] - return ok + _, ok := set[item] + return ok } func CreateServerToken(netID string) (string, error) { @@ -45,18 +54,26 @@ func CreateServerToken(netID string) (string, error) { return "", err } + var accessToken models.AccessToken + servervals := models.ServerConfig{ + APIConnString: "127.0.0.1" + servercfg.GetAPIPort(), + GRPCConnString: "127.0.0.1" + servercfg.GetGRPCPort(), + GRPCSSL: "off", + } + accessToken.ServerConfig = servervals + accessToken.ClientConfig.Network = netID + accessToken.ClientConfig.Key = GenKey() + accesskey.Name = GenKeyName() accesskey.Value = GenKey() accesskey.Uses = 1 - address := "127.0.0.1:" + servercfg.GetGRPCPort() - privAddr := "" - if *network.IsLocal { - privAddr = network.LocalRange + tokenjson, err := json.Marshal(accessToken) + if err != nil { + return accesskey.AccessString, err } - accessstringdec := address + "|"+ address + "|" + address + "|" + netID + "|" + accesskey.Value + "|" + privAddr - accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec)) + accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) network.AccessKeys = append(network.AccessKeys, accesskey) @@ -86,85 +103,85 @@ func CreateServerToken(netID string) (string, error) { func GetPeersList(networkName string) ([]models.PeersResponse, error) { - var peers []models.PeersResponse + var peers []models.PeersResponse - //Connection mongoDB with mongoconn class - collection := mongoconn.Client.Database("netmaker").Collection("nodes") + //Connection mongoDB with mongoconn class + collection := mongoconn.Client.Database("netmaker").Collection("nodes") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - //Get all nodes in the relevant network which are NOT in pending state - filter := bson.M{"network": networkName, "ispending": false} - cur, err := collection.Find(ctx, filter) + //Get all nodes in the relevant network which are NOT in pending state + filter := bson.M{"network": networkName, "ispending": false} + cur, err := collection.Find(ctx, filter) - if err != nil { - return peers, err - } + if err != nil { + return peers, err + } - // Close the cursor once finished and cancel if it takes too long - defer cancel() + // Close the cursor once finished and cancel if it takes too long + defer cancel() - for cur.Next(context.TODO()) { + for cur.Next(context.TODO()) { - var peer models.PeersResponse - err := cur.Decode(&peer) - if err != nil { - log.Fatal(err) - } + var peer models.PeersResponse + err := cur.Decode(&peer) + if err != nil { + log.Fatal(err) + } - // add the node to our node array - //maybe better to just return this? But then that's just GetNodes... - peers = append(peers, peer) - } + // add the node to our node array + //maybe better to just return this? But then that's just GetNodes... + peers = append(peers, peer) + } - //Uh oh, fatal error! This needs some better error handling - //TODO: needs appropriate error handling so the server doesnt shut down. - if err := cur.Err(); err != nil { - log.Fatal(err) - } + //Uh oh, fatal error! This needs some better error handling + //TODO: needs appropriate error handling so the server doesnt shut down. + if err := cur.Err(); err != nil { + log.Fatal(err) + } - return peers, err + return peers, err } func GetIntPeersList() ([]models.PeersResponse, error) { - var peers []models.PeersResponse + var peers []models.PeersResponse - collection := mongoconn.Client.Database("netmaker").Collection("intclients") + collection := mongoconn.Client.Database("netmaker").Collection("intclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - filter := bson.M{"isserver": ""} + filter := bson.M{"isserver": ""} - cur, err := collection.Find(ctx, filter) + cur, err := collection.Find(ctx, filter) - if err != nil { - return peers, err - } + if err != nil { + return peers, err + } - // Close the cursor once finished and cancel if it takes too long - defer cancel() + // Close the cursor once finished and cancel if it takes too long + defer cancel() - for cur.Next(context.TODO()) { + for cur.Next(context.TODO()) { - var peer models.PeersResponse - err := cur.Decode(&peer) - if err != nil { - log.Fatal(err) - } + var peer models.PeersResponse + err := cur.Decode(&peer) + if err != nil { + log.Fatal(err) + } - // add the node to our node array - //maybe better to just return this? But then that's just GetNodes... - peers = append(peers, peer) - } + // add the node to our node array + //maybe better to just return this? But then that's just GetNodes... + peers = append(peers, peer) + } - //Uh oh, fatal error! This needs some better error handling - //TODO: needs appropriate error handling so the server doesnt shut down. - if err := cur.Err(); err != nil { - log.Fatal(err) - } + //Uh oh, fatal error! This needs some better error handling + //TODO: needs appropriate error handling so the server doesnt shut down. + if err := cur.Err(); err != nil { + log.Fatal(err) + } - return peers, err + return peers, err } func IsFieldUnique(network string, field string, value string) bool { @@ -194,23 +211,23 @@ func IsFieldUnique(network string, field string, value string) bool { func ServerIntClientExists() (bool, error) { - collection := mongoconn.Client.Database("netmaker").Collection("intclients") + collection := mongoconn.Client.Database("netmaker").Collection("intclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - filter := bson.M{"isserver": "yes"} + filter := bson.M{"isserver": "yes"} - var result bson.M - err := collection.FindOne(ctx, filter).Decode(&result) + var result bson.M + err := collection.FindOne(ctx, filter).Decode(&result) - defer cancel() + defer cancel() - if err != nil { - if err == mongo.ErrNoDocuments { - return false, nil - } - } - return true, err + if err != nil { + if err == mongo.ErrNoDocuments { + return false, nil + } + } + return true, err } func NetworkExists(name string) (bool, error) { @@ -453,27 +470,29 @@ func IsKeyValid(networkname string, keyvalue string) bool { func IsKeyValidGlobal(keyvalue string) bool { - networks, _ := ListNetworks() - var key models.AccessKey - foundkey := false - isvalid := false + networks, _ := ListNetworks() + var key models.AccessKey + foundkey := false + isvalid := false for _, network := range networks { for i := len(network.AccessKeys) - 1; i >= 0; i-- { - currentkey := network.AccessKeys[i] - if currentkey.Value == keyvalue { - key = currentkey - foundkey = true + currentkey := network.AccessKeys[i] + if currentkey.Value == keyvalue { + key = currentkey + foundkey = true break - } - } - if foundkey { break } + } + } + if foundkey { + break + } } - if foundkey { - if key.Uses > 0 { - isvalid = true - } - } - return isvalid + if foundkey { + if key.Uses > 0 { + isvalid = true + } + } + return isvalid } //TODO: Contains a fatal error return. Need to change @@ -614,72 +633,71 @@ func GetNodeByMacAddress(network string, macaddress string) (models.Node, error) } func DeleteAllIntClients() error { - collection := mongoconn.Client.Database("netmaker").Collection("intclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - // Filter out them ID's again - err := collection.Drop(ctx) - if err != nil { - return err - } - defer cancel() - return nil + collection := mongoconn.Client.Database("netmaker").Collection("intclients") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Filter out them ID's again + err := collection.Drop(ctx) + if err != nil { + return err + } + defer cancel() + return nil } func GetAllIntClients() ([]models.IntClient, error) { - var client models.IntClient - var clients []models.IntClient - collection := mongoconn.Client.Database("netmaker").Collection("intclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - // Filter out them ID's again - cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) - if err != nil { - return []models.IntClient{}, err - } - defer cancel() - for cur.Next(context.TODO()) { - err := cur.Decode(&client) - if err != nil { - return []models.IntClient{}, err - } - // add node to our array - clients = append(clients, client) - } + var client models.IntClient + var clients []models.IntClient + collection := mongoconn.Client.Database("netmaker").Collection("intclients") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Filter out them ID's again + cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) + if err != nil { + return []models.IntClient{}, err + } + defer cancel() + for cur.Next(context.TODO()) { + err := cur.Decode(&client) + if err != nil { + return []models.IntClient{}, err + } + // add node to our array + clients = append(clients, client) + } - //TODO: Fatal error - if err := cur.Err(); err != nil { - return []models.IntClient{}, err - } - return clients, nil + //TODO: Fatal error + if err := cur.Err(); err != nil { + return []models.IntClient{}, err + } + return clients, nil } func GetAllExtClients() ([]models.ExtClient, error) { - var extclient models.ExtClient - var extclients []models.ExtClient - collection := mongoconn.Client.Database("netmaker").Collection("extclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - // Filter out them ID's again - cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) - if err != nil { - return []models.ExtClient{}, err - } - defer cancel() - for cur.Next(context.TODO()) { - err := cur.Decode(&extclient) - if err != nil { - return []models.ExtClient{}, err - } - // add node to our array - extclients = append(extclients, extclient) - } + var extclient models.ExtClient + var extclients []models.ExtClient + collection := mongoconn.Client.Database("netmaker").Collection("extclients") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + // Filter out them ID's again + cur, err := collection.Find(ctx, bson.M{}, options.Find().SetProjection(bson.M{"_id": 0})) + if err != nil { + return []models.ExtClient{}, err + } + defer cancel() + for cur.Next(context.TODO()) { + err := cur.Decode(&extclient) + if err != nil { + return []models.ExtClient{}, err + } + // add node to our array + extclients = append(extclients, extclient) + } - //TODO: Fatal error - if err := cur.Err(); err != nil { - return []models.ExtClient{}, err - } - return extclients, nil + //TODO: Fatal error + if err := cur.Err(); err != nil { + return []models.ExtClient{}, err + } + return extclients, nil } - //This returns a unique address for a node to use //it iterates through the list of IP's in the subnet //and checks against all nodes to see if it's taken, until it finds one. @@ -705,15 +723,15 @@ func UniqueAddress(networkName string) (string, error) { offset = false continue } - if networkName == "comms" { - if IsIPUniqueClients(networkName, ip.String()) { - return ip.String(), err - } - } else { - if IsIPUnique(networkName, ip.String()) && IsIPUniqueExtClients(networkName, ip.String()) { - return ip.String(), err - } - } + if networkName == "comms" { + if IsIPUniqueClients(networkName, ip.String()) { + return ip.String(), err + } + } else { + if IsIPUnique(networkName, ip.String()) && IsIPUniqueExtClients(networkName, ip.String()) { + return ip.String(), err + } + } } //TODO @@ -747,9 +765,9 @@ func UniqueAddress6(networkName string) (string, error) { continue } if networkName == "comms" { - if IsIP6UniqueClients(networkName, ip.String()) { - return ip.String(), err - } + if IsIP6UniqueClients(networkName, ip.String()) { + return ip.String(), err + } } else { if IsIP6Unique(networkName, ip.String()) { return ip.String(), err @@ -798,30 +816,29 @@ func GenKeyName() string { func IsIPUniqueExtClients(network string, ip string) bool { - var extclient models.ExtClient + var extclient models.ExtClient - isunique := true + isunique := true - collection := mongoconn.Client.Database("netmaker").Collection("extclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + collection := mongoconn.Client.Database("netmaker").Collection("extclients") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - filter := bson.M{"address": ip, "network": network} + filter := bson.M{"address": ip, "network": network} - err := collection.FindOne(ctx, filter).Decode(&extclient) + err := collection.FindOne(ctx, filter).Decode(&extclient) - defer cancel() + defer cancel() - if err != nil { - return isunique - } + if err != nil { + return isunique + } - if extclient.Address == ip { - isunique = false - } - return isunique + if extclient.Address == ip { + isunique = false + } + return isunique } - //checks if IP is unique in the address range //used by UniqueAddress func IsIPUnique(network string, ip string) bool { @@ -880,54 +897,54 @@ func IsIP6Unique(network string, ip string) bool { //used by UniqueAddress func IsIP6UniqueClients(network string, ip string) bool { - var client models.IntClient + var client models.IntClient - isunique := true + isunique := true - collection := mongoconn.Client.Database("netmaker").Collection("intclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + collection := mongoconn.Client.Database("netmaker").Collection("intclients") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - filter := bson.M{"address6": ip, "network": network} + filter := bson.M{"address6": ip, "network": network} - err := collection.FindOne(ctx, filter).Decode(&client) + err := collection.FindOne(ctx, filter).Decode(&client) - defer cancel() + defer cancel() - if err != nil { - return isunique - } + if err != nil { + return isunique + } - if client.Address6 == ip { - isunique = false - } - return isunique + if client.Address6 == ip { + isunique = false + } + return isunique } //checks if IP is unique in the address range //used by UniqueAddress func IsIPUniqueClients(network string, ip string) bool { - var client models.IntClient + var client models.IntClient - isunique := true + isunique := true - collection := mongoconn.Client.Database("netmaker").Collection("intclients") - ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + collection := mongoconn.Client.Database("netmaker").Collection("intclients") + ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) - filter := bson.M{"address": ip, "network": network} + filter := bson.M{"address": ip, "network": network} - err := collection.FindOne(ctx, filter).Decode(&client) + err := collection.FindOne(ctx, filter).Decode(&client) - defer cancel() + defer cancel() - if err != nil { - return isunique - } + if err != nil { + return isunique + } - if client.Address == ip { - isunique = false - } - return isunique + if client.Address == ip { + isunique = false + } + return isunique } //called once key has been used by createNode @@ -1040,4 +1057,3 @@ func GetAllNodes() ([]models.Node, error) { } return nodes, nil } - diff --git a/go.mod b/go.mod index ac53d081..dd78ec8e 100644 --- a/go.mod +++ b/go.mod @@ -6,16 +6,16 @@ require ( github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/go-playground/validator/v10 v10.5.0 - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.2 github.com/gorilla/handlers v1.5.1 github.com/gorilla/mux v1.8.0 github.com/jinzhu/copier v0.3.2 // indirect - github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect + github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e github.com/stretchr/testify v1.6.1 github.com/txn2/txeh v1.3.0 github.com/urfave/cli v1.22.5 // indirect - github.com/urfave/cli/v2 v2.3.0 // indirect - github.com/vishvananda/netlink v1.1.0 // indirect + github.com/urfave/cli/v2 v2.3.0 + github.com/vishvananda/netlink v1.1.0 go.mongodb.org/mongo-driver v1.4.3 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect diff --git a/kube/netclient-daemonset.yaml b/kube/netclient-daemonset.yaml new file mode 100644 index 00000000..80dd346f --- /dev/null +++ b/kube/netclient-daemonset.yaml @@ -0,0 +1,58 @@ +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: netclient + labels: + app: netclient +spec: + selector: + matchLabels: + app: netclient + replicas: 1 + template: + metadata: + labels: + app: netclient + spec: + hostNetwork: true + containers: + - name: netclient + image: gravitl/netclient:v0.5.5 + command: ['bash', '-c', "netclient checkin -n $NETWORK; sleep $SLEEP"] + env: + - name: ACCESS_TOKEN + value: "XXXX" + - name: NETWORK + value: "default" + - name: SLEEP + value: 30 + volumeMounts: + - mountPath: /etc/netclient + name: etc-netclient + - mountPath: /usr/bin/wg + name: wg + securityContext: + privileged: true + initContainers: + - name: netclient-join + image: gravitl/netclient:v0.5.5 + command: ['bash', '-c', "netclient join -t $ACCESS_TOKEN --daemon off"] + env: + - name: ACCESS_TOKEN + value: "XXXX" + volumeMounts: + - mountPath: /etc/netclient + name: etc-netclient + - mountPath: /usr/bin/wg + name: wg + securityContext: + privileged: true + volumes: + - hostPath: + path: /etc/netclient + type: DirectoryOrCreate + name: etc-netclient + - hostPath: + path: /usr/bin/wg + type: File + name: wg diff --git a/main.go b/main.go index 1e2b8c19..f88a86c6 100644 --- a/main.go +++ b/main.go @@ -87,7 +87,12 @@ func main() { waitnetwork.Add(1) go runGRPC(&waitnetwork, installserver) } - + if servercfg.IsDNSMode() { + err := controller.SetDNS() + if err != nil { + log.Fatal(err) + } + } //Run Rest Server if servercfg.IsRestBackend() { if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" { diff --git a/models/accessToken.go b/models/accessToken.go index a09de8f5..a55524c6 100644 --- a/models/accessToken.go +++ b/models/accessToken.go @@ -13,6 +13,7 @@ type ClientConfig struct { } type ServerConfig struct { + CoreDNSAddr string `json:"corednsaddr"` APIConnString string `json:"apiconn"` APIHost string `json:"apihost"` APIPort string `json:"apiport"` diff --git a/models/structs.go b/models/structs.go index 37e27e6f..b4603a1a 100644 --- a/models/structs.go +++ b/models/structs.go @@ -8,7 +8,7 @@ type AuthParams struct { } type User struct { - UserName string `json:"username" bson:"username" validate:"alphanum,min=3"` + UserName string `json:"username" bson:"username" validate:"min=3,max=40,regexp=^(([a-zA-Z,\-,\.]*)|([A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,4})){3,40}$"` Password string `json:"password" bson:"password" validate:"required,min=5"` Networks []string `json:"networks" bson:"networks"` IsAdmin bool `json:"isadmin" bson:"isadmin"` diff --git a/netclient/Dockerfile b/netclient/Dockerfile new file mode 100644 index 00000000..7f970f48 --- /dev/null +++ b/netclient/Dockerfile @@ -0,0 +1,9 @@ +FROM debian:latest + +RUN apt-get update && apt-get -y install systemd procps + +WORKDIR /root/ + +COPY netclient . + +CMD ["./netclient checkin"] diff --git a/netclient/command/commands.go b/netclient/command/commands.go index ee5b8f81..aa403ada 100644 --- a/netclient/command/commands.go +++ b/netclient/command/commands.go @@ -58,7 +58,7 @@ func CheckIn(cfg config.ClientConfig) error { log.Println("Required, '-n'. No network provided. Exiting.") os.Exit(1) } - err := functions.CheckIn(cfg.Network) + err := functions.CheckIn(cfg) if err != nil { log.Println("Error checking in: ", err) os.Exit(1) diff --git a/netclient/config/config.go b/netclient/config/config.go index 38f87e1d..e619f20c 100644 --- a/netclient/config/config.go +++ b/netclient/config/config.go @@ -26,6 +26,7 @@ type ClientConfig struct { OperatingSystem string `yaml:"operatingsystem"` } type ServerConfig struct { + CoreDNSAddr string `yaml:"corednsaddr"` GRPCAddress string `yaml:"grpcaddress"` APIAddress string `yaml:"apiaddress"` AccessKey string `yaml:"accesskey"` @@ -55,7 +56,6 @@ type NodeConfig struct { IsLocal string `yaml:"islocal"` IsDualStack string `yaml:"isdualstack"` IsIngressGateway string `yaml:"isingressgateway"` - AllowedIPs []string `yaml:"allowedips"` LocalRange string `yaml:"localrange"` PostUp string `yaml:"postup"` PostDown string `yaml:"postdown"` @@ -85,9 +85,6 @@ func Write(config *ClientConfig, network string) error{ } home := "/etc/netclient" - if err != nil { - log.Fatal(err) - } file := fmt.Sprintf(home + "/netconfig-" + network) f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) defer f.Close() @@ -408,6 +405,7 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, error){ cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange cfg.Server.GRPCSSL = accesstoken.ServerConfig.GRPCSSL cfg.Server.GRPCWireGuard = accesstoken.WG.GRPCWireGuard + cfg.Server.CoreDNSAddr = accesstoken.ServerConfig.CoreDNSAddr if c.String("grpcserver") != "" { cfg.Server.GRPCAddress = c.String("grpcserver") } @@ -427,6 +425,9 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, error){ if c.String("grpcssl") != "" { cfg.Server.GRPCSSL = c.String("grpcssl") } + if c.String("corednsaddr") != "" { + cfg.Server.CoreDNSAddr = c.String("corednsaddr") + } if c.String("grpcwg") != "" { cfg.Server.GRPCWireGuard = c.String("grpcwg") } @@ -440,6 +441,7 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, error){ cfg.Node.LocalRange = c.String("localrange") cfg.Server.GRPCWireGuard = c.String("grpcwg") cfg.Server.GRPCSSL = c.String("grpcssl") + cfg.Server.CoreDNSAddr = c.String("corednsaddr") } cfg.Node.Name = c.String("name") cfg.Node.Interface = c.String("interface") diff --git a/netclient/functions/checkin.go b/netclient/functions/checkin.go index b9f62fa4..cae5b741 100644 --- a/netclient/functions/checkin.go +++ b/netclient/functions/checkin.go @@ -10,6 +10,7 @@ import ( "net" "os/exec" "github.com/gravitl/netmaker/netclient/config" + "github.com/gravitl/netmaker/netclient/local" "github.com/gravitl/netmaker/netclient/wireguard" "github.com/gravitl/netmaker/netclient/server" "github.com/gravitl/netmaker/netclient/auth" @@ -19,7 +20,8 @@ import ( //homedir "github.com/mitchellh/go-homedir" ) -func CheckIn(network string) error { +func CheckIn(cliconf config.ClientConfig) error { + network := cliconf.Network node := server.GetNode(network) cfg, err := config.ReadConfig(network) if err != nil { @@ -32,6 +34,14 @@ func CheckIn(network string) error { setupcheck := true ipchange := false + if nodecfg.DNS == "on" || cliconf.Node.DNS == "on" { + fmt.Println("setting dns") + ifacename := node.Interface + nameserver := servercfg.CoreDNSAddr + network := node.Nodenetwork + _ = local.UpdateDNS(ifacename, network, nameserver) + } + if !(nodecfg.IPForwarding == "off") { out, err := exec.Command("sysctl", "net.ipv4.ip_forward").Output() if err != nil { @@ -125,10 +135,13 @@ func CheckIn(network string) error { var wcclient nodepb.NodeServiceClient var requestOpts grpc.DialOption requestOpts = grpc.WithInsecure() - if cfg.Server.GRPCSSL == "on" { + if servercfg.GRPCSSL == "on" { + log.Println("using SSL") h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) requestOpts = grpc.WithTransportCredentials(h2creds) - } + } else { + log.Println("using insecure GRPC connection") + } conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) if err != nil { fmt.Printf("Cant dial GRPC server: %v", err) diff --git a/netclient/functions/join.go b/netclient/functions/join.go index 58fd826e..089110c0 100644 --- a/netclient/functions/join.go +++ b/netclient/functions/join.go @@ -183,6 +183,7 @@ func JoinNetwork(cfg config.ClientConfig) error { if err != nil { return err } + log.Println("node created on remote server...updating configs") node := res.Node if err != nil { return err @@ -211,16 +212,18 @@ func JoinNetwork(cfg config.ClientConfig) error { return err } } - + log.Println("retrieving remote peers") peers, hasGateway, gateways, err := server.GetPeers(node.Macaddress, cfg.Network, cfg.Server.GRPCAddress, node.Isdualstack, node.Isingressgateway) if err != nil { + log.Println("failed to retrieve peers") return err } err = wireguard.StorePrivKey(cfg.Node.PrivateKey, cfg.Network) if err != nil { return err } + log.Println("starting wireguard") err = wireguard.InitWireguard(node, cfg.Node.PrivateKey, peers, hasGateway, gateways) if err != nil { return err diff --git a/netclient/server/grpc.go b/netclient/server/grpc.go index 05fec703..c4d135c5 100644 --- a/netclient/server/grpc.go +++ b/netclient/server/grpc.go @@ -143,7 +143,12 @@ func GetPeers(macaddress string, network string, server string, dualstack bool, requestOpts := grpc.WithInsecure() - conn, err := grpc.Dial(server, requestOpts) + if cfg.Server.GRPCSSL == "on" { + h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) + requestOpts = grpc.WithTransportCredentials(h2creds) + } + + conn, err := grpc.Dial(server, requestOpts) if err != nil { log.Fatalf("Unable to establish client connection to localhost:50051: %v", err) } @@ -157,15 +162,15 @@ func GetPeers(macaddress string, network string, server string, dualstack bool, ctx := context.Background() ctx, err = auth.SetJWT(wcclient, network) if err != nil { - fmt.Println("Failed to authenticate.") + log.Println("Failed to authenticate.") return peers, hasGateway, gateways, err } var header metadata.MD stream, err := wcclient.GetPeers(ctx, req, grpc.Header(&header)) if err != nil { - fmt.Println("Error retrieving peers") - fmt.Println(err) + log.Println("Error retrieving peers") + log.Println(err) return nil, hasGateway, gateways, err } for { diff --git a/netclient/wireguard/kernel.go b/netclient/wireguard/kernel.go index fdff8e99..ce07077f 100644 --- a/netclient/wireguard/kernel.go +++ b/netclient/wireguard/kernel.go @@ -186,8 +186,7 @@ func InitWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig if node.Address == "" { log.Fatal("no address to configure") } - nameserver := servercfg.GRPCAddress - nameserver = strings.Split(nameserver, ":")[0] + nameserver := servercfg.CoreDNSAddr network := node.Nodenetwork if nodecfg.Network != "" { network = nodecfg.Network diff --git a/servercfg/serverconf.go b/servercfg/serverconf.go index a587000c..d2b6cfd3 100644 --- a/servercfg/serverconf.go +++ b/servercfg/serverconf.go @@ -1,12 +1,13 @@ package servercfg import ( - "github.com/gravitl/netmaker/config" - "net/http" - "io/ioutil" - "os" "errors" + "io/ioutil" + "net/http" + "os" "strconv" + + "github.com/gravitl/netmaker/config" ) func SetHost() error { @@ -20,6 +21,7 @@ func SetHost() error { func GetServerConfig() config.ServerConfig { var cfg config.ServerConfig cfg.APIConnString = GetAPIConnString() + cfg.CoreDNSAddr = GetCoreDNSAddr() cfg.APIHost = GetAPIHost() cfg.APIPort = GetAPIPort() cfg.GRPCConnString = GetGRPCConnString() @@ -28,105 +30,116 @@ func GetServerConfig() config.ServerConfig { cfg.MasterKey = "(hidden)" cfg.AllowedOrigin = GetAllowedOrigin() cfg.RestBackend = "off" + cfg.Verbosity = GetVerbose() if IsRestBackend() { cfg.RestBackend = "on" } cfg.AgentBackend = "off" - if IsAgentBackend() { - cfg.AgentBackend = "on" - } + if IsAgentBackend() { + cfg.AgentBackend = "on" + } cfg.ClientMode = "off" if IsClientMode() { cfg.ClientMode = "on" } cfg.DNSMode = "off" if IsDNSMode() { - cfg.DNSMode = "on" + cfg.DNSMode = "on" + } + cfg.GRPCSSL = "off" + if IsGRPCSSL() { + cfg.GRPCSSL = "on" } - cfg.GRPCSSL = "off" - if IsGRPCSSL() { - cfg.GRPCSSL = "on" - } cfg.DisableRemoteIPCheck = "off" if DisableRemoteIPCheck() { cfg.DisableRemoteIPCheck = "on" } - cfg.DisableDefaultNet = "off" - if DisableDefaultNet() { - cfg.DisableRemoteIPCheck = "on" - } + cfg.DisableDefaultNet = "off" + if DisableDefaultNet() { + cfg.DisableRemoteIPCheck = "on" + } return cfg } -func GetWGConfig() config.WG{ +func GetWGConfig() config.WG { var cfg config.WG if IsGRPCWireGuard() { cfg.GRPCWireGuard = "on" } else { - cfg.GRPCWireGuard = "off" + cfg.GRPCWireGuard = "off" } cfg.GRPCWGInterface = GetGRPCWGInterface() cfg.GRPCWGAddress = GetGRPCWGAddress() - cfg.GRPCWGAddressRange = GetGRPCWGAddressRange() + cfg.GRPCWGAddressRange = GetGRPCWGAddressRange() cfg.GRPCWGPort = GetGRPCWGPort() - cfg.GRPCWGPubKey = GetGRPCWGPubKey() - cfg.GRPCWGPrivKey = GetGRPCWGPrivKey() + cfg.GRPCWGPubKey = GetGRPCWGPubKey() + cfg.GRPCWGPrivKey = GetGRPCWGPrivKey() return cfg } func GetAPIConnString() string { - conn := "" - if os.Getenv("SERVER_API_CONN_STRING") != "" { - conn = os.Getenv("SERVER_API_CONN_STRING") - } else if config.Config.Server.APIConnString != "" { - conn = config.Config.Server.APIConnString - } - return conn + conn := "" + if os.Getenv("SERVER_API_CONN_STRING") != "" { + conn = os.Getenv("SERVER_API_CONN_STRING") + } else if config.Config.Server.APIConnString != "" { + conn = config.Config.Server.APIConnString + } + return conn } func GetAPIHost() string { - serverhost := "127.0.0.1" - if os.Getenv("SERVER_HTTP_HOST") != "" { - serverhost = os.Getenv("SERVER_HTTP_HOST") - } else if config.Config.Server.APIHost != "" { + serverhost := "127.0.0.1" + if os.Getenv("SERVER_HTTP_HOST") != "" { + serverhost = os.Getenv("SERVER_HTTP_HOST") + } else if config.Config.Server.APIHost != "" { serverhost = config.Config.Server.APIHost - } else if os.Getenv("SERVER_HOST") != "" { - serverhost = os.Getenv("SERVER_HOST") - } else { - remoteip, _ := GetPublicIP() - if remoteip != "" { - serverhost = remoteip - } - } + } else if os.Getenv("SERVER_HOST") != "" { + serverhost = os.Getenv("SERVER_HOST") + } else { + remoteip, _ := GetPublicIP() + if remoteip != "" { + serverhost = remoteip + } + } return serverhost } func GetAPIPort() string { apiport := "8081" if os.Getenv("API_PORT") != "" { apiport = os.Getenv("API_PORT") - } else if config.Config.Server.APIPort != "" { + } else if config.Config.Server.APIPort != "" { apiport = config.Config.Server.APIPort } return apiport } func GetDefaultNodeLimit() int32 { - var limit int32 + var limit int32 limit = 999999999 envlimit, err := strconv.Atoi(os.Getenv("DEFAULT_NODE_LIMIT")) if err == nil && envlimit != 0 { - limit = int32(envlimit) - } else if config.Config.Server.DefaultNodeLimit != 0 { - limit = config.Config.Server.DefaultNodeLimit - } - return limit + limit = int32(envlimit) + } else if config.Config.Server.DefaultNodeLimit != 0 { + limit = config.Config.Server.DefaultNodeLimit + } + return limit } func GetGRPCConnString() string { - conn := "" - if os.Getenv("SERVER_GRPC_CONN_STRING") != "" { - conn = os.Getenv("SERVER_GRPC_CONN_STRING") - } else if config.Config.Server.GRPCConnString != "" { - conn = config.Config.Server.GRPCConnString + conn := "" + if os.Getenv("SERVER_GRPC_CONN_STRING") != "" { + conn = os.Getenv("SERVER_GRPC_CONN_STRING") + } else if config.Config.Server.GRPCConnString != "" { + conn = config.Config.Server.GRPCConnString + } + return conn +} + +func GetCoreDNSAddr() string { + addr, _ := GetPublicIP() + if os.Getenv("COREDNS_ADDR") != "" { + addr = os.Getenv("COREDNS_ADDR") + } else if config.Config.Server.CoreDNSAddr != "" { + addr = config.Config.Server.GRPCConnString } - return conn + return addr } func GetGRPCHost() string { @@ -134,51 +147,51 @@ func GetGRPCHost() string { if IsGRPCWireGuard() { serverhost = GetGRPCWGAddress() } else { - if os.Getenv("SERVER_GRPC_HOST") != "" { + if os.Getenv("SERVER_GRPC_HOST") != "" { serverhost = os.Getenv("SERVER_GRPC_HOST") } else if config.Config.Server.GRPCHost != "" { - serverhost = config.Config.Server.GRPCHost - } else if os.Getenv("SERVER_HOST") != "" { - serverhost = os.Getenv("SERVER_HOST") + serverhost = config.Config.Server.GRPCHost + } else if os.Getenv("SERVER_HOST") != "" { + serverhost = os.Getenv("SERVER_HOST") } else { - remoteip, _ := GetPublicIP() + remoteip, _ := GetPublicIP() if remoteip != "" { serverhost = remoteip } } } - return serverhost + return serverhost } func GetGRPCPort() string { - grpcport := "50051" + grpcport := "50051" if os.Getenv("GRPC_PORT") != "" { - grpcport = os.Getenv("GRPC_PORT") - } else if config.Config.Server.GRPCPort != "" { - grpcport = config.Config.Server.GRPCPort + grpcport = os.Getenv("GRPC_PORT") + } else if config.Config.Server.GRPCPort != "" { + grpcport = config.Config.Server.GRPCPort } - return grpcport + return grpcport } func GetMasterKey() string { - key := "secretkey" - if os.Getenv("MASTER_KEY") != "" { - key = os.Getenv("MASTER_KEY") - } else if config.Config.Server.MasterKey != "" { - key = config.Config.Server.MasterKey - } - return key + key := "secretkey" + if os.Getenv("MASTER_KEY") != "" { + key = os.Getenv("MASTER_KEY") + } else if config.Config.Server.MasterKey != "" { + key = config.Config.Server.MasterKey + } + return key } func GetAllowedOrigin() string { - allowedorigin := "*" - if os.Getenv("CORS_ALLOWED_ORIGIN") != "" { - allowedorigin = os.Getenv("CORS_ALLOWED_ORIGIN") - } else if config.Config.Server.AllowedOrigin != "" { - allowedorigin = config.Config.Server.AllowedOrigin - } - return allowedorigin + allowedorigin := "*" + if os.Getenv("CORS_ALLOWED_ORIGIN") != "" { + allowedorigin = os.Getenv("CORS_ALLOWED_ORIGIN") + } else if config.Config.Server.AllowedOrigin != "" { + allowedorigin = config.Config.Server.AllowedOrigin + } + return allowedorigin } func IsRestBackend() bool { - isrest := true - if os.Getenv("REST_BACKEND") != "" { + isrest := true + if os.Getenv("REST_BACKEND") != "" { if os.Getenv("REST_BACKEND") == "off" { isrest = false } @@ -186,113 +199,123 @@ func IsRestBackend() bool { if config.Config.Server.RestBackend == "off" { isrest = false } - } - return isrest + } + return isrest } func IsAgentBackend() bool { - isagent := true - if os.Getenv("AGENT_BACKEND") != "" { - if os.Getenv("AGENT_BACKEND") == "off" { - isagent = false - } - } else if config.Config.Server.AgentBackend != "" { - if config.Config.Server.AgentBackend == "off" { - isagent = false - } - } - return isagent + isagent := true + if os.Getenv("AGENT_BACKEND") != "" { + if os.Getenv("AGENT_BACKEND") == "off" { + isagent = false + } + } else if config.Config.Server.AgentBackend != "" { + if config.Config.Server.AgentBackend == "off" { + isagent = false + } + } + return isagent } func IsClientMode() bool { - isclient := true - if os.Getenv("CLIENT_MODE") != "" { - if os.Getenv("CLIENT_MODE") == "off" { - isclient = false - } - } else if config.Config.Server.ClientMode != "" { - if config.Config.Server.ClientMode == "off" { - isclient = false - } - } - return isclient + isclient := true + if os.Getenv("CLIENT_MODE") != "" { + if os.Getenv("CLIENT_MODE") == "off" { + isclient = false + } + } else if config.Config.Server.ClientMode != "" { + if config.Config.Server.ClientMode == "off" { + isclient = false + } + } + return isclient } func IsDNSMode() bool { - isdns := true - if os.Getenv("DNS_MODE") != "" { - if os.Getenv("DNS_MODE") == "off" { - isdns = false - } - } else if config.Config.Server.DNSMode != "" { - if config.Config.Server.DNSMode == "off" { - isdns = false - } - } - return isdns + isdns := true + if os.Getenv("DNS_MODE") != "" { + if os.Getenv("DNS_MODE") == "off" { + isdns = false + } + } else if config.Config.Server.DNSMode != "" { + if config.Config.Server.DNSMode == "off" { + isdns = false + } + } + return isdns } func IsGRPCSSL() bool { - isssl := false - if os.Getenv("GRPC_SSL") != "" { - if os.Getenv("GRPC_SSL") == "on" { - isssl = true - } - } else if config.Config.Server.DNSMode != "" { - if config.Config.Server.DNSMode == "on" { - isssl = true - } - } - return isssl + isssl := false + if os.Getenv("GRPC_SSL") != "" { + if os.Getenv("GRPC_SSL") == "on" { + isssl = true + } + } else if config.Config.Server.DNSMode != "" { + if config.Config.Server.DNSMode == "on" { + isssl = true + } + } + return isssl } func DisableRemoteIPCheck() bool { - disabled := false - if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" { - if os.Getenv("DISABLE_REMOTE_IP_CHECK") == "on" { - disabled = true - } - } else if config.Config.Server.DisableRemoteIPCheck != "" { - if config.Config.Server.DisableRemoteIPCheck == "on" { - disabled= true - } - } - return disabled + disabled := false + if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" { + if os.Getenv("DISABLE_REMOTE_IP_CHECK") == "on" { + disabled = true + } + } else if config.Config.Server.DisableRemoteIPCheck != "" { + if config.Config.Server.DisableRemoteIPCheck == "on" { + disabled = true + } + } + return disabled } func DisableDefaultNet() bool { - disabled := false - if os.Getenv("DISABLE_DEFAULT_NET") != "" { - if os.Getenv("DISABLE_DEFAULT_NET") == "on" { - disabled = true - } - } else if config.Config.Server.DisableDefaultNet != "" { - if config.Config.Server.DisableDefaultNet == "on" { - disabled= true - } - } - return disabled + disabled := false + if os.Getenv("DISABLE_DEFAULT_NET") != "" { + if os.Getenv("DISABLE_DEFAULT_NET") == "on" { + disabled = true + } + } else if config.Config.Server.DisableDefaultNet != "" { + if config.Config.Server.DisableDefaultNet == "on" { + disabled = true + } + } + return disabled } func GetPublicIP() (string, error) { - endpoint := "" - var err error + endpoint := "" + var err error - iplist := []string{"http://ip.server.gravitl.com", "https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"} - for _, ipserver := range iplist { - resp, err := http.Get(ipserver) - if err != nil { - continue - } - defer resp.Body.Close() - if resp.StatusCode == http.StatusOK { - bodyBytes, err := ioutil.ReadAll(resp.Body) - if err != nil { - continue - } - endpoint = string(bodyBytes) - break - } + iplist := []string{"http://ip.server.gravitl.com", "https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"} + for _, ipserver := range iplist { + resp, err := http.Get(ipserver) + if err != nil { + continue + } + defer resp.Body.Close() + if resp.StatusCode == http.StatusOK { + bodyBytes, err := ioutil.ReadAll(resp.Body) + if err != nil { + continue + } + endpoint = string(bodyBytes) + break + } - } - if err == nil && endpoint == "" { - err = errors.New("Public Address Not Found.") - } - return endpoint, err + } + if err == nil && endpoint == "" { + err = errors.New("Public Address Not Found.") + } + return endpoint, err +} +func GetVerbose() int32 { + level, err := strconv.Atoi(os.Getenv("VERBOSITY")) + if err != nil || level < 0 { + level = 0 + } + if level > 3 { + level = 3 + } + return int32(level) } diff --git a/test/nodecreate.sh b/test/nodecreate.sh index 8feaaac3..906a9882 100755 --- a/test/nodecreate.sh +++ b/test/nodecreate.sh @@ -3,7 +3,7 @@ PUBKEY="DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=" IPADDR="69.173.21.202" MACADDRESS="59:2a:9c:d4:e2:49" -ACCESSKEY="F5LjPlgHHgi1zpir" +ACCESSKEY="9ktiHcUWay2MSKsY" PASSWORD="ppppppp" generate_post_json () @@ -22,5 +22,5 @@ EOF POST_JSON=$(generate_post_json) -curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer mastertoken" localhost:8081/api/nodes/skynet +curl --max-time 5.0 -d "$POST_JSON" -H 'Content-Type: application/json' -H "authorization: Bearer secretkey" localhost:8081/api/nodes/default