@@ -108,66 +108,113 @@ var pamSshCmd = &cobra.Command{
108108
109109var pamSshAccessCmd = & cobra.Command {
110110 Use : "access" ,
111- Short : "Start SSH session to PAM account" ,
112- Long : "Start an SSH session to a PAM-managed SSH account. This command automatically launches an SSH client connected through the Infisical Gateway." ,
113- Example : "infisical pam ssh access --resource prod-servers --account root --project-id b38bef10-2685-43c4-9a2c-635206d60bec --duration 1h" ,
111+ Short : "Start interactive SSH session to PAM account" ,
112+ Long : "Start an interactive SSH session to a PAM-managed SSH account. This command automatically launches an SSH client connected through the Infisical Gateway." ,
113+ Example : "infisical pam ssh access --resource prod-servers --account root --project-id <project-uuid> --duration 1h" ,
114114 DisableFlagsInUseLine : true ,
115115 Args : cobra .NoArgs ,
116116 Run : func (cmd * cobra.Command , args []string ) {
117- util .RequireLogin ()
117+ runSSHCommand (cmd , args , pam.SSHAccessOptions {})
118+ },
119+ }
118120
119- resourceName , _ := cmd .Flags ().GetString ("resource" )
120- accountName , _ := cmd .Flags ().GetString ("account" )
121+ var pamSshExecCmd = & cobra.Command {
122+ Use : "exec [command]" ,
123+ Short : "Execute a command on a PAM SSH account" ,
124+ Long : `Execute a single command on a PAM-managed SSH account and return the output.
125+ This is useful for CI/CD pipelines and scripting where interactive sessions are not needed.` ,
126+ Example : ` # Run a command and get output
127+ infisical pam ssh exec "ls -la /var/log" --resource prod-servers --account root --project-id <project-uuid>
121128
122- if resourceName == "" || accountName == "" {
123- util .PrintErrorMessageAndExit ("Both --resource and --account flags are required" )
124- }
129+ # Use in a script
130+ OUTPUT=$(infisical pam ssh exec "cat /etc/hostname" --resource prod-servers --account root --project-id <project-uuid>)` ,
131+ DisableFlagsInUseLine : true ,
132+ Args : cobra .ExactArgs (1 ),
133+ Run : func (cmd * cobra.Command , args []string ) {
134+ runSSHCommand (cmd , args , pam.SSHAccessOptions {
135+ ExecCommand : args [0 ],
136+ })
137+ },
138+ }
125139
126- durationStr , err := cmd .Flags ().GetString ("duration" )
127- if err != nil {
128- util .HandleError (err , "Unable to parse duration flag" )
129- }
140+ var pamSshProxyCmd = & cobra.Command {
141+ Use : "proxy" ,
142+ Short : "Start SSH proxy for SCP, SFTP, or rsync" ,
143+ Long : `Start an SSH proxy without launching an interactive session.
144+ This is useful for file transfers using SCP, SFTP, rsync, or other SSH-based tools.
145+ The proxy prints connection details and waits until terminated with Ctrl+C.` ,
146+ Example : ` # Start the proxy
147+ infisical pam ssh proxy --resource prod-servers --account root --project-id <project-uuid>
130148
131- _ , err = time .ParseDuration (durationStr )
132- if err != nil {
133- util .HandleError (err , "Invalid duration format. Use formats like '1h', '30m', '2h30m'" )
134- }
149+ # Then in another terminal, use SCP:
150+ scp -P <port> -o StrictHostKeyChecking=no local-file.txt root@127.0.0.1:/remote/path/
135151
136- projectID , err := cmd .Flags ().GetString ("project-id" )
137- if err != nil {
138- util .HandleError (err , "Unable to parse project-id flag" )
139- }
152+ # Or use rsync:
153+ rsync -e "ssh -p <port> -o StrictHostKeyChecking=no" local-dir/ root@127.0.0.1:/remote/path/` ,
154+ DisableFlagsInUseLine : true ,
155+ Args : cobra .NoArgs ,
156+ Run : func (cmd * cobra.Command , args []string ) {
157+ runSSHCommand (cmd , args , pam.SSHAccessOptions {
158+ ProxyOnly : true ,
159+ })
160+ },
161+ }
140162
141- if projectID == "" {
142- workspaceFile , err := util .GetWorkSpaceFromFile ()
143- if err != nil {
144- util .PrintErrorMessageAndExit ("Please either run infisical init to connect to a project or pass in project id with --project-id flag" )
145- }
146- projectID = workspaceFile .WorkspaceId
147- }
163+ // runSSHCommand is the shared implementation for all SSH subcommands
164+ func runSSHCommand (cmd * cobra.Command , args []string , options pam.SSHAccessOptions ) {
165+ util .RequireLogin ()
148166
149- log .Debug ().Msg ("PAM SSH Access: Trying to fetch credentials using logged in details" )
167+ resourceName , _ := cmd .Flags ().GetString ("resource" )
168+ accountName , _ := cmd .Flags ().GetString ("account" )
150169
151- loggedInUserDetails , err := util .GetCurrentLoggedInUserDetails (true )
152- isConnected := util .ValidateInfisicalAPIConnection ()
170+ if resourceName == "" || accountName == "" {
171+ util .PrintErrorMessageAndExit ("Both --resource and --account flags are required" )
172+ }
153173
154- if isConnected {
155- log .Debug ().Msg ("PAM SSH Access: Connected to Infisical instance, checking logged in creds" )
156- }
174+ durationStr , err := cmd .Flags ().GetString ("duration" )
175+ if err != nil {
176+ util .HandleError (err , "Unable to parse duration flag" )
177+ }
178+
179+ _ , err = time .ParseDuration (durationStr )
180+ if err != nil {
181+ util .HandleError (err , "Invalid duration format. Use formats like '1h', '30m', '2h30m'" )
182+ }
183+
184+ projectID , err := cmd .Flags ().GetString ("project-id" )
185+ if err != nil {
186+ util .HandleError (err , "Unable to parse project-id flag" )
187+ }
157188
189+ if projectID == "" {
190+ workspaceFile , err := util .GetWorkSpaceFromFile ()
158191 if err != nil {
159- util .HandleError ( err , "Unable to get logged in user details " )
192+ util .PrintErrorMessageAndExit ( "Please either run infisical init to connect to a project or pass in project id with --project-id flag " )
160193 }
194+ projectID = workspaceFile .WorkspaceId
195+ }
161196
162- if isConnected && loggedInUserDetails .LoginExpired {
163- loggedInUserDetails = util .EstablishUserLoginSession ()
164- }
197+ log .Debug ().Msg ("PAM SSH: Trying to fetch credentials using logged in details" )
165198
166- pam .StartSSHLocalProxy (loggedInUserDetails .UserCredentials .JTWToken , pam.PAMAccessParams {
167- ResourceName : resourceName ,
168- AccountName : accountName ,
169- }, projectID , durationStr )
170- },
199+ loggedInUserDetails , err := util .GetCurrentLoggedInUserDetails (true )
200+ isConnected := util .ValidateInfisicalAPIConnection ()
201+
202+ if isConnected {
203+ log .Debug ().Msg ("PAM SSH: Connected to Infisical instance, checking logged in creds" )
204+ }
205+
206+ if err != nil {
207+ util .HandleError (err , "Unable to get logged in user details" )
208+ }
209+
210+ if isConnected && loggedInUserDetails .LoginExpired {
211+ loggedInUserDetails = util .EstablishUserLoginSession ()
212+ }
213+
214+ pam .StartSSHLocalProxy (loggedInUserDetails .UserCredentials .JTWToken , pam.PAMAccessParams {
215+ ResourceName : resourceName ,
216+ AccountName : accountName ,
217+ }, projectID , durationStr , options )
171218}
172219
173220// ==================== Kubernetes Commands ====================
@@ -340,14 +387,24 @@ func init() {
340387 pamDbAccessCmd .MarkFlagRequired ("resource" )
341388 pamDbAccessCmd .MarkFlagRequired ("account" )
342389
343- // SSH commands
390+ // SSH commands - shared flags helper
391+ addSSHFlags := func (cmd * cobra.Command ) {
392+ cmd .Flags ().String ("resource" , "" , "Name of the PAM resource to access" )
393+ cmd .Flags ().String ("account" , "" , "Name of the account within the resource" )
394+ cmd .Flags ().String ("duration" , "1h" , "Duration for SSH access session (e.g., '1h', '30m', '2h30m')" )
395+ cmd .Flags ().String ("project-id" , "" , "Project ID of the account to access" )
396+ cmd .MarkFlagRequired ("resource" )
397+ cmd .MarkFlagRequired ("account" )
398+ }
399+
344400 pamSshCmd .AddCommand (pamSshAccessCmd )
345- pamSshAccessCmd .Flags ().String ("resource" , "" , "Name of the PAM resource to access" )
346- pamSshAccessCmd .Flags ().String ("account" , "" , "Name of the account within the resource" )
347- pamSshAccessCmd .Flags ().String ("duration" , "1h" , "Duration for SSH access session (e.g., '1h', '30m', '2h30m')" )
348- pamSshAccessCmd .Flags ().String ("project-id" , "" , "Project ID of the account to access" )
349- pamSshAccessCmd .MarkFlagRequired ("resource" )
350- pamSshAccessCmd .MarkFlagRequired ("account" )
401+ addSSHFlags (pamSshAccessCmd )
402+
403+ pamSshCmd .AddCommand (pamSshExecCmd )
404+ addSSHFlags (pamSshExecCmd )
405+
406+ pamSshCmd .AddCommand (pamSshProxyCmd )
407+ addSSHFlags (pamSshProxyCmd )
351408
352409 // Kubernetes commands
353410 pamKubernetesCmd .AddCommand (pamKubernetesAccessCmd )
0 commit comments