Update ssh cli issue/sign according to review

This commit is contained in:
Tuan Dang
2024-12-17 13:12:47 -08:00
parent be36827392
commit 765280eef6
3 changed files with 227 additions and 215 deletions
+1 -1
View File
@@ -10,7 +10,7 @@ require (
github.com/fatih/semgroup v1.2.0 github.com/fatih/semgroup v1.2.0
github.com/gitleaks/go-gitdiff v0.8.0 github.com/gitleaks/go-gitdiff v0.8.0
github.com/h2non/filetype v1.1.3 github.com/h2non/filetype v1.1.3
github.com/infisical/go-sdk v0.4.5 github.com/infisical/go-sdk v0.4.6
github.com/mattn/go-isatty v0.0.20 github.com/mattn/go-isatty v0.0.20
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a
github.com/muesli/mango-cobra v1.2.0 github.com/muesli/mango-cobra v1.2.0
+2
View File
@@ -269,6 +269,8 @@ github.com/infisical/go-sdk v0.4.3 h1:O5ZJ2eCBAZDE9PIAfBPq9Utb2CgQKrhmj9R0oFTRu4
github.com/infisical/go-sdk v0.4.3/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M= github.com/infisical/go-sdk v0.4.3/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
github.com/infisical/go-sdk v0.4.5 h1:rQSgWW+thZQ2WfIbOD5SBflqx3BUUSNlvuDZdexXMFk= github.com/infisical/go-sdk v0.4.5 h1:rQSgWW+thZQ2WfIbOD5SBflqx3BUUSNlvuDZdexXMFk=
github.com/infisical/go-sdk v0.4.5/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M= github.com/infisical/go-sdk v0.4.5/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
github.com/infisical/go-sdk v0.4.6 h1:RXXcmucfO2T5dSzXao9h9pZVn+m2usuflVO45Lpoz/o=
github.com/infisical/go-sdk v0.4.6/go.mod h1:6fWzAwTPIoKU49mQ2Oxu+aFnJu9n7k2JcNrZjzhHM2M=
github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+64 -54
View File
@@ -44,6 +44,13 @@ var sshSignKeyCmd = &cobra.Command{
Run: signKey, Run: signKey,
} }
var algoToFileName = map[infisicalSdkUtil.CertKeyAlgorithm]string{
infisicalSdkUtil.RSA2048: "id_rsa_2048",
infisicalSdkUtil.RSA4096: "id_rsa_4096",
infisicalSdkUtil.ECDSAP256: "id_ecdsa_p256",
infisicalSdkUtil.ECDSAP384: "id_ecdsa_p384",
}
func isValidKeyAlgorithm(algo infisicalSdkUtil.CertKeyAlgorithm) bool { func isValidKeyAlgorithm(algo infisicalSdkUtil.CertKeyAlgorithm) bool {
switch algo { switch algo {
case infisicalSdkUtil.RSA2048, infisicalSdkUtil.RSA4096, infisicalSdkUtil.ECDSAP256, infisicalSdkUtil.ECDSAP384: case infisicalSdkUtil.RSA2048, infisicalSdkUtil.RSA4096, infisicalSdkUtil.ECDSAP256, infisicalSdkUtil.ECDSAP384:
@@ -53,19 +60,6 @@ func isValidKeyAlgorithm(algo infisicalSdkUtil.CertKeyAlgorithm) bool {
} }
} }
func getFileName(algo infisicalSdkUtil.CertKeyAlgorithm) string {
switch algo {
case infisicalSdkUtil.RSA2048:
return "id_rsa_2048"
case infisicalSdkUtil.RSA4096:
return "id_rsa_4096"
case infisicalSdkUtil.ECDSAP256:
return "id_ecdsa_p256"
default:
return "id_ecdsa_p384"
}
}
func isValidCertType(certType infisicalSdkUtil.SshCertType) bool { func isValidCertType(certType infisicalSdkUtil.SshCertType) bool {
switch certType { switch certType {
case infisicalSdkUtil.UserCert, infisicalSdkUtil.HostCert: case infisicalSdkUtil.UserCert, infisicalSdkUtil.HostCert:
@@ -92,8 +86,20 @@ func writeToFile(filePath string, content string, perm os.FileMode) error {
} }
func issueCredentials(cmd *cobra.Command, args []string) { func issueCredentials(cmd *cobra.Command, args []string) {
token, err := util.GetInfisicalToken(cmd)
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
var infisicalToken string var infisicalToken string
if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) {
infisicalToken = token.Token
} else {
util.RequireLogin() util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil { if err != nil {
util.HandleError(err, "Unable to authenticate") util.HandleError(err, "Unable to authenticate")
@@ -103,21 +109,14 @@ func issueCredentials(cmd *cobra.Command, args []string) {
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
} }
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
}
projectId, err := cmd.Flags().GetString("projectId") certificateTemplateId, err := cmd.Flags().GetString("certificateTemplateId")
if err != nil { if err != nil {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
if projectId == "" { if certificateTemplateId == "" {
util.PrintErrorMessageAndExit("You must set the --projectId flag") util.PrintErrorMessageAndExit("You must set the --certificateTemplateId flag")
}
templateName, err := cmd.Flags().GetString("templateName")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
if templateName == "" {
util.PrintErrorMessageAndExit("You must set the --templateName flag")
} }
principalsStr, err := cmd.Flags().GetString("principals") principalsStr, err := cmd.Flags().GetString("principals")
@@ -125,6 +124,11 @@ func issueCredentials(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
// Check if the input string is empty before splitting
if principalsStr == "" {
util.HandleError(fmt.Errorf("no principals provided"), "The 'principals' flag cannot be empty")
}
// Convert the comma-delimited string into a slice of strings // Convert the comma-delimited string into a slice of strings
principals := strings.Split(principalsStr, ",") principals := strings.Split(principalsStr, ",")
for i, principal := range principals { for i, principal := range principals {
@@ -220,16 +224,16 @@ func issueCredentials(cmd *cobra.Command, args []string) {
} else if !info.IsDir() { } else if !info.IsDir() {
util.PrintErrorMessageAndExit("The provided --outFilePath is not a directory") util.PrintErrorMessageAndExit("The provided --outFilePath is not a directory")
} }
}
}
// Define file names based on key algorithm // Define file names based on key algorithm
fileName := getFileName(infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)) fileName := algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)]
// Define file paths // Define file paths
privateKeyPath = filepath.Join(outputDir, fileName) privateKeyPath = filepath.Join(outputDir, fileName)
publicKeyPath = filepath.Join(outputDir, fileName+".pub") publicKeyPath = filepath.Join(outputDir, fileName+".pub")
signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub") signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub")
}
}
// If outFilePath ends with "-cert.pub", ensure the signedKeyPath is set // If outFilePath ends with "-cert.pub", ensure the signedKeyPath is set
if strings.HasSuffix(outFilePath, "-cert.pub") { if strings.HasSuffix(outFilePath, "-cert.pub") {
@@ -252,8 +256,7 @@ func issueCredentials(cmd *cobra.Command, args []string) {
infisicalClient.Auth().SetAccessToken(infisicalToken) infisicalClient.Auth().SetAccessToken(infisicalToken)
creds, err := infisicalClient.Ssh().IssueCredentials(infisicalSdk.IssueSshCredsOptions{ creds, err := infisicalClient.Ssh().IssueCredentials(infisicalSdk.IssueSshCredsOptions{
ProjectID: projectId, CertificateTemplateID: certificateTemplateId,
TemplateName: templateName,
Principals: principals, Principals: principals,
KeyAlgorithm: infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm), KeyAlgorithm: infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm),
CertType: infisicalSdkUtil.SshCertType(certType), CertType: infisicalSdkUtil.SshCertType(certType),
@@ -267,12 +270,12 @@ func issueCredentials(cmd *cobra.Command, args []string) {
// 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 := getFileName(infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)) fileName := algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)]
signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub") signedKeyPath = filepath.Join(outputDir, fileName+"-cert.pub")
} }
if privateKeyPath == "" { if privateKeyPath == "" {
privateKeyPath = filepath.Join(outputDir, getFileName(infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm))) privateKeyPath = filepath.Join(outputDir, algoToFileName[infisicalSdkUtil.CertKeyAlgorithm(keyAlgorithm)])
} }
err = writeToFile(privateKeyPath, creds.PrivateKey, 0600) err = writeToFile(privateKeyPath, creds.PrivateKey, 0600)
if err != nil { if err != nil {
@@ -292,12 +295,24 @@ func issueCredentials(cmd *cobra.Command, args []string) {
util.HandleError(err, "Failed to write Signed Key to file") util.HandleError(err, "Failed to write Signed Key to file")
} }
fmt.Println("Successfully wrote SSH certificate tox:", signedKeyPath) fmt.Println("Successfully wrote SSH certificate to:", signedKeyPath)
} }
func signKey(cmd *cobra.Command, args []string) { func signKey(cmd *cobra.Command, args []string) {
token, err := util.GetInfisicalToken(cmd)
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
var infisicalToken string var infisicalToken string
if token != nil && (token.Type == util.SERVICE_TOKEN_IDENTIFIER || token.Type == util.UNIVERSAL_AUTH_TOKEN_IDENTIFIER) {
infisicalToken = token.Token
} else {
util.RequireLogin() util.RequireLogin()
util.RequireLocalWorkspaceFile()
loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true) loggedInUserDetails, err := util.GetCurrentLoggedInUserDetails(true)
if err != nil { if err != nil {
util.HandleError(err, "Unable to authenticate") util.HandleError(err, "Unable to authenticate")
@@ -307,21 +322,14 @@ func signKey(cmd *cobra.Command, args []string) {
util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again") util.PrintErrorMessageAndExit("Your login session has expired, please run [infisical login] and try again")
} }
infisicalToken = loggedInUserDetails.UserCredentials.JTWToken infisicalToken = loggedInUserDetails.UserCredentials.JTWToken
}
projectId, err := cmd.Flags().GetString("projectId") certificateTemplateId, err := cmd.Flags().GetString("certificateTemplateId")
if err != nil { if err != nil {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
if projectId == "" { if certificateTemplateId == "" {
util.PrintErrorMessageAndExit("You must set the --projectId flag") util.PrintErrorMessageAndExit("You must set the --certificateTemplateId flag")
}
templateName, err := cmd.Flags().GetString("templateName")
if err != nil {
util.HandleError(err, "Unable to parse flag")
}
if templateName == "" {
util.PrintErrorMessageAndExit("You must set the --templateName flag")
} }
publicKey, err := cmd.Flags().GetString("publicKey") publicKey, err := cmd.Flags().GetString("publicKey")
@@ -365,7 +373,7 @@ func signKey(cmd *cobra.Command, args []string) {
publicKey = strings.TrimSpace(string(content)) publicKey = strings.TrimSpace(string(content))
} }
if (strings.TrimSpace(publicKey) == "") { if strings.TrimSpace(publicKey) == "" {
util.HandleError(fmt.Errorf("Public key is empty"), "Invalid input") util.HandleError(fmt.Errorf("Public key is empty"), "Invalid input")
} }
@@ -374,10 +382,13 @@ func signKey(cmd *cobra.Command, args []string) {
util.HandleError(err, "Unable to parse flag") util.HandleError(err, "Unable to parse flag")
} }
// Check if the input string is empty before splitting
if principalsStr == "" {
util.HandleError(fmt.Errorf("no principals provided"), "The 'principals' flag cannot be empty")
}
// Convert the comma-delimited string into a slice of strings // Convert the comma-delimited string into a slice of strings
principals := strings.Split(principalsStr, ",") principals := strings.Split(principalsStr, ",")
// Trim whitespace around each principal
for i, principal := range principals { for i, principal := range principals {
principals[i] = strings.TrimSpace(principal) principals[i] = strings.TrimSpace(principal)
} }
@@ -419,7 +430,7 @@ func signKey(cmd *cobra.Command, args []string) {
} }
// check if public key path exists // check if public key path exists
if (publicKeyFilePath == "") { if publicKeyFilePath == "" {
util.PrintErrorMessageAndExit("--outFilePath must be specified when --publicKeyFilePath is not provided") util.PrintErrorMessageAndExit("--outFilePath must be specified when --publicKeyFilePath is not provided")
} }
@@ -438,7 +449,7 @@ func signKey(cmd *cobra.Command, args []string) {
} }
// Check if outFilePath ends with "-cert.pub" // Check if outFilePath ends with "-cert.pub"
if (!strings.HasSuffix(outFilePath, "-cert.pub")) { if !strings.HasSuffix(outFilePath, "-cert.pub") {
util.PrintErrorMessageAndExit("--outFilePath must end with -cert.pub") util.PrintErrorMessageAndExit("--outFilePath must end with -cert.pub")
} }
@@ -472,8 +483,7 @@ func signKey(cmd *cobra.Command, args []string) {
infisicalClient.Auth().SetAccessToken(infisicalToken) infisicalClient.Auth().SetAccessToken(infisicalToken)
creds, err := infisicalClient.Ssh().SignKey(infisicalSdk.SignSshPublicKeyOptions{ creds, err := infisicalClient.Ssh().SignKey(infisicalSdk.SignSshPublicKeyOptions{
ProjectID: projectId, CertificateTemplateID: certificateTemplateId,
TemplateName: templateName,
PublicKey: publicKey, PublicKey: publicKey,
Principals: principals, Principals: principals,
CertType: infisicalSdkUtil.SshCertType(certType), CertType: infisicalSdkUtil.SshCertType(certType),
@@ -494,8 +504,8 @@ func signKey(cmd *cobra.Command, args []string) {
} }
func init() { func init() {
sshSignKeyCmd.Flags().String("projectId", "", "The projectId to issue credentials for") sshSignKeyCmd.Flags().String("token", "", "Issue SSH certificate using machine identity access token")
sshSignKeyCmd.Flags().String("templateName", "", "The template name to issue credentials for") sshSignKeyCmd.Flags().String("certificateTemplateId", "", "The ID of the SSH certificate template to issue the SSH certificate for")
sshSignKeyCmd.Flags().String("publicKey", "", "The public key to sign") sshSignKeyCmd.Flags().String("publicKey", "", "The public key to sign")
sshSignKeyCmd.Flags().String("publicKeyFilePath", "", "The file path to the public key file to sign") sshSignKeyCmd.Flags().String("publicKeyFilePath", "", "The file path to the public key file to sign")
sshSignKeyCmd.Flags().String("outFilePath", "", "The path to write the SSH certificate to such as ~/.ssh/id_rsa-cert.pub. If not provided, the credentials will be saved to the directory of the specified public key file path or the current working directory") sshSignKeyCmd.Flags().String("outFilePath", "", "The path to write the SSH certificate to such as ~/.ssh/id_rsa-cert.pub. If not provided, the credentials will be saved to the directory of the specified public key file path or the current working directory")
@@ -505,8 +515,8 @@ func init() {
sshSignKeyCmd.Flags().String("keyId", "", "The keyId that the created certificate should have") sshSignKeyCmd.Flags().String("keyId", "", "The keyId that the created certificate should have")
sshCmd.AddCommand(sshSignKeyCmd) sshCmd.AddCommand(sshSignKeyCmd)
sshIssueCredentialsCmd.Flags().String("projectId", "", "The projectId to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("token", "", "Issue SSH credentials using machine identity access token")
sshIssueCredentialsCmd.Flags().String("templateName", "", "The template name to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("certificateTemplateId", "", "The ID of the SSH certificate template to issue SSH credentials for")
sshIssueCredentialsCmd.Flags().String("principals", "", "The principals to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("principals", "", "The principals to issue SSH credentials for")
sshIssueCredentialsCmd.Flags().String("keyAlgorithm", string(infisicalSdkUtil.RSA2048), "The key algorithm to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("keyAlgorithm", string(infisicalSdkUtil.RSA2048), "The key algorithm to issue SSH credentials for")
sshIssueCredentialsCmd.Flags().String("certType", string(infisicalSdkUtil.UserCert), "The cert type to issue SSH credentials for") sshIssueCredentialsCmd.Flags().String("certType", string(infisicalSdkUtil.UserCert), "The cert type to issue SSH credentials for")