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