Create guard tunnel
This commit is contained in:
parent
1bd241e745
commit
717e860f65
3 changed files with 85 additions and 15 deletions
|
@ -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
44
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)
|
||||
}
|
||||
|
|
47
server.go
47
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
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue