Compare commits

..

6 Commits

Author SHA1 Message Date
e9564f5231 Fix ssh cli docs based on review 2024-12-19 22:12:32 -08:00
05cdca9202 Add docs for SSH CLI 2024-12-19 19:47:24 -08:00
5ab0c66dee Merge pull request #2898 from Infisical/cli-ssh-agent
Add SSH CLI capability to load issued SSH credentials into SSH Agent via addToAgent flag
2024-12-19 11:58:02 -08:00
f5a0641671 Add requirement for ssh issue credentials command to include either outFilePath or addToAgent flag 2024-12-19 11:55:44 -08:00
2843818395 Merge pull request #2899 from Infisical/misc/add-custom-metrics-for-errors
misc: added metric for api errors
2024-12-19 14:15:13 -05:00
f97f98b2c3 Add ability to load retrieved ssh credentials into ssh agent with new addToAgent flag 2024-12-18 23:27:11 -08:00
6 changed files with 282 additions and 112 deletions

View File

@ -23,8 +23,8 @@ require (
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.8.1 github.com/spf13/viper v1.8.1
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
golang.org/x/crypto v0.25.0 golang.org/x/crypto v0.31.0
golang.org/x/term v0.22.0 golang.org/x/term v0.27.0
gopkg.in/yaml.v2 v2.4.0 gopkg.in/yaml.v2 v2.4.0
) )
@ -93,9 +93,9 @@ require (
go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/net v0.27.0 // indirect golang.org/x/net v0.27.0 // indirect
golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/oauth2 v0.21.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.10.0 // indirect
golang.org/x/sys v0.22.0 // indirect golang.org/x/sys v0.28.0 // indirect
golang.org/x/text v0.16.0 // indirect golang.org/x/text v0.21.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
google.golang.org/api v0.188.0 // indirect google.golang.org/api v0.188.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240701130421-f6361c86f094 // indirect

View File

@ -453,8 +453,8 @@ golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@ -564,8 +564,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.10.0 h1:3NQrjDixjgGwUOCaF8w2+VYHv0Ve/vGYSbdkTa98gmQ=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@ -620,16 +620,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@ -643,8 +643,8 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@ -6,9 +6,11 @@ package cmd
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"os" "os"
"path/filepath" "path/filepath"
"strings" "strings"
"time"
"github.com/Infisical/infisical-merge/packages/api" "github.com/Infisical/infisical-merge/packages/api"
"github.com/Infisical/infisical-merge/packages/config" "github.com/Infisical/infisical-merge/packages/config"
@ -16,6 +18,8 @@ import (
infisicalSdk "github.com/infisical/go-sdk" infisicalSdk "github.com/infisical/go-sdk"
infisicalSdkUtil "github.com/infisical/go-sdk/packages/util" infisicalSdkUtil "github.com/infisical/go-sdk/packages/util"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
) )
var sshCmd = &cobra.Command{ var sshCmd = &cobra.Command{
@ -81,6 +85,71 @@ func writeToFile(filePath string, content string, perm os.FileMode) error {
return nil return nil
} }
func addCredentialsToAgent(privateKeyContent, certContent string) error {
// Parse the private key
privateKey, err := ssh.ParseRawPrivateKey([]byte(privateKeyContent))
if err != nil {
return fmt.Errorf("failed to parse private key: %w", err)
}
// Parse the certificate
pubKey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(certContent))
if err != nil {
return fmt.Errorf("failed to parse certificate: %w", err)
}
cert, ok := pubKey.(*ssh.Certificate)
if !ok {
return fmt.Errorf("parsed key is not a certificate")
}
// Calculate LifetimeSecs based on certificate's valid-to time
validUntil := time.Unix(int64(cert.ValidBefore), 0)
now := time.Now()
// Handle ValidBefore as either a timestamp or an enumeration
// SSH certificates use ValidBefore as a timestamp unless set to 0 or ~0
if cert.ValidBefore == ssh.CertTimeInfinity {
// If certificate never expires, set default lifetime to 1 year (can adjust as needed)
validUntil = now.Add(365 * 24 * time.Hour)
}
// Calculate the duration until expiration
lifetime := validUntil.Sub(now)
if lifetime <= 0 {
return fmt.Errorf("certificate is already expired")
}
// Convert duration to seconds
lifetimeSecs := uint32(lifetime.Seconds())
// Connect to the SSH agent
socket := os.Getenv("SSH_AUTH_SOCK")
if socket == "" {
return fmt.Errorf("SSH_AUTH_SOCK not set")
}
conn, err := net.Dial("unix", socket)
if err != nil {
return fmt.Errorf("failed to connect to SSH agent: %w", err)
}
defer conn.Close()
agentClient := agent.NewClient(conn)
// Add the key with certificate to the agent
err = agentClient.Add(agent.AddedKey{
PrivateKey: privateKey,
Certificate: cert,
Comment: "Added via Infisical CLI",
LifetimeSecs: lifetimeSecs,
})
if err != nil {
return fmt.Errorf("failed to add key to agent: %w", err)
}
return nil
}
func issueCredentials(cmd *cobra.Command, args []string) { func issueCredentials(cmd *cobra.Command, args []string) {
token, err := util.GetInfisicalToken(cmd) token, err := util.GetInfisicalToken(cmd)
@ -166,6 +235,15 @@ func issueCredentials(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
addToAgent, err := cmd.Flags().GetBool("addToAgent")
if err != nil {
util.HandleError(err, "Unable to parse addToAgent flag")
}
if outFilePath == "" && addToAgent == false {
util.PrintErrorMessageAndExit("You must provide either --outFilePath or --addToAgent flag to use this command")
}
var ( var (
outputDir string outputDir string
privateKeyPath string privateKeyPath string
@ -173,14 +251,7 @@ func issueCredentials(cmd *cobra.Command, args []string) {
signedKeyPath string signedKeyPath string
) )
if outFilePath == "" { if outFilePath != "" {
// Use current working directory
cwd, err := os.Getwd()
if err != nil {
util.HandleError(err, "Failed to get current working directory")
}
outputDir = cwd
} else {
// Expand ~ to home directory if present // Expand ~ to home directory if present
if strings.HasPrefix(outFilePath, "~") { if strings.HasPrefix(outFilePath, "~") {
homeDir, err := os.UserHomeDir() homeDir, err := os.UserHomeDir()
@ -264,6 +335,7 @@ func issueCredentials(cmd *cobra.Command, args []string) {
util.HandleError(err, "Failed to issue SSH credentials") util.HandleError(err, "Failed to issue SSH credentials")
} }
if outFilePath != "" {
// If signedKeyPath wasn't set in the directory scenario, set it now // If signedKeyPath wasn't set in the directory scenario, set it now
if signedKeyPath == "" { if signedKeyPath == "" {
fileName := algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)] fileName := algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)]
@ -292,6 +364,18 @@ func issueCredentials(cmd *cobra.Command, args []string) {
} }
fmt.Println("Successfully wrote SSH certificate to:", signedKeyPath) fmt.Println("Successfully wrote SSH certificate to:", signedKeyPath)
}
// Add SSH credentials to the SSH agent if needed
if addToAgent {
// Call the helper function to handle add-to-agent flow
err := addCredentialsToAgent(creds.PrivateKey, creds.SignedKey)
if err != nil {
util.HandleError(err, "Failed to add keys to SSH agent")
} else {
fmt.Println("The SSH key and certificate have been successfully added to your ssh-agent.")
}
}
} }
func signKey(cmd *cobra.Command, args []string) { func signKey(cmd *cobra.Command, args []string) {
@ -519,6 +603,7 @@ func init() {
sshIssueCredentialsCmd.Flags().String("ttl", "", "The ttl to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("ttl", "", "The ttl to issue SSH credentials for")
sshIssueCredentialsCmd.Flags().String("keyId", "", "The keyId to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("keyId", "", "The keyId to issue SSH credentials for")
sshIssueCredentialsCmd.Flags().String("outFilePath", "", "The path to write the SSH credentials to such as ~/.ssh, ./some_folder, ./some_folder/id_rsa-cert.pub. If not provided, the credentials will be saved to the current working directory") sshIssueCredentialsCmd.Flags().String("outFilePath", "", "The path to write the SSH credentials to such as ~/.ssh, ./some_folder, ./some_folder/id_rsa-cert.pub. If not provided, the credentials will be saved to the current working directory")
sshIssueCredentialsCmd.Flags().Bool("addToAgent", false, "Whether to add issued SSH credentials to the SSH agent")
sshCmd.AddCommand(sshIssueCredentialsCmd) sshCmd.AddCommand(sshIssueCredentialsCmd)
rootCmd.AddCommand(sshCmd) rootCmd.AddCommand(sshCmd)
} }

116
docs/cli/commands/ssh.mdx Normal file
View File

@ -0,0 +1,116 @@
---
title: "infisical ssh"
description: "Generate SSH credentials with the CLI"
---
## Description
[Infisical SSH](/documentation/platform/ssh) lets you issue SSH credentials to clients to provide short-lived, secure SSH access to infrastructure.
This command enables you to obtain SSH credentials used to access a remote host; we recommend using the `issue-credentials` sub-command to generate dynamic SSH credentials for each SSH session.
### Sub-commands
<Accordion title="infisical ssh issue-credentials">
This command is used to issue SSH credentials (SSH certificate, public key, and private key) against a certificate template.
We recommend using the `--addToAgent` flag to automatically load issued SSH credentials to the SSH agent.
```bash
$ infisical ssh issue-credentials --certificateTemplateId=<certificate-template-id> --principals=<principals> --addToAgent
```
### Flags
<Accordion title="--certificateTemplateId">
The ID of the SSH certificate template to issue SSH credentials for.
</Accordion>
<Accordion title="--principals">
A comma-separated list of principals (i.e. usernames like `ec2-user` or hostnames) to issue SSH credentials for.
</Accordion>
<Accordion title="--addToAgent">
Whether to add issued SSH credentials to the SSH agent.
Default value: `false`
Note that either the `--outFilePath` or `--addToAgent` flag must be set for the sub-command to execute successfully.
</Accordion>
<Accordion title="--outFilePath">
The path to write the SSH credentials to such as `~/.ssh`, `./some_folder`, `./some_folder/id_rsa-cert.pub`. If not provided, the credentials will be saved to the current working directory where the command is run.
Note that either the `--outFilePath` or `--addToAgent` flag must be set for the sub-command to execute successfully.
</Accordion>
<Accordion title="--keyAlgorithm">
The key algorithm to issue SSH credentials for.
Default value: `RSA_2048`
Available options: `RSA_2048`, `RSA_4096`, `EC_prime256v1`, `EC_secp384r1`.
</Accordion>
<Accordion title="--certType">
The certificate type to issue SSH credentials for.
Default value: `user`
Available options: `user` or `host`
</Accordion>
<Accordion title="--ttl">
The time-to-live (TTL) for the issued SSH certificate (e.g. `2 days`, `1d`, `2h`, `1y`).
Defaults to the Default TTL value set in the certificate template.
</Accordion>
<Accordion title="--keyId">
A custom Key ID to issue SSH credentials for.
Defaults to the autogenerated Key ID by Infisical.
</Accordion>
<Accordion title="--token">
An authenticated token to use to issue SSH credentials.
</Accordion>
</Accordion>
<Accordion title="infisical ssh sign-key">
This command is used to sign an existing SSH public key against a certificate template; the command outputs the corresponding signed SSH certificate.
```bash
$ infisical ssh sign-key --certificateTemplateId=<certificate-template-id> --publicKey=<public-key> --principals=<principals> --outFilePath=<out-file-path>
```
<Accordion title="--certificateTemplateId">
The ID of the SSH certificate template to issue the SSH certificate for.
</Accordion>
<Accordion title="--publicKey">
The public key to sign.
Note that either the `--publicKey` or `--publicKeyFilePath` flag must be set for the sub-command to execute successfully.
</Accordion>
<Accordion title="--publicKeyFilePath">
The path to the public key file to sign.
Note that either the `--publicKey` or `--publicKeyFilePath` flag must be set for the sub-command to execute successfully.
</Accordion>
<Accordion title="--principals">
A comma-separated list of principals (i.e. usernames like `ec2-user` or hostnames) to issue SSH credentials for.
</Accordion>
<Accordion title="--outFilePath">
The path to write the SSH certificate to such as `~/.ssh/id_rsa-cert.pub`; the specified file must have the `.pub` extension. If not provided, the credentials will be saved to the directory of the specified `--publicKeyFilePath` or the current working directory where the command is run.
</Accordion>
<Accordion title="--certType">
The certificate type to issue SSH credentials for.
Default value: `user`
Available options: `user` or `host`
</Accordion>
<Accordion title="--ttl">
The time-to-live (TTL) for the issued SSH certificate (e.g. `2 days`, `1d`, `2h`, `1y`).
Defaults to the Default TTL value set in the certificate template.
</Accordion>
<Accordion title="--keyId">
A custom Key ID to issue SSH credentials for.
Defaults to the autogenerated Key ID by Infisical.
</Accordion>
<Accordion title="--token">
An authenticated token to use to issue SSH credentials.
</Accordion>
</Accordion>

View File

@ -6,7 +6,7 @@ description: "Learn how to generate SSH credentials to provide secure and centra
## Concept ## Concept
Infisical can be used to issue SSH certificates to clients to provide short-lived, secure SSH access to infrastructure; Infisical can be used to issue SSH credentials to clients to provide short-lived, secure SSH access to infrastructure;
this improves on many limitations of traditional SSH key-based authentication via mitigation of private key compromise, static key management, this improves on many limitations of traditional SSH key-based authentication via mitigation of private key compromise, static key management,
unauthorized access, and SSH key sprawl. unauthorized access, and SSH key sprawl.
@ -159,7 +159,19 @@ as part of the SSH operation.
## Guide to Using Infisical SSH to Access a Host ## Guide to Using Infisical SSH to Access a Host
We show how to obtain a SSH certificate (and optionally a new SSH key pair) for a client to access a host via CLI: We show how to obtain a SSH certificate and use it for a client to access a host via CLI:
<Note>
The subsequent guide assumes the following prerequisites:
- SSH Agent is running: The `ssh-agent` must be actively running on the host machine.
- OpenSSH is installed: The system should have OpenSSH installed; this includes
both the `ssh` client and `ssh-agent`.
- `SSH_AUTH_SOCK` environment variable
is set; the `SSH_AUTH_SOCK` variable should point to the UNIX socket that
`ssh-agent` uses for communication.
</Note>
<Steps> <Steps>
<Step title="Authenticate with Infisical"> <Step title="Authenticate with Infisical">
@ -169,70 +181,26 @@ infisical login
``` ```
</Step> </Step>
<Step title="Obtain a SSH certificate (and optionally new key-pair)"> <Step title="Obtain a SSH certificate and load it into the SSH agent">
Depending on the use-case, a client may either request a SSH certificate along with a new SSH key pair or obtain a SSH certificate for an existing SSH key pair to access a host. Run the `infisical ssh issue-credentials` command, specifying the `--addToAgent` flag to automatically load the SSH certificate into the SSH agent.
<AccordionGroup>
<Accordion title="Using New Key Pair (Recommended)">
If you wish to obtain a new SSH key pair in conjunction with the SSH certificate, then you can use the `infisical ssh issue-credentials` command.
```bash ```bash
infisical ssh issue-credentials --certificateTemplateId=<certificate-template-id> --principals=<username> infisical ssh issue-credentials --certificateTemplateId=<certificate-template-id> --principals=<username> --addToAgent
``` ```
The following flags may be relevant: Here's some guidance on each flag:
- `certificateTemplateId`: The ID of the certificate template to use for issuing the SSH certificate. - `certificateTemplateId`: The ID of the certificate template to use for issuing the SSH certificate.
- `principals`: The comma-delimited username(s) or hostname(s) to include in the SSH certificate. - `principals`: The comma-delimited username(s) or hostname(s) to include in the SSH certificate.
- `outFilePath` (optional): The path to the file to write the SSH certificate to.
<Note> For fuller documentation on commands and flags supported by the Infisical CLI for SSH, refer to the docs [here](/cli/commands/ssh).
If `outFilePath` is not specified, the SSH certificate will be written to the current working directory where the command is run.
</Note>
</Accordion>
<Accordion title="Using Existing Key Pair">
If you have an existing SSH key pair, then you can use the `infisical ssh sign-key` command with either
the `--publicKey` flag or the `--publicKeyFilePath` flag to obtain a SSH certificate corresponding to
the existing credential.
```bash
infisical ssh sign-key --publicKeyFilePath=<public-key-file-path> --certificateTemplateId=<certificate-template-id> --principals=<username>
```
The following flags may be relevant:
- `publicKey`: The public key to sign.
- `publicKeyFilePath`: The path to the public key file to sign.
- `certificateTemplateId`: The ID of the certificate template to use for issuing the SSH certificate.
- `principals`: The comma-delimited username(s) or hostname(s) to include in the SSH certificate.
- `outFilePath` (optional): The path to the file to write the SSH certificate to.
<Note>
If `outFilePath` is not specified but `publicKeyFilePath` is then the SSH certificate will be written to the directory of the public key file; if the public key file is called `id_rsa.pub`, then the file containing the SSH certificate will be called `id_rsa-cert.pub`.
Otherwise, if `outFilePath` is not specified, the SSH certificate will be written to the current working directory where the command is run.
</Note>
</Accordion>
</AccordionGroup>
</Step> </Step>
<Step title="SSH into the host"> <Step title="SSH into the host">
Once you have obtained the SSH certificate, you can use it to SSH into the desired host. Finally, SSH into the desired host; the SSH operation will be performed using the SSH certificate loaded into the SSH agent.
```bash ```bash
ssh -i /path/to/private_key.pem \ ssh username@hostname
-o CertificateFile=/path/to/ssh-cert.pub \
username@hostname
``` ```
<Note>
We recommend setting up aliases so you can more easily SSH into the desired host.
For example, you may set up an SSH alias using the SSH client configuration file (usually `~/.ssh/config`), defining a host alias including the file path to the issued SSH credential(s).
</Note>
</Step> </Step>
</Steps> </Steps>

View File

@ -320,6 +320,7 @@
"cli/commands/run", "cli/commands/run",
"cli/commands/secrets", "cli/commands/secrets",
"cli/commands/dynamic-secrets", "cli/commands/dynamic-secrets",
"cli/commands/ssh",
"cli/commands/export", "cli/commands/export",
"cli/commands/token", "cli/commands/token",
"cli/commands/service-token", "cli/commands/service-token",