Basic SSH server scaffolding and password auth
This commit is contained in:
parent
968706823c
commit
570f7147c6
@ -104,11 +104,11 @@ func setDefaults() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Opt["http"] = map[string]interface{}{
|
Opt["http"] = map[string]interface{}{
|
||||||
"bind_addr": "127.0.0.1:9090",
|
"listen": "127.0.0.1:9090",
|
||||||
}
|
}
|
||||||
|
|
||||||
Opt["ssh"] = map[string]interface{}{
|
Opt["ssh"] = map[string]interface{}{
|
||||||
"bind_addr": "127.0.0.1:2222",
|
"listen": "127.0.0.1:2222",
|
||||||
"host_key_dir": "~/.config/" + common.Title + "/.ssh",
|
"host_key_dir": "~/.config/" + common.Title + "/.ssh",
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +188,7 @@ func argParse() {
|
|||||||
func processOpts() {
|
func processOpts() {
|
||||||
// string options and their exported variables
|
// string options and their exported variables
|
||||||
stringOpt := map[string]*string{
|
stringOpt := map[string]*string{
|
||||||
"http.bind_addr": &HTTPBind,
|
"http.listen": &HTTPBind,
|
||||||
"logger.directory": &LogDir,
|
"logger.directory": &LogDir,
|
||||||
"http.api_key": &APIKey,
|
"http.api_key": &APIKey,
|
||||||
"ssh.listen": &SSHListen,
|
"ssh.listen": &SSHListen,
|
||||||
|
@ -63,6 +63,11 @@ func Start() {
|
|||||||
startDB()
|
startDB()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StartTest() {
|
||||||
|
testMode()
|
||||||
|
Start()
|
||||||
|
}
|
||||||
|
|
||||||
func Close() {
|
func Close() {
|
||||||
if err := db.SyncAndCloseAll(); err != nil {
|
if err := db.SyncAndCloseAll(); err != nil {
|
||||||
log.Warn().Err(err).Msg("error syncing and closing db")
|
log.Warn().Err(err).Msg("error syncing and closing db")
|
||||||
|
@ -84,6 +84,9 @@ func NewUserPass(hashIt bool, username, password string) *UserPass {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (up *UserPass) Authenticate() error {
|
func (up *UserPass) Authenticate() error {
|
||||||
|
if up.Username == "" {
|
||||||
|
return errors.New("username cannot be empty")
|
||||||
|
}
|
||||||
user, err := GetUser(up.Username)
|
user, err := GetUser(up.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn().Err(err).Str("username", up.Username).Msg("error getting user")
|
log.Warn().Err(err).Str("username", up.Username).Msg("error getting user")
|
||||||
|
@ -1,24 +1,44 @@
|
|||||||
package sshui
|
package sshui
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/gliderlabs/ssh"
|
"github.com/gliderlabs/ssh"
|
||||||
|
|
||||||
"git.tcp.direct/kayos/ziggs/internal/config"
|
"git.tcp.direct/kayos/ziggs/internal/config"
|
||||||
|
"git.tcp.direct/kayos/ziggs/internal/data"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ServeSSH() {
|
func ServeSSH() error {
|
||||||
var opts []ssh.Option
|
var opts []ssh.Option
|
||||||
|
|
||||||
if config.SSHHostKey != "" {
|
switch config.SSHHostKey {
|
||||||
|
case "":
|
||||||
|
privateKey, err := rsa.GenerateKey(rand.Reader, 4096)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err = privateKey.Validate(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
dir, _ := filepath.Split(config.Filename)
|
||||||
|
newFile := filepath.Join(dir, "host_rsa")
|
||||||
|
if err = os.WriteFile(newFile, encodePrivateKeyToPEM(privateKey), 0600); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
config.Snek.Set("ssh.host_key", newFile)
|
||||||
|
default:
|
||||||
opts = append(opts, ssh.HostKeyFile(config.SSHHostKey))
|
opts = append(opts, ssh.HostKeyFile(config.SSHHostKey))
|
||||||
}
|
}
|
||||||
|
|
||||||
opts = append(opts, ssh.PasswordAuth(func(ctx ssh.Context, password string) bool {
|
opts = append(opts, ssh.PasswordAuth(func(ctx ssh.Context, password string) bool {
|
||||||
|
attempt := data.NewUserPass(false, ctx.User(), password)
|
||||||
return false
|
err := attempt.Authenticate()
|
||||||
|
return err == nil
|
||||||
}))
|
}))
|
||||||
|
|
||||||
log.Fatal(ssh.ListenAndServe(":2222", nil, opts...))
|
return ssh.ListenAndServe(config.SSHListen, nil, opts...)
|
||||||
}
|
}
|
||||||
|
61
internal/sshui/server_test.go
Normal file
61
internal/sshui/server_test.go
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
package sshui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
|
||||||
|
"git.tcp.direct/kayos/ziggs/internal/config"
|
||||||
|
"git.tcp.direct/kayos/ziggs/internal/data"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServeSSH(t *testing.T) {
|
||||||
|
config.Init()
|
||||||
|
data.StartTest()
|
||||||
|
go func() {
|
||||||
|
t.Log("Starting SSH server")
|
||||||
|
err := ServeSSH()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
_, err := data.NewUser("test", data.NewUserPass(true, "test", "test"))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
t.Run("GoodLoginPassword", func(t *testing.T) {
|
||||||
|
client, err := ssh.Dial("tcp", config.SSHListen, &ssh.ClientConfig{
|
||||||
|
User: "test",
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password("test"),
|
||||||
|
},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
session, err := client.NewSession()
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
session.Close()
|
||||||
|
client.Close()
|
||||||
|
})
|
||||||
|
t.Run("BadLoginPassword", func(t *testing.T) {
|
||||||
|
client, err := ssh.Dial("tcp", config.SSHListen, &ssh.ClientConfig{
|
||||||
|
User: "test",
|
||||||
|
Auth: []ssh.AuthMethod{
|
||||||
|
ssh.Password("yeet"),
|
||||||
|
},
|
||||||
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
|
})
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected error, got nil")
|
||||||
|
}
|
||||||
|
if client != nil {
|
||||||
|
client.Close()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
22
internal/sshui/util.go
Normal file
22
internal/sshui/util.go
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package sshui
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/pem"
|
||||||
|
)
|
||||||
|
|
||||||
|
// encodePrivateKeyToPEM encodes Private Key from RSA to PEM format
|
||||||
|
func encodePrivateKeyToPEM(privateKey *rsa.PrivateKey) []byte {
|
||||||
|
// Get ASN.1 DER format
|
||||||
|
privDER := x509.MarshalPKCS1PrivateKey(privateKey)
|
||||||
|
|
||||||
|
// pem.Block
|
||||||
|
privBlock := pem.Block{
|
||||||
|
Type: "RSA PRIVATE KEY",
|
||||||
|
Headers: map[string]string{"gr33tz": "tcp.direct"},
|
||||||
|
Bytes: privDER,
|
||||||
|
}
|
||||||
|
|
||||||
|
return pem.EncodeToMemory(&privBlock)
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user