Skip to content

Commit

Permalink
feat: sec
Browse files Browse the repository at this point in the history
Signed-off-by: Carlos A Becker <[email protected]>
  • Loading branch information
caarlos0 committed Jan 10, 2022
1 parent ca5a2db commit 9d64674
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 32 deletions.
9 changes: 4 additions & 5 deletions _example/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@
listen: 127.0.0.1 # defaults to 0.0.0.0
port: 2223 # defaults to 22 or 2222, whichever is open

# endpoints to list:
endpoints:
endpoints: # endpoints to list
- name: appname # prefer to avoid spaces
address: foo.local:2234 # address in host:port format
user: notme # overrides user, defauts to client's user

# users to allow access to the list
users:
users: # users to allow access to the list
- name: carlos # user login
public-keys: # their public keys
- ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDXTlLdHrS+PRjQ6gfWXBy17bUF8PeKbhMNl5wcOo/fS8Tb9+cV6rWbADtAHJWR3oGAYZ0nLSUVTil3y493uPxvmFGdAvJoue5X4QWweoObrFeVg5PXEZZZyVrKiTvE5DNdwZrR4QtXcNO3qdaaF9pnLFjCGGzfOAlYIRRnX5k7hkd5FFUQiAKIPxPGG7sfWVnobqwC/eWqk8fUVxhe8/i3uiiaql+QdMMuK0Bs4uuZFiKVuHgKdpgwsHOt57uazGTq+bKo5RIR4Mdr+/F4sBH3ndamOK7FOSwfM/4WUlmgWucHmW6mMndy6gyYQzsbZYUsrcoD3Ym5OXBGLgIarMEL [email protected]
- key1

27 changes: 0 additions & 27 deletions cmd/wishlist/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ func main() {
config.Factory = func(e wishlist.Endpoint) (*ssh.Server, error) {
return wish.NewServer(
wish.WithAddress(e.Address),
publicKeyAccessOption(config.Users),
wish.WithMiddleware(
append(
e.Middlewares,
Expand All @@ -56,32 +55,6 @@ func main() {
}
}

func publicKeyAccessOption(users []wishlist.User) ssh.Option {
if len(users) == 0 {
// if no users, assume everyone can login
return func(s *ssh.Server) error { return nil }
}
return wish.WithPublicKeyAuth(func(ctx ssh.Context, key ssh.PublicKey) bool {
for _, user := range users {
if user.Name == ctx.User() {
for _, pubkey := range user.PublicKeys {
upk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
if err != nil {
log.Printf("invalid key for user %q: %v", user.Name, err)
return false
}
if ssh.KeysEqual(upk, key) {
log.Printf("authorized %s@%s...", ctx.User(), pubkey[:30])
return true
}
}
}
}
log.Printf("denied %s@%s", ctx.User(), key.Type())
return false
})
}

func getConfig(path string) (wishlist.Config, error) {
var allErrs error
for _, fn := range []func() string{
Expand Down
30 changes: 30 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"time"

"github.com/charmbracelet/wish"
"github.com/gliderlabs/ssh"
"github.com/hashicorp/go-multierror"
)

Expand Down Expand Up @@ -79,6 +80,8 @@ func listenAndServe(config *Config, endpoint Endpoint) (func() error, error) {
if err != nil {
return nil, err
}
s.PublicKeyHandler = publicKeyAccessOption(config.Users)

log.Printf("Starting SSH server for %s on ssh://%s", endpoint.Name, endpoint.Address)
ln, err := net.Listen("tcp", endpoint.Address)
if err != nil {
Expand Down Expand Up @@ -120,3 +123,30 @@ func getFirstOpenPort(addr string, ports ...int64) (int64, error) {
}
return 0, fmt.Errorf("all ports unavailable")
}

func publicKeyAccessOption(users []User) ssh.PublicKeyHandler {
if len(users) == 0 {
// if no users, assume everyone can login
return nil
}

return func(ctx ssh.Context, key ssh.PublicKey) bool {
for _, user := range users {
if user.Name == ctx.User() {
for _, pubkey := range user.PublicKeys {
upk, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
if err != nil {
log.Printf("invalid key for user %q: %v", user.Name, err)
return false
}
if ssh.KeysEqual(upk, key) {
log.Printf("authorized %s@%s...", ctx.User(), pubkey[:30])
return true
}
}
}
}
log.Printf("denied %s@%s", ctx.User(), key.Type())
return false
}
}
88 changes: 88 additions & 0 deletions server_test.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
package wishlist

import (
"context"
"io"
"net"
"strconv"
"sync"
"testing"

"github.com/gliderlabs/ssh"
"github.com/stretchr/testify/require"
)

Expand Down Expand Up @@ -66,6 +69,91 @@ func TestGetFirstOpenPort(t *testing.T) {
})
}

func TestPublicKeyHandler(t *testing.T) {
t.Run("no users", func(t *testing.T) {
require.Nil(t, publicKeyAccessOption([]User{}))
})

t.Run("with users", func(t *testing.T) {
pubkey := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIMYKQ6pT3+iZBROfFKKT/4GVc1Xws776bE67cF3zUQPS foo@bar"
pubkey2 := "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDfBMpbghW82c1zk9LauP7G/LqXtTeQrU6Do9FUY1FJ5 foo@bar"
k, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey))
require.NoError(t, err)

t.Run("authorized", func(t *testing.T) {
require.True(t, publicKeyAccessOption([]User{
{
Name: "test",
PublicKeys: []string{pubkey},
},
})(fakeCtx{}, k))
})

t.Run("unauthorized wrong username", func(t *testing.T) {
require.False(t, publicKeyAccessOption([]User{
{
Name: "not-test",
PublicKeys: []string{pubkey},
},
})(fakeCtx{}, k))
})

t.Run("unauthorized wrong key", func(t *testing.T) {
require.False(t, publicKeyAccessOption([]User{
{
Name: "test",
PublicKeys: []string{pubkey2},
},
})(fakeCtx{}, k))
})

t.Run("invalid key", func(t *testing.T) {
require.False(t, publicKeyAccessOption([]User{
{
Name: "test",
PublicKeys: []string{"giberrish"},
},
})(fakeCtx{}, k))
})
})

}

type fakeCtx struct {
context.Context
sync.Locker
}

func (ctx fakeCtx) User() string {
return "test"
}

func (ctx fakeCtx) SessionID() string {
return "test"
}

func (ctx fakeCtx) ClientVersion() string {
return "1.0"
}

func (ctx fakeCtx) ServerVersion() string {
return "1.0"
}

func (ctx fakeCtx) RemoteAddr() net.Addr {
return &net.IPAddr{}
}

func (ctx fakeCtx) LocalAddr() net.Addr {
return &net.IPAddr{}
}

func (ctx fakeCtx) Permissions() *ssh.Permissions {
return nil
}

func (ctx fakeCtx) SetValue(key, value interface{}) {}

func portFromAddr(tb testing.TB, addr string) int64 {
tb.Helper()
_, port, err := net.SplitHostPort(addr)
Expand Down

0 comments on commit 9d64674

Please sign in to comment.