@@ -7,23 +7,22 @@ import (
77 "os/exec"
88 "strings"
99
10+ gossh "golang.org/x/crypto/ssh"
11+
1012 "github.com/zanel1u/cloud-cli-proxy/internal/store/repository"
1113)
1214
1315type resolverRepo interface {
1416 GetHostByShortID (ctx context.Context , shortID string ) (repository.HostSSHAuth , error )
17+ ListSSHKeysByUserAndPurpose (ctx context.Context , userID , purpose string ) ([]repository.SSHKey , error )
1518}
1619
17- // ContainerTarget is the SSH endpoint and credentials for dialing the user container.
1820type ContainerTarget struct {
1921 Addr string
2022 User string
2123 Password string
2224}
2325
24- // RepoResolver implements ContainerResolver using the database repository.
25- // It validates the host's entry_password, checks that the owning user is
26- // active and the host is running, then resolves the container's SSH address.
2726type RepoResolver struct {
2827 repo resolverRepo
2928}
@@ -42,10 +41,43 @@ func (r *RepoResolver) ResolveContainer(ctx context.Context, hostShortID, passwo
4241 return ContainerTarget {}, fmt .Errorf ("invalid credentials" )
4342 }
4443
44+ return r .resolveTarget (ctx , auth )
45+ }
46+
47+ func (r * RepoResolver ) ResolveContainerByPublicKey (ctx context.Context , hostShortID string , clientKey gossh.PublicKey ) (ContainerTarget , error ) {
48+ auth , err := r .repo .GetHostByShortID (ctx , hostShortID )
49+ if err != nil {
50+ return ContainerTarget {}, fmt .Errorf ("host not found" )
51+ }
52+
53+ inboundKeys , err := r .repo .ListSSHKeysByUserAndPurpose (ctx , auth .UserID , "inbound" )
54+ if err != nil || len (inboundKeys ) == 0 {
55+ return ContainerTarget {}, fmt .Errorf ("no inbound keys configured" )
56+ }
57+
58+ clientKeyData := clientKey .Marshal ()
59+ matched := false
60+ for _ , k := range inboundKeys {
61+ parsed , _ , _ , _ , err := gossh .ParseAuthorizedKey ([]byte (k .PublicKey ))
62+ if err != nil {
63+ continue
64+ }
65+ if clientKey .Type () == parsed .Type () && subtle .ConstantTimeCompare (clientKeyData , parsed .Marshal ()) == 1 {
66+ matched = true
67+ break
68+ }
69+ }
70+ if ! matched {
71+ return ContainerTarget {}, fmt .Errorf ("public key not authorized" )
72+ }
73+
74+ return r .resolveTarget (ctx , auth )
75+ }
76+
77+ func (r * RepoResolver ) resolveTarget (ctx context.Context , auth repository.HostSSHAuth ) (ContainerTarget , error ) {
4578 if auth .UserStatus != "active" {
4679 return ContainerTarget {}, fmt .Errorf ("user suspended" )
4780 }
48-
4981 if auth .HostStatus != "running" {
5082 return ContainerTarget {}, fmt .Errorf ("host not running (status: %s)" , auth .HostStatus )
5183 }
0 commit comments