Merge branch 'develop' into refactor-controllers

This commit is contained in:
Matthew R Kasun 2021-05-09 10:52:42 -04:00
commit 073be947c6
37 changed files with 2517 additions and 1283 deletions

View file

@ -1,6 +1,6 @@
#first stage - builder
FROM golang:1.14-stretch as builder
FROM golang:latest as builder
COPY . /app
@ -10,22 +10,28 @@ ENV GO111MODULE=auto
RUN CGO_ENABLED=0 GOOS=linux go build -o app main.go
WORKDIR /app/netclient
ENV GO111MODULE=auto
RUN CGO_ENABLED=0 GOOS=linux go build -o netclient main.go
#second stage
FROM alpine:latest
FROM debian:latest
RUN apt-get update && apt-get -y install systemd procps
WORKDIR /root/
RUN apk add --no-cache tzdata
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
COPY --from=builder /app .
COPY --from=builder /app/config config
COPY --from=builder /app/netclient netclient
EXPOSE 8081
EXPOSE 50051
CMD ["./app", "--clientmode=off"]
CMD ["./app"]

105
README.md
View file

@ -1,3 +1,4 @@
<p align="center">
<img src="netmaker.png"><break/>
</p>
@ -6,7 +7,11 @@
</p>
## What is Netmaker?
Netmaker is a tool for creating and managing virtual networks. The goal is to provide functionality similar to Tailscale, ZeroTier, and Nebula, but faster, easier, and more dynamic. It should be like clicking a button. Netmaker consists of a server, an agent, and a UI. You spin up the Netmaker server and then install netclient (the agent) on your computers. Netmaker will do the rest. It will tell all of your computers how to reach each other and will keep them informed of any changes to the network.
Netmaker is a tool for creating and managing virtual networks. If you have servers spread across multiple locations, data centers, or clouds, they all live on separate networks. This can make life very difficult. Netmaker takes all those machines and puts them on a single, flat network so that they can talk to each other easily and securely.
Think of it like Tailscale, ZeroTier, or Nebula, but faster, easier, and more dynamic.
You spin up the Netmaker server and UI, and then install the Netclient (agent) on your computers. Netmaker will do the rest. It will tell all of your computers how to reach each other and will keep them informed of any changes to the network.
Netmaker's handy dandy UI can be found [here](https://github.com/gravitl/netmaker-ui).
@ -28,7 +33,9 @@ Under the hood, Netmaker uses WireGuard to create encrypted tunnels between ever
## Compatible Systems
Netmaker works on most linux systems that have systemd. It works with Fedora, Ubuntu, and Raspian. Just make sure you have WireGuard installed. Having a problem? Open an issue or Contact us.
Netmaker is primarily designed for **linux**, specifically **systemd-based linux.** This includes Fedora, Ubuntu, and Raspian. Just make sure you have WireGuard installed. Having a problem? Open an issue or Contact us.
In version 0.3 we have released Private DNS. Nameservers can be configured manually on any system, but to have the Netclient add dns automatically, it requires **resolvectl.**
In future releases, we have plans to support other platforms such as Windows and MacOS.
@ -47,33 +54,91 @@ In future releases, we have plans to support other platforms such as Windows and
[Intro/Overview Video Tutorial](https://youtu.be/PWLPT320Ybo)
[Site-to-Site Video Tutorial](https://youtu.be/krCKBJhwwDk)
#### Prereqs:
1. A server with an IP reachable by your computers (a small ec2 instance or droplet would do just fine).
2. Linux installed on the above server (we use Ubuntu, but anything that runs Docker should work).
3. Install Docker if running in Docker Mode (see below).
### Note about permissions
The default installation requires special privileges on the server side, because Netmaker will control the local kernel Wireguard. This can be turned off and run in non-privileged mode if necessary (but disables some features). For more details, see the **Usage** docs.
### Prereqs
1. A running linux server to host Netmaker, with an IP reachable by your computers (Debian-based preferred but not required).
2. Linux installed on the above server (Debian-based preferred but not required).
3. Install Docker and Docker Compose if running in Docker Mode (see below).
4. System dependencies installed:
- Docker (if running in default Docker mode. DO NOT use snap install for docker.)
- Docker Compose
- Wireguard + Resolvectl (if running in default Client mode)
#### CoreDNS Preparation
v0.3 introduces CoreDNS as a private nameserver. To run CoreDNS on your server host, you must disable systemd-resolved to open port 53:
1. systemctl stop systemd-resolved
2. systemctl disable systemd-resolved
3. vim /etc/systemd/resolved.conf
- uncomment **DNS=** and add 8.8.8.8 or whatever is your preference
- uncomment **DNSStubListener=** and set to **"no"**
4. sudo ln -sf /run/systemd/resolve/resolv.conf /etc/resolv.conf
#### Launch Netmaker:
Netmaker v0.2 introduces the server as a 'client'. This means the server can add itself into networks if you would like. To do so, Netmaker requires privileged access where it is running, and needs to modify the host filesystem. To run in this mode, we are not currently using Docker.
**If you would like to run with "client mode", you can use the following script to deploy:**
`sudo curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.2/netmaker-install-clientmode.sh | sudo SERVER_DOMAIN=< your server IP > sh -`
**If you would like to run without "client mode", and manually add/remove the server from networks:**
### Launch Netmaker
Note, this installs Netmaker with CoreDNS and a Netclient (privileged). If you want to run the server non-privileged or without CoreDNS, see the advanced usage docs.
1. Clone this repo or just copy contents of "docker-compose.yml" to your Netmaker server (from prereqs).
2. In docker-compose.yml, change BACKEND_URL to the public IP of your server.
3. Run `sudo docker-compose up`
4. Navigate to your server's IP in the browser and you should see the Netmaker UI asking to create a new admin user.
5. Create a new admin user
6. You are now ready to begin using Netmaker. There should be a default network you can use or you can create your own. Then, Create a key or enable manual node sign up so that your nodes can connect.
6. You are now ready to begin using Netmaker.
#### On your machines :
A command will be displayed when you generate a token for signing up nodes. Run it on each machine you would like to connect.
`curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.2/netclient-install.sh | KEY=<your access key> sh -`
(Note: Key can be left out if manual node signup is enabled)
### Create a Network
You can also just use the "default" network.
1. Click "CREATE NETWORK" in the upper left of your console
2. Enter a valid address range, e.g. 10.11.12.0/24
3. Enter a name such as "homenet"
4. Additional options:
- **Dual Stack**: Machines will recieve a private IPv6 address in addition to their IPv4 address.
- **Local:** Will use local address range for endpoints instead of public. Use Case: Home or Office network where most devices do not have public IP's. In this case you can create a gateway into the network after creating the Local Network.
After Network creation, you can edit the network in the NETWORK DETAILS pane, modifying the address range and default options. You can also toggle on **Allow Node Signup Without Keys**, which makes the next step unnecessary, but allows anyone to create a node in your network, which will be cordoned in pending state.
### Create Keys
1. Click the "ACCESS KEYS" tab
2. Click "ADD NEW ACCESSS KEY"
3. Give your key a name and number of uses
4. Several values will be displayed. Save these somewhere, as they will only be displayed once:
- **Access Key:** Use only in special edge cases where server connection string must be modified
- **Access Token:** Use on machines that already have the netclient utility
- **Install Command:** Use on machines that do not have the netclient utility
### Install Agent:
For machines **without** netclient, run the install command (from above): `curl -sfL https://raw.githubusercontent.com/gravitl/netmaker/v0.2/netclient-install.sh | KEY=<your access key> sh -`
For machines **with** netclient run the following (with access token from above): `sudo netclient -c install -t <access token>`
For networks with **manual signup** enabled (see above), install using the network name: `sudo netclient -c install -n <network name>`
### Manage Nodes
Your machines should now be visible in the control pane.
**Modify nodes:** Click the pencil icon in the NODES pane to modify details like WireGuard port, address, and node name. You can also **DELETE** nodes here and they will lose network access.
**Approve nodes:** If a node is in pending state (signed up without key), you can approve it. An icon will appear for pending nodes that need approval.
**Gateway Mode:** Click the Gateway icon to enable gateway mode on a given node. A popup will allow you to choose an existing network, or enter a custom address range.
*Example: You create a network in netmaker called Homenet. It has several machines on your home server. You create another network called Cloudnet. It has several machines in AWS. You have one server (server X) which is added to both networks. On Cloudnet, you make Server X a gateway to Homenet. Now, the cloudnet machines have access to your homenet machines. via Server X.*
*On Homenet, you add Server Y, a machine in AWS, and make it a gateway to a custom address range 172.16.0.0/16. The machines on your home network now have access to any AWS machines in that address range via Server Y*
### Manage DNS
On the DNS tab you can create custom DNS entries for a given network.
1. All dns entries will be *postfixed* with a private TLD of the network name, for example, ".mynet"
2. Default DNS is created for node name + TLD, for instance, node-c42wt.mynet. This is not editable.
3. Click ADD ENTRY to add custom DNS
- You can click CHOOSE NODE to direct DNS to a specific node in the network
- You can also specify any custom address you would like, which can be outside the network (for instance, the IP for google.com)
- Add a dns entry name, which will be postfixed with the network TLD. E.g. if you enter "privateapi.com", it will become "privateapi.com.networkname"
### Uninstalling Client
To uninstall the client from a network: `sudo netclient -c remove -n < networkname >`
To uninstall entirely, run the above for each network, and then run `sudo rm -rf /etc/netclient`
### Uninstralling Netmaker
To uninstall the netmaker server, simply run `docker-compose down`
#### LICENSE
@ -81,5 +146,5 @@ Netmaker's source code and all artifacts in this repository are freely available
#### CONTACT
Email: info@gravitl.com
Email: alex@gravitl.com
Discord: https://discord.gg/zRb9Vfhk8A

View file

@ -15,7 +15,7 @@ import (
//setting dev by default
func getEnv() string {
env := os.Getenv("APP_ENV")
env := os.Getenv("NETMAKER_ENV")
if len(env) == 0 {
return "dev"
@ -35,16 +35,17 @@ type EnvironmentConfig struct {
// ServerConfig :
type ServerConfig struct {
Host string `yaml:"host"`
ApiPort string `yaml:"apiport"`
GrpcPort string `yaml:"grpcport"`
APIHost string `yaml:"apihost"`
APIPort string `yaml:"apiport"`
GRPCHost string `yaml:"grpchost"`
GRPCPort string `yaml:"grpcport"`
MasterKey string `yaml:"masterkey"`
AllowedOrigin string `yaml:"allowedorigin"`
RestBackend bool `yaml:"restbackend"`
AgentBackend bool `yaml:"agentbackend"`
DefaultNetName string `yaml:"defaultnetname"`
DefaultNetRange string `yaml:"defaultnetrange"`
CreateDefault bool `yaml:"createdefault"`
RestBackend string `yaml:"restbackend"`
AgentBackend string `yaml:"agentbackend"`
ClientMode string `yaml:"clientmode"`
DNSMode string `yaml:"dnsmode"`
DisableRemoteIPCheck string `yaml:"disableremoteipcheck"`
}
type MongoConnConfig struct {
@ -60,13 +61,16 @@ type MongoConnConfig struct {
func readConfig() *EnvironmentConfig {
file := fmt.Sprintf("config/environments/%s.yaml", getEnv())
f, err := os.Open(file)
var cfg EnvironmentConfig
if err != nil {
log.Fatal(err)
os.Exit(2)
//log.Fatal(err)
//os.Exit(2)
log.Println("Unable to open config file at config/environments/" + getEnv())
log.Println("Will proceed with defaults or enironment variables (no config file).")
return &cfg
}
defer f.Close()
var cfg EnvironmentConfig
decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {

View file

@ -1,17 +1,18 @@
server:
host: "localhost"
apiport: "8081"
grpcport: "50051"
masterkey: "secretkey"
allowedorigin: "*"
restbackend: true
agentbackend: true
defaultnetname: "default"
defaultnetrange: "10.10.10.0/24"
createdefault: true
apihost: "" # defaults to 127.0.0.1 or remote ip (SERVER_HOST) if DisableRemoteIPCheck is not set to true. SERVER_API_HOST if set
apiport: "" # defaults to 8081 or HTTP_PORT (if set)
grpchost: "" # defaults to 127.0.0.1 or remote ip (SERVER_HOST) if DisableRemoteIPCheck is not set to true. SERVER_GRPC_HOST if set.
grpcport: "" # defaults to 50051 or GRPC_PORT (if set)
masterkey: "" # defaults to 'secretkey' or MASTER_KEY (if set)
allowedorigin: "" # defaults to '*' or CORS_ALLOWED_ORIGIN (if set)
restbackend: "" # defaults to "on" or REST_BACKEND (if set)
agentbackend: "" # defaults to "on" or AGENT_BACKEND (if set)
clientmode: "" # defaults to "on" or CLIENT_MODE (if set)
dnsmode: "" # defaults to "on" or DNS_MODE (if set)
disableremoteipcheck: "" # defaults to "false" or DISABLE_REMOTE_IP_CHECK (if set)
mongoconn:
user: "mongoadmin"
pass: "mongopass"
host: "localhost"
port: "27017"
opts: '/?authSource=admin'
user: "" # defaults to "mongoadmin" or MONGO_ADMIN (if set)
pass: "" # defaults to "mongopass" or MONGO_PASS (if set)
host: "" # defaults to 127.0.0.1 or MONGO_HOST (if set)
port: "" # defaults to 27017 or MONGO_PORT (if set)
opts: '' # defaults to '/?authSource=admin' or MONGO_OPTS (if set)

View file

@ -10,6 +10,7 @@ import (
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/servercfg"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo/options"
"golang.org/x/crypto/bcrypt"
@ -210,6 +211,9 @@ func UpdateNode(nodechange models.NodeUpdate, node models.Node) (models.Node, er
if notifynetwork {
errN = SetNetworkNodesLastModified(queryNetwork)
}
if servercfg.IsDNSMode() {
errN = SetDNS()
}
return returnnode, errN
}
@ -236,6 +240,9 @@ func DeleteNode(macaddress string, network string) (bool, error) {
err = SetNetworkNodesLastModified(network)
fmt.Println("Deleted node " + macaddress + " from network " + network)
if servercfg.IsDNSMode() {
err = SetDNS()
}
return deleted, err
}
@ -279,16 +286,12 @@ func CreateNode(node models.Node, networkName string) (models.Node, error) {
//Anyways, this scrolls through all the IP Addresses in the network range and checks against nodes
//until one is open and then returns it
node.Address, err = functions.UniqueAddress(networkName)
fmt.Println("Setting node address: " + node.Address)
if err != nil {
return node, err
}
fmt.Println("Setting node address: " + node.Address)
node.Address6, err = functions.UniqueAddress6(networkName)
if node.Address6 != "" {
fmt.Println("Setting node ipv6 address: " + node.Address6)
}
if err != nil {
return node, err
}
@ -330,7 +333,9 @@ func CreateNode(node models.Node, networkName string) (models.Node, error) {
}
SetNetworkNodesLastModified(node.Network)
if servercfg.IsDNSMode() {
err = SetDNS()
}
return node, err
}

View file

@ -2,6 +2,7 @@ package controller
import (
"github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/servercfg"
"os/signal"
"os"
"fmt"
@ -10,7 +11,6 @@ import (
"github.com/gorilla/mux"
"github.com/gorilla/handlers"
"sync"
"github.com/gravitl/netmaker/config"
)
@ -22,7 +22,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
// Currently allowed dev origin is all. Should change in prod
// should consider analyzing the allowed methods further
headersOk := handlers.AllowedHeaders([]string{"Access-Control-Allow-Origin", "X-Requested-With", "Content-Type", "authorization"})
originsOk := handlers.AllowedOrigins([]string{config.Config.Server.AllowedOrigin})
originsOk := handlers.AllowedOrigins([]string{servercfg.GetAllowedOrigin()})
methodsOk := handlers.AllowedMethods([]string{"GET", "PUT", "POST", "DELETE"})
nodeHandlers(r)
@ -32,10 +32,7 @@ func HandleRESTRequests(wg *sync.WaitGroup) {
fileHandlers(r)
serverHandlers(r)
port := config.Config.Server.ApiPort
if os.Getenv("API_PORT") != "" {
port = os.Getenv("API_PORT")
}
port := servercfg.GetAPIPort()
srv := &http.Server{Addr: ":" + port, Handler: handlers.CORS(originsOk, headersOk, methodsOk)(r)}
go func(){

View file

@ -175,6 +175,40 @@ func GetCustomDNS(network string) ([]models.DNSEntry, error) {
return dns, err
}
func SetDNS() error {
hostfile := txeh.Hosts{}
var corefilestring string
networks, err := functions.ListNetworks()
if err != nil {
return err
}
for _, net := range networks {
corefilestring = corefilestring + net.NetID + " "
dns, err := GetDNS(net.NetID)
if err != nil {
return err
}
for _, entry := range dns {
hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
if err != nil {
return err
}
}
}
if corefilestring == "" {
corefilestring = "example.com"
}
err = hostfile.SaveAs("./config/dnsconfig/netmaker.hosts")
if err != nil {
return err
}
err = functions.SetCorefile(corefilestring)
return err
}
func GetDNSEntryNum(domain string, network string) (int, error) {
num := 0
@ -418,7 +452,7 @@ func pushDNS(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
err := WriteHosts()
err := SetDNS()
if err != nil {
returnErrorResponse(w, r, formatError(err, "internal"))
@ -427,35 +461,6 @@ func pushDNS(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("DNS Pushed to CoreDNS")
}
func WriteHosts() error {
//hostfile, err := txeh.NewHostsDefault()
hostfile := txeh.Hosts{}
/*
if err != nil {
return err
}
*/
networks, err := functions.ListNetworks()
if err != nil {
return err
}
for _, net := range networks {
dns, err := GetDNS(net.NetID)
if err != nil {
return err
}
for _, entry := range dns {
hostfile.AddHost(entry.Address, entry.Name+"."+entry.Network)
if err != nil {
return err
}
}
}
err = hostfile.SaveAs("./config/netmaker.hosts")
return err
}
func ValidateDNSCreate(entry models.DNSEntry) error {
v := validator.New()

View file

@ -7,16 +7,15 @@ import (
"errors"
"fmt"
"net/http"
"os"
"strings"
"time"
"github.com/go-playground/validator/v10"
"github.com/gorilla/mux"
"github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/servercfg"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
@ -91,7 +90,7 @@ func SecurityCheck(netname, token string) error {
//Consider a more secure way of setting master key
func authenticateMaster(tokenString string) bool {
if tokenString == config.Config.Server.MasterKey || (tokenString == os.Getenv("MASTER_KEY") && tokenString != "") {
if tokenString == servercfg.GetMasterKey() {
return true
}
return false
@ -598,12 +597,6 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
return models.AccessKey{}, errors.New("Duplicate AccessKey Name")
}
}
_, gconf, err := functions.GetGlobalConfig()
if err != nil {
//returnErrorResponse(w, r, formatError(err, "internal"))
return models.AccessKey{}, err
}
privAddr := ""
if network.IsLocal != nil {
if *network.IsLocal {
@ -611,9 +604,10 @@ func CreateAccessKey(accesskey models.AccessKey, network models.Network) (models
}
}
//netID := params["networkname"]
address := gconf.ServerGRPC + gconf.PortGRPC
accessstringdec := address + "|" + network.NetID + "|" + accesskey.Value + "|" + privAddr
netID := params["networkname"]
address := servercfg.GetGRPCHost() + ":" + servercfg.GetGRPCPort()
accessstringdec := address + "|" + netID + "|" + accesskey.Value + "|" + privAddr
accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
//validate accesskey
v := validator.New()

View file

@ -3,11 +3,11 @@ package controller
import (
"context"
"fmt"
"strconv"
"github.com/gravitl/netmaker/functions"
nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"go.mongodb.org/mongo-driver/mongo"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
@ -36,11 +36,21 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe
}
*/
// Cast to ReadNodeRes type
dualvar := false
if network.IsDualStack != nil {
dualvar = *network.IsDualStack
}
localvar := false
if network.IsLocal != nil {
localvar = *network.IsLocal
}
response := &nodepb.ReadNodeRes{
Node: &nodepb.Node{
Macaddress: node.MacAddress,
Name: node.Name,
Address: node.Address,
Address6: node.Address6,
Endpoint: node.Endpoint,
Password: node.Password,
Nodenetwork: node.Network,
@ -49,11 +59,13 @@ func (s *NodeServiceServer) ReadNode(ctx context.Context, req *nodepb.ReadNodeRe
Postdown: node.PostDown,
Postup: node.PostUp,
Checkininterval: node.CheckInInterval,
Dnsoff: !servercfg.IsDNSMode(),
Ispending: node.IsPending,
Publickey: node.PublicKey,
Listenport: node.ListenPort,
Keepalive: node.PersistentKeepalive,
Islocal: *network.IsLocal,
Islocal: localvar,
Isdualstack: dualvar,
Localrange: network.LocalRange,
},
}
@ -71,6 +83,7 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
LocalAddress: data.GetLocaladdress(),
Name: data.GetName(),
Address: data.GetAddress(),
Address6: data.GetAddress6(),
AccessKey: data.GetAccesskey(),
Endpoint: data.GetEndpoint(),
PersistentKeepalive: data.GetKeepalive(),
@ -97,8 +110,6 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
return nil, status.Errorf(codes.NotFound, fmt.Sprintf("Could not find network: %v", err))
} else {
fmt.Println("Creating node in network " + network.NetID)
fmt.Println("Network is local? " + strconv.FormatBool(*network.IsLocal))
fmt.Println("Range if local: " + network.LocalRange)
}
if !validKey {
@ -123,6 +134,15 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
fmt.Sprintf("Internal error: %v", err),
)
}
dualvar := false
if network.IsDualStack != nil {
dualvar = *network.IsDualStack
}
localvar := false
if network.IsLocal != nil {
localvar = *network.IsLocal
}
// return the node in a CreateNodeRes type
response := &nodepb.CreateNodeRes{
Node: &nodepb.Node{
@ -130,15 +150,18 @@ func (s *NodeServiceServer) CreateNode(ctx context.Context, req *nodepb.CreateNo
Localaddress: node.LocalAddress,
Name: node.Name,
Address: node.Address,
Address6: node.Address6,
Endpoint: node.Endpoint,
Password: node.Password,
Interface: node.Interface,
Nodenetwork: node.Network,
Dnsoff: !servercfg.IsDNSMode(),
Ispending: node.IsPending,
Publickey: node.PublicKey,
Listenport: node.ListenPort,
Keepalive: node.PersistentKeepalive,
Islocal: *network.IsLocal,
Islocal: localvar,
Isdualstack: dualvar,
Localrange: network.LocalRange,
},
}
@ -160,6 +183,7 @@ func (s *NodeServiceServer) CheckIn(ctx context.Context, req *nodepb.CheckInReq)
// ID: primitive.NilObjectID,
MacAddress: data.GetMacaddress(),
Address: data.GetAddress(),
Address6: data.GetAddress6(),
Endpoint: data.GetEndpoint(),
Network: data.GetNodenetwork(),
Password: data.GetPassword(),
@ -199,11 +223,12 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNo
// Get the node data from the request
data := req.GetNode()
// Now we have to convert this into a NodeItem type to convert into BSON
nodechange := models.NodeUpdate{
nodechange := models.Node{
// ID: primitive.NilObjectID,
MacAddress: data.GetMacaddress(),
Name: data.GetName(),
Address: data.GetAddress(),
Address6: data.GetAddress6(),
LocalAddress: data.GetLocaladdress(),
Endpoint: data.GetEndpoint(),
Password: data.GetPassword(),
@ -243,12 +268,22 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNo
fmt.Sprintf("Could not find node with supplied Mac Address: %v", err),
)
}
dualvar := false
if network.IsDualStack != nil {
dualvar = *network.IsDualStack
}
localvar := false
if network.IsLocal != nil {
localvar = *network.IsLocal
}
return &nodepb.UpdateNodeRes{
Node: &nodepb.Node{
Macaddress: newnode.MacAddress,
Localaddress: newnode.LocalAddress,
Name: newnode.Name,
Address: newnode.Address,
Address6: newnode.Address6,
Endpoint: newnode.Endpoint,
Password: newnode.Password,
Interface: newnode.Interface,
@ -257,9 +292,11 @@ func (s *NodeServiceServer) UpdateNode(ctx context.Context, req *nodepb.UpdateNo
Nodenetwork: newnode.Network,
Ispending: newnode.IsPending,
Publickey: newnode.PublicKey,
Dnsoff: !servercfg.IsDNSMode(),
Listenport: newnode.ListenPort,
Keepalive: newnode.PersistentKeepalive,
Islocal: *network.IsLocal,
Islocal: localvar,
Isdualstack: dualvar,
Localrange: network.LocalRange,
},
}, nil
@ -308,6 +345,7 @@ func (s *NodeServiceServer) GetPeers(req *nodepb.GetPeersReq, stream nodepb.Node
stream.Send(&nodepb.GetPeersRes{
Peers: &nodepb.PeersResponse{
Address: peers[i].Address,
Address6: peers[i].Address6,
Endpoint: peers[i].Endpoint,
Gatewayrange: peers[i].GatewayRange,
Isgateway: peers[i].IsGateway,

View file

@ -3,7 +3,7 @@ package controller
import (
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/serverctl"
"github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/servercfg"
"encoding/json"
"strings"
"net/http"
@ -12,6 +12,7 @@ import (
func serverHandlers(r *mux.Router) {
r.HandleFunc("/api/server/addnetwork/{network}", securityCheckServer(http.HandlerFunc(addNetwork))).Methods("POST")
r.HandleFunc("/api/server/getconfig", securityCheckServer(http.HandlerFunc(getConfig))).Methods("GET")
r.HandleFunc("/api/server/removenetwork/{network}", securityCheckServer(http.HandlerFunc(removeNetwork))).Methods("DELETE")
}
@ -49,7 +50,7 @@ func securityCheckServer(next http.Handler) http.HandlerFunc {
}
//Consider a more secure way of setting master key
func authenticateMasterServer(tokenString string) bool {
if tokenString == config.Config.Server.MasterKey {
if tokenString == servercfg.GetMasterKey() {
return true
}
return false
@ -72,6 +73,18 @@ func removeNetwork(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode("Server removed from network " + params["network"])
}
func getConfig(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")
// get params
scfg := servercfg.GetConfig()
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(scfg)
}
func addNetwork(w http.ResponseWriter, r *http.Request) {
// Set header
w.Header().Set("Content-Type", "application/json")

View file

@ -228,7 +228,7 @@ func getUser(w http.ResponseWriter, r *http.Request) {
user, err := GetUser(params["username"])
if err != nil {
mongoconn.GetError(err, w)
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
@ -349,34 +349,26 @@ func UpdateUser(userchange models.User, user models.User) (models.User, error) {
func updateUser(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var params = mux.Vars(r)
var user models.User
//start here
user, err := GetUser(params["username"])
if err != nil {
json.NewEncoder(w).Encode(err)
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
var userchange models.User
// we decode our body request params
err = json.NewDecoder(r.Body).Decode(&userchange)
if err != nil {
json.NewEncoder(w).Encode(err)
returnErrorResponse(w, r, formatError(err, "internal"))
return
}
user, err = UpdateUser(userchange, user)
if err != nil {
returnErrorResponse(w, r, formatError(err, "badrequest"))
return
}
json.NewEncoder(w).Encode(user)
}

66
docker-compose.nodns.yml Normal file
View file

@ -0,0 +1,66 @@
version: "3.4"
volumes:
dnsconfig:
driver: local
services:
mongodb:
image: mongo:4.2
ports:
- "27017:27017"
container_name: mongodb
volumes:
- mongovol:/data/db
restart: always
environment:
MONGO_INITDB_ROOT_USERNAME: mongoadmin
MONGO_INITDB_ROOT_PASSWORD: mongopass
netmaker:
privileged: true
container_name: netmaker
build: netmaker
depends_on:
- mongodb
image: gravitl/netmaker:v0.3
ports:
- "8081:8081"
- "50051:50051"
volumes:
- ./:/local
- /etc/netclient:/etc/netclient
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg:ro
- /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
- /run/systemd/system:/run/systemd/system
- /etc/systemd/system:/etc/systemd/system
- /sys/fs/cgroup:/sys/fs/cgroup
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: always
network_mode: host
netmaker-ui:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.3
links:
- "netmaker:api"
ports:
- "80:80"
environment:
BACKEND_URL: "http://3.236.149.180:8081"
coredns:
depends_on:
- netmaker
image: coredns/coredns
command: -conf /root/dnsconfig/Corefile
container_name: coredns
restart: always
ports:
- "53:53/udp"
volumes:
- dnsconfig:/root/dnsconfig
volumes:
mongovol: {}
dnsconfig: {}

View file

@ -1,5 +1,8 @@
version: "3.3"
version: "3.4"
volumes:
dnsconfig:
driver: local
services:
mongodb:
image: mongo:4.2
@ -13,24 +16,51 @@ services:
MONGO_INITDB_ROOT_USERNAME: mongoadmin
MONGO_INITDB_ROOT_PASSWORD: mongopass
netmaker:
privileged: true
container_name: netmaker
build: netmaker
depends_on:
- mongodb
image: gravitl/netmaker:v0.2
image: gravitl/netmaker:v0.3
ports:
- "8081:8081"
- "50051:50051"
environment:
MONGO_HOST: mongodb
volumes:
- ./:/local
- /etc/netclient:/etc/netclient
- dnsconfig:/root/config/dnsconfig
- /usr/bin/wg:/usr/bin/wg:ro
- /var/run/dbus/system_bus_socket:/var/run/dbus/system_bus_socket
- /run/systemd/system:/run/systemd/system
- /etc/systemd/system:/etc/systemd/system
- /sys/fs/cgroup:/sys/fs/cgroup
cap_add:
- NET_ADMIN
- SYS_MODULE
restart: always
network_mode: host
netmaker-ui:
container_name: netmaker-ui
depends_on:
- netmaker
image: gravitl/netmaker-ui:v0.2
image: gravitl/netmaker-ui:v0.3
links:
- "netmaker:api"
ports:
- "80:80"
environment:
BACKEND_URL: "http://localhost:8081"
BACKEND_URL: "http://3.236.149.180:8081"
coredns:
depends_on:
- netmaker
image: coredns/coredns
command: -conf /root/dnsconfig/Corefile
container_name: coredns
restart: always
ports:
- "53:53/udp"
volumes:
- dnsconfig:/root/dnsconfig
volumes:
mongovol: {}
dnsconfig: {}

View file

@ -16,6 +16,7 @@ import (
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/servercfg"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
@ -38,22 +39,13 @@ func CreateServerToken(netID string) (string, error) {
accesskey.Name = GenKeyName()
accesskey.Value = GenKey()
accesskey.Uses = 1
_, gconf, errG := GetGlobalConfig()
if errG != nil {
return "", errG
}
address := "localhost" + gconf.PortGRPC
address := "127.0.0.1:" + servercfg.GetGRPCPort()
privAddr := ""
if *network.IsLocal {
privAddr = network.LocalRange
}
fmt.Println("Token details:")
fmt.Println(" grpc address + port: " + address)
fmt.Println(" network: " + netID)
fmt.Println(" private range: " + privAddr)
accessstringdec := address + "|" + netID + "|" + accesskey.Value + "|" + privAddr
accesskey.AccessString = base64.StdEncoding.EncodeToString([]byte(accessstringdec))
@ -131,8 +123,6 @@ func NetworkExists(name string) (bool, error) {
if err == mongo.ErrNoDocuments {
return false, nil
}
fmt.Println("ERROR RETRIEVING GROUP!")
fmt.Println(err)
}
return true, err
}
@ -529,13 +519,12 @@ func UniqueAddress6(networkName string) (string, error) {
var network models.Network
network, err := GetParentNetwork(networkName)
if !*network.IsDualStack {
return "", nil
}
if err != nil {
fmt.Println("UniqueAddress6 encountered an error")
return "666", err
fmt.Println("Network Not Found")
return "", err
}
if network.IsDualStack == nil || *network.IsDualStack == false {
return "", nil
}
offset := true
@ -549,7 +538,7 @@ func UniqueAddress6(networkName string) (string, error) {
offset = false
continue
}
if IsIPUnique(networkName, ip.String()) {
if IsIP6Unique(networkName, ip.String()) {
return ip.String(), err
}
}
@ -558,35 +547,6 @@ func UniqueAddress6(networkName string) (string, error) {
return "W1R3: NO UNIQUE ADDRESSES AVAILABLE", err1
}
//pretty simple get
func GetGlobalConfig() (bool, models.GlobalConfig, error) {
create := false
filter := bson.M{}
var globalconf models.GlobalConfig
collection := mongoconn.Client.Database("netmaker").Collection("config")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
err := collection.FindOne(ctx, filter).Decode(&globalconf)
defer cancel()
if err == mongo.ErrNoDocuments {
fmt.Println("Global config does not exist. Need to create.")
create = true
return create, globalconf, err
} else if err != nil {
fmt.Println(err)
fmt.Println("Could not get global config")
return create, globalconf, err
}
return create, globalconf, err
}
//generate an access key value
func GenKey() string {
@ -650,6 +610,34 @@ func IsIPUnique(network string, ip string) bool {
return isunique
}
//checks if IP is unique in the address range
//used by UniqueAddress
func IsIP6Unique(network string, ip string) bool {
var node models.Node
isunique := true
collection := mongoconn.Client.Database("netmaker").Collection("nodes")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
filter := bson.M{"address6": ip, "network": network}
err := collection.FindOne(ctx, filter).Decode(&node)
defer cancel()
if err != nil {
fmt.Println(err)
return isunique
}
if node.Address6 == ip {
isunique = false
}
return isunique
}
//called once key has been used by createNode
//reduces value by one and deletes if necessary
func DecrimentKey(networkName string, keyvalue string) {

View file

@ -2,9 +2,8 @@ package functions
import (
"time"
"os"
"github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/servercfg"
"github.com/dgrijalva/jwt-go"
)
@ -51,7 +50,7 @@ func CreateUserJWT(username string, isadmin bool) (response string, err error) {
func VerifyUserToken(tokenString string) (username string, isadmin bool, err error) {
claims := &models.UserClaims{}
if tokenString == config.Config.Server.MasterKey || (tokenString == os.Getenv("MASTER_KEY") && tokenString != "") {
if tokenString == servercfg.GetMasterKey() {
return "masteradministrator", true, nil
}
@ -71,7 +70,7 @@ func VerifyToken(tokenString string) (macaddress string, network string, err err
//this may be a stupid way of serving up a master key
//TODO: look into a different method. Encryption?
if tokenString == config.Config.Server.MasterKey || (tokenString == os.Getenv("MASTER_KEY") && tokenString != "") {
if tokenString == servercfg.GetMasterKey() {
return "mastermac", "", nil
}

51
functions/local.go Normal file
View file

@ -0,0 +1,51 @@
package functions
import (
"fmt"
"path/filepath"
"log"
"os"
"io/ioutil"
)
func FileExists(f string) bool {
info, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func SetCorefile(domains string) error {
dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
if err != nil {
return err
}
_, err = os.Stat(dir + "/config/dnsconfig")
if os.IsNotExist(err) {
os.Mkdir(dir +"/config/dnsconfig", 744)
} else if err != nil {
fmt.Println("couldnt find or create /config/dnsconfig")
return err
}
corefile := domains + ` {
reload 15s
hosts /root/dnsconfig/netmaker.hosts {
fallthrough
}
forward . 8.8.8.8 8.8.4.4
log
}
`
corebytes := []byte(corefile)
err = ioutil.WriteFile(dir + "/config/dnsconfig/Corefile", corebytes, 0644)
if err != nil {
log.Println(err)
log.Println("")
return err
}
return err
}

3
go.mod
View file

@ -5,7 +5,7 @@ go 1.15
require (
github.com/dgrijalva/jwt-go v3.2.0+incompatible
github.com/go-playground/validator/v10 v10.5.0
github.com/golang/protobuf v1.4.3
github.com/golang/protobuf v1.4.3 // indirect
github.com/gorilla/handlers v1.5.1
github.com/gorilla/mux v1.8.0
github.com/stretchr/testify v1.6.1
@ -20,5 +20,6 @@ require (
golang.zx2c4.com/wireguard/wgctrl v0.0.0-20200609130330-bd2cb7843e1b
google.golang.org/genproto v0.0.0-20210201151548-94839c025ad4 // indirect
google.golang.org/grpc v1.35.0
google.golang.org/protobuf v1.25.0
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c
)

File diff suppressed because it is too large Load diff

View file

@ -24,6 +24,7 @@ message Node {
string id = 1;
string name = 2;
string address = 3;
string address6 = 26;
int32 listenport = 4;
string publickey = 5;
string endpoint = 6;
@ -44,7 +45,9 @@ message Node {
string postchanges = 21;
string allowedips = 22;
bool islocal = 23;
string localrange = 24;
bool isdualstack = 27;
bool dnsoff = 24;
string localrange = 25;
}
message CheckInResponse {
@ -63,6 +66,7 @@ message PeersResponse {
string publickey = 5;
string endpoint = 6;
string address = 3;
string address6 = 8;
int32 listenport = 4;
string localaddress = 7;
int32 keepalive = 13;

248
main.go
View file

@ -5,20 +5,11 @@ package main
import (
"log"
"flag"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/controllers"
"github.com/gravitl/netmaker/servercfg"
"github.com/gravitl/netmaker/serverctl"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/config"
"go.mongodb.org/mongo-driver/bson"
"fmt"
"time"
"net/http"
"strings"
"errors"
"io/ioutil"
"os"
"os/exec"
"net"
@ -26,70 +17,82 @@ import (
"strconv"
"sync"
"os/signal"
"go.mongodb.org/mongo-driver/mongo"
service "github.com/gravitl/netmaker/controllers"
nodepb "github.com/gravitl/netmaker/grpc"
"google.golang.org/grpc"
)
var ServerGRPC string
var PortGRPC string
//Start MongoDB Connection and start API Request Handler
func main() {
//Client Mode Prereq Check
if servercfg.IsClientMode() {
cmd := exec.Command("id", "-u")
output, err := cmd.Output()
var clientmode string
var defaultnet string
flag.StringVar(&clientmode, "clientmode", "on", "Have a client on the server")
flag.StringVar(&defaultnet, "defaultnet", "on", "Create a default network")
flag.Parse()
if clientmode == "on" {
cmd := exec.Command("id", "-u")
output, err := cmd.Output()
if err != nil {
log.Fatal(err)
}
i, err := strconv.Atoi(string(output[:len(output)-1]))
if err != nil {
log.Fatal(err)
}
if i != 0 {
log.Fatal("To run in client mode requires root privileges. Either turn off client mode with the --clientmode=off flag, or run with sudo.")
}
if err != nil {
fmt.Println("Error running 'id -u' for prereq check. Please investigate or disable client mode.")
log.Fatal(err)
}
i, err := strconv.Atoi(string(output[:len(output)-1]))
if err != nil {
fmt.Println("Error retrieving uid from 'id -u' for prereq check. Please investigate or disable client mode.")
log.Fatal(err)
}
if i != 0 {
log.Fatal("To run in client mode requires root privileges. Either disable client mode or run with sudo.")
}
}
log.Println("Server starting...")
//Start Mongodb
mongoconn.ConnectDatabase()
installserver := false
if !(defaultnet == "off") {
if config.Config.Server.CreateDefault {
created, err := createDefaultNetwork()
if err != nil {
fmt.Printf("Error creating default network: %v", err)
}
if created && clientmode != "off" {
installserver = true
}
//Create the default network (default: 10.10.10.0/24)
created, err := serverctl.CreateDefaultNetwork()
if err != nil {
fmt.Printf("Error creating default network: %v", err)
}
if created && servercfg.IsClientMode() {
installserver = true
}
//NOTE: Removed Check and Logic for DNS Mode
//Reasoning. DNS Logic is very small on server. Can run with little/no impact. Just sets a tiny config file.
//Real work is done by CoreDNS
//We can just not run CoreDNS. On Agent side is only necessary check for IsDNSMode, which we will pass.
var waitnetwork sync.WaitGroup
if config.Config.Server.AgentBackend {
//Run Agent Server
if servercfg.IsAgentBackend() {
if !(servercfg.DisableRemoteIPCheck()) && servercfg.GetGRPCHost() == "127.0.0.1" {
err := servercfg.SetHost()
if err != nil {
fmt.Println("Unable to Set host. Exiting.")
log.Fatal(err)
}
}
waitnetwork.Add(1)
go runGRPC(&waitnetwork, installserver)
}
if config.Config.Server.RestBackend {
//Run Rest Server
if servercfg.IsRestBackend() {
if !servercfg.DisableRemoteIPCheck() && servercfg.GetAPIHost() == "127.0.0.1" {
err := servercfg.SetHost()
if err != nil {
fmt.Println("Unable to Set host. Exiting.")
log.Fatal(err)
}
}
waitnetwork.Add(1)
controller.HandleRESTRequests(&waitnetwork)
}
if !config.Config.Server.RestBackend && !config.Config.Server.AgentBackend {
fmt.Println("Oops! No Server Mode selected. Nothing being served.")
if !servercfg.IsAgentBackend() && !servercfg.IsRestBackend() {
fmt.Println("Oops! No Server Mode selected. Nothing is being served! Set either Agent mode (AGENT_BACKEND) or Rest mode (REST_BACKEND) to 'true'.")
}
waitnetwork.Wait()
fmt.Println("Exiting now.")
@ -105,38 +108,9 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
// Pipe flags to one another (log.LstdFLags = log.Ldate | log.Ltime)
log.SetFlags(log.LstdFlags | log.Lshortfile)
// Start our listener, 50051 is the default gRPC port
grpcport := ":50051"
if config.Config.Server.GrpcPort != "" {
grpcport = ":" + config.Config.Server.GrpcPort
}
if os.Getenv("GRPC_PORT") != "" {
grpcport = ":" + os.Getenv("GRPC_PORT")
}
PortGRPC = grpcport
if os.Getenv("BACKEND_URL") == "" {
if config.Config.Server.Host == "" {
ServerGRPC, _ = getPublicIP()
} else {
ServerGRPC = config.Config.Server.Host
}
} else {
ServerGRPC = os.Getenv("BACKEND_URL")
}
fmt.Println("GRPC Server set to: " + ServerGRPC)
fmt.Println("GRPC Port set to: " + PortGRPC)
var gconf models.GlobalConfig
gconf.ServerGRPC = ServerGRPC
gconf.PortGRPC = PortGRPC
gconf.Name = "netmaker"
err := setGlobalConfig(gconf)
grpcport := servercfg.GetGRPCPort()
if err != nil && err != mongo.ErrNoDocuments{
log.Fatalf("Unable to set global config: %v", err)
}
listener, err := net.Listen("tcp", grpcport)
listener, err := net.Listen("tcp", ":"+grpcport)
// Handle errors if any
if err != nil {
log.Fatalf("Unable to listen on port" + grpcport + ": %v", err)
@ -163,8 +137,8 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
fmt.Println("Agent Server succesfully started on port " + grpcport + " (gRPC)")
if installserver {
fmt.Println("Adding server to " + config.Config.Server.DefaultNetName)
success, err := serverctl.AddNetwork(config.Config.Server.DefaultNetName)
fmt.Println("Adding server to default network")
success, err := serverctl.AddNetwork("default")
if err != nil {
fmt.Printf("Error adding to default network: %v", err)
fmt.Println("")
@ -179,8 +153,6 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
}
fmt.Println("Setup complete. You are ready to begin using netmaker.")
// Right way to stop the server using a SHUTDOWN HOOK
// Create a channel to receive OS signals
c := make(chan os.Signal)
@ -202,113 +174,6 @@ func runGRPC(wg *sync.WaitGroup, installserver bool) {
mongoconn.Client.Disconnect(context.TODO())
fmt.Println("MongoDB connection closed.")
}
func setGlobalConfig(globalconf models.GlobalConfig) (error) {
collection := mongoconn.Client.Database("netmaker").Collection("config")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
create, _, err := functions.GetGlobalConfig()
if create {
_, err := collection.InsertOne(ctx, globalconf)
defer cancel()
if err != nil {
if err == mongo.ErrNoDocuments || strings.Contains(err.Error(), "no documents in result"){
return nil
} else {
return err
}
}
} else {
filter := bson.M{"name": "netmaker"}
update := bson.D{
{"$set", bson.D{
{"servergrpc", globalconf.ServerGRPC},
{"portgrpc", globalconf.PortGRPC},
}},
}
err := collection.FindOneAndUpdate(ctx, filter, update).Decode(&globalconf)
if err == mongo.ErrNoDocuments {
//if err == mongo.ErrNoDocuments || strings.Contains(err.Error(), "no documents in result"){
return nil
}
}
return err
}
func createDefaultNetwork() (bool, error) {
iscreated := false
exists, err := functions.NetworkExists(config.Config.Server.DefaultNetName)
if exists || err != nil {
fmt.Println("Default network already exists")
fmt.Println("Skipping default network create")
return iscreated, err
} else {
var network models.Network
network.NetID = config.Config.Server.DefaultNetName
network.AddressRange = config.Config.Server.DefaultNetRange
network.DisplayName = config.Config.Server.DefaultNetName
network.SetDefaults()
network.SetNodesLastModified()
network.SetNetworkLastModified()
network.KeyUpdateTimeStamp = time.Now().Unix()
priv := false
network.IsLocal = &priv
network.KeyUpdateTimeStamp = time.Now().Unix()
allow := true
network.AllowManualSignUp = &allow
fmt.Println("Creating default network.")
collection := mongoconn.Client.Database("netmaker").Collection("networks")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// insert our network into the network table
_, err = collection.InsertOne(ctx, network)
defer cancel()
}
if err == nil {
iscreated = true
}
return iscreated, err
}
func getPublicIP() (string, error) {
iplist := []string{"https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
endpoint := ""
var err error
for _, ipserver := range iplist {
resp, err := http.Get(ipserver)
if err != nil {
continue
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
continue
}
endpoint = string(bodyBytes)
break
}
}
if err == nil && endpoint == "" {
err = errors.New("Public Address Not Found.")
}
return endpoint, err
}
func authServerUnaryInterceptor() grpc.ServerOption {
return grpc.UnaryInterceptor(controller.AuthServerUnaryInterceptor)
@ -316,4 +181,3 @@ func authServerUnaryInterceptor() grpc.ServerOption {
func authServerStreamInterceptor() grpc.ServerOption {
return grpc.StreamInterceptor(controller.AuthServerStreamInterceptor)
}

View file

@ -93,6 +93,7 @@ type PeersResponse struct {
PublicKey string `json:"publickey" bson:"publickey"`
Endpoint string `json:"endpoint" bson:"endpoint"`
Address string `json:"address" bson:"address"`
Address6 string `json:"address6" bson:"address6"`
LocalAddress string `json:"localaddress" bson:"localaddress"`
IsGateway bool `json:"isgateway" bson:"isgateway"`
GatewayRange string `json:"gatewayrange" bson:"gatewayrange"`

View file

@ -2,13 +2,10 @@ package mongoconn
import (
"context"
"encoding/json"
"log"
"os"
"net/http"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
"github.com/gravitl/netmaker/config"
"github.com/gravitl/netmaker/servercfg"
)
var Client *mongo.Client
@ -21,53 +18,14 @@ var port string
var opts string
func setVars() {
//defaults
user = "admin"
pass = "password"
host = "localhost"
port = "27017"
opts = "/?authSource=admin"
//override with settings from config file
if config.Config.MongoConn.User != "" {
user = config.Config.MongoConn.User
}
if config.Config.MongoConn.Pass != "" {
pass = config.Config.MongoConn.Pass
}
if config.Config.MongoConn.Host != "" {
host = config.Config.MongoConn.Host
}
if config.Config.MongoConn.Port != "" {
port = config.Config.MongoConn.Port
}
if config.Config.MongoConn.Opts != "" {
opts = config.Config.MongoConn.Opts
}
//override with settings from env
if os.Getenv("MONGO_USER") != "" {
user = os.Getenv("MONGO_USER")
}
if os.Getenv("MONGO_PASS") != "" {
pass = os.Getenv("MONGO_PASS")
}
if os.Getenv("MONGO_HOST") != "" {
host = os.Getenv("MONGO_HOST")
}
if os.Getenv("MONGO_PORT") != "" {
port = os.Getenv("MONGO_PORT")
}
if os.Getenv("MONGO_OPTS") != "" {
opts = os.Getenv("MONGO_OPTS")
}
user = servercfg.GetMongoUser()
pass = servercfg.GetMongoPass()
host = servercfg.GetMongoHost()
port = servercfg.GetMongoPort()
opts = servercfg.GetMongoOpts()
}
//TODO: are we even using this besides at startup? Is it truely necessary?
//TODO: Use config file instead of os.Getenv
func ConnectDatabase() {
log.Println("Database connecting...")
// Set client options
setVars()
@ -80,9 +38,11 @@ func ConnectDatabase() {
opts )
// Connect to MongoDB
log.Println("Connecting to MongoDB at " + host + ":" + port + "...")
client, err := mongo.Connect(context.TODO(), clientOptions)
Client = client
if err != nil {
log.Println("Error encountered connecting to MongoDB. Terminating.")
log.Fatal(err)
}
@ -90,36 +50,14 @@ func ConnectDatabase() {
err = Client.Ping(context.TODO(), nil)
if err != nil {
log.Println("Error encountered pinging MongoDB. Terminating.")
log.Fatal(err)
}
NodeDB = Client.Database("netmaker").Collection("nodes")
NetworkDB = Client.Database("netmaker").Collection("networks")
log.Println("Database Connected.")
}
//TODO: IDK if we're using ConnectDB any more.... I think we're just using Client.Database
//Review and see if this is necessary
// ConnectDB : This is helper function to connect mongoDB
func ConnectDB(db string, targetCollection string) *mongo.Collection {
// Set client options
//clientOptions := options.Client().ApplyURI("mongodb://mongoadmin:mongopassword@localhost:27017/?authSource=admin")
clientOptions := options.Client().ApplyURI("mongodb://" + os.Getenv("MONGO_USER") + ":" +
os.Getenv("MONGO_PASS") + "@" + os.Getenv("MONGO_HOST") + ":" + os.Getenv("MONGO_PORT") + os.Getenv("MONGO_OPTS") )
// Connect to MongoDB
client, err := mongo.Connect(context.TODO(), clientOptions)
if err != nil {
log.Fatal(err)
}
//collection := client.Database("go_rest_api").Collection("wg")
collection := client.Database(db).Collection(targetCollection)
return collection
log.Println("MongoDB Connected.")
}
// ErrorResponse : This is error model.
@ -127,17 +65,3 @@ type ErrorResponse struct {
StatusCode int `json:"status"`
ErrorMessage string `json:"message"`
}
// GetError : This is helper function to prepare error model.
func GetError(err error, w http.ResponseWriter) {
var response = ErrorResponse{
ErrorMessage: err.Error(),
StatusCode: http.StatusInternalServerError,
}
message, _ := json.Marshal(response)
w.WriteHeader(response.StatusCode)
w.Write(message)
}

View file

@ -31,8 +31,11 @@ type NodeConfig struct {
MacAddress string `yaml:"macaddress"`
LocalAddress string `yaml:"localaddress"`
WGAddress string `yaml:"wgaddress"`
WGAddress6 string `yaml:"wgaddress6"`
RoamingOff bool `yaml:"roamingoff"`
DNSOff bool `yaml:"dnsoff"`
IsLocal bool `yaml:"islocal"`
IsDualStack bool `yaml:"isdualstack"`
AllowedIPs string `yaml:"allowedips"`
LocalRange string `yaml:"localrange"`
PostUp string `yaml:"postup"`
@ -43,6 +46,7 @@ type NodeConfig struct {
PrivateKey string `yaml:"privatekey"`
Endpoint string `yaml:"endpoint"`
PostChanges string `yaml:"postchanges"`
IPForwarding string `yaml:"ipforwarding"`
}
//reading in the env file
@ -234,9 +238,3 @@ func ReadConfig(network string) (*ClientConfig, error) {
}
return &cfg, err
}
/*
func init() {
Config = readConfig()
}
*/

View file

@ -72,7 +72,7 @@ func GetFreePort(rangestart int32) (int32, error){
return portno, err
}
func Install(accesskey string, password string, server string, network string, noauto bool, accesstoken string, inputname string) error {
func Install(accesskey string, password string, server string, network string, noauto bool, accesstoken string, inputname string, pubip string, dnsoff bool, ipforward string) error {
tserver := ""
tnetwork := ""
@ -143,6 +143,9 @@ func Install(accesskey string, password string, server string, network string, n
servercfg := cfg.Server
fmt.Println("SERVER SETTINGS:")
nodecfg.DNSOff = dnsoff
nodecfg.IPForwarding = ipforward
if server == "" {
if servercfg.Address == "" && tserver == "" {
log.Fatal("no server provided")
@ -252,6 +255,9 @@ func Install(accesskey string, password string, server string, network string, n
}
fmt.Println(" Local Address: " + localaddress)
if pubip != "" && pubip != "nopubip" {
endpoint = pubip
} else {
if nodecfg.Endpoint == "" {
if islocal && localaddress != "" {
endpoint = localaddress
@ -269,6 +275,7 @@ func Install(accesskey string, password string, server string, network string, n
endpoint = nodecfg.Endpoint
fmt.Println("Endpoint set in config. Setting to address: " + endpoint)
}
}
fmt.Println(" Endpoint: " + endpoint)
@ -404,6 +411,7 @@ func Install(accesskey string, password string, server string, network string, n
fmt.Println("NODE RECIEVED SETTINGS: ")
fmt.Println(" Password: " + node.Password)
fmt.Println(" WG Address: " + node.Address)
fmt.Println(" WG ipv6 Address: " + node.Address6)
fmt.Println(" Network: " + node.Nodenetwork)
fmt.Println(" Public Endpoint: " + node.Endpoint)
fmt.Println(" Local Address: " + node.Localaddress)
@ -416,8 +424,12 @@ func Install(accesskey string, password string, server string, network string, n
fmt.Println(" Public Key: " + node.Publickey)
fmt.Println(" Mac Address: " + node.Macaddress)
fmt.Println(" Is Local?: " + strconv.FormatBool(node.Islocal))
fmt.Println(" Is Dual Stack?: " + strconv.FormatBool(node.Isdualstack))
fmt.Println(" Local Range: " + node.Localrange)
if node.Dnsoff==true && !nodecfg.DNSOff {
nodecfg.DNSOff = true
}
if !islocal && node.Islocal && node.Localrange != "" {
fmt.Println("Resetting local settings for local network.")
node.Localaddress, err = getLocalIP(node.Localrange)
@ -442,7 +454,7 @@ func Install(accesskey string, password string, server string, network string, n
}
}
peers, hasGateway, gateways, err := getPeers(node.Macaddress, network, server)
peers, hasGateway, gateways, err := getPeers(node.Macaddress, network, server, node.Isdualstack)
if err != nil {
return err
@ -514,7 +526,7 @@ func getLocalIP(localrange string) (string, error) {
func getPublicIP() (string, error) {
iplist := []string{"https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
iplist := []string{"http://ip.client.gravitl.com","https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
endpoint := ""
var err error
for _, ipserver := range iplist {
@ -588,9 +600,19 @@ func modConfig(node *nodepb.Node) error{
if node.Address != ""{
nodecfg.WGAddress = node.Address
}
if node.Address6 != ""{
nodecfg.WGAddress6 = node.Address6
}
if node.Postchanges != "" {
nodecfg.PostChanges = node.Postchanges
}
if node.Dnsoff == true {
nodecfg.DNSOff = node.Dnsoff
}
if node.Isdualstack == true {
nodecfg.IsDualStack = true
}
if node.Localrange != "" && node.Islocal {
nodecfg.IsLocal = true
nodecfg.LocalRange = node.Localrange
@ -638,6 +660,7 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
nodecfg := modcfg.Node
servercfg := modcfg.Server
fmt.Println("beginning local WG config")
@ -659,7 +682,14 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
if node.Address == "" {
log.Fatal("no address to configure")
}
nameserver := servercfg.Address
nameserver = strings.Split(nameserver, ":")[0]
network := node.Nodenetwork
if nodecfg.Network != "" {
network = nodecfg.Network
} else if node.Nodenetwork != "" {
network = node.Nodenetwork
}
cmdIPDevLinkAdd := &exec.Cmd {
Path: ipExec,
Args: []string{ ipExec, "link", "add", "dev", ifacename, "type", "wireguard" },
@ -673,7 +703,6 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
Stderr: os.Stdout,
}
currentiface, err := net.InterfaceByName(ifacename)
@ -733,6 +762,33 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
fmt.Printf("This is inconvenient: %v", err)
}
}
//=========DNS Setup==========\\
if nodecfg.DNSOff != true {
_, err := exec.LookPath("resolvectl")
if err != nil {
fmt.Println(err)
fmt.Println("WARNING: resolvectl not present. Unable to set dns. Install resolvectl or run manually.")
} else {
_, err = exec.Command("resolvectl", "domain", ifacename, "~"+network).Output()
if err != nil {
fmt.Println(err)
fmt.Println("WARNING: Error encountered setting dns. Aborted setting dns.")
} else {
_, err = exec.Command("resolvectl", "default-route", ifacename, "false").Output()
if err != nil {
fmt.Println(err)
fmt.Println("WARNING: Error encountered setting dns. Aborted setting dns.")
} else {
_, err = exec.Command("resolvectl", "dns", ifacename, nameserver).Output()
fmt.Println(err)
}
}
}
}
//=========End DNS Setup=======\\
cmdIPLinkUp := &exec.Cmd {
Path: ipExec,
Args: []string{ ipExec, "link", "set", "up", "dev", ifacename},
@ -773,7 +829,15 @@ func initWireguard(node *nodepb.Node, privkey string, peers []wgtypes.PeerConfig
if err != nil {
fmt.Println("Error encountered adding gateway: " + err.Error())
}
}
}
if (node.Address6 != "" && node.Isdualstack) {
fmt.Println("Adding address: " + node.Address6)
out, err := exec.Command(ipExec, "address", "add", "dev", ifacename, node.Address6+"/64").Output()
if err != nil {
fmt.Println(out)
fmt.Println("Error encountered adding ipv6: " + err.Error())
}
}
return err
}
@ -867,7 +931,7 @@ func setWGConfig(network string) error {
nodecfg := cfg.Node
node := getNode(network)
peers, hasGateway, gateways, err := getPeers(node.Macaddress, nodecfg.Network, servercfg.Address)
peers, hasGateway, gateways, err := getPeers(node.Macaddress, nodecfg.Network, servercfg.Address, node.Isdualstack)
if err != nil {
return err
}
@ -952,6 +1016,23 @@ func CheckIn(network string) error {
setupcheck := true
ipchange := false
if !(nodecfg.IPForwarding == "off") {
out, err := exec.Command("sysctl", "net.ipv4.ip_forward").Output()
if err != nil {
fmt.Println(err)
fmt.Println("WARNING: Error encountered setting ip forwarding. This can break functionality.")
} else {
s := strings.Fields(string(out))
if s[2] != "1" {
_, err = exec.Command("sysctl", "-w", "net.ipv4.ip_forward=1").Output()
if err != nil {
fmt.Println(err)
fmt.Println("WARNING: Error encountered setting ip forwarding. You may want to investigate this.")
}
}
}
}
if !nodecfg.RoamingOff {
if !nodecfg.IsLocal {
fmt.Println("Checking to see if public addresses have changed")
@ -1220,6 +1301,7 @@ func getNode(network string) nodepb.Node {
node.Nodenetwork = nodecfg.Network
node.Localaddress = nodecfg.LocalAddress
node.Address = nodecfg.WGAddress
node.Address6 = nodecfg.WGAddress6
node.Listenport = nodecfg.Port
node.Keepalive = nodecfg.KeepAlive
node.Postup = nodecfg.PostUp
@ -1228,9 +1310,8 @@ func getNode(network string) nodepb.Node {
node.Macaddress = nodecfg.MacAddress
node.Endpoint = nodecfg.Endpoint
node.Password = nodecfg.Password
//spew.Dump(node)
node.Dnsoff = nodecfg.DNSOff
node.Isdualstack = nodecfg.IsDualStack
return node
}
@ -1371,7 +1452,7 @@ func DeleteInterface(ifacename string, postdown string) error{
return err
}
func getPeers(macaddress string, network string, server string) ([]wgtypes.PeerConfig, bool, []string, error) {
func getPeers(macaddress string, network string, server string, dualstack bool) ([]wgtypes.PeerConfig, bool, []string, error) {
//need to implement checkin on server side
hasGateway := false
var gateways []string
@ -1470,6 +1551,13 @@ func getPeers(macaddress string, network string, server string) ([]wgtypes.PeerC
allowedips = append(allowedips, *ipnet)
}
}
if res.Peers.Address6 != "" && dualstack {
var addr6 = net.IPNet{
IP: net.ParseIP(res.Peers.Address6),
Mask: net.CIDRMask(128, 128),
}
allowedips = append(allowedips, addr6)
}
if keepalive != 0 {
peer = wgtypes.PeerConfig{
PublicKey: pubkey,

View file

@ -6,6 +6,7 @@ import (
"io/ioutil"
"path/filepath"
"io"
"strings"
"log"
"os"
"os/exec"
@ -20,6 +21,27 @@ func FileExists(f string) bool {
return !info.IsDir()
}
func SetDNS(nameserver string) error {
bytes, err := ioutil.ReadFile("/etc/resolv.conf")
if err != nil {
return err
}
resolvstring := string(bytes)
// //check whether s contains substring text
hasdns := strings.Contains(resolvstring, nameserver)
if hasdns {
return nil
}
resolv, err := os.OpenFile("/etc/resolv.conf",os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return err
}
defer resolv.Close()
_, err = resolv.WriteString("nameserver " + nameserver + "\n")
return err
}
func ConfigureSystemD(network string) error {
/*
path, err := os.Getwd()
@ -118,40 +140,32 @@ WantedBy=timers.target
return err
}
}
sysExec, err := exec.LookPath("systemctl")
//sysExec, err := exec.LookPath("systemctl")
cmdSysEnableService := &exec.Cmd {
cmdSysEnableService := exec.Command("systemctl", "enable", "netclient@.service")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "enable", "netclient@.service" },
Stdout: os.Stdout,
Stderr: os.Stdout,
}
/*
cmdSysStartService := &exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "start", "netclient@.service"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
*/
cmdSysDaemonReload := &exec.Cmd {
}*/
cmdSysDaemonReload := exec.Command("systemctl", "daemon-reload")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "daemon-reload"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
cmdSysEnableTimer := &exec.Cmd {
}*/
cmdSysEnableTimer := exec.Command("systemctl", "enable", "netclient-"+network+".timer")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "enable", "netclient-"+network+".timer" },
Stdout: os.Stdout,
Stderr: os.Stdout,
}
cmdSysStartTimer := &exec.Cmd {
}*/
cmdSysStartTimer := exec.Command("systemctl", "start", "netclient-"+network+".timer")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "start", "netclient-"+network+".timer"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
}*/
err = cmdSysEnableService.Run()
if err != nil {
@ -191,7 +205,7 @@ func isOnlyService(network string) (bool, error) {
}
func RemoveSystemDServices(network string) error {
sysExec, err := exec.LookPath("systemctl")
//sysExec, err := exec.LookPath("systemctl")
fullremove, err := isOnlyService(network)
@ -199,36 +213,36 @@ func RemoveSystemDServices(network string) error {
fmt.Println(err)
}
cmdSysDisableService := &exec.Cmd {
cmdSysDisableService := exec.Command("systemctl","disable","netclient@.service")/* &exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "disable", "netclient@.service"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
cmdSysDaemonReload := &exec.Cmd {
}*/
cmdSysDaemonReload := exec.Command("systemctl","daemon-reload")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "daemon-reload"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
cmdSysResetFailed := &exec.Cmd {
}*/
cmdSysResetFailed := exec.Command("systemctl","reset-failed")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "reset-failed"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
cmdSysStopTimer := &exec.Cmd {
}*/
cmdSysStopTimer := exec.Command("systemctl", "stop", "netclient-"+network+".timer")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "stop", "netclient-"+network+".timer" },
Stdout: os.Stdout,
Stderr: os.Stdout,
}
cmdSysDisableTimer := &exec.Cmd {
}*/
cmdSysDisableTimer := exec.Command("systemctl", "disable", "netclient-"+network+".timer")/*&exec.Cmd {
Path: sysExec,
Args: []string{ sysExec, "disable", "netclient-"+network+".timer"},
Stdout: os.Stdout,
Stderr: os.Stdout,
}
}*/
//err = cmdSysStopService.Run()
if err != nil {

View file

@ -39,8 +39,10 @@ func main() {
tname := flag.String("name", "noname", "give the node a name at runtime")
tserver := flag.String("s", "localhost:50051", "The location (including port) of the remote gRPC server.")
tnetwork := flag.String("n", "nonetwork", "The node network you are attempting to join.")
tdnsoff := flag.Bool("dnsoff", false, "DNS Mode. If true, netclient will not alter system dns. false by default.")
tpublicip := flag.String("ip4", "nopubip", "The node network you are attempting to join.")
tnoauto := flag.Bool("na", false, "No auto mode. If true, netmclient will not be installed as a system service and you will have to retrieve updates manually via checkin command.")
tnoforward := flag.Bool("nf", false, "No Forward mode. If true, netclient will not check for IP forwarding. This may break functionality")
tipforward := flag.String("nf", "on", "No Forward mode. If true, netclient will not check for IP forwarding. This may break functionality")
command := flag.String("c", "required", "The command to run")
@ -89,7 +91,7 @@ func main() {
fmt.Println("Required, '-n'. No network provided. Exiting.")
os.Exit(1)
}
/*
if !*tnoforward {
forward := exec.Command("sysctl", "net.ipv4.ip_forward")
out, err := forward.Output()
@ -106,9 +108,9 @@ func main() {
log.Fatal("It is recommended to enable IP Forwarding. Current status is: " + s[2] + ", but should be 1. if you would like to run without IP Forwarding, re-run with flag '-nf true'")
}
}
*/
fmt.Println("Beginning agent installation.")
err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname)
err := functions.Install(*taccesskey, *tpassword, *tserver, *tnetwork, *tnoauto, *taccesstoken, *tname, *tpublicip, *tdnsoff, *tipforward)
if err != nil {
fmt.Println("Error encountered while installing.")
if !strings.Contains(err.Error(), "ALREADY_INSTALLED") {

View file

@ -0,0 +1,60 @@
#!/bin/sh
set -e
[ -z "$SERVER_DOMAIN" ] && echo "Need to set SERVER_DOMAIN (format: 1.2.3.4 or mybackend.com)" && exit 1;
docker volume create mongovol && docker run -d --name mongodb -v mongovol:/data/db --network host -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=mongopass mongo --bind_ip 0.0.0.0
mkdir -p /etc/netmaker/config/environments
wget -O /etc/netmaker/netmaker https://github.com/gravitl/netmaker/releases/download/latest/netmaker
chmod +x /etc/netmaker/netmaker
cat >/etc/netmaker/config/environments/dev.yaml<<EOL
server:
host: "$SERVER_DOMAIN"
apiport: "8081"
grpcport: "50051"
masterkey: "secretkey"
allowedorigin: "*"
restbackend: true
agentbackend: true
defaultnetname: "default"
defaultnetrange: "10.10.10.0/24"
createdefault: true
mongoconn:
user: "mongoadmin"
pass: "mongopass"
host: "localhost"
port: "27017"
opts: '/?authSource=admin'
EOL
cat >/etc/netmaker/config/Corefile<<EOL
. {
hosts /root/netmaker.hosts
}
EOL
cat >/etc/systemd/system/netmaker.service<<EOL
[Unit]
Description=Netmaker Server
After=network.target
[Service]
Type=simple
Restart=on-failure
WorkingDirectory=/etc/netmaker
ExecStart=/etc/netmaker/netmaker
[Install]
WantedBy=multi-user.target
EOL
systemctl daemon-reload
systemctl start netmaker.service
docker run -d --name netmaker-ui -p 80:80 -e BACKEND_URL="http://$SERVER_DOMAIN:8081" gravitl/netmaker-ui:v0.2
docker run -d --name coredns --restart=always --volume=/etc/netmaker/config/:/root/ -p 52:53/udp coredns/coredns -conf /root/Corefile

101
scripts/netmaker-install-local.sh Executable file
View file

@ -0,0 +1,101 @@
#!/bin/sh
set -x
[ -z "$SERVER_DOMAIN" ] && echo "Need to set SERVER_DOMAIN (format: 1.2.3.4 or mybackend.com)" && exit 1;
install() {
docker volume create mongovol && docker run -d --name mongodb -v mongovol:/data/db -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=mongoadmin -e MONGO_INITDB_ROOT_PASSWORD=mongopass mongo
echo "Giving Mongo Time to Start"
sleep 10
echo "Installing Netmaker API"
mkdir -p /etc/netmaker/config/environments
mkdir -p /etc/netmaker/config/dnsconfig
cp ../netmaker /etc/netmaker/netmaker
chmod +x /etc/netmaker/netmaker
cat >/etc/netmaker/config/environments/dev.yaml<<EOL
server:
host: "$SERVER_DOMAIN"
apiport: "8081"
grpcport: "50051"
masterkey: "secretkey"
allowedorigin: "*"
restbackend: true
agentbackend: true
defaultnetname: "default"
defaultnetrange: "10.10.10.0/24"
createdefault: true
mongoconn:
user: "mongoadmin"
pass: "mongopass"
host: "127.0.0.1"
port: "27017"
opts: '/?authSource=admin'
EOL
cat >/etc/netmaker/config/dnsconfig/Corefile<<EOL
. {
hosts ./root/netmaker.hosts {
fallthrough
}
forward . 8.8.8.8 8.8.4.4
log
}
EOL
cat >/etc/systemd/system/netmaker.service<<EOL
[Unit]
Description=Netmaker Server
After=network.target
[Service]
Type=simple
Restart=on-failure
WorkingDirectory=/etc/netmaker
ExecStart=/etc/netmaker/netmaker
[Install]
WantedBy=multi-user.target
EOL
systemctl daemon-reload
systemctl start netmaker.service
sudo docker pull coredns/coredns
sudo docker pull gravitl/netmaker-ui:v0.3
systemctl stop systemd-resolved
systemctl disable systemd-resolved
echo "Running CoreDNS"
sudo docker run -d --name coredns --restart=always --volume=/etc/netmaker/config/dnsconfig/:/root/ -p 53:53/udp coredns/coredns -conf /root/Corefile
echo "Running UI"
sudo docker run -d --name netmaker-ui -p 80:80 -e BACKEND_URL="http://$SERVER_DOMAIN:8081" gravitl/netmaker-ui:v0.3
echo "Setup Complete"
}
cleanup() {
sudo docker kill mongodb || true
sudo docker rm mongodb || true
sudo docker volume rm mongovol || true
sudo docker kill coredns || true
sudo docker rm coredns || true
sudo docker kill netmaker-ui || true
sudo docker rm netmaker-ui || true
sudo netclient -c remove -n default || true
sudo rm -rf /etc/systemd/system/netmaker.service || true
sudo rm -rf /etc/netmaker || true
sudo systemctl enable systemd-resolved
sudo systemctl start systemd-resolved
sleep 5
sudo systemctl restart systemd-resolved
}
trap cleanup ERR
cleanup
install

12
scripts/uninstall-netclient.sh Executable file
View file

@ -0,0 +1,12 @@
#!/bin/sh
set -x
echo "Starting."
sudo netclient -c remove-all || true
sudo rm -rf /usr/local/bin/netclient || true
sudo rm -rf /etc/netclient|| true
find /etc/systemd/system/ -name 'netclient*' -exec rm {} \;
sudo systemctl daemon-reload || true
echo "Done."

26
scripts/uninstall-netmaker.sh Executable file
View file

@ -0,0 +1,26 @@
#!/bin/sh
set -x
echo "Starting."
sudo docker kill mongodb || true
sudo docker rm mongodb || true
sudo docker volume rm mongovol || true
sudo docker volume rm `docker volume ls -q -f dangling=true` || true
sudo docker kill coredns || true
sudo docker rm coredns || true
sudo docker kill netmaker-ui || true
sudo docker rm netmaker-ui || true
sudo netclient -c remove -n default || true
sudo rm -rf /etc/systemd/system/netmaker.service || true
sudo rm -rf /etc/netmaker || true
sudo rm -rf /usr/local/bin/netclient || true
sudo rm -rf /etc/netclient|| true
find /etc/systemd/system/ -name 'netclient*' -exec rm {} \;
sudo systemctl daemon-reload || true
sudo systemctl enable systemd-resolved || true
sudo systemctl start systemd-resolved || true
sleep 5
sudo systemctl restart systemd-resolved || true
echo "Done."

53
servercfg/mongoconf.go Normal file
View file

@ -0,0 +1,53 @@
package servercfg
import (
"github.com/gravitl/netmaker/config"
"os"
)
func GetMongoUser() string {
user := "mongoadmin"
if os.Getenv("MONGO_ADMIN") != "" {
user = os.Getenv("MONGO_ADMIN")
} else if config.Config.MongoConn.User != "" {
user = config.Config.MongoConn.User
}
return user
}
func GetMongoPass() string {
pass := "mongopass"
if os.Getenv("MONGO_PASS") != "" {
pass = os.Getenv("MONGO_PASS")
} else if config.Config.MongoConn.Pass != "" {
pass = config.Config.MongoConn.Pass
}
return pass
}
func GetMongoHost() string {
host := "127.0.0.1"
if os.Getenv("MONGO_HOST") != "" {
host = os.Getenv("MONGO_HOST")
} else if config.Config.MongoConn.Host != "" {
host = config.Config.MongoConn.Host
}
return host
}
func GetMongoPort() string {
port := "27017"
if os.Getenv("MONGO_PORT") != "" {
port = os.Getenv("MONGO_PORT")
} else if config.Config.MongoConn.Port != "" {
port = config.Config.MongoConn.Port
}
return port
}
func GetMongoOpts() string {
opts := "/?authSource=admin"
if os.Getenv("MONGO_OPTS") != "" {
opts = os.Getenv("MONGO_OPTS")
} else if config.Config.MongoConn.Opts != "" {
opts = config.Config.MongoConn.Opts
}
return opts
}

199
servercfg/serverconf.go Normal file
View file

@ -0,0 +1,199 @@
package servercfg
import (
"github.com/gravitl/netmaker/config"
"net/http"
"io/ioutil"
"os"
"errors"
)
func SetHost() error {
remoteip, err := GetPublicIP()
if err != nil {
return err
}
os.Setenv("SERVER_HOST", remoteip)
return nil
}
func GetConfig() config.ServerConfig {
var cfg config.ServerConfig
cfg.APIHost = GetAPIHost()
cfg.APIPort = GetAPIPort()
cfg.GRPCHost = GetGRPCHost()
cfg.GRPCPort = GetGRPCPort()
cfg.MasterKey = "(hidden)"
cfg.AllowedOrigin = GetAllowedOrigin()
cfg.RestBackend = "off"
if IsRestBackend() {
cfg.RestBackend = "on"
}
cfg.AgentBackend = "off"
if IsAgentBackend() {
cfg.AgentBackend = "on"
}
cfg.ClientMode = "off"
if IsClientMode() {
cfg.ClientMode = "on"
}
cfg.DNSMode = "off"
if IsDNSMode() {
cfg.DNSMode = "on"
}
cfg.DisableRemoteIPCheck = "off"
if DisableRemoteIPCheck() {
cfg.DisableRemoteIPCheck = "on"
}
return cfg
}
func GetAPIHost() string {
serverhost := "127.0.0.1"
if os.Getenv("SERVER_HTTP_HOST") != "" {
serverhost = os.Getenv("SERVER_HTTP_HOST")
} else if config.Config.Server.APIHost != "" {
serverhost = config.Config.Server.APIHost
} else if os.Getenv("SERVER_HOST") != "" {
serverhost = os.Getenv("SERVER_HOST")
}
return serverhost
}
func GetAPIPort() string {
apiport := "8081"
if os.Getenv("API_PORT") != "" {
apiport = os.Getenv("API_PORT")
} else if config.Config.Server.APIPort != "" {
apiport = config.Config.Server.APIPort
}
return apiport
}
func GetGRPCHost() string {
serverhost := "127.0.0.1"
if os.Getenv("SERVER_GRPC_HOST") != "" {
serverhost = os.Getenv("SERVER_GRPC_HOST")
} else if config.Config.Server.GRPCHost != "" {
serverhost = config.Config.Server.GRPCHost
} else if os.Getenv("SERVER_HOST") != "" {
serverhost = os.Getenv("SERVER_HOST")
}
return serverhost
}
func GetGRPCPort() string {
grpcport := "50051"
if os.Getenv("GRPC_PORT") != "" {
grpcport = os.Getenv("GRPC_PORT")
} else if config.Config.Server.GRPCPort != "" {
grpcport = config.Config.Server.GRPCPort
}
return grpcport
}
func GetMasterKey() string {
key := "secretkey"
if os.Getenv("MASTER_KEY") != "" {
key = os.Getenv("MASTER_KEY")
} else if config.Config.Server.MasterKey != "" {
key = config.Config.Server.MasterKey
}
return key
}
func GetAllowedOrigin() string {
allowedorigin := "*"
if os.Getenv("CORS_ALLOWED_ORIGIN") != "" {
allowedorigin = os.Getenv("CORS_ALLOWED_ORIGIN")
} else if config.Config.Server.AllowedOrigin != "" {
allowedorigin = config.Config.Server.AllowedOrigin
}
return allowedorigin
}
func IsRestBackend() bool {
isrest := true
if os.Getenv("REST_BACKEND") != "" {
if os.Getenv("REST_BACKEND") == "off" {
isrest = false
}
} else if config.Config.Server.RestBackend != "" {
if config.Config.Server.RestBackend == "off" {
isrest = false
}
}
return isrest
}
func IsAgentBackend() bool {
isagent := true
if os.Getenv("AGENT_BACKEND") != "" {
if os.Getenv("AGENT_BACKEND") == "off" {
isagent = false
}
} else if config.Config.Server.AgentBackend != "" {
if config.Config.Server.AgentBackend == "off" {
isagent = false
}
}
return isagent
}
func IsClientMode() bool {
isclient := true
if os.Getenv("CLIENT_MODE") != "" {
if os.Getenv("CLIENT_MODE") == "off" {
isclient = false
}
} else if config.Config.Server.ClientMode != "" {
if config.Config.Server.ClientMode == "off" {
isclient = false
}
}
return isclient
}
func IsDNSMode() bool {
isdns := true
if os.Getenv("DNS_MODE") != "" {
if os.Getenv("DNS_MODE") == "off" {
isdns = false
}
} else if config.Config.Server.DNSMode != "" {
if config.Config.Server.DNSMode == "off" {
isdns = false
}
}
return isdns
}
func DisableRemoteIPCheck() bool {
disabled := false
if os.Getenv("DISABLE_REMOTE_IP_CHECK") != "" {
if os.Getenv("DISABLE_REMOTE_IP_CHECK") == "on" {
disabled = true
}
} else if config.Config.Server.DisableRemoteIPCheck != "" {
if config.Config.Server.DisableRemoteIPCheck == "on" {
disabled= true
}
}
return disabled
}
func GetPublicIP() (string, error) {
endpoint := ""
var err error
iplist := []string{"http://ip.server.gravitl.com", "https://ifconfig.me", "http://api.ipify.org", "http://ipinfo.io/ip"}
for _, ipserver := range iplist {
resp, err := http.Get(ipserver)
if err != nil {
continue
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK {
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
continue
}
endpoint = string(bodyBytes)
break
}
}
if err == nil && endpoint == "" {
err = errors.New("Public Address Not Found.")
}
return endpoint, err
}

View file

@ -2,17 +2,65 @@ package serverctl
import (
"fmt"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/mongoconn"
"github.com/gravitl/netmaker/servercfg"
"io"
"time"
"context"
"errors"
"net/http"
"os"
"os/exec"
)
func CreateDefaultNetwork() (bool, error) {
fmt.Println("Creating default network...")
iscreated := false
exists, err := functions.NetworkExists("default")
if exists || err != nil {
fmt.Println("Default network already exists. Skipping...")
return iscreated, err
} else {
var network models.Network
network.NetID = "default"
network.AddressRange = "10.10.10.0/24"
network.DisplayName = "default"
network.SetDefaults()
network.SetNodesLastModified()
network.SetNetworkLastModified()
network.KeyUpdateTimeStamp = time.Now().Unix()
priv := false
network.IsLocal = &priv
network.KeyUpdateTimeStamp = time.Now().Unix()
allow := true
network.AllowManualSignUp = &allow
fmt.Println("Creating default network.")
collection := mongoconn.Client.Database("netmaker").Collection("networks")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
// insert our network into the network table
_, err = collection.InsertOne(ctx, network)
defer cancel()
}
if err == nil {
iscreated = true
}
return iscreated, err
}
func DownloadNetclient() error {
/*
// Get the data
resp, err := http.Get("https://github.com/gravitl/netmaker/releases/download/latest/netclient")
if err != nil {
@ -23,16 +71,58 @@ func DownloadNetclient() error {
// Create the file
out, err := os.Create("/etc/netclient/netclient")
*/
if !FileExists("/etc/netclient/netclient") {
_, err := copy("./netclient/netclient", "/etc/netclient/netclient")
if err != nil {
fmt.Println("could not create /etc/netclient")
return err
}
defer out.Close()
}
//defer out.Close()
// Write the body to file
_, err = io.Copy(out, resp.Body)
return err
//_, err = io.Copy(out, resp.Body)
return nil
}
func FileExists(f string) bool {
info, err := os.Stat(f)
if os.IsNotExist(err) {
return false
}
return !info.IsDir()
}
func copy(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {
return 0, err
}
if !sourceFileStat.Mode().IsRegular() {
return 0, fmt.Errorf("%s is not a regular file", src)
}
source, err := os.Open(src)
if err != nil {
return 0, err
}
defer source.Close()
destination, err := os.Create(dst)
if err != nil {
return 0, err
}
defer destination.Close()
nBytes, err := io.Copy(destination, source)
err = os.Chmod(dst, 0755)
if err != nil {
fmt.Println(err)
}
return nBytes, err
}
func RemoveNetwork(network string) (bool, error) {
_, err := os.Stat("/etc/netclient/netclient")
if err != nil {
@ -50,7 +140,13 @@ func RemoveNetwork(network string) (bool, error) {
}
func AddNetwork(network string) (bool, error) {
_, err := os.Stat("/etc/netclient")
pubip, err := servercfg.GetPublicIP()
if err != nil {
fmt.Println("could not get public IP.")
return false, err
}
_, err = os.Stat("/etc/netclient")
if os.IsNotExist(err) {
os.Mkdir("/etc/netclient", 744)
} else if err != nil {
@ -78,7 +174,7 @@ func AddNetwork(network string) (bool, error) {
return false, err
}
fmt.Println("Client is ready. Running install.")
out, err := exec.Command("/etc/netclient/netclient","-c","install","-t",token,"-name","netmaker").Output()
out, err := exec.Command("/etc/netclient/netclient","-c","install","-t",token,"-name","netmaker","-ip4",pubip).Output()
fmt.Println(string(out))
if err != nil {
return false, errors.New(string(out) + err.Error())

View file

@ -2,7 +2,6 @@ package main
import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
@ -46,20 +45,6 @@ func TestMain(m *testing.M) {
var waitgroup sync.WaitGroup
waitgroup.Add(1)
go controller.HandleRESTRequests(&waitgroup)
var gconf models.GlobalConfig
gconf.ServerGRPC = "localhost:8081"
gconf.PortGRPC = "50051"
//err := SetGlobalConfig(gconf)
collection := mongoconn.Client.Database("netmaker").Collection("config")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
//create, _, err := functions.GetGlobalConfig()
_, err := collection.InsertOne(ctx, gconf)
if err != nil {
panic("could not create config store")
}
//wait for http server to start
time.Sleep(time.Second * 1)
os.Exit(m.Run())
}