ssh: Implement authentication for new ssh server
This commit is contained in:
parent
66b397e9da
commit
45f63935cd
10 changed files with 259 additions and 1 deletions
104
internal/dis/component/ssh/ssh.go
Normal file
104
internal/dis/component/ssh/ssh.go
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/sshx"
|
||||
"github.com/gliderlabs/ssh"
|
||||
"github.com/tkw1536/goprogram/stream"
|
||||
"github.com/tkw1536/proxyssh/feature"
|
||||
)
|
||||
|
||||
type SSH struct {
|
||||
component.Base
|
||||
Instances *instances.Instances
|
||||
}
|
||||
|
||||
const (
|
||||
etx rune = 3
|
||||
eot rune = 4
|
||||
)
|
||||
|
||||
const welcomeMessage = `Welcome to the WissKI SSH Server.
|
||||
You've successfully authenticated, but we don't provide shell access to the main server.
|
||||
You may use this connection as part of a proxy jump to connect to your server.
|
||||
For example:
|
||||
|
||||
ssh -J %s:2222 www-data@%s
|
||||
|
||||
Press CTRL-C to close this connection.
|
||||
`
|
||||
|
||||
// Server returns an ssh server that implements the main ssh server
|
||||
func (s *SSH) Server(context context.Context, ios stream.IOStream) (*ssh.Server, error) {
|
||||
var server ssh.Server
|
||||
|
||||
banner := fmt.Sprintf(welcomeMessage, s.Config.DefaultDomain, "example."+s.Config.DefaultDomain)
|
||||
|
||||
server.Handle(func(session ssh.Session) {
|
||||
io.WriteString(session, banner)
|
||||
|
||||
buffer := bufio.NewReader(session)
|
||||
for {
|
||||
res, _, err := buffer.ReadRune()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if res == etx || res == eot {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
server.PublicKeyHandler = feature.AuthorizeKeys(
|
||||
slogger{IOStream: ios},
|
||||
func(ctx ssh.Context) (keys []ssh.PublicKey, err error) {
|
||||
keys, err = s.GlobalKeys()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
instances, err := s.Instances.All()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, instance := range instances {
|
||||
ikeys, err := instance.SSH().Keys()
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
keys = append(keys, ikeys...)
|
||||
}
|
||||
return keys, nil
|
||||
})
|
||||
return &server, nil
|
||||
}
|
||||
|
||||
func (s *SSH) GlobalKeys() ([]ssh.PublicKey, error) {
|
||||
file, err := s.Environment.Open(s.Config.GlobalAuthorizedKeysFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
bytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sshx.ParseAllKeys(bytes), nil
|
||||
}
|
||||
|
||||
type slogger struct {
|
||||
stream.IOStream
|
||||
}
|
||||
|
||||
func (s slogger) Print(v ...any) {
|
||||
fmt.Fprint(s.Stderr, v...)
|
||||
}
|
||||
func (s slogger) Printf(format string, v ...any) {
|
||||
fmt.Fprintf(s.Stderr, format, v...)
|
||||
}
|
||||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/resolver"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/solr"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/ssh"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/triplestore"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/web"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/lazy"
|
||||
|
|
@ -72,6 +73,10 @@ func (dis *Distillery) Resolver() *resolver.Resolver {
|
|||
func (dis *Distillery) SQL() *sql.SQL {
|
||||
return export[*sql.SQL](dis)
|
||||
}
|
||||
func (dis *Distillery) SSH() *ssh.SSH {
|
||||
return export[*ssh.SSH](dis)
|
||||
}
|
||||
|
||||
func (dis *Distillery) Triplestore() *triplestore.Triplestore {
|
||||
return export[*triplestore.Triplestore](dis)
|
||||
}
|
||||
|
|
@ -119,7 +124,7 @@ func (dis *Distillery) allComponents() []initFunc {
|
|||
s.PollInterval = time.Second
|
||||
}),
|
||||
|
||||
// instainces
|
||||
// instances
|
||||
auto[*instances.Instances],
|
||||
auto[*meta.Meta],
|
||||
auto[*malt.Malt],
|
||||
|
|
@ -132,6 +137,9 @@ func (dis *Distillery) allComponents() []initFunc {
|
|||
auto[*exporter.Filesystem],
|
||||
auto[*exporter.Pathbuilders],
|
||||
|
||||
// ssh server
|
||||
auto[*ssh.SSH],
|
||||
|
||||
// Control server
|
||||
auto[*control.Control],
|
||||
auto[*static.Static],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
package barrel
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/mstore"
|
||||
|
|
@ -13,3 +15,11 @@ type Barrel struct {
|
|||
Locker *locker.Locker
|
||||
MStore *mstore.MStore
|
||||
}
|
||||
|
||||
func (barrel *Barrel) DataPath() string {
|
||||
return filepath.Join(barrel.FilesystemBase, "data")
|
||||
}
|
||||
|
||||
func (barrel *Barrel) AuthorizedKeysPath() string {
|
||||
return filepath.Join(barrel.DataPath(), "authorized_keys")
|
||||
}
|
||||
|
|
|
|||
32
internal/wisski/ingredient/barrel/ssh/ssh.go
Normal file
32
internal/wisski/ingredient/barrel/ssh/ssh.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package ssh
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/environment"
|
||||
"github.com/FAU-CDI/wisski-distillery/pkg/sshx"
|
||||
"github.com/gliderlabs/ssh"
|
||||
)
|
||||
|
||||
type SSH struct {
|
||||
ingredient.Base
|
||||
Barrel *barrel.Barrel
|
||||
}
|
||||
|
||||
func (ssh *SSH) Keys() ([]ssh.PublicKey, error) {
|
||||
file, err := ssh.Environment.Open(ssh.Barrel.AuthorizedKeysPath())
|
||||
if environment.IsNotExist(err) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bytes, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sshx.ParseAllKeys(bytes), nil
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/drush"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/provisioner"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/barrel/ssh"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/bookkeeping"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/info"
|
||||
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/locker"
|
||||
|
|
@ -76,6 +77,10 @@ func (wisski *WissKI) Info() *info.Info {
|
|||
return export[*info.Info](wisski)
|
||||
}
|
||||
|
||||
func (wisski *WissKI) SSH() *ssh.SSH {
|
||||
return export[*ssh.SSH](wisski)
|
||||
}
|
||||
|
||||
//
|
||||
// All components
|
||||
// THESE SHOULD NEVER BE CALLED DIRECTLY
|
||||
|
|
@ -112,5 +117,7 @@ func (wisski *WissKI) allIngredients() []initFunc {
|
|||
auto[*drush.Drush],
|
||||
|
||||
auto[*reserve.Reserve],
|
||||
|
||||
auto[*ssh.SSH],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue