diff --git a/go.mod b/go.mod index 68d1267..b58253f 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,14 @@ go 1.12 require ( github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect + github.com/crosbymichael/guard v0.0.0-20190716141324-5c2daadf8067 github.com/gliderlabs/ssh v0.2.2 + github.com/gogo/protobuf v1.3.0 // indirect github.com/gomodule/redigo v2.0.0+incompatible + github.com/pkg/errors v0.8.1 github.com/sirupsen/logrus v1.4.2 github.com/urfave/cli v1.22.1 go.etcd.io/bbolt v1.3.3 golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 + google.golang.org/grpc v1.23.1 ) diff --git a/go.sum b/go.sum index a3f2bd4..70374a8 100644 --- a/go.sum +++ b/go.sum @@ -1,15 +1,30 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/crosbymichael/guard v0.0.0-20190716141324-5c2daadf8067 h1:jlV8Svz9lOwvxWBt2RN3uA1JUZ8AFj46boym2+Fx488= +github.com/crosbymichael/guard v0.0.0-20190716141324-5c2daadf8067/go.mod h1:+l2fIHwwiNb/sUw9RcsUH6wXnO07793PC4XjDWCuiHs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gliderlabs/ssh v0.2.2 h1:6zsha5zo/TWhRhwqCD3+EarCAgZ2yN28ipRnGPnwkI0= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/gogo/protobuf v1.3.0 h1:G8O7TerXerS4F6sx9OV7/nRfJdnXgHZu/S/7F2SN+UE= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= @@ -28,11 +43,26 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7 h1:0hQKqeLdqlt5iIwVOBErRisrHJAN57yOiPRQItI20fU= golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894 h1:Cz4ceDQGXuKRnVBDTS23GTn/pU5OE2C0WrNTOYK1Uuc= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/grpc v1.23.1 h1:q4XQuHFC6I28BKZpo6IYyb3mNO+l7lSOxRuYTCiDfXk= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index ca2c887..a9c1bb0 100644 --- a/main.go +++ b/main.go @@ -44,12 +44,26 @@ func main() { Usage: "redis url", Value: "redis://127.0.0.1:6379", }, + cli.StringFlag{ + Name: "guard-addr", + Usage: "guard server address", + Value: "10.199.199.1:10100", + }, + cli.StringFlag{ + Name: "guard-tunnel", + Usage: "guard tunnel to use for peers", + Value: "guard0", + }, + cli.StringFlag{ + Name: "guard-dns", + Usage: "dns to use for peers", + Value: "10.199.254.1", + }, } app.Before = func(cx *cli.Context) error { if cx.Bool("debug") { logrus.SetLevel(logrus.DebugLevel) } - return nil } app.Action = func(cx *cli.Context) error { @@ -59,6 +73,9 @@ func main() { HostKeyPath: cx.String("host-key"), RedisURL: cx.String("redis"), Subnet: cx.String("subnet"), + GuardAddr: cx.String("guard-addr"), + GuardTunnel: cx.String("guard-tunnel"), + GuardDNS: cx.String("guard-dns"), } srv, err := NewServer(cfg) if err != nil { diff --git a/net.go b/net.go index f0e105e..81fbe54 100644 --- a/net.go +++ b/net.go @@ -8,10 +8,6 @@ import ( "github.com/gomodule/redigo/redis" ) -const ( - ipsKey = "gatekeeper/ips" -) - type subnetRange struct { Start net.IP End net.IP diff --git a/server.go b/server.go index 818282d..f6b33d5 100644 --- a/server.go +++ b/server.go @@ -1,15 +1,29 @@ package main import ( + "bytes" + "context" "fmt" "io" "io/ioutil" + "net" "os" + "path" "path/filepath" "sync" + "time" + v1 "github.com/crosbymichael/guard/api/v1" "github.com/gliderlabs/ssh" + "github.com/gomodule/redigo/redis" + "github.com/pkg/errors" "github.com/sirupsen/logrus" + "google.golang.org/grpc" +) + +const ( + ipsKey = "gatekeeper/ips" + configKeyPrefix = "gatekeeper/config/" ) type ServerConfig struct { @@ -19,6 +33,9 @@ type ServerConfig struct { HostKeyPath string RedisURL string Subnet string + GuardAddr string + GuardTunnel string + GuardDNS string } type Server struct { @@ -43,15 +60,14 @@ func (s *Server) Run() error { } ssh.Handle(func(session ssh.Session) { - //authorizedKey := gossh.MarshalAuthorizedKey(session.PublicKey()) id := s.getID(session.PublicKey()) - ip, ipnet, err := s.getOrAllocateIP(id, s.cfg.Subnet) + config, err := s.getConfig(id) if err != nil { logrus.Error(err) return } - logrus.Debugf("config: id=%s ip=%s net=%s", id, ip, ipnet) - io.WriteString(session, ip.String()+"\n") + logrus.Debugf("config: id=%s", id) + io.WriteString(session, config+"\n") }) pubKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool { @@ -65,7 +81,7 @@ func (s *Server) Run() error { if _, err := os.Stat(s.cfg.HostKeyPath); err == nil { opts = append(opts, ssh.HostKeyFile(s.cfg.HostKeyPath)) } - return ssh.ListenAndServe(fmt.Sprintf(":%d", s.cfg.ListenPort), nil, pubKeyOption) + return ssh.ListenAndServe(fmt.Sprintf(":%d", s.cfg.ListenPort), nil, opts...) } func (s *Server) loadKeys() error { @@ -112,3 +128,63 @@ func (s *Server) isAuthorized(ctx ssh.Context, key ssh.PublicKey) bool { }).Warn("access denied") return false } + +func (s *Server) getConfig(id string) (string, error) { + c, err := s.getConn() + if err != nil { + return "", err + } + defer c.Close() + key := path.Join(configKeyPrefix, id) + cfg, err := redis.String(c.Do("GET", key)) + if err != nil && err != redis.ErrNil { + return "", err + } + if cfg != "" { + return cfg, nil + } + + conn, err := grpc.Dial(s.cfg.GuardAddr, grpc.WithInsecure()) + if err != nil { + return "", errors.Wrap(err, "error connecting to guard server") + } + defer conn.Close() + ctx, cancel := context.WithTimeout(context.Background(), time.Second*10) + defer cancel() + + client := v1.NewWireguardClient(conn) + + ip, ipnet, err := s.getOrAllocateIP(id, s.cfg.Subnet) + if err != nil { + return "", err + } + + r, err := client.NewPeer(ctx, &v1.NewPeerRequest{ + ID: s.cfg.GuardTunnel, + PeerID: id, + Address: ip.String() + "/32", + }) + if err != nil { + return "", err + } + // generate peer tunnel config + t := &v1.Tunnel{ + PrivateKey: r.Peer.PrivateKey, + Address: r.Peer.AllowedIPs[0], + DNS: s.cfg.GuardDNS, + Peers: []*v1.Peer{ + { + ID: r.Tunnel.ID, + PublicKey: r.Tunnel.PublicKey, + Endpoint: net.JoinHostPort(r.Tunnel.Endpoint, r.Tunnel.ListenPort), + AllowedIPs: []string{ipnet.String()}, + }, + }, + } + b := bytes.NewBuffer(nil) + t.Render(b) + if _, err := c.Do("SET", key, b.String()); err != nil { + return "", err + } + return b.String(), nil +}