Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
afeiszli committed Mar 25, 2021
0 parents commit 0bacbd9
Show file tree
Hide file tree
Showing 56 changed files with 9,205 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
netmaker
netclient/netclient
netclient/files/netclient
31 changes: 31 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#first stage - builder

FROM golang:1.14-stretch as builder

COPY . /app

WORKDIR /app

ENV GO111MODULE=auto

RUN CGO_ENABLED=0 GOOS=linux go build -o app main.go


#second stage

FROM alpine:latest

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

EXPOSE 8081
EXPOSE 50051

CMD ["./app"]

60 changes: 60 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@

<p align="center">
<img src="netmaker.png"><break/>
</p>
<p align="center">
<i>Connect any computers together over a secure, fast, private network, and manage multiple networks from a central server.</i>
</p>

## What is Netmaker?
Netmaker lets you easily create secure virtual networks: Just spin up a Netmaker server and install the agent on your computers. Netmaker relies on WireGuard to create encrypted tunnels between every node in your virtual network, creating a mesh overlay. Netmaker takes the work out of manually configuring machines and updating them every time something changes in your network. The agents are self-updating and pull necessary changes from the server.

Netmaker also has a handy dandy UI, which you can find in [this repo](https://github.com/falconcat-inc/WireCat-UI). We recommend deploying the UI alongside the server to make the experience easier and better.

## Why Netmaker?
1. Create a flat, secure network between multiple/hybrid cloud environments
2. Integrate central and edge services + IoT
3. Secure an office or home network while providing remote connectivity
4. Manage cryptocurrency proof-of-stake machines
5. Provide an additional layer of security on an existing network
6. Encrypt Kubernetes inter-node communications
7. Secure site-to-site connections

<p align="center">
<img src="mesh-diagram.png">
</p>

## Docs
**For more information, please read the docs, or check out the Quick Start below:**

- [Getting Started](docs/GETTING_STARTED.md)
- [API Documentation](docs/API.md)
- [Product Roadmap](docs/ROADMAP.md)
- [Contributing](docs/CONTRIBUTING.md)


## Quick Start

Setup Docker (Prereq):
1. Create an access token on github with artifact access.
2. On your VPS, create a file in the home dir called TOKEN.txt with the value of your token inside.
3. `cat ~/TOKEN.txt | sudo docker login https://docker.pkg.github.com -u GITHUB_USERNAME --password-stdin`

Setup Server:
1. Clone this repo or just copy contents of "docker-compose.yml" to a machine with a public IP.
2. In docker-compose.yml, change BACKEND_URL to the public IP ofthat machine.
3. Run `sudo docker-compose up`
4. Navigate to your IP and you should see the WireCat UI asking for a new admin user (if not or if it takes you straight to login screen without asking for user creation, investigate the error).
5. Create the admin user
6. Click "Create Group" and fill out the details (group == network)
7. You are now ready to begin using WireCat. Create a key or "allow manual node sign up."

Run on each machine in network:
1. Get the binary: `sudo wget 52.55.6.84:8081/meshclient/files/meshclient`
2. Make it executable: `sudo chmod +x meshclient`
3. Run the install command: `sudo ./meshclient -c install -g <group name> -s <server:port> -k <key value>`


#### LICENSE

Netmaker's source code and all artifacts in this repository are freely available. All versions are published under the Server Side Public License (SSPL), version 1, which can be found under the "licensing" directory: [LICENSE.txt](licensing/LICENSE.txt).
79 changes: 79 additions & 0 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
//Environment file for getting variables
//Currently the only thing it does is set the master password
//Should probably have it take over functions from OS such as port and mongodb connection details
//Reads from the config/environments/dev.yaml file by default
//TODO: Add vars for mongo and remove from OS vars in mongoconn
package config

import (
"os"
"fmt"
"log"
"gopkg.in/yaml.v3"
)

//setting dev by default
func getEnv() string {

env := os.Getenv("APP_ENV")

if len(env) == 0 {
return "dev"
}

return env
}

// Config : application config stored as global variable
var Config *EnvironmentConfig

// EnvironmentConfig :
type EnvironmentConfig struct {
Server ServerConfig `yaml:"server"`
MongoConn MongoConnConfig `yaml:"mongoconn"`
}

// ServerConfig :
type ServerConfig struct {
Host string `yaml:"host"`
ApiPort string `yaml:"apiport"`
GrpcPort string `yaml:"grpcport"`
MasterKey string `yaml:"masterkey"`
AllowedOrigin string `yaml:"allowedorigin"`
RestBackend bool `yaml:"restbackend"`
AgentBackend bool `yaml:"agentbackend"`
}

type MongoConnConfig struct {
User string `yaml:"user"`
Pass string `yaml:"pass"`
Host string `yaml:"host"`
Port string `yaml:"port"`
Opts string `yaml:"opts"`
}


//reading in the env file
func readConfig() *EnvironmentConfig {
file := fmt.Sprintf("config/environments/%s.yaml", getEnv())
f, err := os.Open(file)
if err != nil {
log.Fatal(err)
os.Exit(2)
}
defer f.Close()

var cfg EnvironmentConfig
decoder := yaml.NewDecoder(f)
err = decoder.Decode(&cfg)
if err != nil {
log.Fatal(err)
os.Exit(2)
}
return &cfg

}

func init() {
Config = readConfig()
}
14 changes: 14 additions & 0 deletions config/environments/dev.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
server:
host: "localhost"
apiport: "8081"
grpcport: "50051"
masterkey: "secretkey"
allowedorigin: "*"
restbackend: true
agentbackend: true
mongoconn:
user: "mongoadmin"
pass: "mongopass"
host: "localhost"
port: "27017"
opts: '/?authSource=admin'
Binary file added controllers/.nodeHttpController.go.swp
Binary file not shown.
154 changes: 154 additions & 0 deletions controllers/authGrpc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package controller

import (
"errors"
"context"
"golang.org/x/crypto/bcrypt"
"time"
nodepb "github.com/gravitl/netmaker/grpc"
"github.com/gravitl/netmaker/models"
"github.com/gravitl/netmaker/functions"
"github.com/gravitl/netmaker/mongoconn"
"go.mongodb.org/mongo-driver/bson"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/grpc/codes"

)

func AuthServerUnaryInterceptor(ctx context.Context,
req interface{},
info *grpc.UnaryServerInfo,
handler grpc.UnaryHandler) (interface{}, error) {
// Skip authorize when GetJWT is requested

if info.FullMethod != "/node.NodeService/Login" {
if info.FullMethod != "/node.NodeService/CreateNode" {

err := grpcAuthorize(ctx)
if err != nil {
return nil, err
}
}
}


// Calls the handler
h, err := handler(ctx, req)

return h, err
}
func AuthServerStreamInterceptor(
srv interface{},
stream grpc.ServerStream,
info *grpc.StreamServerInfo,
handler grpc.StreamHandler,
) error {
if info.FullMethod == "/node.NodeService/GetPeers" {
if err := grpcAuthorize(stream.Context()); err != nil {
return err
}
}


// Calls the handler
return handler(srv, stream)
}

func grpcAuthorize(ctx context.Context) error {


md, ok := metadata.FromIncomingContext(ctx)

if !ok {
return status.Errorf(codes.InvalidArgument, "Retrieving metadata is failed")
}

authHeader, ok := md["authorization"]
if !ok {
return status.Errorf(codes.Unauthenticated, "Authorization token is not supplied")
}

authToken := authHeader[0]

mac, group, err := functions.VerifyToken(authToken)

if err != nil { return err }

groupexists, err := functions.GroupExists(group)

if err != nil {
return err
}
emptynode := models.Node{}
node, err := functions.GetNodeByMacAddress(group, mac)
if err != nil || node == emptynode {
return status.Errorf(codes.Unauthenticated, "Node does not exist.")
}

//check that the request is for a valid group
//if (groupCheck && !groupexists) || err != nil {
if (!groupexists) {

return status.Errorf(codes.Unauthenticated, "Group does not exist.")

} else {
return nil
}
}


//Node authenticates using its password and retrieves a JWT for authorization.
func (s *NodeServiceServer) Login(ctx context.Context, req *nodepb.LoginRequest) (*nodepb.LoginResponse, error) {

//out := new(LoginResponse)
macaddress := req.GetMacaddress()
password := req.GetPassword()

var result models.NodeAuth

err := errors.New("Generic server error.")


if macaddress == "" {
//TODO: Set Error response
err = errors.New("Missing Mac Address.")
return nil, err
} else if password == "" {
err = errors.New("Missing Password.")
return nil, err
} else {
//Search DB for node with Mac Address. Ignore pending nodes (they should not be able to authenticate with API untill approved).
collection := mongoconn.Client.Database("wirecat").Collection("nodes")
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
var err = collection.FindOne(ctx, bson.M{ "macaddress": macaddress, "ispending": false }).Decode(&result)

defer cancel()

if err != nil {
return nil, err
}

//compare password from request to stored password in database
//might be able to have a common hash (certificates?) and compare those so that a password isn't passed in in plain text...
//TODO: Consider a way of hashing the password client side before sending, or using certificates
err = bcrypt.CompareHashAndPassword([]byte(result.Password), []byte(password))
if err != nil && result.Password != password {
return nil, err
} else {
//Create a new JWT for the node
tokenString, _ := functions.CreateJWT(macaddress, result.Group)

if tokenString == "" {
err = errors.New("Something went wrong. Could not retrieve token.")
return nil, err
}

response := &nodepb.LoginResponse{
Accesstoken: tokenString,
}
return response, nil
}
}
}
Loading

0 comments on commit 0bacbd9

Please sign in to comment.