Create guard tunnel

This commit is contained in:
Michael Crosby 2019-07-11 12:06:54 +00:00
parent 1bd241e745
commit 717e860f65
3 changed files with 85 additions and 15 deletions

View file

@ -9,6 +9,15 @@ A GRPC server for managing wireguard tunnels.
Wireguard and it's untilities, `wg`, `wg-quick`, and `wg-quick@.service` must be installed on the system hosting
the `guard` server.
### Run the server
When you run the wireguard server it will automatically create its own wireguard tunnel
that the server binds to. This makes the server secure to manage across your network.
Use the `--address` flag to manage this server.
```bash
> sudo guard server
```
### Create a new tunnel

44
main.go
View file

@ -59,7 +59,7 @@ func main() {
cli.StringFlag{
Name: "address,a",
Usage: "grpc address",
Value: "127.0.0.1:10100",
Value: "10.199.199.1:10100",
},
cli.StringFlag{
Name: "sentry-dsn",
@ -112,6 +112,28 @@ var serverCommand = cli.Command{
server := newGRPC()
v1.RegisterWireguardServer(server, wg)
ctx := cancelContext()
address := clix.GlobalString("address")
host, _, err := net.SplitHostPort(address)
if err != nil {
return errors.Wrap(err, "splitting tunnel address")
}
r, err := wg.Create(ctx, &v1.CreateRequest{
ID: guardTunnel,
Address: host + "/32",
Endpoint: address,
})
if err == nil {
logrus.Info("created guard0 tunnel")
if err := jsonTunnel(r.Tunnel); err != nil {
return err
}
} else if err != ErrTunnelExists {
return err
}
// create our server tunnel
signals := make(chan os.Signal, 32)
signal.Notify(signals, syscall.SIGTERM, syscall.SIGINT)
@ -119,7 +141,7 @@ var serverCommand = cli.Command{
<-signals
server.Stop()
}()
l, err := net.Listen("tcp", clix.GlobalString("address"))
l, err := net.Listen("tcp", address)
if err != nil {
return errors.Wrap(err, "listen tcp")
}
@ -162,9 +184,7 @@ var createCommand = cli.Command{
if err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(r.Tunnel)
return jsonTunnel(r.Tunnel)
},
}
@ -298,9 +318,7 @@ var peersCommand = cli.Command{
if err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(r.Tunnel)
return jsonTunnel(r.Tunnel)
},
},
{
@ -325,9 +343,7 @@ var peersCommand = cli.Command{
if err != nil {
return err
}
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(r.Tunnel)
return jsonTunnel(r.Tunnel)
},
},
},
@ -390,3 +406,9 @@ func cancelContext() context.Context {
}()
return ctx
}
func jsonTunnel(t *v1.Tunnel) error {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(t)
}

View file

@ -42,13 +42,18 @@ import (
v1 "github.com/crosbymichael/guard/api/v1"
"github.com/gogo/protobuf/types"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
var empty = &types.Empty{}
var (
empty = &types.Empty{}
ErrTunnelExists = errors.New("tunnel exists")
)
const (
defaultWireguardDir = "/etc/wireguard"
tunnelData = "tunnel.json"
guardTunnel = "guard0"
)
func newServer(dir string) (*server, error) {
@ -74,6 +79,7 @@ func (s *server) Create(ctx context.Context, r *v1.CreateRequest) (*v1.TunnelRes
if r.Endpoint == "" {
return nil, errors.New("endpoint cannot be empty")
}
log := logrus.WithField("tunnel", r.ID)
host, port, err := net.SplitHostPort(r.Endpoint)
if err != nil {
return nil, errors.Wrap(err, "cannot split endpoint into host and port")
@ -81,7 +87,8 @@ func (s *server) Create(ctx context.Context, r *v1.CreateRequest) (*v1.TunnelRes
path := filepath.Join(s.dir, r.ID)
if err := os.Mkdir(path, 0700); err != nil {
if os.IsExist(err) {
return nil, errors.New("tunnel already exists")
log.Error("existing tunnel")
return nil, ErrTunnelExists
}
return nil, errors.Wrap(err, "create tunnel directory")
}
@ -101,21 +108,24 @@ func (s *server) Create(ctx context.Context, r *v1.CreateRequest) (*v1.TunnelRes
PublicKey: pub,
Endpoint: host,
}
if err := s.saveTunnel(&t); err != nil {
log.WithError(err).Error("save tunnel")
return nil, err
}
if err := s.saveConf(&t); err != nil {
log.WithError(err).Error("save config")
os.RemoveAll(path)
return nil, err
}
if err := wgquick(ctx, "enable", t.ID); err != nil {
log.WithError(err).Error("enable tunnel")
return nil, errors.Wrap(err, "enable tunnel")
}
if err := wgquick(ctx, "start", t.ID); err != nil {
log.WithError(err).Error("start tunnel")
return nil, errors.Wrap(err, "start tunnel")
}
log.Info("tunnel created")
return &v1.TunnelResponse{
Tunnel: &t,
}, nil
@ -125,16 +135,23 @@ func (s *server) NewPeer(ctx context.Context, r *v1.NewPeerRequest) (*v1.PeerRes
if r.ID == "" {
return nil, errors.New("tunnel id cannot be empty")
}
log := logrus.WithFields(logrus.Fields{
"tunnel": r.ID,
"peer": r.PeerID,
})
t, err := s.loadTunnel(r.ID)
if err != nil {
log.WithError(err).Error("load tunnel")
return nil, err
}
peerKey, err := newPrivateKey(ctx)
if err != nil {
log.WithError(err).Error("new private key")
return nil, err
}
publicKey, err := publicKey(ctx, peerKey)
if err != nil {
log.WithError(err).Error("new public key")
return nil, err
}
peer := &v1.Peer{
@ -146,13 +163,17 @@ func (s *server) NewPeer(ctx context.Context, r *v1.NewPeerRequest) (*v1.PeerRes
},
}
t.Peers = append(t.Peers, peer)
// TODO: make atomic swaps
if err := s.saveTunnel(t); err != nil {
log.WithError(err).Error("save tunnel")
return nil, err
}
if err := s.saveConf(t); err != nil {
log.WithError(err).Error("save config")
return nil, err
}
if err := wgquick(ctx, "restart", t.ID); err != nil {
log.WithError(err).Error("restart config")
return nil, errors.Wrap(err, "restart tunnel")
}
return &v1.PeerResponse{
@ -165,16 +186,23 @@ func (s *server) AddPeer(ctx context.Context, r *v1.AddPeerRequest) (*v1.TunnelR
if r.ID == "" {
return nil, errors.New("tunnel id cannot be empty")
}
log := logrus.WithFields(logrus.Fields{
"tunnel": r.ID,
"peer": r.PeerID,
})
t, err := s.loadTunnel(r.ID)
if err != nil {
log.WithError(err).Error("load tunnel")
return nil, err
}
t.Peers = append(t.Peers, r.Peer)
if err := s.saveTunnel(t); err != nil {
log.WithError(err).Error("save tunnel")
return nil, err
}
if err := s.saveConf(t); err != nil {
log.WithError(err).Error("save config")
return nil, err
}
if err := wgquick(ctx, "restart", t.ID); err != nil {
@ -192,6 +220,11 @@ func (s *server) DeletePeer(ctx context.Context, r *v1.DeletePeerRequest) (*v1.T
if r.PeerID == "" {
return nil, errors.New("peer id cannot be empty")
}
log := logrus.WithFields(logrus.Fields{
"tunnel": r.ID,
"peer": r.PeerID,
})
t, err := s.loadTunnel(r.ID)
if err != nil {
return nil, err
@ -210,8 +243,10 @@ func (s *server) DeletePeer(ctx context.Context, r *v1.DeletePeerRequest) (*v1.T
return nil, err
}
if err := wgquick(ctx, "restart", t.ID); err != nil {
log.WithError(err).Error("restart config")
return nil, errors.Wrap(err, "restart tunnel")
}
log.Info("delete peer")
return &v1.TunnelResponse{
Tunnel: t,
}, nil
@ -221,11 +256,14 @@ func (s *server) Delete(ctx context.Context, r *v1.DeleteRequest) (*types.Empty,
if r.ID == "" {
return nil, errors.New("tunnel id cannot be empty")
}
log := logrus.WithField("tunnel", r.ID)
path := filepath.Join(s.dir, r.ID)
if err := wgquick(ctx, "disable", r.ID); err != nil {
log.WithError(err).Error("disable tunnel")
return nil, errors.Wrap(err, "disable tunnel")
}
if err := wgquick(ctx, "stop", r.ID); err != nil {
log.WithError(err).Error("stop tunnel")
return nil, errors.Wrap(err, "stop tunnel")
}
if err := os.RemoveAll(path); err != nil {
@ -234,6 +272,7 @@ func (s *server) Delete(ctx context.Context, r *v1.DeleteRequest) (*types.Empty,
if err := os.Remove(filepath.Join(s.dir, fmt.Sprintf("%s.conf", r.ID))); err != nil {
return nil, errors.Wrap(err, "remove configuration")
}
log.Info("delete tunnel")
return empty, nil
}