initial commit

Signed-off-by: Evan Hazlett <ejhazlett@gmail.com>
This commit is contained in:
Evan Hazlett 2019-09-12 23:08:41 -04:00
parent 0128006918
commit f0c2ae6902
No known key found for this signature in database
GPG Key ID: A519480096146526
8 changed files with 316 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
bin/*

15
Makefile Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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)
}
}