added logging

This commit is contained in:
afeiszli
2021-07-19 11:30:27 -04:00
52 changed files with 1315 additions and 849 deletions

View File

@@ -34,4 +34,3 @@ EXPOSE 8081
EXPOSE 50051 EXPOSE 50051
CMD ["./app"] CMD ["./app"]

26
Dockerfile-netclient Normal file
View File

@@ -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"]

View File

@@ -20,7 +20,7 @@ services:
container_name: netmaker container_name: netmaker
depends_on: depends_on:
- mongodb - mongodb
image: gravitl/netmaker:v0.5 image: gravitl/netmaker:v0.5.7
volumes: volumes:
- ./:/local - ./:/local
- /etc/netclient:/etc/netclient - /etc/netclient:/etc/netclient
@@ -37,6 +37,7 @@ services:
network_mode: host network_mode: host
environment: environment:
DNS_MODE: "off" DNS_MODE: "off"
SERVER_HOST: "192.168.50.149"
netmaker-ui: netmaker-ui:
container_name: netmaker-ui container_name: netmaker-ui
depends_on: depends_on:
@@ -47,7 +48,7 @@ services:
ports: ports:
- "80:80" - "80:80"
environment: environment:
BACKEND_URL: "http://HOST_IP:8081" BACKEND_URL: "http://192.168.50.149:8081"
volumes: volumes:
mongovol: {} mongovol: {}
dnsconfig: {} dnsconfig: {}

View File

@@ -6,22 +6,23 @@
package config package config
import ( import (
"os" "fmt"
"fmt" "log"
"log" "os"
"gopkg.in/yaml.v3"
"gopkg.in/yaml.v3"
) )
//setting dev by default //setting dev by default
func getEnv() string { func getEnv() string {
env := os.Getenv("NETMAKER_ENV") env := os.Getenv("NETMAKER_ENV")
if len(env) == 0 { if len(env) == 0 {
return "dev" return "dev"
} }
return env return env
} }
// Config : application config stored as global variable // Config : application config stored as global variable
@@ -29,76 +30,77 @@ var Config *EnvironmentConfig
// EnvironmentConfig : // EnvironmentConfig :
type EnvironmentConfig struct { type EnvironmentConfig struct {
Server ServerConfig `yaml:"server"` Server ServerConfig `yaml:"server"`
MongoConn MongoConnConfig `yaml:"mongoconn"` MongoConn MongoConnConfig `yaml:"mongoconn"`
WG WG `yaml:"wg"` WG WG `yaml:"wg"`
} }
// ServerConfig : // ServerConfig :
type ServerConfig struct { type ServerConfig struct {
APIConnString string `yaml:"apiconn"` CoreDNSAddr string `yaml:"corednsaddr"`
APIHost string `yaml:"apihost"` APIConnString string `yaml:"apiconn"`
APIPort string `yaml:"apiport"` APIHost string `yaml:"apihost"`
GRPCConnString string `yaml:"grpcconn"` APIPort string `yaml:"apiport"`
GRPCHost string `yaml:"grpchost"` GRPCConnString string `yaml:"grpcconn"`
GRPCPort string `yaml:"grpcport"` GRPCHost string `yaml:"grpchost"`
GRPCSecure string `yaml:"grpcsecure"` GRPCPort string `yaml:"grpcport"`
DefaultNodeLimit int32 `yaml:"defaultnodelimit"` GRPCSecure string `yaml:"grpcsecure"`
MasterKey string `yaml:"masterkey"` DefaultNodeLimit int32 `yaml:"defaultnodelimit"`
AllowedOrigin string `yaml:"allowedorigin"` MasterKey string `yaml:"masterkey"`
RestBackend string `yaml:"restbackend"` AllowedOrigin string `yaml:"allowedorigin"`
AgentBackend string `yaml:"agentbackend"` RestBackend string `yaml:"restbackend"`
ClientMode string `yaml:"clientmode"` AgentBackend string `yaml:"agentbackend"`
DNSMode string `yaml:"dnsmode"` ClientMode string `yaml:"clientmode"`
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"` DNSMode string `yaml:"dnsmode"`
DisableDefaultNet string `yaml:"disabledefaultnet"` DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
GRPCSSL string `yaml:"grpcssl"` DisableDefaultNet string `yaml:"disabledefaultnet"`
GRPCSSL string `yaml:"grpcssl"`
Verbosity int32 `yaml:"verbosity"`
} }
type WG struct { type WG struct {
RegisterKeyRequired string `yaml:"keyrequired"` RegisterKeyRequired string `yaml:"keyrequired"`
GRPCWireGuard string `yaml:"grpcwg"` GRPCWireGuard string `yaml:"grpcwg"`
GRPCWGInterface string `yaml:"grpciface"` GRPCWGInterface string `yaml:"grpciface"`
GRPCWGAddress string `yaml:"grpcaddr"` GRPCWGAddress string `yaml:"grpcaddr"`
GRPCWGAddressRange string `yaml:"grpcaddrrange"` GRPCWGAddressRange string `yaml:"grpcaddrrange"`
GRPCWGPort string `yaml:"grpcport"` GRPCWGPort string `yaml:"grpcport"`
GRPCWGPubKey string `yaml:"pubkey"` GRPCWGPubKey string `yaml:"pubkey"`
GRPCWGPrivKey string `yaml:"privkey"` GRPCWGPrivKey string `yaml:"privkey"`
} }
type MongoConnConfig struct { type MongoConnConfig struct {
User string `yaml:"user"` User string `yaml:"user"`
Pass string `yaml:"pass"` Pass string `yaml:"pass"`
Host string `yaml:"host"` Host string `yaml:"host"`
Port string `yaml:"port"` Port string `yaml:"port"`
Opts string `yaml:"opts"` Opts string `yaml:"opts"`
} }
//reading in the env file //reading in the env file
func readConfig() *EnvironmentConfig { func readConfig() *EnvironmentConfig {
file := fmt.Sprintf("config/environments/%s.yaml", getEnv()) file := fmt.Sprintf("config/environments/%s.yaml", getEnv())
f, err := os.Open(file) f, err := os.Open(file)
var cfg EnvironmentConfig var cfg EnvironmentConfig
if err != nil { if err != nil {
//log.Fatal(err) //log.Fatal(err)
//os.Exit(2) //os.Exit(2)
//log.Println("Unable to open config file at config/environments/" + getEnv()) //log.Println("Unable to open config file at config/environments/" + getEnv())
//log.Println("Will proceed with defaults or enironment variables (no config file).") //log.Println("Will proceed with defaults or enironment variables (no config file).")
return &cfg return &cfg
} }
defer f.Close() defer f.Close()
decoder := yaml.NewDecoder(f) decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg) err = decoder.Decode(&cfg)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
os.Exit(2) os.Exit(2)
} }
return &cfg return &cfg
} }
func init() { func init() {
Config = readConfig() Config = readConfig()
} }

View File

@@ -1,4 +1,4 @@
default comms { default k8scom tester textx comms {
reload 15s reload 15s
hosts /root/dnsconfig/netmaker.hosts { hosts /root/dnsconfig/netmaker.hosts {
fallthrough fallthrough

View File

@@ -1 +0,0 @@
10.10.10.1 netmaker.default

View File

@@ -9,6 +9,7 @@ import (
"net/http" "net/http"
"strings" "strings"
"time" "time"
"github.com/go-playground/validator/v10" "github.com/go-playground/validator/v10"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
@@ -20,8 +21,11 @@ import (
"go.mongodb.org/mongo-driver/mongo/options" "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) { 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", 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(getNetwork))).Methods("GET")
r.HandleFunc("/api/networks/{networkname}", securityCheck(false, http.HandlerFunc(updateNetwork))).Methods("PUT") 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}/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(createAccessKey))).Methods("POST")
r.HandleFunc("/api/networks/{networkname}/keys", securityCheck(false, http.HandlerFunc(getAccessKeys))).Methods("GET") 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") 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) var params = mux.Vars(r)
bearerToken := r.Header.Get("Authorization") bearerToken := r.Header.Get("Authorization")
err := SecurityCheck(reqAdmin, params["networkname"], bearerToken) err, networks, username := SecurityCheck(reqAdmin, params["networkname"], bearerToken)
if err != nil { if err != nil {
if strings.Contains(err.Error(), "does not exist") { if strings.Contains(err.Error(), "does not exist") {
errorResponse.Code = http.StatusNotFound errorResponse.Code = http.StatusNotFound
@@ -54,18 +58,26 @@ func securityCheck(reqAdmin bool, next http.Handler) http.HandlerFunc {
returnErrorResponse(w, r, errorResponse) returnErrorResponse(w, r, errorResponse)
return 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) next.ServeHTTP(w, r)
} }
} }
func SecurityCheck(reqAdmin bool, netname, token string) error { func SecurityCheck(reqAdmin bool, netname, token string) (error, []string, string) {
hasnetwork := netname != ""
networkexists, err := functions.NetworkExists(netname) networkexists, err := functions.NetworkExists(netname)
if err != nil { if err != nil {
return err return err, nil, ""
} }
if hasnetwork && !networkexists { if netname != "" && !networkexists {
return errors.New("This network does not exist") return errors.New("This network does not exist"), nil, ""
} }
var hasBearer = true var hasBearer = true
@@ -77,23 +89,30 @@ func SecurityCheck(reqAdmin bool, netname, token string) error {
} else { } else {
authToken = tokenSplit[1] authToken = tokenSplit[1]
} }
userNetworks := []string{}
//all endpoints here require master so not as complicated //all endpoints here require master so not as complicated
if !hasBearer || !authenticateMaster(authToken) { isMasterAuthenticated := authenticateMaster(authToken)
_, networks, isadmin, err := functions.VerifyUserToken(authToken) username := ""
if !hasBearer || !isMasterAuthenticated {
userName, networks, isadmin, err := functions.VerifyUserToken(authToken)
username = userName
if err != nil { if err != nil {
return errors.New("Error verifying user token") return errors.New("Error verifying user token"), nil, username
} }
if !isadmin && reqAdmin { if !isadmin && reqAdmin {
return errors.New("You are unauthorized to access this endpoint") return errors.New("You are unauthorized to access this endpoint"), nil, username
} 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")
} }
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 //Consider a more secure way of setting master key
@@ -107,16 +126,33 @@ func authenticateMaster(tokenString string) bool {
//simple get all networks function //simple get all networks function
func getNetworks(w http.ResponseWriter, r *http.Request) { func getNetworks(w http.ResponseWriter, r *http.Request) {
allnetworks, err := functions.ListNetworks() headerNetworks := r.Header.Get("networks")
if err != nil { networksSlice := []string{}
returnErrorResponse(w, r, formatError(err, "internal")) marshalErr := json.Unmarshal([]byte(headerNetworks), &networksSlice)
if marshalErr != nil {
returnErrorResponse(w, r, formatError(marshalErr, "internal"))
return 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) networks := RemoveComms(allnetworks)
functions.PrintUserLog(r.Header.Get("user"), "fetched networks.", 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(networks) json.NewEncoder(w).Encode(networks)
return
} }
func RemoveComms(networks []models.Network) []models.Network { func RemoveComms(networks []models.Network) []models.Network {
@@ -137,13 +173,13 @@ func RemoveComms(networks []models.Network) []models.Network {
func ValidateNetworkUpdate(network models.NetworkUpdate) error { func ValidateNetworkUpdate(network models.NetworkUpdate) error {
v := validator.New() v := validator.New()
_ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool { _ = v.RegisterValidation("netid_valid", func(fl validator.FieldLevel) bool {
if fl.Field().String() == "" { if fl.Field().String() == "" {
return true return true
} }
inCharSet := functions.NameInNetworkCharSet(fl.Field().String()) inCharSet := functions.NameInNetworkCharSet(fl.Field().String())
return inCharSet return inCharSet
}) })
// _ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool { // _ = v.RegisterValidation("addressrange_valid", func(fl validator.FieldLevel) bool {
// isvalid := fl.Field().String() == "" || functions.IsIpCIDR(fl.Field().String()) // 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")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "fetched network "+netname, 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(network) json.NewEncoder(w).Encode(network)
} }
@@ -257,6 +294,7 @@ func keyUpdate(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "updated key on network "+netname, 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(network) json.NewEncoder(w).Encode(network)
} }
@@ -329,7 +367,8 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
var network models.Network var network models.Network
network, err := functions.GetParentNetwork(params["networkname"]) netname := params["networkname"]
network, err := functions.GetParentNetwork(netname)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -359,45 +398,47 @@ func updateNetwork(w http.ResponseWriter, r *http.Request) {
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "updated network "+netname, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(returnednetwork) json.NewEncoder(w).Encode(returnednetwork)
} }
func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) { func updateNetworkNodeLimit(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
var network models.Network var network models.Network
network, err := functions.GetParentNetwork(params["networkname"]) netname := params["networkname"]
if err != nil { network, err := functions.GetParentNetwork(netname)
returnErrorResponse(w, r, formatError(err, "internal")) if err != nil {
return 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.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) { 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. //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 { if err != nil {
errtype := "badrequest" errtype := "badrequest"
if strings.Contains(err.Error(), "Node check failed"){ if strings.Contains(err.Error(), "Node check failed") {
errtype = "forbidden" errtype = "forbidden"
} }
returnErrorResponse(w, r, formatError(err, errtype)) returnErrorResponse(w, r, formatError(err, errtype))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "deleted network "+network, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(count) json.NewEncoder(w).Encode(count)
} }
@@ -585,6 +627,7 @@ func createNetwork(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "created network "+network.NetID, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
//json.NewEncoder(w).Encode(result) //json.NewEncoder(w).Encode(result)
} }
@@ -633,7 +676,8 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r) var params = mux.Vars(r)
var accesskey models.AccessKey var accesskey models.AccessKey
//start here //start here
network, err := functions.GetParentNetwork(params["networkname"]) netname := params["networkname"]
network, err := functions.GetParentNetwork(netname)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -648,6 +692,7 @@ func createAccessKey(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "created access key "+netname, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(key) json.NewEncoder(w).Encode(key)
//w.Write([]byte(accesskey.AccessString)) //w.Write([]byte(accesskey.AccessString))
@@ -666,10 +711,10 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
accesskey.Uses = 1 accesskey.Uses = 1
} }
checkkeys, err := GetKeys(network.NetID) checkkeys, err := GetKeys(network.NetID)
if err != nil { if err != nil {
return models.AccessKey{}, errors.New("could not retrieve network keys") return models.AccessKey{}, errors.New("could not retrieve network keys")
} }
for _, key := range checkkeys { for _, key := range checkkeys {
if key.Name == accesskey.Name { if key.Name == accesskey.Name {
@@ -685,10 +730,11 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
netID := network.NetID netID := network.NetID
var accessToken models.AccessToken var accessToken models.AccessToken
s := servercfg.GetServerConfig() s := servercfg.GetServerConfig()
w := servercfg.GetWGConfig() w := servercfg.GetWGConfig()
servervals := models.ServerConfig{ servervals := models.ServerConfig{
CoreDNSAddr: s.CoreDNSAddr,
APIConnString: s.APIConnString, APIConnString: s.APIConnString,
APIHost: s.APIHost, APIHost: s.APIHost,
APIPort: s.APIPort, APIPort: s.APIPort,
@@ -696,27 +742,27 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
GRPCHost: s.GRPCHost, GRPCHost: s.GRPCHost,
GRPCPort: s.GRPCPort, GRPCPort: s.GRPCPort,
GRPCSSL: s.GRPCSSL, GRPCSSL: s.GRPCSSL,
} }
wgvals := models.WG{ wgvals := models.WG{
GRPCWireGuard: w.GRPCWireGuard, GRPCWireGuard: w.GRPCWireGuard,
GRPCWGAddress: w.GRPCWGAddress, GRPCWGAddress: w.GRPCWGAddress,
GRPCWGPort: w.GRPCWGPort, GRPCWGPort: w.GRPCWGPort,
GRPCWGPubKey: w.GRPCWGPubKey, GRPCWGPubKey: w.GRPCWGPubKey,
GRPCWGEndpoint: s.APIHost, GRPCWGEndpoint: s.APIHost,
} }
accessToken.ServerConfig = servervals accessToken.ServerConfig = servervals
accessToken.WG = wgvals accessToken.WG = wgvals
accessToken.ClientConfig.Network = netID accessToken.ClientConfig.Network = netID
accessToken.ClientConfig.Key = accesskey.Value accessToken.ClientConfig.Key = accesskey.Value
accessToken.ClientConfig.LocalRange = privAddr accessToken.ClientConfig.LocalRange = privAddr
tokenjson, err := json.Marshal(accessToken) tokenjson, err := json.Marshal(accessToken)
if err != nil { if err != nil {
return accesskey, err return accesskey, err
} }
accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson))
//validate accesskey //validate accesskey
v := validator.New() v := validator.New()
@@ -752,52 +798,51 @@ func GetSignupToken(netID string) (models.AccessKey, error) {
var accesskey models.AccessKey var accesskey models.AccessKey
var accessToken models.AccessToken var accessToken models.AccessToken
s := servercfg.GetServerConfig() s := servercfg.GetServerConfig()
w := servercfg.GetWGConfig() w := servercfg.GetWGConfig()
servervals := models.ServerConfig{ servervals := models.ServerConfig{
APIConnString: s.APIConnString, APIConnString: s.APIConnString,
APIHost: s.APIHost, APIHost: s.APIHost,
APIPort: s.APIPort, APIPort: s.APIPort,
GRPCConnString: s.GRPCConnString, GRPCConnString: s.GRPCConnString,
GRPCHost: s.GRPCHost, GRPCHost: s.GRPCHost,
GRPCPort: s.GRPCPort, GRPCPort: s.GRPCPort,
GRPCSSL: s.GRPCSSL, GRPCSSL: s.GRPCSSL,
} }
wgvals := models.WG{ wgvals := models.WG{
GRPCWireGuard: w.GRPCWireGuard, GRPCWireGuard: w.GRPCWireGuard,
GRPCWGAddress: w.GRPCWGAddress, GRPCWGAddress: w.GRPCWGAddress,
GRPCWGPort: w.GRPCWGPort, GRPCWGPort: w.GRPCWGPort,
GRPCWGPubKey: w.GRPCWGPubKey, GRPCWGPubKey: w.GRPCWGPubKey,
GRPCWGEndpoint: s.APIHost, GRPCWGEndpoint: s.APIHost,
} }
accessToken.ServerConfig = servervals accessToken.ServerConfig = servervals
accessToken.WG = wgvals accessToken.WG = wgvals
tokenjson, err := json.Marshal(accessToken) tokenjson, err := json.Marshal(accessToken)
if err != nil { if err != nil {
return accesskey, err return accesskey, err
} }
accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson)) accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(tokenjson))
return accesskey, nil return accesskey, nil
} }
func getSignupToken(w http.ResponseWriter, r *http.Request) { func getSignupToken(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
netID := params["networkname"] netID := params["networkname"]
token, err := GetSignupToken(netID) token, err := GetSignupToken(netID)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
w.WriteHeader(http.StatusOK) functions.PrintUserLog(r.Header.Get("user"), "got signup token "+netID, 2)
json.NewEncoder(w).Encode(token) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(token)
} }
//pretty simple get //pretty simple get
func getAccessKeys(w http.ResponseWriter, r *http.Request) { func getAccessKeys(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") 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")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "fetched access keys on network "+network, 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(keys) json.NewEncoder(w).Encode(keys)
} }
@@ -836,6 +882,7 @@ func deleteAccessKey(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "deleted access key "+keyname+" on network "+netname, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
} }
func DeleteKey(keyname, netname string) error { func DeleteKey(keyname, netname string) error {

View File

@@ -5,10 +5,11 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"log"
"net/http" "net/http"
"strings" "strings"
"time" "time"
"log"
"github.com/gorilla/mux" "github.com/gorilla/mux"
"github.com/gravitl/netmaker/functions" "github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models" "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 //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. //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 = "" var macaddress = ""
_, networks, isadmin, errN := functions.VerifyUserToken(authToken) username, networks, isadmin, errN := functions.VerifyUserToken(authToken)
isnetadmin := isadmin isnetadmin := isadmin
if errN == nil && isadmin { if errN == nil && isadmin {
macaddress = "mastermac" macaddress = "mastermac"
isAuthorized = true isAuthorized = true
} else { } else {
mac, _, err := functions.VerifyToken(authToken) mac, _, err := functions.VerifyToken(authToken)
if err != nil { if err != nil {
@@ -202,11 +203,11 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
} }
macaddress = mac macaddress = mac
} }
if !isadmin && params["network"] != ""{ if !isadmin && params["network"] != "" {
if functions.SliceContains(networks, params["network"]){ if functions.SliceContains(networks, params["network"]) {
isnetadmin = true isnetadmin = true
} }
} }
//The mastermac (login with masterkey from config) can do everything!! May be dangerous. //The mastermac (login with masterkey from config) can do everything!! May be dangerous.
if macaddress == "mastermac" { if macaddress == "mastermac" {
isAuthorized = true isAuthorized = true
@@ -223,20 +224,20 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
if isnetadmin { if isnetadmin {
isAuthorized = true isAuthorized = true
} else { } else {
node, err := functions.GetNodeByMacAddress(params["network"], macaddress) node, err := functions.GetNodeByMacAddress(params["network"], macaddress)
if err != nil { if err != nil {
errorResponse = models.ErrorResponse{ errorResponse = models.ErrorResponse{
Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.", Code: http.StatusUnauthorized, Message: "W1R3: Missing Auth Token.",
}
returnErrorResponse(w, r, errorResponse)
return
} }
returnErrorResponse(w, r, errorResponse) isAuthorized = (node.Network == params["network"])
return
}
isAuthorized = (node.Network == params["network"])
} }
case "node": case "node":
if isnetadmin { if isnetadmin {
isAuthorized = true isAuthorized = true
} else { } else {
isAuthorized = (macaddress == params["macaddress"]) isAuthorized = (macaddress == params["macaddress"])
} }
case "master": case "master":
@@ -253,6 +254,10 @@ func authorize(networkCheck bool, authNetwork string, next http.Handler) http.Ha
return return
} else { } else {
//If authorized, this function passes along it's request and output to the appropriate route function. //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) next.ServeHTTP(w, r)
} }
} }
@@ -266,13 +271,15 @@ func getNetworkNodes(w http.ResponseWriter, r *http.Request) {
var nodes []models.Node var nodes []models.Node
var params = mux.Vars(r) var params = mux.Vars(r)
nodes, err := GetNetworkNodes(params["network"]) networkName := params["network"]
nodes, err := GetNetworkNodes(networkName)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
//Returns all the nodes in JSON format //Returns all the nodes in JSON format
functions.PrintUserLog(r.Header.Get("user"), "fetched nodes on network"+networkName, 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(nodes) json.NewEncoder(w).Encode(nodes)
} }
@@ -319,6 +326,7 @@ func getAllNodes(w http.ResponseWriter, r *http.Request) {
return return
} }
//Return all the nodes in JSON format //Return all the nodes in JSON format
functions.PrintUserLog(r.Header.Get("user"), "fetched nodes", 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(nodes) json.NewEncoder(w).Encode(nodes)
} }
@@ -391,6 +399,7 @@ func getNode(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "fetched node "+params["macaddress"], 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node) json.NewEncoder(w).Encode(node)
} }
@@ -409,6 +418,7 @@ func getLastModified(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "called last modified", 2)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(network.NodesLastModified) json.NewEncoder(w).Encode(network.NodesLastModified)
} }
@@ -503,6 +513,7 @@ func createNode(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "created new node "+node.Name+" on network "+node.Network, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node) json.NewEncoder(w).Encode(node)
} }
@@ -517,7 +528,7 @@ func uncordonNode(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
fmt.Println("Node " + node.Name + " uncordoned.") functions.PrintUserLog(r.Header.Get("user"), "uncordoned node "+node.Name, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("SUCCESS") json.NewEncoder(w).Encode("SUCCESS")
} }
@@ -563,6 +574,7 @@ func createEgressGateway(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "created egress gateway on node "+gateway.NodeID+" on network "+gateway.NetID, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node) json.NewEncoder(w).Encode(node)
} }
@@ -594,13 +606,13 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
nodechange.PostUp = node.PostUp nodechange.PostUp = node.PostUp
} }
} }
if node.PostDown != "" { if node.PostDown != "" {
if !strings.Contains(node.PostDown, nodechange.PostDown) { if !strings.Contains(node.PostDown, nodechange.PostDown) {
nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown
} else { } else {
nodechange.PostDown = node.PostDown nodechange.PostDown = node.PostDown
} }
} }
collection := mongoconn.Client.Database("netmaker").Collection("nodes") 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)
// Create filter // Create filter
@@ -637,7 +649,7 @@ func CreateEgressGateway(gateway models.EgressGatewayRequest) (models.Node, erro
func ValidateEgressGateway(gateway models.EgressGatewayRequest) error { func ValidateEgressGateway(gateway models.EgressGatewayRequest) error {
var err error var err error
//isIp := functions.IsIpCIDR(gateway.RangeString) //isIp := functions.IsIpCIDR(gateway.RangeString)
empty := len(gateway.Ranges)==0 empty := len(gateway.Ranges) == 0
if empty { if empty {
err = errors.New("IP Ranges Cannot Be 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) { func deleteEgressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) 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 { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "delete egress gateway "+nodeMac+" on network "+netid, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node) json.NewEncoder(w).Encode(node)
} }
@@ -705,15 +720,19 @@ func DeleteEgressGateway(network, macaddress string) (models.Node, error) {
} }
return node, nil return node, nil
} }
// == INGRESS == // == INGRESS ==
func createIngressGateway(w http.ResponseWriter, r *http.Request) { func createIngressGateway(w http.ResponseWriter, r *http.Request) {
var params = mux.Vars(r) var params = mux.Vars(r)
w.Header().Set("Content-Type", "application/json") 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 { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "created ingress gateway on node "+nodeMac+" on network "+netid, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node) 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) { func CreateIngressGateway(netid string, macaddress string) (models.Node, error) {
node, err := functions.GetNodeByMacAddress(netid, macaddress) node, err := functions.GetNodeByMacAddress(netid, macaddress)
if err != nil { if err != nil {
return models.Node{}, err return models.Node{}, err
} }
network, err := functions.GetParentNetwork(netid) network, err := functions.GetParentNetwork(netid)
if err != nil { if err != nil {
log.Println("Could not find network.") log.Println("Could not find network.")
return models.Node{}, err return models.Node{}, err
} }
var nodechange models.Node var nodechange models.Node
nodechange.IngressGatewayRange = network.AddressRange 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.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" nodechange.PostDown = "iptables -D FORWARD -i " + node.Interface + " -j ACCEPT; iptables -t nat -D POSTROUTING -o " + node.Interface + " -j MASQUERADE"
if node.PostUp != "" { if node.PostUp != "" {
if !strings.Contains(node.PostUp, nodechange.PostUp) { if !strings.Contains(node.PostUp, nodechange.PostUp) {
nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp nodechange.PostUp = node.PostUp + "; " + nodechange.PostUp
} else { } else {
nodechange.PostUp = node.PostUp nodechange.PostUp = node.PostUp
} }
} }
if node.PostDown != "" { if node.PostDown != "" {
if !strings.Contains(node.PostDown, nodechange.PostDown) { if !strings.Contains(node.PostDown, nodechange.PostDown) {
nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown nodechange.PostDown = node.PostDown + "; " + nodechange.PostDown
} else { } else {
nodechange.PostDown = node.PostDown nodechange.PostDown = node.PostDown
} }
} }
collection := mongoconn.Client.Database("netmaker").Collection("nodes") 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)
// Create filter // Create filter
filter := bson.M{"macaddress": macaddress, "network": netid} filter := bson.M{"macaddress": macaddress, "network": netid}
node.SetLastModified() node.SetLastModified()
// prepare update model. // prepare update model.
update := bson.D{ update := bson.D{
{"$set", bson.D{ {"$set", bson.D{
{"postup", nodechange.PostUp}, {"postup", nodechange.PostUp},
{"postdown", nodechange.PostDown}, {"postdown", nodechange.PostDown},
{"isingressgateway", true}, {"isingressgateway", true},
{"ingressgatewayrange", nodechange.IngressGatewayRange}, {"ingressgatewayrange", nodechange.IngressGatewayRange},
{"lastmodified", node.LastModified}, {"lastmodified", node.LastModified},
}}, }},
} }
var nodeupdate models.Node var nodeupdate models.Node
err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate) err = collection.FindOneAndUpdate(ctx, filter, update).Decode(&nodeupdate)
defer cancel() defer cancel()
if err != nil { if err != nil {
log.Println("error updating node to gateway") log.Println("error updating node to gateway")
return models.Node{}, err return models.Node{}, err
} }
err = SetNetworkNodesLastModified(netid) err = SetNetworkNodesLastModified(netid)
if err != nil { if err != nil {
return node, err return node, err
} }
//Get updated values to return //Get updated values to return
node, err = functions.GetNodeByMacAddress(netid, macaddress) node, err = functions.GetNodeByMacAddress(netid, macaddress)
if err != nil { if err != nil {
log.Println("error finding node after update") log.Println("error finding node after update")
return node, err return node, err
} }
return node, nil return node, nil
} }
func deleteIngressGateway(w http.ResponseWriter, r *http.Request) { func deleteIngressGateway(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
node, err := DeleteIngressGateway(params["network"], params["macaddress"]) nodeMac := params["macaddress"]
node, err := DeleteIngressGateway(params["network"], nodeMac)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
} }
functions.PrintUserLog(r.Header.Get("user"), "deleted ingress gateway"+nodeMac, 1)
w.WriteHeader(http.StatusOK) w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(node) json.NewEncoder(w).Encode(node)
} }

View File

@@ -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}", 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(deleteUser))).Methods("DELETE")
r.HandleFunc("/api/users/{username}", authorizeUser(http.HandlerFunc(getUser))).Methods("GET") 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. //Node authenticates using its password and retrieves a JWT for authorization.
@@ -63,12 +63,13 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
return return
} }
username := authRequest.UserName
var successResponse = models.SuccessResponse{ var successResponse = models.SuccessResponse{
Code: http.StatusOK, Code: http.StatusOK,
Message: "W1R3: Device " + authRequest.UserName + " Authorized", Message: "W1R3: Device " + username + " Authorized",
Response: models.SuccessfulUserLoginResponse{ Response: models.SuccessfulUserLoginResponse{
AuthToken: jwt, AuthToken: jwt,
UserName: authRequest.UserName, UserName: username,
}, },
} }
//Send back the JWT //Send back the JWT
@@ -78,6 +79,7 @@ func authenticateUser(response http.ResponseWriter, request *http.Request) {
returnErrorResponse(response, request, errorResponse) returnErrorResponse(response, request, errorResponse)
return return
} }
functions.PrintUserLog(username, "was authenticated", 2)
response.Header().Set("Content-Type", "application/json") response.Header().Set("Content-Type", "application/json")
response.Write(successJSONResponse) response.Write(successJSONResponse)
} }
@@ -112,7 +114,7 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
} }
//Create a new JWT for the node //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 return tokenString, nil
} }
@@ -126,36 +128,39 @@ func VerifyAuthRequest(authRequest models.UserAuthParams) (string, error) {
func authorizeUser(next http.Handler) http.HandlerFunc { func authorizeUser(next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
//get the auth token //get the auth token
bearerToken := r.Header.Get("Authorization") bearerToken := r.Header.Get("Authorization")
err := ValidateUserToken(bearerToken, params["username"], false) username := params["username"]
err := ValidateUserToken(bearerToken, username, false)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "unauthorized")) returnErrorResponse(w, r, formatError(err, "unauthorized"))
return return
} }
r.Header.Set("user", username)
next.ServeHTTP(w, r) next.ServeHTTP(w, r)
} }
} }
func authorizeUserAdm(next http.Handler) http.HandlerFunc { func authorizeUserAdm(next http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) { return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
//get the auth token //get the auth token
bearerToken := r.Header.Get("Authorization") bearerToken := r.Header.Get("Authorization")
err := ValidateUserToken(bearerToken, params["username"], true) username := params["username"]
if err != nil { err := ValidateUserToken(bearerToken, username, true)
returnErrorResponse(w, r, formatError(err, "unauthorized")) if err != nil {
return returnErrorResponse(w, r, formatError(err, "unauthorized"))
} return
next.ServeHTTP(w, r) }
} r.Header.Set("user", username)
next.ServeHTTP(w, r)
}
} }
func ValidateUserToken(token string, user string, adminonly bool) error { func ValidateUserToken(token string, user string, adminonly bool) error {
var tokenSplit = strings.Split(token, " ") var tokenSplit = strings.Split(token, " ")
@@ -241,79 +246,74 @@ func GetUser(username string) (models.User, error) {
func GetUsers() ([]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 { if err != nil {
return users, err return users, err
} }
defer cancel() defer cancel()
for cur.Next(context.TODO()) { for cur.Next(context.TODO()) {
var user models.User var user models.User
err := cur.Decode(&user) err := cur.Decode(&user)
if err != nil { if err != nil {
return users, err return users, err
} }
// add network our array // add network our array
users = append(users, user) users = append(users, user)
} }
if err := cur.Err(); err != nil { if err := cur.Err(); err != nil {
return users, err return users, err
} }
return users, err return users, err
} }
//Get an individual node. Nothin fancy here folks. //Get an individual node. Nothin fancy here folks.
func getUser(w http.ResponseWriter, r *http.Request) { func getUser(w http.ResponseWriter, r *http.Request) {
// set header. // set header.
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) 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 { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return 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) { func CreateUser(user models.User) (models.User, error) {
hasadmin, err := HasAdmin() err := ValidateUser("create", user)
if hasadmin && user.IsAdmin {
return models.User{}, errors.New("Admin already Exists")
}
err = ValidateUser("create", user)
if err != nil { if err != nil {
return models.User{}, err return models.User{}, err
} }
@@ -326,7 +326,7 @@ func CreateUser(user models.User) (models.User, error) {
//set password to encrypted password //set password to encrypted password
user.Password = string(hash) user.Password = string(hash)
tokenString, _ := functions.CreateUserJWT(user.UserName,user.Networks, user.IsAdmin) tokenString, _ := functions.CreateUserJWT(user.UserName, user.Networks, user.IsAdmin)
if tokenString == "" { if tokenString == "" {
//returnErrorResponse(w, r, errorResponse) //returnErrorResponse(w, r, errorResponse)
@@ -350,35 +350,34 @@ func createAdmin(w http.ResponseWriter, r *http.Request) {
var admin models.User var admin models.User
//get node from body of request //get node from body of request
_ = json.NewDecoder(r.Body).Decode(&admin) _ = json.NewDecoder(r.Body).Decode(&admin)
admin.IsAdmin = true admin.IsAdmin = true
admin, err := CreateUser(admin) admin, err := CreateUser(admin)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
functions.PrintUserLog(admin.UserName, "was made a new admin", 1)
json.NewEncoder(w).Encode(admin) json.NewEncoder(w).Encode(admin)
} }
func createUser(w http.ResponseWriter, r *http.Request) { 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 var user models.User
//get node from body of request //get node from body of request
_ = json.NewDecoder(r.Body).Decode(&user) _ = json.NewDecoder(r.Body).Decode(&user)
user, err := CreateUser(user) user, err := CreateUser(user)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
functions.PrintUserLog(user.UserName, "was created", 1)
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(user)
} }
func UpdateUser(userchange models.User, user models.User) (models.User, error) { func UpdateUser(userchange models.User, user models.User) (models.User, error) {
err := ValidateUser("update", userchange) err := ValidateUser("update", userchange)
@@ -391,9 +390,9 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) {
if userchange.UserName != "" { if userchange.UserName != "" {
user.UserName = userchange.UserName user.UserName = userchange.UserName
} }
if len(userchange.Networks) > 0 { if len(userchange.Networks) > 0 {
user.Networks = userchange.Networks user.Networks = userchange.Networks
} }
if userchange.Password != "" { if userchange.Password != "" {
//encrypt that password so we never see it again //encrypt that password so we never see it again
hash, err := bcrypt.GenerateFromPassword([]byte(userchange.Password), 5) 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 params = mux.Vars(r)
var user models.User var user models.User
//start here //start here
user, err := GetUser(params["username"]) username := params["username"]
user, err := GetUser(username)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
return return
@@ -463,32 +463,35 @@ func updateUser(w http.ResponseWriter, r *http.Request) {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
functions.PrintUserLog(username, "was updated", 1)
json.NewEncoder(w).Encode(user) json.NewEncoder(w).Encode(user)
} }
func updateUserAdm(w http.ResponseWriter, r *http.Request) { func updateUserAdm(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json") w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r) var params = mux.Vars(r)
var user models.User var user models.User
//start here //start here
user, err := GetUser(params["username"]) username := params["username"]
if err != nil { user, err := GetUser(username)
returnErrorResponse(w, r, formatError(err, "internal")) if err != nil {
return returnErrorResponse(w, r, formatError(err, "internal"))
} return
var userchange models.User }
// we decode our body request params var userchange models.User
err = json.NewDecoder(r.Body).Decode(&userchange) // we decode our body request params
if err != nil { err = json.NewDecoder(r.Body).Decode(&userchange)
returnErrorResponse(w, r, formatError(err, "internal")) if err != nil {
return returnErrorResponse(w, r, formatError(err, "internal"))
} return
}
user, err = UpdateUser(userchange, user) user, err = UpdateUser(userchange, user)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "badrequest")) returnErrorResponse(w, r, formatError(err, "badrequest"))
return return
} }
json.NewEncoder(w).Encode(user) functions.PrintUserLog(username, "was updated (admin)", 1)
json.NewEncoder(w).Encode(user)
} }
func DeleteUser(user string) (bool, error) { func DeleteUser(user string) (bool, error) {
@@ -521,7 +524,8 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
// get params // get params
var params = mux.Vars(r) var params = mux.Vars(r)
success, err := DeleteUser(params["username"]) username := params["username"]
success, err := DeleteUser(username)
if err != nil { if err != nil {
returnErrorResponse(w, r, formatError(err, "internal")) returnErrorResponse(w, r, formatError(err, "internal"))
@@ -531,6 +535,7 @@ func deleteUser(w http.ResponseWriter, r *http.Request) {
return return
} }
functions.PrintUserLog(username, "was deleted", 1)
json.NewEncoder(w).Encode(params["username"] + " deleted.") json.NewEncoder(w).Encode(params["username"] + " deleted.")
} }

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
docs/_build/html/_images/exclient1.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

BIN
docs/_build/html/_images/exclient2.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

BIN
docs/_build/html/_images/exclient3.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

BIN
docs/_build/html/_images/exclient4.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View File

@@ -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. 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 Components
=========== ===========
@@ -88,15 +87,17 @@ The Netmaker server interacts with (as of v0.3) a MongoDB instance, which holds
Netclient Netclient
---------------- ----------------
The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker `GitHub Repository <https://github.com/gravitl/netmaker/tree/master/netclient>`_. 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 <https://github.com/gravitl/netmaker/tree/master/netclient>`_. 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. 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 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 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. 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 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 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). 4. Admin runs the netclient install script on any given node (machine).
5. Netclient decodes key, which contains the GRPC server location and port 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 6. Netclient uses information to register and set up WireGuard tunnel to GRPC server
7. Netclient reaches out to GRPC server with this information, authenticating via access key. 7. Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers
8. Netmaker server verifies information and creates the node, setting default values for any missing information. 8. Netclient reaches out to GRPC server with this information, authenticating via access key.
9. Timestamp is set for the network (see #16). 9. Netmaker server verifies information and creates the node, setting default values for any missing information.
10. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network. 10. Timestamp is set for the network (see #16).
11. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard 11. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network.
12. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the network). 12. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard
13. Netmaker sends back peers list, including current known configurations of all nodes in network. 13. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the 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. 14. Netmaker sends back peers list, including current known configurations of all nodes in network.
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. 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. 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. 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 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: To manage a node automatically, the Netmaker client (netclient) requires **systemd-based linux.** Compatible systems include:
- Fedora - Fedora
- Ubuntu - 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". - **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". - **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).

View File

@@ -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. 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. 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.

View File

@@ -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 <access key> -s 1.2.3.4 -p 50052`` * 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 **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. 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`` 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) 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. 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.

View File

@@ -273,6 +273,8 @@
<li class="md-nav__item"><a href="#netmaker-ui" class="md-nav__link">Netmaker UI</a> <li class="md-nav__item"><a href="#netmaker-ui" class="md-nav__link">Netmaker UI</a>
</li> </li>
<li class="md-nav__item"><a href="#coredns" class="md-nav__link">CoreDNS</a> <li class="md-nav__item"><a href="#coredns" class="md-nav__link">CoreDNS</a>
</li>
<li class="md-nav__item"><a href="#external-client" class="md-nav__link">External Client</a>
</li></ul> </li></ul>
</nav> </nav>
</li> </li>
@@ -785,6 +787,8 @@
<li class="md-nav__item"><a href="#netmaker-ui" class="md-nav__link">Netmaker UI</a> <li class="md-nav__item"><a href="#netmaker-ui" class="md-nav__link">Netmaker UI</a>
</li> </li>
<li class="md-nav__item"><a href="#coredns" class="md-nav__link">CoreDNS</a> <li class="md-nav__item"><a href="#coredns" class="md-nav__link">CoreDNS</a>
</li>
<li class="md-nav__item"><a href="#external-client" class="md-nav__link">External Client</a>
</li></ul> </li></ul>
</nav> </nav>
</li> </li>
@@ -840,7 +844,7 @@
<h3 id="systemd">SystemD<a class="headerlink" href="#systemd" title="Permalink to this headline"></a></h3> <h3 id="systemd">SystemD<a class="headerlink" href="#systemd" title="Permalink to this headline"></a></h3>
<p>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.</p> <p>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.</p>
<p>Netmakers 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.</p> <p>Netmakers 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.</p>
@@ -856,16 +860,17 @@
<h3 id="netclient">Netclient<a class="headerlink" href="#netclient" title="Permalink to this headline"></a></h3> <h3 id="netclient">Netclient<a class="headerlink" href="#netclient" title="Permalink to this headline"></a></h3>
<p>The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker <a class="reference external" href="https://github.com/gravitl/netmaker/tree/master/netclient">GitHub Repository</a>. 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.</p> <p>The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker <a class="reference external" href="https://github.com/gravitl/netmaker/tree/master/netclient">GitHub Repository</a>. 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.</p>
<p>The netclient is installed via a simple bash script, which pulls the latest binary and runs install command.</p> <p>The netclient is installed via a simple bash script, which pulls the latest binary and runs register and join commands.</p>
<p>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.</p> <p>The register command adds a WireGuard tunnel directly to the netmaker server, for all subsequent communication.</p>
<p>The netclient then sets itself up in systemd, and configures WireGuard. At this point it should be part of the network.</p> <p>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.</p>
<p>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.</p> <p>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.</p>
<p>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 &lt; network name &gt;).</p>
<p>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.</p> <p>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.</p>
<h3 id="mongodb">MongoDB<a class="headerlink" href="#mongodb" title="Permalink to this headline"></a></h3> <h3 id="mongodb">MongoDB<a class="headerlink" href="#mongodb" title="Permalink to this headline"></a></h3>
<p>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.</p> <p>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.</p>
<h3 id="netmaker-ui">Netmaker UI<a class="headerlink" href="#netmaker-ui" title="Permalink to this headline"></a></h3> <h3 id="netmaker-ui">Netmaker UI<a class="headerlink" href="#netmaker-ui" title="Permalink to this headline"></a></h3>
@@ -878,6 +883,13 @@
<p>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 <a class="reference internal" href="server-installation.html"><span class="doc">Server Installation</span></a> guide.</p> <p>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 <a class="reference internal" href="server-installation.html"><span class="doc">Server Installation</span></a> guide.</p>
<h3 id="external-client">External Client<a class="headerlink" href="#external-client" title="Permalink to this headline"></a></h3>
<p>The external client is simply a manually configured WireGuard connection to your network, which Netmaker helps to manage.</p>
<p>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.</p>
<p>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.</p>
<p>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.</p>
<h2 id="technical-process">Technical Process<a class="headerlink" href="#technical-process" title="Permalink to this headline"></a></h2> <h2 id="technical-process">Technical Process<a class="headerlink" href="#technical-process" title="Permalink to this headline"></a></h2>
<p>Below is a high level, step-by-step overview of the flow of communications within Netmaker (assuming Netmaker has already been installed):</p> <p>Below is a high level, step-by-step overview of the flow of communications within Netmaker (assuming Netmaker has already been installed):</p>
@@ -887,6 +899,7 @@
<li><p>Both of the above requests are routed to the server via an API call from the front end</p></li> <li><p>Both of the above requests are routed to the server via an API call from the front end</p></li>
<li><p>Admin runs the netclient install script on any given node (machine).</p></li> <li><p>Admin runs the netclient install script on any given node (machine).</p></li>
<li><p>Netclient decodes key, which contains the GRPC server location and port</p></li> <li><p>Netclient decodes key, which contains the GRPC server location and port</p></li>
<li><p>Netclient uses information to register and set up WireGuard tunnel to GRPC server</p></li>
<li><p>Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers</p></li> <li><p>Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers</p></li>
<li><p>Netclient reaches out to GRPC server with this information, authenticating via access key.</p></li> <li><p>Netclient reaches out to GRPC server with this information, authenticating via access key.</p></li>
<li><p>Netmaker server verifies information and creates the node, setting default values for any missing information.</p></li> <li><p>Netmaker server verifies information and creates the node, setting default values for any missing information.</p></li>
@@ -902,6 +915,7 @@
<h2 id="compatible-systems-for-netclient">Compatible Systems for Netclient<a class="headerlink" href="#compatible-systems-for-netclient" title="Permalink to this headline"></a></h2> <h2 id="compatible-systems-for-netclient">Compatible Systems for Netclient<a class="headerlink" href="#compatible-systems-for-netclient" title="Permalink to this headline"></a></h2>
<p>To manage a node manually, the Netclient can be compiled and run for most linux distibutions, with a prerequisite of WireGuard.</p>
<dl class="simple"> <dl class="simple">
<dt>To manage a node automatically, the Netmaker client (netclient) requires <strong>systemd-based linux.</strong> Compatible systems include:</dt><dd><ul class="simple"> <dt>To manage a node automatically, the Netmaker client (netclient) requires <strong>systemd-based linux.</strong> Compatible systems include:</dt><dd><ul class="simple">
<li><p>Fedora</p></li> <li><p>Fedora</p></li>
@@ -931,7 +945,6 @@
<ul class="simple"> <ul class="simple">
<li><p><strong>Double NAT</strong>: Netmaker is currently unable to route traffic for devices behind a “double NAT”.</p></li> <li><p><strong>Double NAT</strong>: Netmaker is currently unable to route traffic for devices behind a “double NAT”.</p></li>
<li><p><strong>CGNAT</strong>: Netmaker is currently unable to route traffic for for devices behind a “carrier-grade NAT”.</p></li> <li><p><strong>CGNAT</strong>: Netmaker is currently unable to route traffic for for devices behind a “carrier-grade NAT”.</p></li>
<li><p><strong>Windows/iPhone/Android</strong>: 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).</p></li>
</ul> </ul>

View File

@@ -333,7 +333,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="quick-start.html#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <a href="quick-start.html#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
@@ -459,6 +459,10 @@
<li class="md-nav__item"><a href="#external-clients--page-root" class="md-nav__link">External Clients</a><nav class="md-nav"> <li class="md-nav__item"><a href="#external-clients--page-root" class="md-nav__link">External Clients</a><nav class="md-nav">
<ul class="md-nav__list"> <ul class="md-nav__list">
<li class="md-nav__item"><a href="#introduction" class="md-nav__link">Introduction</a> <li class="md-nav__item"><a href="#introduction" class="md-nav__link">Introduction</a>
</li>
<li class="md-nav__item"><a href="#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
</li>
<li class="md-nav__item"><a href="#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
</li></ul> </li></ul>
</nav> </nav>
</li> </li>
@@ -471,6 +475,20 @@
<a href="#introduction" class="md-nav__link">Introduction</a> <a href="#introduction" class="md-nav__link">Introduction</a>
</li>
<li class="md-nav__item">
<a href="#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
</li>
<li class="md-nav__item">
<a href="#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
</li></ul> </li></ul>
</li> </li>
@@ -729,6 +747,10 @@
<li class="md-nav__item"><a href="#external-clients--page-root" class="md-nav__link">External Clients</a><nav class="md-nav"> <li class="md-nav__item"><a href="#external-clients--page-root" class="md-nav__link">External Clients</a><nav class="md-nav">
<ul class="md-nav__list"> <ul class="md-nav__list">
<li class="md-nav__item"><a href="#introduction" class="md-nav__link">Introduction</a> <li class="md-nav__item"><a href="#introduction" class="md-nav__link">Introduction</a>
</li>
<li class="md-nav__item"><a href="#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
</li>
<li class="md-nav__item"><a href="#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
</li></ul> </li></ul>
</nav> </nav>
</li> </li>
@@ -759,6 +781,34 @@
<p>Important to note, an external client is not <strong>reachable</strong> 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 <strong>not</strong> for use cases where one wishes to make a resource accessible on the network. For that, use netclient.</p> <p>Important to note, an external client is not <strong>reachable</strong> 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 <strong>not</strong> for use cases where one wishes to make a resource accessible on the network. For that, use netclient.</p>
<h2 id="configuring-an-ingress-gateway">Configuring an Ingress Gateway<a class="headerlink" href="#configuring-an-ingress-gateway" title="Permalink to this headline"></a></h2>
<p>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.</p>
<a class="reference internal image-reference" href="_images/exclient1.png"><img alt="Gateway" class="align-center" src="_images/exclient1.png" style="width: 80%;"/></a>
<h2 id="adding-clients-to-a-gateway">Adding Clients to a Gateway<a class="headerlink" href="#adding-clients-to-a-gateway" title="Permalink to this headline"></a></h2>
<p>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.</p>
<a class="reference internal image-reference" href="_images/exclient2.png"><img alt="Gateway" class="align-center" src="_images/exclient2.png" style="width: 80%;"/></a>
<p>After creating a client, you can edit the name to something more logical.</p>
<a class="reference internal image-reference" href="_images/exclient3.png"><img alt="Gateway" class="align-center" src="_images/exclient3.png" style="width: 80%;"/></a>
<p>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.</p>
<a class="reference internal image-reference" href="_images/exclient4.png"><img alt="Gateway" class="align-center" src="_images/exclient4.png" style="width: 80%;"/></a>
<p>Example config file:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="p">[</span><span class="n">Interface</span><span class="p">]</span>
<span class="n">Address</span> <span class="o">=</span> <span class="mf">10.7.11.5</span><span class="o">/</span><span class="mi">32</span>
<span class="n">PrivateKey</span> <span class="o">=</span> <span class="n">EJf6Yy51M</span><span class="o">/</span><span class="n">YDaZgedRpuxMmrqul35WfjmHvRZR1rQ0U</span><span class="o">=</span>
<span class="p">[</span><span class="n">Peer</span><span class="p">]</span>
<span class="n">PublicKey</span> <span class="o">=</span> <span class="n">m</span><span class="o">/</span><span class="n">RPuMVsbpgQ</span><span class="o">+</span><span class="n">RkxlgK2mG</span><span class="o">+</span><span class="n">dDFlzqn</span><span class="o">+</span><span class="n">ua2zJt8Wn7GA</span><span class="o">=</span>
<span class="n">AllowedIPs</span> <span class="o">=</span> <span class="mf">10.7.11.0</span><span class="o">/</span><span class="mi">24</span>
<span class="n">Endpoint</span> <span class="o">=</span> <span class="mf">3.236.60.247</span><span class="p">:</span><span class="mi">51822</span>
<span class="n">PersistentKeepalive</span> <span class="o">=</span> <span class="mi">20</span>
</pre></div>
</div>
<p>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.</p>
</article> </article>

View File

@@ -331,7 +331,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="quick-start.html#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <a href="quick-start.html#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
@@ -453,6 +453,20 @@
<a href="external-clients.html#introduction" class="md-nav__link">Introduction</a> <a href="external-clients.html#introduction" class="md-nav__link">Introduction</a>
</li>
<li class="md-nav__item">
<a href="external-clients.html#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
</li>
<li class="md-nav__item">
<a href="external-clients.html#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
</li></ul> </li></ul>
</li> </li>

View File

@@ -332,7 +332,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="quick-start.html#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <a href="quick-start.html#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
@@ -454,6 +454,20 @@
<a href="external-clients.html#introduction" class="md-nav__link">Introduction</a> <a href="external-clients.html#introduction" class="md-nav__link">Introduction</a>
</li>
<li class="md-nav__item">
<a href="external-clients.html#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
</li>
<li class="md-nav__item">
<a href="external-clients.html#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
</li></ul> </li></ul>
</li> </li>
@@ -797,7 +811,7 @@
<li class="toctree-l2"><a class="reference internal" href="quick-start.html#deploy-nodes">Deploy Nodes</a></li> <li class="toctree-l2"><a class="reference internal" href="quick-start.html#deploy-nodes">Deploy Nodes</a></li>
<li class="toctree-l2"><a class="reference internal" href="quick-start.html#manage-nodes">Manage Nodes</a></li> <li class="toctree-l2"><a class="reference internal" href="quick-start.html#manage-nodes">Manage Nodes</a></li>
<li class="toctree-l2"><a class="reference internal" href="quick-start.html#uninstalling-the-netclient">Uninstalling the netclient</a></li> <li class="toctree-l2"><a class="reference internal" href="quick-start.html#uninstalling-the-netclient">Uninstalling the netclient</a></li>
<li class="toctree-l2"><a class="reference internal" href="quick-start.html#uninstralling-netmaker">Uninstralling Netmaker</a></li> <li class="toctree-l2"><a class="reference internal" href="quick-start.html#uninstalling-netmaker">Uninstalling Netmaker</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
@@ -845,6 +859,8 @@
<ul> <ul>
<li class="toctree-l1"><a class="reference internal" href="external-clients.html">External Clients</a><ul> <li class="toctree-l1"><a class="reference internal" href="external-clients.html">External Clients</a><ul>
<li class="toctree-l2"><a class="reference internal" href="external-clients.html#introduction">Introduction</a></li> <li class="toctree-l2"><a class="reference internal" href="external-clients.html#introduction">Introduction</a></li>
<li class="toctree-l2"><a class="reference internal" href="external-clients.html#configuring-an-ingress-gateway">Configuring an Ingress Gateway</a></li>
<li class="toctree-l2"><a class="reference internal" href="external-clients.html#adding-clients-to-a-gateway">Adding Clients to a Gateway</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

View File

@@ -308,7 +308,7 @@
</li> </li>
<li class="md-nav__item"><a href="#uninstalling-the-netclient" class="md-nav__link">Uninstalling the netclient</a> <li class="md-nav__item"><a href="#uninstalling-the-netclient" class="md-nav__link">Uninstalling the netclient</a>
</li> </li>
<li class="md-nav__item"><a href="#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <li class="md-nav__item"><a href="#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
</nav> </nav>
</li> </li>
@@ -367,7 +367,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <a href="#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
@@ -764,7 +764,7 @@
</li> </li>
<li class="md-nav__item"><a href="#uninstalling-the-netclient" class="md-nav__link">Uninstalling the netclient</a> <li class="md-nav__item"><a href="#uninstalling-the-netclient" class="md-nav__link">Uninstalling the netclient</a>
</li> </li>
<li class="md-nav__item"><a href="#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <li class="md-nav__item"><a href="#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
</nav> </nav>
</li> </li>
@@ -822,14 +822,15 @@
<li><p>Click ADD NEW ACCESS KEY</p></li> <li><p>Click ADD NEW ACCESS KEY</p></li>
<li><p>Give it a name (ex: “mykey”) and a number of uses (ex: 25)</p></li> <li><p>Give it a name (ex: “mykey”) and a number of uses (ex: 25)</p></li>
<li><p>Click CREATE KEY (<strong>Important:</strong> Do not click out of the following screen until you have saved your key details. It will appear only once.)</p></li> <li><p>Click CREATE KEY (<strong>Important:</strong> Do not click out of the following screen until you have saved your key details. It will appear only once.)</p></li>
<li><p>Copy the bottom command under “Your agent install command with access token” and save it somewhere locally. E.x: <code class="docutils literal notranslate"><span class="pre">curl</span> <span class="pre">-sfL</span> <span class="pre">https://raw.githubusercontent.com/gravitl/netmaker/v0.3/scripts/netclient-install.sh</span> <span class="pre">|</span> <span class="pre">KEY=vm3ow4thatogiwnsla3thsl3894ths</span> <span class="pre">sh</span> <span class="pre">-</span></code></p></li> <li><p>Copy the bottom command under “Your agent install command with access token” and save it somewhere locally. E.x: <code class="docutils literal notranslate"><span class="pre">curl</span> <span class="pre">-sfL</span> <span class="pre">https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.sh</span> <span class="pre">|</span> <span class="pre">KEY=vm3ow4thatogiwnsla3thsl3894ths</span> <span class="pre">sh</span> <span class="pre">-</span></code>. <strong>A change is required here. Change netclient-install.sh in this command to netclient-install.slim.sh, EX:</strong></p></li>
</ol> </ol>
<p><code class="docutils literal notranslate"><span class="pre">curl</span> <span class="pre">-sfL</span> <span class="pre">https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.slim.sh</span> <span class="pre">|</span> <span class="pre">KEY=vm3ow4thatogiwnsla3thsl3894ths</span> <span class="pre">sh</span> <span class="pre">-</span></code></p>
<a class="reference internal image-reference" href="_images/access-key.png"><img alt="Access Key Screen" class="align-center" src="_images/access-key.png" style="width: 80%;"/></a> <a class="reference internal image-reference" href="_images/access-key.png"><img alt="Access Key Screen" class="align-center" src="_images/access-key.png" style="width: 80%;"/></a>
<p>You will use this command to install the netclient on your nodes. There are three different values for three different scenarios:</p> <p>You will use this command to install the netclient on your nodes. There are three different values for three different scenarios:</p>
<ul class="simple"> <ul class="simple">
<li><p>The <strong>Access Key</strong> 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. <code class="docutils literal notranslate"><span class="pre">netclient</span> <span class="pre">-c</span> <span class="pre">install</span> <span class="pre">-k</span> <span class="pre">&lt;access</span> <span class="pre">key&gt;</span> <span class="pre">-s</span> <span class="pre">1.2.3.4</span> <span class="pre">-p</span> <span class="pre">50052</span></code></p></li> <li><p>The <strong>Access Key</strong> 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. <code class="docutils literal notranslate"><span class="pre">netclient</span> <span class="pre">-c</span> <span class="pre">install</span> <span class="pre">-k</span> <span class="pre">&lt;access</span> <span class="pre">key&gt;</span> <span class="pre">-s</span> <span class="pre">1.2.3.4</span> <span class="pre">-p</span> <span class="pre">50052</span></code></p></li>
<li><p>The <strong>Access Token</strong> 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: <code class="docutils literal notranslate"><span class="pre">netclient</span> <span class="pre">-c</span> <span class="pre">install</span> <span class="pre">-t</span> <span class="pre">&lt;access</span> <span class="pre">token&gt;</span></code>. 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 <strong>mynet</strong> network and now you are adding it to <strong>default</strong>.</p></li> <li><p>The <strong>Access Token</strong> 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: <code class="docutils literal notranslate"><span class="pre">netclient</span> <span class="pre">-c</span> <span class="pre">install</span> <span class="pre">-t</span> <span class="pre">&lt;access</span> <span class="pre">token&gt;</span></code>. 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 <strong>mynet</strong> network and now you are adding it to <strong>default</strong>.</p></li>
<li><p>The <strong>install command</strong> 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.</p></li> <li><p>The <strong>install command</strong> 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: <strong>netclient register -k keyvalue</strong>. This command will not work without secure GRPC enabled and will return a 500 error.</p></li>
</ul> </ul>
<p>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.</p> <p>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.</p>
@@ -848,7 +849,7 @@
</ul> </ul>
</div></blockquote> </div></blockquote>
<ol class="arabic simple" start="4"> <ol class="arabic simple" start="4">
<li><p>Run the install command, Ex: <code class="docutils literal notranslate"><span class="pre">curl</span> <span class="pre">-sfL</span> <span class="pre">https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.sh</span> <span class="pre">|</span> <span class="pre">KEY=vm3ow4thatogiwnsla3thsl3894ths</span> <span class="pre">sh</span> <span class="pre">-</span></code></p></li> <li><p>Run the install command, Ex: <code class="docutils literal notranslate"><span class="pre">curl</span> <span class="pre">-sfL</span> <span class="pre">https://raw.githubusercontent.com/gravitl/netmaker/v0.5/scripts/netclient-install.slim.sh</span> <span class="pre">|</span> <span class="pre">KEY=vm3ow4thatogiwnsla3thsl3894ths</span> <span class="pre">sh</span> <span class="pre">-</span></code></p></li>
</ol> </ol>
<p>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 <a class="reference internal" href="client-installation.html"><span class="doc">client installation</span></a> documentation. If this process failed and you do not see your node in the console (see below), then reference the <a class="reference internal" href="troubleshoot.html"><span class="doc">troubleshooting</span></a> documentation.</p> <p>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 <a class="reference internal" href="client-installation.html"><span class="doc">client installation</span></a> documentation. If this process failed and you do not see your node in the console (see below), then reference the <a class="reference internal" href="troubleshoot.html"><span class="doc">troubleshooting</span></a> documentation.</p>
<a class="reference internal image-reference" href="_images/nc-install-output.png"><img alt="Output from Netclient Install" class="align-center" src="_images/nc-install-output.png" style="width: 80%;"/></a> <a class="reference internal image-reference" href="_images/nc-install-output.png"><img alt="Output from Netclient Install" class="align-center" src="_images/nc-install-output.png" style="width: 80%;"/></a>
@@ -873,7 +874,7 @@
</ol> </ol>
<h2 id="uninstralling-netmaker">Uninstralling Netmaker<a class="headerlink" href="#uninstralling-netmaker" title="Permalink to this headline"></a></h2> <h2 id="uninstalling-netmaker">Uninstalling Netmaker<a class="headerlink" href="#uninstalling-netmaker" title="Permalink to this headline"></a></h2>
<p>To uninstall Netmaker from the server, simply run <code class="docutils literal notranslate"><span class="pre">docker-compose</span> <span class="pre">down</span></code> or <code class="docutils literal notranslate"><span class="pre">docker-compose</span> <span class="pre">down</span> <span class="pre">--volumes</span></code> to remove the docker volumes for a future installation.</p> <p>To uninstall Netmaker from the server, simply run <code class="docutils literal notranslate"><span class="pre">docker-compose</span> <span class="pre">down</span></code> or <code class="docutils literal notranslate"><span class="pre">docker-compose</span> <span class="pre">down</span> <span class="pre">--volumes</span></code> to remove the docker volumes for a future installation.</p>

View File

@@ -337,7 +337,7 @@
<li class="md-nav__item"> <li class="md-nav__item">
<a href="quick-start.html#uninstralling-netmaker" class="md-nav__link">Uninstralling Netmaker</a> <a href="quick-start.html#uninstalling-netmaker" class="md-nav__link">Uninstalling Netmaker</a>
</li></ul> </li></ul>
@@ -459,6 +459,20 @@
<a href="external-clients.html#introduction" class="md-nav__link">Introduction</a> <a href="external-clients.html#introduction" class="md-nav__link">Introduction</a>
</li>
<li class="md-nav__item">
<a href="external-clients.html#configuring-an-ingress-gateway" class="md-nav__link">Configuring an Ingress Gateway</a>
</li>
<li class="md-nav__item">
<a href="external-clients.html#adding-clients-to-a-gateway" class="md-nav__link">Adding Clients to a Gateway</a>
</li></ul> </li></ul>
</li> </li>

File diff suppressed because one or more lines are too long

View File

@@ -1009,7 +1009,7 @@
<span class="nt">container_name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">netmaker</span> <span class="nt">container_name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">netmaker</span>
<span class="nt">depends_on</span><span class="p">:</span> <span class="nt">depends_on</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">mongodb</span> <span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">mongodb</span>
<span class="nt">image</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">gravitl/netmaker:v0.3</span> <span class="nt">image</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">gravitl/netmaker:v0.5</span>
<span class="nt">volumes</span><span class="p">:</span> <span class="c1"># 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)</span> <span class="nt">volumes</span><span class="p">:</span> <span class="c1"># 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)</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">./:/local</span> <span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">./:/local</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">/etc/netclient:/etc/netclient</span> <span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">/etc/netclient:/etc/netclient</span>
@@ -1053,7 +1053,7 @@
<span class="nt">container_name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">netmaker-ui</span> <span class="nt">container_name</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">netmaker-ui</span>
<span class="nt">depends_on</span><span class="p">:</span> <span class="nt">depends_on</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">netmaker</span> <span class="p p-Indicator">-</span> <span class="l l-Scalar l-Scalar-Plain">netmaker</span>
<span class="nt">image</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">gravitl/netmaker-ui:v0.3</span> <span class="nt">image</span><span class="p">:</span> <span class="l l-Scalar l-Scalar-Plain">gravitl/netmaker-ui:v0.5</span>
<span class="nt">links</span><span class="p">:</span> <span class="nt">links</span><span class="p">:</span>
<span class="p p-Indicator">-</span> <span class="s">"netmaker:api"</span> <span class="p p-Indicator">-</span> <span class="s">"netmaker:api"</span>
<span class="nt">ports</span><span class="p">:</span> <span class="nt">ports</span><span class="p">:</span>

View File

@@ -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. 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 Components
=========== ===========
@@ -88,15 +87,17 @@ The Netmaker server interacts with (as of v0.3) a MongoDB instance, which holds
Netclient Netclient
---------------- ----------------
The netclient is, at its core, a golang binary. Source code can be found in the netclient folder of the Netmaker `GitHub Repository <https://github.com/gravitl/netmaker/tree/master/netclient>`_. 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 <https://github.com/gravitl/netmaker/tree/master/netclient>`_. 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. 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 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 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. 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 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 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). 4. Admin runs the netclient install script on any given node (machine).
5. Netclient decodes key, which contains the GRPC server location and port 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 6. Netclient uses information to register and set up WireGuard tunnel to GRPC server
7. Netclient reaches out to GRPC server with this information, authenticating via access key. 7. Netclient retrieves/sets local information, including open ports for WireGuard, public IP, and generating key pairs for peers
8. Netmaker server verifies information and creates the node, setting default values for any missing information. 8. Netclient reaches out to GRPC server with this information, authenticating via access key.
9. Timestamp is set for the network (see #16). 9. Netmaker server verifies information and creates the node, setting default values for any missing information.
10. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network. 10. Timestamp is set for the network (see #16).
11. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard 11. Netmaker returns settings as response to netclient. Some settings may be added or modified based on the network.
12. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the network). 12. Netclient recieves response. If successful, it takes any additional info returned from Netmaker and configures the local system/WireGuard
13. Netmaker sends back peers list, including current known configurations of all nodes in network. 13. Netclient sends another request to Netmaker's GRPC server, this time to retrieve the peers list (all other clients in the 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. 14. Netmaker sends back peers list, including current known configurations of all nodes in network.
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. 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. 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. 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 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: To manage a node automatically, the Netmaker client (netclient) requires **systemd-based linux.** Compatible systems include:
- Fedora - Fedora
- Ubuntu - 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". - **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". - **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).

View File

@@ -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

View File

@@ -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. 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. 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.

BIN
docs/images/exclient1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

BIN
docs/images/exclient2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

BIN
docs/images/exclient3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

BIN
docs/images/exclient4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

View File

@@ -52,7 +52,10 @@ Create Key
#. Click ADD NEW ACCESS KEY #. Click ADD NEW ACCESS KEY
#. Give it a name (ex: "mykey") and a number of uses (ex: 25) #. 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.) #. 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 .. image:: images/access-key.png
:width: 80% :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 <access key> -s 1.2.3.4 -p 50052`` * 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 **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**.
<<<<<<< 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. 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 Deploy Nodes
@@ -76,7 +84,7 @@ Deploy Nodes
* ``which wg`` (should show wg binary present) * ``which wg`` (should show wg binary present)
* ``pidof systemd && echo "systemd found" || echo "systemd not found"`` * ``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. 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`` 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) 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. 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.

View File

@@ -7,6 +7,7 @@ package functions
import ( import (
"context" "context"
"encoding/base64" "encoding/base64"
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"log" "log"
@@ -14,6 +15,7 @@ import (
"net" "net"
"strings" "strings"
"time" "time"
"github.com/gravitl/netmaker/models" "github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mongoconn" "github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/servercfg" "github.com/gravitl/netmaker/servercfg"
@@ -23,17 +25,24 @@ import (
"go.mongodb.org/mongo-driver/mongo/options" "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 //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 //node has that value for the same field within the network
func SliceContains(slice []string, item string) bool { func SliceContains(slice []string, item string) bool {
set := make(map[string]struct{}, len(slice)) set := make(map[string]struct{}, len(slice))
for _, s := range slice { for _, s := range slice {
set[s] = struct{}{} set[s] = struct{}{}
} }
_, ok := set[item] _, ok := set[item]
return ok return ok
} }
func CreateServerToken(netID string) (string, error) { func CreateServerToken(netID string) (string, error) {
@@ -45,18 +54,26 @@ func CreateServerToken(netID string) (string, error) {
return "", err 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.Name = GenKeyName()
accesskey.Value = GenKey() accesskey.Value = GenKey()
accesskey.Uses = 1 accesskey.Uses = 1
address := "127.0.0.1:" + servercfg.GetGRPCPort()
privAddr := "" tokenjson, err := json.Marshal(accessToken)
if *network.IsLocal { if err != nil {
privAddr = network.LocalRange 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) network.AccessKeys = append(network.AccessKeys, accesskey)
@@ -86,85 +103,85 @@ func CreateServerToken(netID string) (string, error) {
func GetPeersList(networkName string) ([]models.PeersResponse, error) { func GetPeersList(networkName string) ([]models.PeersResponse, error) {
var peers []models.PeersResponse var peers []models.PeersResponse
//Connection mongoDB with mongoconn class //Connection mongoDB with mongoconn class
collection := mongoconn.Client.Database("netmaker").Collection("nodes") 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 //Get all nodes in the relevant network which are NOT in pending state
filter := bson.M{"network": networkName, "ispending": false} filter := bson.M{"network": networkName, "ispending": false}
cur, err := collection.Find(ctx, filter) cur, err := collection.Find(ctx, filter)
if err != nil { if err != nil {
return peers, err return peers, err
} }
// Close the cursor once finished and cancel if it takes too long // Close the cursor once finished and cancel if it takes too long
defer cancel() defer cancel()
for cur.Next(context.TODO()) { for cur.Next(context.TODO()) {
var peer models.PeersResponse var peer models.PeersResponse
err := cur.Decode(&peer) err := cur.Decode(&peer)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// add the node to our node array // add the node to our node array
//maybe better to just return this? But then that's just GetNodes... //maybe better to just return this? But then that's just GetNodes...
peers = append(peers, peer) peers = append(peers, peer)
} }
//Uh oh, fatal error! This needs some better error handling //Uh oh, fatal error! This needs some better error handling
//TODO: needs appropriate error handling so the server doesnt shut down. //TODO: needs appropriate error handling so the server doesnt shut down.
if err := cur.Err(); err != nil { if err := cur.Err(); err != nil {
log.Fatal(err) log.Fatal(err)
} }
return peers, err return peers, err
} }
func GetIntPeersList() ([]models.PeersResponse, error) { 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 { if err != nil {
return peers, err return peers, err
} }
// Close the cursor once finished and cancel if it takes too long // Close the cursor once finished and cancel if it takes too long
defer cancel() defer cancel()
for cur.Next(context.TODO()) { for cur.Next(context.TODO()) {
var peer models.PeersResponse var peer models.PeersResponse
err := cur.Decode(&peer) err := cur.Decode(&peer)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
// add the node to our node array // add the node to our node array
//maybe better to just return this? But then that's just GetNodes... //maybe better to just return this? But then that's just GetNodes...
peers = append(peers, peer) peers = append(peers, peer)
} }
//Uh oh, fatal error! This needs some better error handling //Uh oh, fatal error! This needs some better error handling
//TODO: needs appropriate error handling so the server doesnt shut down. //TODO: needs appropriate error handling so the server doesnt shut down.
if err := cur.Err(); err != nil { if err := cur.Err(); err != nil {
log.Fatal(err) log.Fatal(err)
} }
return peers, err return peers, err
} }
func IsFieldUnique(network string, field string, value string) bool { 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) { 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 var result bson.M
err := collection.FindOne(ctx, filter).Decode(&result) err := collection.FindOne(ctx, filter).Decode(&result)
defer cancel() defer cancel()
if err != nil { if err != nil {
if err == mongo.ErrNoDocuments { if err == mongo.ErrNoDocuments {
return false, nil return false, nil
} }
} }
return true, err return true, err
} }
func NetworkExists(name string) (bool, error) { func NetworkExists(name string) (bool, error) {
@@ -453,27 +470,29 @@ func IsKeyValid(networkname string, keyvalue string) bool {
func IsKeyValidGlobal(keyvalue string) bool { func IsKeyValidGlobal(keyvalue string) bool {
networks, _ := ListNetworks() networks, _ := ListNetworks()
var key models.AccessKey var key models.AccessKey
foundkey := false foundkey := false
isvalid := false isvalid := false
for _, network := range networks { for _, network := range networks {
for i := len(network.AccessKeys) - 1; i >= 0; i-- { for i := len(network.AccessKeys) - 1; i >= 0; i-- {
currentkey := network.AccessKeys[i] currentkey := network.AccessKeys[i]
if currentkey.Value == keyvalue { if currentkey.Value == keyvalue {
key = currentkey key = currentkey
foundkey = true foundkey = true
break break
} }
} }
if foundkey { break } if foundkey {
break
}
} }
if foundkey { if foundkey {
if key.Uses > 0 { if key.Uses > 0 {
isvalid = true isvalid = true
} }
} }
return isvalid return isvalid
} }
//TODO: Contains a fatal error return. Need to change //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 { func DeleteAllIntClients() 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 out them ID's again // Filter out them ID's again
err := collection.Drop(ctx) err := collection.Drop(ctx)
if err != nil { if err != nil {
return err return err
} }
defer cancel() defer cancel()
return nil return nil
} }
func GetAllIntClients() ([]models.IntClient, error) { func GetAllIntClients() ([]models.IntClient, error) {
var client models.IntClient var client models.IntClient
var clients []models.IntClient var clients []models.IntClient
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 out them ID's again // Filter out them ID's again
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 { if err != nil {
return []models.IntClient{}, err return []models.IntClient{}, err
} }
defer cancel() defer cancel()
for cur.Next(context.TODO()) { for cur.Next(context.TODO()) {
err := cur.Decode(&client) err := cur.Decode(&client)
if err != nil { if err != nil {
return []models.IntClient{}, err return []models.IntClient{}, err
} }
// add node to our array // add node to our array
clients = append(clients, client) clients = append(clients, client)
} }
//TODO: Fatal error //TODO: Fatal error
if err := cur.Err(); err != nil { if err := cur.Err(); err != nil {
return []models.IntClient{}, err return []models.IntClient{}, err
} }
return clients, nil return clients, nil
} }
func GetAllExtClients() ([]models.ExtClient, error) { func GetAllExtClients() ([]models.ExtClient, error) {
var extclient models.ExtClient var extclient models.ExtClient
var extclients []models.ExtClient var extclients []models.ExtClient
collection := mongoconn.Client.Database("netmaker").Collection("extclients") collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// Filter out them ID's again // Filter out them ID's again
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 { if err != nil {
return []models.ExtClient{}, err return []models.ExtClient{}, err
} }
defer cancel() defer cancel()
for cur.Next(context.TODO()) { for cur.Next(context.TODO()) {
err := cur.Decode(&extclient) err := cur.Decode(&extclient)
if err != nil { if err != nil {
return []models.ExtClient{}, err return []models.ExtClient{}, err
} }
// add node to our array // add node to our array
extclients = append(extclients, extclient) extclients = append(extclients, extclient)
} }
//TODO: Fatal error //TODO: Fatal error
if err := cur.Err(); err != nil { if err := cur.Err(); err != nil {
return []models.ExtClient{}, err return []models.ExtClient{}, err
} }
return extclients, nil return extclients, nil
} }
//This returns a unique address for a node to use //This returns a unique address for a node to use
//it iterates through the list of IP's in the subnet //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. //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 offset = false
continue continue
} }
if networkName == "comms" { if networkName == "comms" {
if IsIPUniqueClients(networkName, ip.String()) { if IsIPUniqueClients(networkName, ip.String()) {
return ip.String(), err return ip.String(), err
} }
} else { } else {
if IsIPUnique(networkName, ip.String()) && IsIPUniqueExtClients(networkName, ip.String()) { if IsIPUnique(networkName, ip.String()) && IsIPUniqueExtClients(networkName, ip.String()) {
return ip.String(), err return ip.String(), err
} }
} }
} }
//TODO //TODO
@@ -747,9 +765,9 @@ func UniqueAddress6(networkName string) (string, error) {
continue continue
} }
if networkName == "comms" { if networkName == "comms" {
if IsIP6UniqueClients(networkName, ip.String()) { if IsIP6UniqueClients(networkName, ip.String()) {
return ip.String(), err return ip.String(), err
} }
} else { } else {
if IsIP6Unique(networkName, ip.String()) { if IsIP6Unique(networkName, ip.String()) {
return ip.String(), err return ip.String(), err
@@ -798,30 +816,29 @@ func GenKeyName() string {
func IsIPUniqueExtClients(network string, ip string) bool { 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") collection := mongoconn.Client.Database("netmaker").Collection("extclients")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 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 { if err != nil {
return isunique return isunique
} }
if extclient.Address == ip { if extclient.Address == ip {
isunique = false isunique = false
} }
return isunique return isunique
} }
//checks if IP is unique in the address range //checks if IP is unique in the address range
//used by UniqueAddress //used by UniqueAddress
func IsIPUnique(network string, ip string) bool { func IsIPUnique(network string, ip string) bool {
@@ -880,54 +897,54 @@ func IsIP6Unique(network string, ip string) bool {
//used by UniqueAddress //used by UniqueAddress
func IsIP6UniqueClients(network string, ip string) bool { 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") 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{"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 { if err != nil {
return isunique return isunique
} }
if client.Address6 == ip { if client.Address6 == ip {
isunique = false isunique = false
} }
return isunique return isunique
} }
//checks if IP is unique in the address range //checks if IP is unique in the address range
//used by UniqueAddress //used by UniqueAddress
func IsIPUniqueClients(network string, ip string) bool { 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") 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{"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 { if err != nil {
return isunique return isunique
} }
if client.Address == ip { if client.Address == ip {
isunique = false isunique = false
} }
return isunique return isunique
} }
//called once key has been used by createNode //called once key has been used by createNode
@@ -1040,4 +1057,3 @@ func GetAllNodes() ([]models.Node, error) {
} }
return nodes, nil return nodes, nil
} }

8
go.mod
View File

@@ -6,16 +6,16 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgrijalva/jwt-go v3.2.0+incompatible github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-playground/validator/v10 v10.5.0 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/handlers v1.5.1
github.com/gorilla/mux v1.8.0 github.com/gorilla/mux v1.8.0
github.com/jinzhu/copier v0.3.2 // indirect 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/stretchr/testify v1.6.1
github.com/txn2/txeh v1.3.0 github.com/txn2/txeh v1.3.0
github.com/urfave/cli v1.22.5 // indirect github.com/urfave/cli v1.22.5 // indirect
github.com/urfave/cli/v2 v2.3.0 // indirect github.com/urfave/cli/v2 v2.3.0
github.com/vishvananda/netlink v1.1.0 // indirect github.com/vishvananda/netlink v1.1.0
go.mongodb.org/mongo-driver v1.4.3 go.mongodb.org/mongo-driver v1.4.3
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9
golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect golang.org/x/net v0.0.0-20210119194325-5f4716e94777 // indirect

View File

@@ -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

View File

@@ -87,7 +87,12 @@ func main() {
waitnetwork.Add(1) waitnetwork.Add(1)
go runGRPC(&waitnetwork, installserver) go runGRPC(&waitnetwork, installserver)
} }
if servercfg.IsDNSMode() {
err := controller.SetDNS()
if err != nil {
log.Fatal(err)
}
}
//Run Rest Server //Run Rest Server
if servercfg.IsRestBackend() { if servercfg.IsRestBackend() {
if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" { if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" {

View File

@@ -13,6 +13,7 @@ type ClientConfig struct {
} }
type ServerConfig struct { type ServerConfig struct {
CoreDNSAddr string `json:"corednsaddr"`
APIConnString string `json:"apiconn"` APIConnString string `json:"apiconn"`
APIHost string `json:"apihost"` APIHost string `json:"apihost"`
APIPort string `json:"apiport"` APIPort string `json:"apiport"`

View File

@@ -8,7 +8,7 @@ type AuthParams struct {
} }
type User 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"` Password string `json:"password" bson:"password" validate:"required,min=5"`
Networks []string `json:"networks" bson:"networks"` Networks []string `json:"networks" bson:"networks"`
IsAdmin bool `json:"isadmin" bson:"isadmin"` IsAdmin bool `json:"isadmin" bson:"isadmin"`

9
netclient/Dockerfile Normal file
View File

@@ -0,0 +1,9 @@
FROM debian:latest
RUN apt-get update && apt-get -y install systemd procps
WORKDIR /root/
COPY netclient .
CMD ["./netclient checkin"]

View File

@@ -58,7 +58,7 @@ func CheckIn(cfg config.ClientConfig) error {
log.Println("Required, '-n'. No network provided. Exiting.") log.Println("Required, '-n'. No network provided. Exiting.")
os.Exit(1) os.Exit(1)
} }
err := functions.CheckIn(cfg.Network) err := functions.CheckIn(cfg)
if err != nil { if err != nil {
log.Println("Error checking in: ", err) log.Println("Error checking in: ", err)
os.Exit(1) os.Exit(1)

View File

@@ -26,6 +26,7 @@ type ClientConfig struct {
OperatingSystem string `yaml:"operatingsystem"` OperatingSystem string `yaml:"operatingsystem"`
} }
type ServerConfig struct { type ServerConfig struct {
CoreDNSAddr string `yaml:"corednsaddr"`
GRPCAddress string `yaml:"grpcaddress"` GRPCAddress string `yaml:"grpcaddress"`
APIAddress string `yaml:"apiaddress"` APIAddress string `yaml:"apiaddress"`
AccessKey string `yaml:"accesskey"` AccessKey string `yaml:"accesskey"`
@@ -55,7 +56,6 @@ type NodeConfig struct {
IsLocal string `yaml:"islocal"` IsLocal string `yaml:"islocal"`
IsDualStack string `yaml:"isdualstack"` IsDualStack string `yaml:"isdualstack"`
IsIngressGateway string `yaml:"isingressgateway"` IsIngressGateway string `yaml:"isingressgateway"`
AllowedIPs []string `yaml:"allowedips"`
LocalRange string `yaml:"localrange"` LocalRange string `yaml:"localrange"`
PostUp string `yaml:"postup"` PostUp string `yaml:"postup"`
PostDown string `yaml:"postdown"` PostDown string `yaml:"postdown"`
@@ -85,9 +85,6 @@ func Write(config *ClientConfig, network string) error{
} }
home := "/etc/netclient" home := "/etc/netclient"
if err != nil {
log.Fatal(err)
}
file := fmt.Sprintf(home + "/netconfig-" + network) file := fmt.Sprintf(home + "/netconfig-" + network)
f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm) f, err := os.OpenFile(file, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.ModePerm)
defer f.Close() defer f.Close()
@@ -408,6 +405,7 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, error){
cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange cfg.Node.LocalRange = accesstoken.ClientConfig.LocalRange
cfg.Server.GRPCSSL = accesstoken.ServerConfig.GRPCSSL cfg.Server.GRPCSSL = accesstoken.ServerConfig.GRPCSSL
cfg.Server.GRPCWireGuard = accesstoken.WG.GRPCWireGuard cfg.Server.GRPCWireGuard = accesstoken.WG.GRPCWireGuard
cfg.Server.CoreDNSAddr = accesstoken.ServerConfig.CoreDNSAddr
if c.String("grpcserver") != "" { if c.String("grpcserver") != "" {
cfg.Server.GRPCAddress = c.String("grpcserver") cfg.Server.GRPCAddress = c.String("grpcserver")
} }
@@ -427,6 +425,9 @@ func GetCLIConfig(c *cli.Context) (ClientConfig, error){
if c.String("grpcssl") != "" { if c.String("grpcssl") != "" {
cfg.Server.GRPCSSL = c.String("grpcssl") cfg.Server.GRPCSSL = c.String("grpcssl")
} }
if c.String("corednsaddr") != "" {
cfg.Server.CoreDNSAddr = c.String("corednsaddr")
}
if c.String("grpcwg") != "" { if c.String("grpcwg") != "" {
cfg.Server.GRPCWireGuard = 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.Node.LocalRange = c.String("localrange")
cfg.Server.GRPCWireGuard = c.String("grpcwg") cfg.Server.GRPCWireGuard = c.String("grpcwg")
cfg.Server.GRPCSSL = c.String("grpcssl") cfg.Server.GRPCSSL = c.String("grpcssl")
cfg.Server.CoreDNSAddr = c.String("corednsaddr")
} }
cfg.Node.Name = c.String("name") cfg.Node.Name = c.String("name")
cfg.Node.Interface = c.String("interface") cfg.Node.Interface = c.String("interface")

View File

@@ -10,6 +10,7 @@ import (
"net" "net"
"os/exec" "os/exec"
"github.com/gravitl/netmaker/netclient/config" "github.com/gravitl/netmaker/netclient/config"
"github.com/gravitl/netmaker/netclient/local"
"github.com/gravitl/netmaker/netclient/wireguard" "github.com/gravitl/netmaker/netclient/wireguard"
"github.com/gravitl/netmaker/netclient/server" "github.com/gravitl/netmaker/netclient/server"
"github.com/gravitl/netmaker/netclient/auth" "github.com/gravitl/netmaker/netclient/auth"
@@ -19,7 +20,8 @@ import (
//homedir "github.com/mitchellh/go-homedir" //homedir "github.com/mitchellh/go-homedir"
) )
func CheckIn(network string) error { func CheckIn(cliconf config.ClientConfig) error {
network := cliconf.Network
node := server.GetNode(network) node := server.GetNode(network)
cfg, err := config.ReadConfig(network) cfg, err := config.ReadConfig(network)
if err != nil { if err != nil {
@@ -32,6 +34,14 @@ func CheckIn(network string) error {
setupcheck := true setupcheck := true
ipchange := false 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") { if !(nodecfg.IPForwarding == "off") {
out, err := exec.Command("sysctl", "net.ipv4.ip_forward").Output() out, err := exec.Command("sysctl", "net.ipv4.ip_forward").Output()
if err != nil { if err != nil {
@@ -125,10 +135,13 @@ func CheckIn(network string) error {
var wcclient nodepb.NodeServiceClient var wcclient nodepb.NodeServiceClient
var requestOpts grpc.DialOption var requestOpts grpc.DialOption
requestOpts = grpc.WithInsecure() requestOpts = grpc.WithInsecure()
if cfg.Server.GRPCSSL == "on" { if servercfg.GRPCSSL == "on" {
log.Println("using SSL")
h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}}) h2creds := credentials.NewTLS(&tls.Config{NextProtos: []string{"h2"}})
requestOpts = grpc.WithTransportCredentials(h2creds) requestOpts = grpc.WithTransportCredentials(h2creds)
} } else {
log.Println("using insecure GRPC connection")
}
conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts) conn, err := grpc.Dial(servercfg.GRPCAddress, requestOpts)
if err != nil { if err != nil {
fmt.Printf("Cant dial GRPC server: %v", err) fmt.Printf("Cant dial GRPC server: %v", err)

View File

@@ -183,6 +183,7 @@ func JoinNetwork(cfg config.ClientConfig) error {
if err != nil { if err != nil {
return err return err
} }
log.Println("node created on remote server...updating configs")
node := res.Node node := res.Node
if err != nil { if err != nil {
return err return err
@@ -211,16 +212,18 @@ func JoinNetwork(cfg config.ClientConfig) error {
return err return err
} }
} }
log.Println("retrieving remote peers")
peers, hasGateway, gateways, err := server.GetPeers(node.Macaddress, cfg.Network, cfg.Server.GRPCAddress, node.Isdualstack, node.Isingressgateway) peers, hasGateway, gateways, err := server.GetPeers(node.Macaddress, cfg.Network, cfg.Server.GRPCAddress, node.Isdualstack, node.Isingressgateway)
if err != nil { if err != nil {
log.Println("failed to retrieve peers")
return err return err
} }
err = wireguard.StorePrivKey(cfg.Node.PrivateKey, cfg.Network) err = wireguard.StorePrivKey(cfg.Node.PrivateKey, cfg.Network)
if err != nil { if err != nil {
return err return err
} }
log.Println("starting wireguard")
err = wireguard.InitWireguard(node, cfg.Node.PrivateKey, peers, hasGateway, gateways) err = wireguard.InitWireguard(node, cfg.Node.PrivateKey, peers, hasGateway, gateways)
if err != nil { if err != nil {
return err return err

View File

@@ -143,7 +143,12 @@ func GetPeers(macaddress string, network string, server string, dualstack bool,
requestOpts := grpc.WithInsecure() 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 { if err != nil {
log.Fatalf("Unable to establish client connection to localhost:50051: %v", err) 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 := context.Background()
ctx, err = auth.SetJWT(wcclient, network) ctx, err = auth.SetJWT(wcclient, network)
if err != nil { if err != nil {
fmt.Println("Failed to authenticate.") log.Println("Failed to authenticate.")
return peers, hasGateway, gateways, err return peers, hasGateway, gateways, err
} }
var header metadata.MD var header metadata.MD
stream, err := wcclient.GetPeers(ctx, req, grpc.Header(&header)) stream, err := wcclient.GetPeers(ctx, req, grpc.Header(&header))
if err != nil { if err != nil {
fmt.Println("Error retrieving peers") log.Println("Error retrieving peers")
fmt.Println(err) log.Println(err)
return nil, hasGateway, gateways, err return nil, hasGateway, gateways, err
} }
for { for {

View File

@@ -186,8 +186,7 @@ func InitWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
if node.Address == "" { if node.Address == "" {
log.Fatal("no address to configure") log.Fatal("no address to configure")
} }
nameserver := servercfg.GRPCAddress nameserver := servercfg.CoreDNSAddr
nameserver = strings.Split(nameserver, ":")[0]
network := node.Nodenetwork network := node.Nodenetwork
if nodecfg.Network != "" { if nodecfg.Network != "" {
network = nodecfg.Network network = nodecfg.Network

View File

@@ -1,12 +1,13 @@
package servercfg package servercfg
import ( import (
"github.com/gravitl/netmaker/config"
"net/http"
"io/ioutil"
"os"
"errors" "errors"
"io/ioutil"
"net/http"
"os"
"strconv" "strconv"
"github.com/gravitl/netmaker/config"
) )
func SetHost() error { func SetHost() error {
@@ -20,6 +21,7 @@ func SetHost() error {
func GetServerConfig() config.ServerConfig { func GetServerConfig() config.ServerConfig {
var cfg config.ServerConfig var cfg config.ServerConfig
cfg.APIConnString = GetAPIConnString() cfg.APIConnString = GetAPIConnString()
cfg.CoreDNSAddr = GetCoreDNSAddr()
cfg.APIHost = GetAPIHost() cfg.APIHost = GetAPIHost()
cfg.APIPort = GetAPIPort() cfg.APIPort = GetAPIPort()
cfg.GRPCConnString = GetGRPCConnString() cfg.GRPCConnString = GetGRPCConnString()
@@ -28,105 +30,116 @@ func GetServerConfig() config.ServerConfig {
cfg.MasterKey = "(hidden)" cfg.MasterKey = "(hidden)"
cfg.AllowedOrigin = GetAllowedOrigin() cfg.AllowedOrigin = GetAllowedOrigin()
cfg.RestBackend = "off" cfg.RestBackend = "off"
cfg.Verbosity = GetVerbose()
if IsRestBackend() { if IsRestBackend() {
cfg.RestBackend = "on" cfg.RestBackend = "on"
} }
cfg.AgentBackend = "off" cfg.AgentBackend = "off"
if IsAgentBackend() { if IsAgentBackend() {
cfg.AgentBackend = "on" cfg.AgentBackend = "on"
} }
cfg.ClientMode = "off" cfg.ClientMode = "off"
if IsClientMode() { if IsClientMode() {
cfg.ClientMode = "on" cfg.ClientMode = "on"
} }
cfg.DNSMode = "off" cfg.DNSMode = "off"
if IsDNSMode() { 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" cfg.DisableRemoteIPCheck = "off"
if DisableRemoteIPCheck() { if DisableRemoteIPCheck() {
cfg.DisableRemoteIPCheck = "on" cfg.DisableRemoteIPCheck = "on"
} }
cfg.DisableDefaultNet = "off" cfg.DisableDefaultNet = "off"
if DisableDefaultNet() { if DisableDefaultNet() {
cfg.DisableRemoteIPCheck = "on" cfg.DisableRemoteIPCheck = "on"
} }
return cfg return cfg
} }
func GetWGConfig() config.WG{ func GetWGConfig() config.WG {
var cfg config.WG var cfg config.WG
if IsGRPCWireGuard() { if IsGRPCWireGuard() {
cfg.GRPCWireGuard = "on" cfg.GRPCWireGuard = "on"
} else { } else {
cfg.GRPCWireGuard = "off" cfg.GRPCWireGuard = "off"
} }
cfg.GRPCWGInterface = GetGRPCWGInterface() cfg.GRPCWGInterface = GetGRPCWGInterface()
cfg.GRPCWGAddress = GetGRPCWGAddress() cfg.GRPCWGAddress = GetGRPCWGAddress()
cfg.GRPCWGAddressRange = GetGRPCWGAddressRange() cfg.GRPCWGAddressRange = GetGRPCWGAddressRange()
cfg.GRPCWGPort = GetGRPCWGPort() cfg.GRPCWGPort = GetGRPCWGPort()
cfg.GRPCWGPubKey = GetGRPCWGPubKey() cfg.GRPCWGPubKey = GetGRPCWGPubKey()
cfg.GRPCWGPrivKey = GetGRPCWGPrivKey() cfg.GRPCWGPrivKey = GetGRPCWGPrivKey()
return cfg return cfg
} }
func GetAPIConnString() string { func GetAPIConnString() string {
conn := "" conn := ""
if os.Getenv("SERVER_API_CONN_STRING") != "" { if os.Getenv("SERVER_API_CONN_STRING") != "" {
conn = os.Getenv("SERVER_API_CONN_STRING") conn = os.Getenv("SERVER_API_CONN_STRING")
} else if config.Config.Server.APIConnString != "" { } else if config.Config.Server.APIConnString != "" {
conn = config.Config.Server.APIConnString conn = config.Config.Server.APIConnString
} }
return conn return conn
} }
func GetAPIHost() string { func GetAPIHost() string {
serverhost := "127.0.0.1" serverhost := "127.0.0.1"
if os.Getenv("SERVER_HTTP_HOST") != "" { if os.Getenv("SERVER_HTTP_HOST") != "" {
serverhost = os.Getenv("SERVER_HTTP_HOST") serverhost = os.Getenv("SERVER_HTTP_HOST")
} else if config.Config.Server.APIHost != "" { } else if config.Config.Server.APIHost != "" {
serverhost = config.Config.Server.APIHost serverhost = config.Config.Server.APIHost
} else if os.Getenv("SERVER_HOST") != "" { } else if os.Getenv("SERVER_HOST") != "" {
serverhost = os.Getenv("SERVER_HOST") serverhost = os.Getenv("SERVER_HOST")
} else { } else {
remoteip, _ := GetPublicIP() remoteip, _ := GetPublicIP()
if remoteip != "" { if remoteip != "" {
serverhost = remoteip serverhost = remoteip
} }
} }
return serverhost return serverhost
} }
func GetAPIPort() string { func GetAPIPort() string {
apiport := "8081" apiport := "8081"
if os.Getenv("API_PORT") != "" { if os.Getenv("API_PORT") != "" {
apiport = 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 apiport = config.Config.Server.APIPort
} }
return apiport return apiport
} }
func GetDefaultNodeLimit() int32 { func GetDefaultNodeLimit() int32 {
var limit int32 var limit int32
limit = 999999999 limit = 999999999
envlimit, err := strconv.Atoi(os.Getenv("DEFAULT_NODE_LIMIT")) envlimit, err := strconv.Atoi(os.Getenv("DEFAULT_NODE_LIMIT"))
if err == nil && envlimit != 0 { if err == nil && envlimit != 0 {
limit = int32(envlimit) limit = int32(envlimit)
} else if config.Config.Server.DefaultNodeLimit != 0 { } else if config.Config.Server.DefaultNodeLimit != 0 {
limit = config.Config.Server.DefaultNodeLimit limit = config.Config.Server.DefaultNodeLimit
} }
return limit return limit
} }
func GetGRPCConnString() string { func GetGRPCConnString() string {
conn := "" conn := ""
if os.Getenv("SERVER_GRPC_CONN_STRING") != "" { if os.Getenv("SERVER_GRPC_CONN_STRING") != "" {
conn = os.Getenv("SERVER_GRPC_CONN_STRING") conn = os.Getenv("SERVER_GRPC_CONN_STRING")
} else if config.Config.Server.GRPCConnString != "" { } else if config.Config.Server.GRPCConnString != "" {
conn = 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 { func GetGRPCHost() string {
@@ -134,51 +147,51 @@ func GetGRPCHost() string {
if IsGRPCWireGuard() { if IsGRPCWireGuard() {
serverhost = GetGRPCWGAddress() serverhost = GetGRPCWGAddress()
} else { } else {
if os.Getenv("SERVER_GRPC_HOST") != "" { if os.Getenv("SERVER_GRPC_HOST") != "" {
serverhost = os.Getenv("SERVER_GRPC_HOST") serverhost = os.Getenv("SERVER_GRPC_HOST")
} else if config.Config.Server.GRPCHost != "" { } else if config.Config.Server.GRPCHost != "" {
serverhost = config.Config.Server.GRPCHost serverhost = config.Config.Server.GRPCHost
} else if os.Getenv("SERVER_HOST") != "" { } else if os.Getenv("SERVER_HOST") != "" {
serverhost = os.Getenv("SERVER_HOST") serverhost = os.Getenv("SERVER_HOST")
} else { } else {
remoteip, _ := GetPublicIP() remoteip, _ := GetPublicIP()
if remoteip != "" { if remoteip != "" {
serverhost = remoteip serverhost = remoteip
} }
} }
} }
return serverhost return serverhost
} }
func GetGRPCPort() string { func GetGRPCPort() string {
grpcport := "50051" grpcport := "50051"
if os.Getenv("GRPC_PORT") != "" { if os.Getenv("GRPC_PORT") != "" {
grpcport = os.Getenv("GRPC_PORT") grpcport = os.Getenv("GRPC_PORT")
} else if config.Config.Server.GRPCPort != "" { } else if config.Config.Server.GRPCPort != "" {
grpcport = config.Config.Server.GRPCPort grpcport = config.Config.Server.GRPCPort
} }
return grpcport return grpcport
} }
func GetMasterKey() string { func GetMasterKey() string {
key := "secretkey" key := "secretkey"
if os.Getenv("MASTER_KEY") != "" { if os.Getenv("MASTER_KEY") != "" {
key = os.Getenv("MASTER_KEY") key = os.Getenv("MASTER_KEY")
} else if config.Config.Server.MasterKey != "" { } else if config.Config.Server.MasterKey != "" {
key = config.Config.Server.MasterKey key = config.Config.Server.MasterKey
} }
return key return key
} }
func GetAllowedOrigin() string { func GetAllowedOrigin() string {
allowedorigin := "*" allowedorigin := "*"
if os.Getenv("CORS_ALLOWED_ORIGIN") != "" { if os.Getenv("CORS_ALLOWED_ORIGIN") != "" {
allowedorigin = os.Getenv("CORS_ALLOWED_ORIGIN") allowedorigin = os.Getenv("CORS_ALLOWED_ORIGIN")
} else if config.Config.Server.AllowedOrigin != "" { } else if config.Config.Server.AllowedOrigin != "" {
allowedorigin = config.Config.Server.AllowedOrigin allowedorigin = config.Config.Server.AllowedOrigin
} }
return allowedorigin return allowedorigin
} }
func IsRestBackend() bool { func IsRestBackend() bool {
isrest := true isrest := true
if os.Getenv("REST_BACKEND") != "" { if os.Getenv("REST_BACKEND") != "" {
if os.Getenv("REST_BACKEND") == "off" { if os.Getenv("REST_BACKEND") == "off" {
isrest = false isrest = false
} }
@@ -186,113 +199,123 @@ func IsRestBackend() bool {
if config.Config.Server.RestBackend == "off" { if config.Config.Server.RestBackend == "off" {
isrest = false isrest = false
} }
} }
return isrest return isrest
} }
func IsAgentBackend() bool { func IsAgentBackend() bool {
isagent := true isagent := true
if os.Getenv("AGENT_BACKEND") != "" { if os.Getenv("AGENT_BACKEND") != "" {
if os.Getenv("AGENT_BACKEND") == "off" { if os.Getenv("AGENT_BACKEND") == "off" {
isagent = false isagent = false
} }
} else if config.Config.Server.AgentBackend != "" { } else if config.Config.Server.AgentBackend != "" {
if config.Config.Server.AgentBackend == "off" { if config.Config.Server.AgentBackend == "off" {
isagent = false isagent = false
} }
} }
return isagent return isagent
} }
func IsClientMode() bool { func IsClientMode() bool {
isclient := true isclient := true
if os.Getenv("CLIENT_MODE") != "" { if os.Getenv("CLIENT_MODE") != "" {
if os.Getenv("CLIENT_MODE") == "off" { if os.Getenv("CLIENT_MODE") == "off" {
isclient = false isclient = false
} }
} else if config.Config.Server.ClientMode != "" { } else if config.Config.Server.ClientMode != "" {
if config.Config.Server.ClientMode == "off" { if config.Config.Server.ClientMode == "off" {
isclient = false isclient = false
} }
} }
return isclient return isclient
} }
func IsDNSMode() bool { func IsDNSMode() bool {
isdns := true isdns := true
if os.Getenv("DNS_MODE") != "" { if os.Getenv("DNS_MODE") != "" {
if os.Getenv("DNS_MODE") == "off" { if os.Getenv("DNS_MODE") == "off" {
isdns = false isdns = false
} }
} else if config.Config.Server.DNSMode != "" { } else if config.Config.Server.DNSMode != "" {
if config.Config.Server.DNSMode == "off" { if config.Config.Server.DNSMode == "off" {
isdns = false isdns = false
} }
} }
return isdns return isdns
} }
func IsGRPCSSL() bool { func IsGRPCSSL() bool {
isssl := false isssl := false
if os.Getenv("GRPC_SSL") != "" { if os.Getenv("GRPC_SSL") != "" {
if os.Getenv("GRPC_SSL") == "on" { if os.Getenv("GRPC_SSL") == "on" {
isssl = true isssl = true
} }
} else if config.Config.Server.DNSMode != "" { } else if config.Config.Server.DNSMode != "" {
if config.Config.Server.DNSMode == "on" { if config.Config.Server.DNSMode == "on" {
isssl = true isssl = true
} }
} }
return isssl return isssl
} }
func DisableRemoteIPCheck() bool { func DisableRemoteIPCheck() bool {
disabled := false disabled := false
if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" { if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" {
if os.Getenv("DISABLE_REMOTE_IP_CHECK") == "on" { if os.Getenv("DISABLE_REMOTE_IP_CHECK") == "on" {
disabled = true disabled = true
} }
} else if config.Config.Server.DisableRemoteIPCheck != "" { } else if config.Config.Server.DisableRemoteIPCheck != "" {
if config.Config.Server.DisableRemoteIPCheck == "on" { if config.Config.Server.DisableRemoteIPCheck == "on" {
disabled= true disabled = true
} }
} }
return disabled return disabled
} }
func DisableDefaultNet() bool { func DisableDefaultNet() bool {
disabled := false disabled := false
if os.Getenv("DISABLE_DEFAULT_NET") != "" { if os.Getenv("DISABLE_DEFAULT_NET") != "" {
if os.Getenv("DISABLE_DEFAULT_NET") == "on" { if os.Getenv("DISABLE_DEFAULT_NET") == "on" {
disabled = true disabled = true
} }
} else if config.Config.Server.DisableDefaultNet != "" { } else if config.Config.Server.DisableDefaultNet != "" {
if config.Config.Server.DisableDefaultNet == "on" { if config.Config.Server.DisableDefaultNet == "on" {
disabled= true disabled = true
} }
} }
return disabled return disabled
} }
func GetPublicIP() (string, error) { func GetPublicIP() (string, error) {
endpoint := "" endpoint := ""
var err error var err error
iplist := []string{"http://ip.server.gravitl.com", "https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"} iplist := []string{"http://ip.server.gravitl.com", "https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
for _, ipserver := range iplist { for _, ipserver := range iplist {
resp, err := http.Get(ipserver) resp, err := http.Get(ipserver)
if err != nil { if err != nil {
continue continue
} }
defer resp.Body.Close() defer resp.Body.Close()
if resp.StatusCode == http.StatusOK { if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body) bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil { if err != nil {
continue continue
} }
endpoint = string(bodyBytes) endpoint = string(bodyBytes)
break break
} }
} }
if err == nil && endpoint == "" { if err == nil && endpoint == "" {
err = errors.New("Public Address Not Found.") err = errors.New("Public Address Not Found.")
} }
return endpoint, err 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)
} }

View File

@@ -3,7 +3,7 @@
PUBKEY="DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34=" PUBKEY="DM5qhLAE20PG9BbfBCger+Ac9D2NDOwCtY1rbYDLf34="
IPADDR="69.173.21.202" IPADDR="69.173.21.202"
MACADDRESS="59:2a:9c:d4:e2:49" MACADDRESS="59:2a:9c:d4:e2:49"
ACCESSKEY="F5LjPlgHHgi1zpir" ACCESSKEY="9ktiHcUWay2MSKsY"
PASSWORD="ppppppp" PASSWORD="ppppppp"
generate_post_json () generate_post_json ()
@@ -22,5 +22,5 @@ EOF
POST_JSON=$(generate_post_json) 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