Skip to content

Commit

Permalink
update gokit, start using structured logging and improved CLI primi…
Browse files Browse the repository at this point in the history
…tives
  • Loading branch information
joonas-fi committed Dec 25, 2024
1 parent 1dfaec8 commit 0e52ee1
Show file tree
Hide file tree
Showing 5 changed files with 101 additions and 1,195 deletions.
8 changes: 5 additions & 3 deletions cmd/function22/genkey.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package main

import (
"context"
"crypto/ed25519"
"crypto/rand"
"fmt"
"os"

"github.com/ScaleFT/sshkeys"
"github.com/function61/gokit/app/cli"
"github.com/function61/gokit/os/osutil"
"github.com/spf13/cobra"
)
Expand All @@ -16,9 +18,9 @@ func generateHostKeyEntrypoint() *cobra.Command {
Use: "host-key-generate",
Short: "Generates the host key at " + defaultHostKeyFile,
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) {
osutil.ExitIfError(generateHostKey())
},
Run: cli.WrapRun(func(_ context.Context, _ []string) error {
return generateHostKey()
}),
}
}

Expand Down
65 changes: 32 additions & 33 deletions cmd/function22/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"syscall"
"unsafe"

"github.com/function61/gokit/app/cli"
"github.com/function61/gokit/os/osutil"
"github.com/function61/gokit/os/systemdinstaller"
gliderssh "github.com/gliderlabs/ssh"
Expand All @@ -24,48 +25,46 @@ func installEntrypoint() *cobra.Command {
Use: "install",
Short: "Make the server start on system startup",
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, _ []string) {
osutil.ExitIfError(func() error {
if validateHostKey {
hostKeyExists, err := osutil.Exists(defaultHostKeyFile)
if err != nil {
return err
}

if !hostKeyExists { // pro-tip
return errors.New("host key doesn't exist. create it by running $ " + os.Args[0] + " host-key-generate")
}
Run: cli.WrapRun(func(_ context.Context, _ []string) error {
if validateHostKey {
hostKeyExists, err := osutil.Exists(defaultHostKeyFile)
if err != nil {
return err
}

if allowedUsers == "" {
return errors.New("you need to specify --allowed-users=")
if !hostKeyExists { // pro-tip
return errors.New("host key doesn't exist. create it by running $ " + os.Args[0] + " host-key-generate")
}
}

options := []systemdinstaller.Option{
systemdinstaller.Docs(
"https://github.com/function61/function22",
"https://function61.com/"),
systemdinstaller.Env("SSH_ALLOWED_USERS", allowedUsers),
}
if allowedUsers == "" {
return errors.New("you need to specify --allowed-users=")
}

if interfaceName != "" {
options = append(
options,
systemdinstaller.Env("SSH_LISTEN_INTERFACE", interfaceName),
systemdinstaller.WaitNetworkInterface(interfaceName))
}
options := []systemdinstaller.Option{
systemdinstaller.Docs(
"https://github.com/function61/function22",
"https://function61.com/"),
systemdinstaller.Env("SSH_ALLOWED_USERS", allowedUsers),
}

service := systemdinstaller.Service("function22", tagline, options...)
if interfaceName != "" {
options = append(
options,
systemdinstaller.Env("SSH_LISTEN_INTERFACE", interfaceName),
systemdinstaller.WaitNetworkInterface(interfaceName))
}

if err := systemdinstaller.Install(service); err != nil {
return err
}
service := systemdinstaller.Service("function22", tagline, options...)

if err := systemdinstaller.Install(service); err != nil {
return err
}

fmt.Println(systemdinstaller.EnableAndStartCommandHints(service))
fmt.Println(systemdinstaller.EnableAndStartCommandHints(service))

return nil
}())
},
return nil
}),
}

cmd.Flags().StringVarP(&allowedUsers, "allowed-users", "", allowedUsers, "Users allowed for SSH access: 'user1,user2'")
Expand Down
57 changes: 24 additions & 33 deletions cmd/function22/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"bufio"
"context"
"fmt"
"log"
"log/slog"
"net"
"os"
Expand All @@ -16,9 +15,8 @@ import (

"github.com/creack/pty"
"github.com/function61/function22/pkg/linuxuser"
"github.com/function61/gokit/app/dynversion"
"github.com/function61/gokit/app/cli"
"github.com/function61/gokit/io/bidipipe"
"github.com/function61/gokit/log/logex"
"github.com/function61/gokit/os/osutil"
gliderssh "github.com/gliderlabs/ssh"
"github.com/spf13/cobra"
Expand All @@ -31,27 +29,22 @@ const (

func main() {
app := &cobra.Command{
Use: os.Args[0],
Short: tagline,
Version: dynversion.Version,
Args: cobra.NoArgs,
Run: func(_ *cobra.Command, args []string) {
rootLogger := logex.StandardLogger()

osutil.ExitIfError(logic(
osutil.CancelOnInterruptOrTerminate(rootLogger),
true,
rootLogger))
},
Short: tagline,
Args: cobra.NoArgs,
Run: cli.WrapRun(func(ctx context.Context, _ []string) error {
return logic(ctx)
}),
}

cli.AddLogLevelControls(app.Flags())

app.AddCommand(generateHostKeyEntrypoint())
app.AddCommand(installEntrypoint())

osutil.ExitIfError(app.Execute())
cli.Execute(app)
}

func logic(ctx context.Context, verbose bool, logger *log.Logger) error {
func logic(ctx context.Context) error {
listenInterface := os.Getenv("SSH_LISTEN_INTERFACE")

allowedUsersSerialized, err := osutil.GetenvRequired("SSH_ALLOWED_USERS") // "user1,user2"
Expand Down Expand Up @@ -89,8 +82,8 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {

if err := gliderssh.Serve(sshPortListener, func(s gliderssh.Session) {
// user now definitely exists in *knownUsers*
if err := s.Exit(handleSSHConnection(s, *knownUsers[s.User()], verbose, logger)); err != nil {
logger.Printf("session.Exit(): %v", err)
if err := s.Exit(handleSSHConnection(s, *knownUsers[s.User()])); err != nil {
slog.Error("session.Exit()", "err", err)
}
},
gliderssh.HostKeyFile(defaultHostKeyFile),
Expand All @@ -99,7 +92,7 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {

account, found := knownUsers[username]
if !found {
logger.Printf("login attempt for unknown user: %s", username)
slog.Warn("login attempt for unknown user", "username", username)
return false
}

Expand All @@ -111,7 +104,7 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {
}),
gliderssh.PublicKeyAuth(func(ctx gliderssh.Context, userKey gliderssh.PublicKey) bool {
if _, allowed := knownUsers[ctx.User()]; !allowed {
logger.Printf("login attempt for unknown user: %s", ctx.User())
slog.Warn("login attempt for unknown user", "username", ctx.User())
return false
}

Expand All @@ -121,7 +114,7 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {
if os.IsNotExist(err) { // user simply doesn't have them
return false
} else {
logger.Printf("error reading authorized_keys: %v", err)
slog.Error("error reading authorized_keys", "err", err)
return false
}
}
Expand All @@ -131,7 +124,7 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {
for authorizedKeys.Scan() {
authorizedKey, _, _, _, err := gliderssh.ParseAuthorizedKey(authorizedKeys.Bytes())
if err != nil {
logger.Printf("ParseAuthorizedKey: %v", err)
slog.Error("ParseAuthorizedKey", "err", err)
return false
}

Expand All @@ -140,7 +133,7 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {
}
}
if err := authorizedKeys.Err(); err != nil {
logger.Printf("error scanning: %v", err)
slog.Error("scanning", "err", err)
return false
}

Expand All @@ -158,18 +151,16 @@ func logic(ctx context.Context, verbose bool, logger *log.Logger) error {
return nil
}

func handleSSHConnection(s gliderssh.Session, account linuxuser.Account, verbose bool, logger *log.Logger) int {
if verbose {
user := s.User()
func handleSSHConnection(s gliderssh.Session, account linuxuser.Account) int {
user := s.User()

tcpAddress := s.RemoteAddr().(*net.TCPAddr)
tcpAddress := s.RemoteAddr().(*net.TCPAddr)

logger.Printf("new session for %q from %v", user, tcpAddress)
defer logger.Printf("closing session for %q from %v", user, tcpAddress)
}
slog.Debug("new session", "user", user, "tcpAddress", tcpAddress)
defer slog.Debug("closing session", "user", user, "tcpAddress", tcpAddress)

if subsys := s.Subsystem(); subsys != "" { // what does this do? AFAIK SCP is a subsystem but even it doesn't set it?
logger.Printf("unsupported subsystem specified: %s", subsys)
slog.Error("unsupported subsystem specified", "subsys", subsys)
fmt.Fprint(s, "unsupported subsystem specified\n")
return 1
}
Expand Down Expand Up @@ -256,7 +247,7 @@ func handleSSHConnection(s gliderssh.Session, account linuxuser.Account, verbose
if isPty {
terminal, err := pty.Start(cmd)
if err != nil {
logger.Printf("running shell: %v", err)
slog.Error("starting shell", "err", err)
return 1
}
defer terminal.Close()
Expand Down
24 changes: 19 additions & 5 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
module github.com/function61/function22

go 1.16
go 1.21

toolchain go1.21.5

require (
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5
github.com/ScaleFT/sshkeys v0.0.0-20200327173127-6142f742bca5
github.com/creack/pty v1.1.17
github.com/function61/gokit v0.0.0-20220102105228-f5a0195f3643
github.com/function61/gokit v0.0.0-20241219151907-f0ce2a6aac07
github.com/gliderlabs/ssh v0.3.3
github.com/spf13/cobra v1.2.1
inet.af/netaddr v0.0.0-20211027220019-c74959edd3b6
tailscale.com v1.18.1
github.com/spf13/cobra v1.6.1
)

require (
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
github.com/dchest/bcrypt_pbkdf v0.0.0-20150205184540-83f37f9c154a // indirect
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/lmittmann/tint v1.0.5 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/pkg/xattr v0.4.4 // indirect
github.com/spf13/pflag v1.0.5 // indirect
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/sys v0.6.0 // indirect
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 // indirect
)
Loading

0 comments on commit 0e52ee1

Please sign in to comment.