Commit bb8ef88
Eric Bower
·
2025-12-12 20:57:46 -0500 EST
parent 94c4b4d
feat: access control using ssh certs
7 files changed,
+55,
-16
+1,
-1
| ... | ... | @@ -46,7 +46,7 @@ func StartSshServer() { | |
| 46 | 46 | } | |
| 47 | 47 | handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap) | |
| 48 | 48 | ||
| 49 | - | sshAuth := shared.NewSshAuthHandler(dbh, logger) | |
| 49 | + | sshAuth := shared.NewSshAuthHandler(dbh, logger, "feeds") | |
| 50 | 50 | ||
| 51 | 51 | // Create a new SSH server | |
| 52 | 52 | server, err := pssh.NewSSHServerWithConfig( |
+1,
-1
| ... | ... | @@ -45,7 +45,7 @@ func StartSshServer() { | |
| 45 | 45 | "fallback": filehandlers.NewScpPostHandler(dbh, cfg, hooks), | |
| 46 | 46 | } | |
| 47 | 47 | handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap) | |
| 48 | - | sshAuth := shared.NewSshAuthHandler(dbh, logger) | |
| 48 | + | sshAuth := shared.NewSshAuthHandler(dbh, logger, "pastes") | |
| 49 | 49 | ||
| 50 | 50 | // Create a new SSH server | |
| 51 | 51 | server, err := pssh.NewSSHServerWithConfig( |
+1,
-1
| ... | ... | @@ -34,7 +34,7 @@ func StartSshServer(cfg *PgsConfig, killCh chan error) { | |
| 34 | 34 | ctx, | |
| 35 | 35 | ) | |
| 36 | 36 | ||
| 37 | - | sshAuth := shared.NewSshAuthHandler(cfg.DB, logger) | |
| 37 | + | sshAuth := shared.NewSshAuthHandler(cfg.DB, logger, "pgs") | |
| 38 | 38 | ||
| 39 | 39 | webTunnel := &tunkit.WebTunnelHandler{ | |
| 40 | 40 | Logger: logger, |
+3,
-2
| ... | ... | @@ -64,7 +64,7 @@ func StartSshServer() { | |
| 64 | 64 | DBPool: dbpool, | |
| 65 | 65 | } | |
| 66 | 66 | ||
| 67 | - | sshAuth := shared.NewSshAuthHandler(dbpool, logger) | |
| 67 | + | sshAuth := shared.NewSshAuthHandler(dbpool, logger, "pico") | |
| 68 | 68 | ||
| 69 | 69 | // Create a new SSH server | |
| 70 | 70 | server, err := pssh.NewSSHServerWithConfig( |
| ... | ... | @@ -76,7 +76,8 @@ func StartSshServer() { | |
| 76 | 76 | promPort, | |
| 77 | 77 | "ssh_data/term_info_ed25519", | |
| 78 | 78 | func(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | |
| 79 | - | perms, _ := sshAuth.PubkeyAuthHandler(conn, key) | |
| 79 | + | perms, err := sshAuth.PubkeyAuthHandler(conn, key) | |
| 80 | + | logger.Warn("pubkey auth handler", "err", err) | |
| 80 | 81 | if perms == nil { | |
| 81 | 82 | perms = &ssh.Permissions{ | |
| 82 | 83 | Extensions: map[string]string{ |
+1,
-1
| ... | ... | @@ -46,7 +46,7 @@ func StartSshServer() { | |
| 46 | 46 | Access: syncmap.New[string, []string](), | |
| 47 | 47 | } | |
| 48 | 48 | ||
| 49 | - | sshAuth := shared.NewSshAuthHandler(dbh, logger) | |
| 49 | + | sshAuth := shared.NewSshAuthHandler(dbh, logger, "pipe") | |
| 50 | 50 | ||
| 51 | 51 | // Create a new SSH server | |
| 52 | 52 | server, err := pssh.NewSSHServerWithConfig( |
+1,
-1
| ... | ... | @@ -59,7 +59,7 @@ func StartSshServer() { | |
| 59 | 59 | } | |
| 60 | 60 | handler := filehandlers.NewFileHandlerRouter(cfg, dbh, fileMap) | |
| 61 | 61 | ||
| 62 | - | sshAuth := shared.NewSshAuthHandler(dbh, logger) | |
| 62 | + | sshAuth := shared.NewSshAuthHandler(dbh, logger, "prose") | |
| 63 | 63 | ||
| 64 | 64 | // Create a new SSH server | |
| 65 | 65 | server, err := pssh.NewSSHServerWithConfig( |
| ... | ... | @@ -4,6 +4,7 @@ import ( | |
| 4 | 4 | "fmt" | |
| 5 | 5 | "log/slog" | |
| 6 | 6 | "strings" | |
| 7 | + | "time" | |
| 7 | 8 | ||
| 8 | 9 | "github.com/picosh/pico/pkg/db" | |
| 9 | 10 | "github.com/picosh/utils" |
| ... | ... | @@ -13,8 +14,9 @@ import ( | |
| 13 | 14 | const adminPrefix = "admin__" | |
| 14 | 15 | ||
| 15 | 16 | type SshAuthHandler struct { | |
| 16 | - | DB AuthFindUser | |
| 17 | - | Logger *slog.Logger | |
| 17 | + | DB AuthFindUser | |
| 18 | + | Logger *slog.Logger | |
| 19 | + | Principal string | |
| 18 | 20 | } | |
| 19 | 21 | ||
| 20 | 22 | type AuthFindUser interface { |
| ... | ... | @@ -23,18 +25,54 @@ type AuthFindUser interface { | |
| 23 | 25 | FindFeature(userID, name string) (*db.FeatureFlag, error) | |
| 24 | 26 | } | |
| 25 | 27 | ||
| 26 | - | func NewSshAuthHandler(dbh AuthFindUser, logger *slog.Logger) *SshAuthHandler { | |
| 28 | + | func NewSshAuthHandler(dbh AuthFindUser, logger *slog.Logger, principal string) *SshAuthHandler { | |
| 27 | 29 | return &SshAuthHandler{ | |
| 28 | - | DB: dbh, | |
| 29 | - | Logger: logger, | |
| 30 | + | DB: dbh, | |
| 31 | + | Logger: logger, | |
| 32 | + | Principal: principal, | |
| 30 | 33 | } | |
| 31 | 34 | } | |
| 32 | 35 | ||
| 33 | 36 | func (r *SshAuthHandler) PubkeyAuthHandler(conn ssh.ConnMetadata, key ssh.PublicKey) (*ssh.Permissions, error) { | |
| 34 | - | pubkey := utils.KeyForKeyText(key) | |
| 35 | - | user, err := r.DB.FindUserByPubkey(pubkey) | |
| 37 | + | log := r.Logger | |
| 38 | + | var user *db.User | |
| 39 | + | var err error | |
| 40 | + | pubkey := "" | |
| 41 | + | ||
| 42 | + | cert, ok := key.(*ssh.Certificate) | |
| 43 | + | if ok { | |
| 44 | + | if cert.CertType != ssh.UserCert { | |
| 45 | + | return nil, fmt.Errorf("ssh-cert has type %d", cert.CertType) | |
| 46 | + | } | |
| 47 | + | ||
| 48 | + | found := false | |
| 49 | + | for _, princ := range cert.ValidPrincipals { | |
| 50 | + | if princ == "admin" || princ == r.Principal { | |
| 51 | + | found = true | |
| 52 | + | break | |
| 53 | + | } | |
| 54 | + | } | |
| 55 | + | if !found { | |
| 56 | + | return nil, fmt.Errorf("ssh-cert principals not valid") | |
| 57 | + | } | |
| 58 | + | ||
| 59 | + | clock := time.Now | |
| 60 | + | unixNow := clock().Unix() | |
| 61 | + | if after := int64(cert.ValidAfter); after < 0 || unixNow < int64(cert.ValidAfter) { | |
| 62 | + | return nil, fmt.Errorf("ssh-cert is not yet valid") | |
| 63 | + | } | |
| 64 | + | if before := int64(cert.ValidBefore); cert.ValidBefore != uint64(ssh.CertTimeInfinity) && (unixNow >= before || before < 0) { | |
| 65 | + | return nil, fmt.Errorf("ssh-cert has expired") | |
| 66 | + | } | |
| 67 | + | ||
| 68 | + | pubkey = utils.KeyForKeyText(cert.SignatureKey) | |
| 69 | + | } else { | |
| 70 | + | pubkey = utils.KeyForKeyText(key) | |
| 71 | + | } | |
| 72 | + | ||
| 73 | + | user, err = r.DB.FindUserByPubkey(pubkey) | |
| 36 | 74 | if err != nil { | |
| 37 | - | r.Logger.Error( | |
| 75 | + | log.Error( | |
| 38 | 76 | "could not find user for key", | |
| 39 | 77 | "keyType", key.Type(), | |
| 40 | 78 | "key", string(key.Marshal()), |