From 717e860f65ed01844de076040e5b68ff369132bb Mon Sep 17 00:00:00 2001 From: Michael Crosby Date: Thu, 11 Jul 2019 12:06:54 +0000 Subject: [PATCH] Create guard tunnel --- README.md | 9 +++++++++ main.go | 44 +++++++++++++++++++++++++++++++++----------- server.go | 47 +++++++++++++++++++++++++++++++++++++++++++---- 3 files changed, 85 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 9564ef2..72ced0c 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/main.go b/main.go index 788c24a..8cd4ac2 100644 --- a/main.go +++ b/main.go @@ -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) +} diff --git a/server.go b/server.go index 880b875..b8503dc 100644 --- a/server.go +++ b/server.go @@ -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 }