@@ -2,6 +2,7 @@ package register
22
33import (
44 "context"
5+ "errors"
56 "fmt"
67 "os"
78 "os/user"
@@ -22,8 +23,12 @@ import (
2223const BrevKeyComment = "# brev-cli"
2324
2425// GrantSSHAccessToNode installs the user's public key in authorized_keys and
25- // calls GrantNodeSSHAccess to record access server-side. If the RPC fails,
26- // the installed key is rolled back.
26+ // calls GrantNodeSSHAccess to record access server-side.
27+ //
28+ // On a transient transport error (connect.CodeInternal, e.g. connection reset),
29+ // the key is left in authorized_keys so the caller can retry without
30+ // re-installing it. On a permanent application error (auth, not found, etc.)
31+ // the key is rolled back.
2732func GrantSSHAccessToNode (
2833 ctx context.Context ,
2934 t * terminal.Terminal ,
@@ -34,9 +39,10 @@ func GrantSSHAccessToNode(
3439 osUser * user.User ,
3540) error {
3641 if targetUser .PublicKey != "" {
37- if err := InstallAuthorizedKey (osUser , targetUser .PublicKey ); err != nil {
42+ added , err := InstallAuthorizedKey (osUser , targetUser .PublicKey )
43+ if err != nil {
3844 t .Vprintf (" %s\n " , t .Yellow (fmt .Sprintf ("Warning: failed to install SSH public key: %v" , err )))
39- } else {
45+ } else if added {
4046 t .Vprint (" Brev public key added to authorized_keys." )
4147 }
4248 }
@@ -48,6 +54,14 @@ func GrantSSHAccessToNode(
4854 LinuxUser : osUser .Username ,
4955 }))
5056 if err != nil {
57+ // Transport errors (connection reset, EOF) are transient — leave the key
58+ // installed so retries don't need to reinstall it, and signal the caller
59+ // with a distinct error type.
60+ var connectErr * connect.Error
61+ if errors .As (err , & connectErr ) && connectErr .Code () == connect .CodeInternal {
62+ return fmt .Errorf ("failed to grant SSH access (transient): %w" , err )
63+ }
64+ // Permanent error — roll back the key so we don't leave an unrecorded entry.
5165 if targetUser .PublicKey != "" {
5266 if rerr := RemoveAuthorizedKey (osUser , targetUser .PublicKey ); rerr != nil {
5367 t .Vprintf (" %s\n " , t .Yellow (fmt .Sprintf ("Warning: failed to remove SSH key after failed grant: %v" , rerr )))
@@ -62,38 +76,39 @@ func GrantSSHAccessToNode(
6276// InstallAuthorizedKey appends the given public key to the user's
6377// ~/.ssh/authorized_keys if it isn't already present. The key is tagged with
6478// a brev-cli comment so it can be removed later by RemoveBrevAuthorizedKeys.
65- func InstallAuthorizedKey (u * user.User , pubKey string ) error {
79+ // Returns true if the key was newly written, false if it was already present.
80+ func InstallAuthorizedKey (u * user.User , pubKey string ) (bool , error ) {
6681 pubKey = strings .TrimSpace (pubKey )
6782 if pubKey == "" {
68- return nil
83+ return false , nil
6984 }
7085
7186 sshDir := filepath .Join (u .HomeDir , ".ssh" )
7287 if err := os .MkdirAll (sshDir , 0o700 ); err != nil {
73- return fmt .Errorf ("creating .ssh directory: %w" , err )
88+ return false , fmt .Errorf ("creating .ssh directory: %w" , err )
7489 }
7590
7691 authKeysPath := filepath .Join (sshDir , "authorized_keys" )
7792
7893 existing , err := os .ReadFile (authKeysPath ) // #nosec G304
7994 if err != nil && ! os .IsNotExist (err ) {
80- return fmt .Errorf ("reading authorized_keys: %w" , err )
95+ return false , fmt .Errorf ("reading authorized_keys: %w" , err )
8196 }
8297
8398 taggedKey := pubKey + " " + BrevKeyComment
8499
85100 if strings .Contains (string (existing ), taggedKey ) {
86- return nil // already present with tag
101+ return false , nil // already present with tag
87102 }
88103
89104 // If the key exists but isn't tagged, replace it with the tagged version
90105 // so that RemoveBrevAuthorizedKeys can find it later.
91106 if strings .Contains (string (existing ), pubKey ) {
92107 updated := strings .ReplaceAll (string (existing ), pubKey , taggedKey )
93108 if err := os .WriteFile (authKeysPath , []byte (updated ), 0o600 ); err != nil {
94- return fmt .Errorf ("writing authorized_keys: %w" , err )
109+ return false , fmt .Errorf ("writing authorized_keys: %w" , err )
95110 }
96- return nil
111+ return true , nil
97112 }
98113
99114 // Ensure existing content ends with a newline before appending.
@@ -104,10 +119,10 @@ func InstallAuthorizedKey(u *user.User, pubKey string) error {
104119 content += taggedKey + "\n "
105120
106121 if err := os .WriteFile (authKeysPath , []byte (content ), 0o600 ); err != nil {
107- return fmt .Errorf ("writing authorized_keys: %w" , err )
122+ return false , fmt .Errorf ("writing authorized_keys: %w" , err )
108123 }
109124
110- return nil
125+ return true , nil
111126}
112127
113128// RemoveAuthorizedKey removes a specific public key from the user's
0 commit comments