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{}{
|
||||
"bind_addr": "127.0.0.1:9090",
|
||||
"listen": "127.0.0.1:9090",
|
||||
}
|
||||
|
||||
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",
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ func argParse() {
|
|||
func processOpts() {
|
||||
// string options and their exported variables
|
||||
stringOpt := map[string]*string{
|
||||
"http.bind_addr": &HTTPBind,
|
||||
"http.listen": &HTTPBind,
|
||||
"logger.directory": &LogDir,
|
||||
"http.api_key": &APIKey,
|
||||
"ssh.listen": &SSHListen,
|
||||
|
|
|
@ -63,6 +63,11 @@ func Start() {
|
|||
startDB()
|
||||
}
|
||||
|
||||
func StartTest() {
|
||||
testMode()
|
||||
Start()
|
||||
}
|
||||
|
||||
func Close() {
|
||||
if err := db.SyncAndCloseAll(); err != nil {
|
||||
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 {
|
||||
if up.Username == "" {
|
||||
return errors.New("username cannot be empty")
|
||||
}
|
||||
user, err := GetUser(up.Username)
|
||||
if err != nil {
|
||||
log.Warn().Err(err).Str("username", up.Username).Msg("error getting user")
|
||||
|
|
|
@ -1,24 +1,44 @@
|
|||
package sshui
|
||||
|
||||
import (
|
||||
"log"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gliderlabs/ssh"
|
||||
|
||||
"git.tcp.direct/kayos/ziggs/internal/config"
|
||||
"git.tcp.direct/kayos/ziggs/internal/data"
|
||||
)
|
||||
|
||||
func ServeSSH() {
|
||||
func ServeSSH() error {
|
||||
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.PasswordAuth(func(ctx ssh.Context, password string) bool {
|
||||
|
||||
return false
|
||||
attempt := data.NewUserPass(false, ctx.User(), password)
|
||||
err := attempt.Authenticate()
|
||||
return err == nil
|
||||
}))
|
||||
|
||||
log.Fatal(ssh.ListenAndServe(":2222", nil, opts...))
|
||||
return ssh.ListenAndServe(config.SSHListen, nil, opts...)
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
})
|
||||
}
|
|
@ -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