77 "os"
88 "os/user"
99 "path/filepath"
10+ "strconv"
1011 "strings"
1112 "time"
1213
@@ -132,7 +133,7 @@ func RemoveAuthorizedKeyLine(u *user.User, line string) error {
132133
133134// GrantSSHAccessToNode installs the user's public key in authorized_keys and
134135// calls GrantNodeSSHAccess to record access server-side. If the RPC fails,
135- // the installed key is rolled back.
136+ // the installed key is rolled back. port is the target SSH port (e.g. 22).
136137func GrantSSHAccessToNode (
137138 ctx context.Context ,
138139 t * terminal.Terminal ,
@@ -141,6 +142,7 @@ func GrantSSHAccessToNode(
141142 reg * DeviceRegistration ,
142143 targetUser * entity.User ,
143144 osUser * user.User ,
145+ port uint32 ,
144146) error {
145147 if targetUser .PublicKey != "" {
146148 if added , err := InstallAuthorizedKey (osUser , targetUser .PublicKey , targetUser .ID ); err != nil {
@@ -163,6 +165,7 @@ func GrantSSHAccessToNode(
163165 ExternalNodeId : reg .ExternalNodeID ,
164166 UserId : targetUser .ID ,
165167 LinuxUser : osUser .Username ,
168+ Port : int32 (port ),
166169 }))
167170 if err != nil {
168171 // Retryable error
@@ -192,6 +195,44 @@ func GrantSSHAccessToNode(
192195 return nil
193196}
194197
198+ const defaultSSHPort = 22
199+
200+ // testSSHPort is set by tests to avoid blocking on stdin. When non-nil,
201+ // PromptSSHPort returns this value without prompting.
202+ var testSSHPort * uint32
203+
204+ // SetTestSSHPort sets the port returned by PromptSSHPort without prompting.
205+ // Only for use in tests; call ClearTestSSHPort when done.
206+ func SetTestSSHPort (port uint32 ) { testSSHPort = & port }
207+
208+ // ClearTestSSHPort clears the test port override.
209+ func ClearTestSSHPort () { testSSHPort = nil }
210+
211+ // PromptSSHPort prompts the user for the target SSH port, defaulting to 22 if
212+ // they press Enter or leave it empty. Returns an error for invalid port numbers.
213+ func PromptSSHPort (t * terminal.Terminal ) (uint32 , error ) {
214+ if testSSHPort != nil {
215+ return * testSSHPort , nil
216+ }
217+ portStr := terminal .PromptGetInput (terminal.PromptContent {
218+ Label : " SSH port (default 22): " ,
219+ Default : "22" ,
220+ AllowEmpty : true ,
221+ })
222+ portStr = strings .TrimSpace (portStr )
223+ if portStr == "" {
224+ return defaultSSHPort , nil
225+ }
226+ n , err := strconv .ParseUint (portStr , 10 , 16 )
227+ if err != nil {
228+ return 0 , fmt .Errorf ("invalid port %q: %w" , portStr , err )
229+ }
230+ if n < 1 || n > 65535 {
231+ return 0 , fmt .Errorf ("port must be between 1 and 65535, got %d" , n )
232+ }
233+ return uint32 (n ), nil
234+ }
235+
195236// InstallAuthorizedKey appends the given public key to the user's
196237// ~/.ssh/authorized_keys if it isn't already present. The key is tagged with
197238// a brev-cli comment (including the user ID) so it can be identified and
0 commit comments