initial commit
Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
parent
0128006918
commit
f0c2ae6902
8 changed files with 316 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
bin/*
|
15
Makefile
Normal file
15
Makefile
Normal file
|
@ -0,0 +1,15 @@
|
|||
COMMIT=$(shell git rev-parse HEAD | head -c 8)$(shell if ! git diff --no-ext-diff --quiet --exit-code; then echo .m; fi)
|
||||
APP=gatekeeper
|
||||
BUILD=-dev
|
||||
REPO=ehazlett/$(APP)
|
||||
|
||||
all: build
|
||||
|
||||
build:
|
||||
@>&2 echo " -> building ${COMMIT}"
|
||||
@CGO_ENABLED=0 go build -installsuffix cgo -ldflags "-w -X github.com/$(REPO)/version.GitCommit=$(COMMIT) -X github.com/$(REPO)/version.Build=$(BUILD)" -o ./bin/$(APP) .
|
||||
|
||||
clean:
|
||||
@rm -rf bin
|
||||
|
||||
.PHONY: build clean
|
11
go.mod
Normal file
11
go.mod
Normal file
|
@ -0,0 +1,11 @@
|
|||
module github.com/ehazlett/gatekeeper
|
||||
|
||||
go 1.12
|
||||
|
||||
require (
|
||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 // indirect
|
||||
github.com/gliderlabs/ssh v0.2.2
|
||||
github.com/sirupsen/logrus v1.4.2
|
||||
github.com/urfave/cli v1.22.1
|
||||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7
|
||||
)
|
34
go.sum
Normal file
34
go.sum
Normal file
|
@ -0,0 +1,34 @@
|
|||
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/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/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/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
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=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/urfave/cli v1.22.1 h1:+mkCCcOFKPnCmVYVcURKps1Xe+3zP90gSYGNfRkjoIY=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
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/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
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=
|
61
main.go
Normal file
61
main.go
Normal file
|
@ -0,0 +1,61 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/ehazlett/gatekeeper/version"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli"
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = version.Name
|
||||
app.Author = "@darknet"
|
||||
app.Description = version.Description
|
||||
app.Version = version.BuildVersion()
|
||||
app.Flags = []cli.Flag{
|
||||
cli.BoolFlag{
|
||||
Name: "debug, D",
|
||||
Usage: "enable debug logging",
|
||||
},
|
||||
cli.IntFlag{
|
||||
Name: "port, p",
|
||||
Usage: "listen port",
|
||||
Value: 2222,
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "key-dir, d",
|
||||
Usage: "path to authorized public keys",
|
||||
Value: "",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "host-key, k",
|
||||
Usage: "path to host key",
|
||||
Value: "/etc/ssh/ssh_host_rsa_key",
|
||||
},
|
||||
}
|
||||
app.Before = func(cx *cli.Context) error {
|
||||
if cx.Bool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
app.Action = func(cx *cli.Context) error {
|
||||
cfg := &ServerConfig{
|
||||
ListenPort: cx.Int("port"),
|
||||
KeysPath: cx.String("key-dir"),
|
||||
HostKeyPath: cx.String("host-key"),
|
||||
}
|
||||
srv, err := NewServer(cfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.Run()
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
106
server.go
Normal file
106
server.go
Normal file
|
@ -0,0 +1,106 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/sirupsen/logrus"
|
||||
gossh "golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
type ServerConfig struct {
|
||||
ListenPort int
|
||||
KeysPath string
|
||||
HostKeyPath string
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
cfg *ServerConfig
|
||||
publicKeys []ssh.PublicKey
|
||||
mu *sync.Mutex
|
||||
}
|
||||
|
||||
func NewServer(cfg *ServerConfig) (*Server, error) {
|
||||
if cfg.ListenPort == 0 {
|
||||
cfg.ListenPort = 2222
|
||||
}
|
||||
return &Server{
|
||||
cfg: cfg,
|
||||
mu: &sync.Mutex{},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) loadKeys() error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
if s.cfg.KeysPath == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
keys, err := ioutil.ReadDir(s.cfg.KeysPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pubKeys := []ssh.PublicKey{}
|
||||
for _, k := range keys {
|
||||
logrus.Debugf("loading public key %s", k.Name())
|
||||
p := filepath.Join(s.cfg.KeysPath, k.Name())
|
||||
data, err := ioutil.ReadFile(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k, _, _, _, err := ssh.ParseAuthorizedKey(data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pubKeys = append(pubKeys, k)
|
||||
}
|
||||
|
||||
s.publicKeys = pubKeys
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Server) Run() error {
|
||||
if err := s.loadKeys(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ssh.Handle(func(s ssh.Session) {
|
||||
authorizedKey := gossh.MarshalAuthorizedKey(s.PublicKey())
|
||||
io.WriteString(s, fmt.Sprintf("pub key used by %s\n", s.User()))
|
||||
s.Write(authorizedKey)
|
||||
})
|
||||
|
||||
pubKeyOption := ssh.PublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
|
||||
return s.isAuthorized(ctx, key)
|
||||
})
|
||||
|
||||
logrus.Infof("starting ssh server on port %d", s.cfg.ListenPort)
|
||||
opts := []ssh.Option{
|
||||
pubKeyOption,
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
func (s *Server) isAuthorized(ctx ssh.Context, key ssh.PublicKey) bool {
|
||||
for _, k := range s.publicKeys {
|
||||
if ssh.KeysEqual(key, k) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"user": ctx.User(),
|
||||
"addr": ctx.RemoteAddr(),
|
||||
}).Warn("access denied")
|
||||
return false
|
||||
}
|
51
version/version.go
Normal file
51
version/version.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
Copyright 2019 Stellar Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies
|
||||
or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package version
|
||||
|
||||
import "runtime"
|
||||
|
||||
var (
|
||||
// Name is the name of the application
|
||||
Name = "gatekeeper"
|
||||
|
||||
// Version defines the application version
|
||||
Version = "0.1.0"
|
||||
|
||||
// Description is the application description
|
||||
Description = "darknet gatekeeper"
|
||||
|
||||
// Build will be overwritten automatically by the build system
|
||||
Build = "-dev"
|
||||
|
||||
// GitCommit will be overwritten automatically by the build system
|
||||
GitCommit = "HEAD"
|
||||
)
|
||||
|
||||
// BuildVersion returns the build version information including version, build and git commit
|
||||
func BuildVersion() string {
|
||||
return Version + Build + " (" + GitCommit + ") " + runtime.GOOS + "/" + runtime.GOARCH
|
||||
}
|
||||
|
||||
// FullVersion returns the build version information including version, build and git commit
|
||||
func FullVersion() string {
|
||||
return Name + "/" + BuildVersion()
|
||||
}
|
37
version/version_test.go
Normal file
37
version/version_test.go
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
Copyright 2019 Stellar Project
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in the
|
||||
Software without restriction, including without limitation the rights to use, copy,
|
||||
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
and to permit persons to whom the Software is furnished to do so, subject to the
|
||||
following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies
|
||||
or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
|
||||
PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
|
||||
FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
|
||||
USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package version
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFullVersion(t *testing.T) {
|
||||
version := FullVersion()
|
||||
|
||||
expected := Name + "/" + Version + Build + " (" + GitCommit + ") " + runtime.GOOS + "/" + runtime.GOARCH
|
||||
|
||||
if version != expected {
|
||||
t.Fatalf("invalid version returned: %s", version)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue