mirror of
				https://github.com/optim-enterprises-bv/vault.git
				synced 2025-10-30 18:17:55 +00:00 
			
		
		
		
	Make transit import command work for the transform backend (#20668)
* Add import and import-version commands for the transform backend
This commit is contained in:
		
							
								
								
									
										3
									
								
								changelog/20668.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								changelog/20668.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | |||||||
|  | ```release-note:bug | ||||||
|  | secrets/transform: Added importing of keys and key versions into the Transform secrets engine using the command 'vault transform import' and 'vault transform import-version'. | ||||||
|  | ``` | ||||||
| @@ -721,6 +721,21 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) map[string]cli.Co | |||||||
| 				BaseCommand: getBaseCommand(), | 				BaseCommand: getBaseCommand(), | ||||||
| 			}, nil | 			}, nil | ||||||
| 		}, | 		}, | ||||||
|  | 		"transform": func() (cli.Command, error) { | ||||||
|  | 			return &TransformCommand{ | ||||||
|  | 				BaseCommand: getBaseCommand(), | ||||||
|  | 			}, nil | ||||||
|  | 		}, | ||||||
|  | 		"transform import": func() (cli.Command, error) { | ||||||
|  | 			return &TransformImportCommand{ | ||||||
|  | 				BaseCommand: getBaseCommand(), | ||||||
|  | 			}, nil | ||||||
|  | 		}, | ||||||
|  | 		"transform import-version": func() (cli.Command, error) { | ||||||
|  | 			return &TransformImportVersionCommand{ | ||||||
|  | 				BaseCommand: getBaseCommand(), | ||||||
|  | 			}, nil | ||||||
|  | 		}, | ||||||
| 		"transit": func() (cli.Command, error) { | 		"transit": func() (cli.Command, error) { | ||||||
| 			return &TransitCommand{ | 			return &TransitCommand{ | ||||||
| 				BaseCommand: getBaseCommand(), | 				BaseCommand: getBaseCommand(), | ||||||
|   | |||||||
							
								
								
									
										41
									
								
								command/transform.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								command/transform.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/mitchellh/cli" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var _ cli.Command = (*TransformCommand)(nil) | ||||||
|  |  | ||||||
|  | type TransformCommand struct { | ||||||
|  | 	*BaseCommand | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformCommand) Synopsis() string { | ||||||
|  | 	return "Interact with Vault's Transform Secrets Engine" | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformCommand) Help() string { | ||||||
|  | 	helpText := ` | ||||||
|  | Usage: vault transform <subcommand> [options] [args] | ||||||
|  |  | ||||||
|  |   This command has subcommands for interacting with Vault's Transform Secrets | ||||||
|  |   Engine. Here are some simple examples, and more detailed examples are | ||||||
|  |   available in the subcommands or the documentation. | ||||||
|  |  | ||||||
|  |   To import a key into a new FPE transformation: | ||||||
|  |  | ||||||
|  |   $ vault transform import transform/transformations/fpe/new-transformation @path/to/key \ | ||||||
|  |       template=identifier \ | ||||||
|  | 	  allowed_roles=physical-access  | ||||||
|  |  | ||||||
|  |   Please see the individual subcommand help for detailed usage information. | ||||||
|  | ` | ||||||
|  |  | ||||||
|  | 	return strings.TrimSpace(helpText) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformCommand) Run(args []string) int { | ||||||
|  | 	return cli.RunResultHelp | ||||||
|  | } | ||||||
							
								
								
									
										76
									
								
								command/transform_import_key.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								command/transform_import_key.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,76 @@ | |||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"regexp" | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/mitchellh/cli" | ||||||
|  | 	"github.com/posener/complete" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	_                cli.Command             = (*TransformImportCommand)(nil) | ||||||
|  | 	_                cli.CommandAutocomplete = (*TransformImportCommand)(nil) | ||||||
|  | 	transformKeyPath                         = regexp.MustCompile("^(.*)/transformations/(fpe|tokenization)/([^/]*)$") | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TransformImportCommand struct { | ||||||
|  | 	*BaseCommand | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportCommand) Synopsis() string { | ||||||
|  | 	return "Import a key into the Transform secrets engines." | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportCommand) Help() string { | ||||||
|  | 	helpText := ` | ||||||
|  | Usage: vault transform import PATH KEY [options...] | ||||||
|  |  | ||||||
|  |   Using the Transform key wrapping system, imports key material from | ||||||
|  |   the base64 encoded KEY (either directly on the CLI or via @path notation), | ||||||
|  |   into a new FPE or tokenization transformation whose API path is PATH.  | ||||||
|  |  | ||||||
|  |   To import a new key version into an existing tokenization transformation,  | ||||||
|  |   use import_version.  | ||||||
|  |    | ||||||
|  |   The remaining options after KEY (key=value style) are passed on to  | ||||||
|  |   Create/Update FPE Transformation or Create/Update Tokenization Transformation  | ||||||
|  |   API endpoints. | ||||||
|  |  | ||||||
|  |   For example: | ||||||
|  |   $ vault transform import transform/transformations/tokenization/application-form @path/to/key \         | ||||||
|  |        allowed_roles=legacy-system  | ||||||
|  | ` + c.Flags().Help() | ||||||
|  |  | ||||||
|  | 	return strings.TrimSpace(helpText) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportCommand) Flags() *FlagSets { | ||||||
|  | 	return c.flagSet(FlagSetHTTP) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportCommand) AutocompleteArgs() complete.Predictor { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportCommand) AutocompleteFlags() complete.Flags { | ||||||
|  | 	return c.Flags().Completions() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportCommand) Run(args []string) int { | ||||||
|  | 	return ImportKey(c.BaseCommand, "import", transformImportKeyPath, c.Flags(), args) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func transformImportKeyPath(s string, operation string) (path string, apiPath string, err error) { | ||||||
|  | 	parts := transformKeyPath.FindStringSubmatch(s) | ||||||
|  | 	if len(parts) != 4 { | ||||||
|  | 		return "", "", errors.New("expected transform path and key name in the form :path:/transformations/fpe|tokenization/:name:") | ||||||
|  | 	} | ||||||
|  | 	path = parts[1] | ||||||
|  | 	transformation := parts[2] | ||||||
|  | 	keyName := parts[3] | ||||||
|  | 	apiPath = path + "/transformations/" + transformation + "/" + keyName + "/" + operation | ||||||
|  |  | ||||||
|  | 	return path, apiPath, nil | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								command/transform_import_key_version.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								command/transform_import_key_version.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | |||||||
|  | // Copyright (c) HashiCorp, Inc. | ||||||
|  | // SPDX-License-Identifier: MPL-2.0 | ||||||
|  |  | ||||||
|  | package command | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"strings" | ||||||
|  |  | ||||||
|  | 	"github.com/mitchellh/cli" | ||||||
|  | 	"github.com/posener/complete" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | var ( | ||||||
|  | 	_ cli.Command             = (*TransformImportVersionCommand)(nil) | ||||||
|  | 	_ cli.CommandAutocomplete = (*TransformImportVersionCommand)(nil) | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | type TransformImportVersionCommand struct { | ||||||
|  | 	*BaseCommand | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportVersionCommand) Synopsis() string { | ||||||
|  | 	return "Import key material into a new key version in the Transform secrets engines." | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportVersionCommand) Help() string { | ||||||
|  | 	helpText := ` | ||||||
|  | Usage: vault transform import-version PATH KEY [...] | ||||||
|  |  | ||||||
|  |   Using the Transform key wrapping system, imports new key material from | ||||||
|  |   the base64 encoded KEY (either directly on the CLI or via @path notation), | ||||||
|  |   into an existing tokenization transformation whose API path is PATH.  | ||||||
|  |  | ||||||
|  |   The remaining options after KEY (key=value style) are passed on to  | ||||||
|  |   Create/Update Tokenization Transformation API endpoint. | ||||||
|  |  | ||||||
|  |   For example: | ||||||
|  |   $ vault transform import-version transform/transformations/tokenization/application-form @path/to/new_version \         | ||||||
|  |        allowed_roles=legacy-system | ||||||
|  | ` + c.Flags().Help() | ||||||
|  |  | ||||||
|  | 	return strings.TrimSpace(helpText) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportVersionCommand) Flags() *FlagSets { | ||||||
|  | 	return c.flagSet(FlagSetHTTP) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportVersionCommand) AutocompleteArgs() complete.Predictor { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportVersionCommand) AutocompleteFlags() complete.Flags { | ||||||
|  | 	return c.Flags().Completions() | ||||||
|  | } | ||||||
|  |  | ||||||
|  | func (c *TransformImportVersionCommand) Run(args []string) int { | ||||||
|  | 	return ImportKey(c.BaseCommand, "import_version", transformImportKeyPath, c.Flags(), args) | ||||||
|  | } | ||||||
| @@ -10,6 +10,7 @@ import ( | |||||||
| 	"crypto/x509" | 	"crypto/x509" | ||||||
| 	"encoding/base64" | 	"encoding/base64" | ||||||
| 	"encoding/pem" | 	"encoding/pem" | ||||||
|  | 	"errors" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"os" | 	"os" | ||||||
| 	"regexp" | 	"regexp" | ||||||
| @@ -68,11 +69,25 @@ func (c *TransitImportCommand) AutocompleteFlags() complete.Flags { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *TransitImportCommand) Run(args []string) int { | func (c *TransitImportCommand) Run(args []string) int { | ||||||
| 	return importKey(c.BaseCommand, "import", c.Flags(), args) | 	return ImportKey(c.BaseCommand, "import", transitImportKeyPath, c.Flags(), args) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func transitImportKeyPath(s string, operation string) (path string, apiPath string, err error) { | ||||||
|  | 	parts := keyPath.FindStringSubmatch(s) | ||||||
|  | 	if len(parts) != 3 { | ||||||
|  | 		return "", "", errors.New("expected transit path and key name in the form :path:/keys/:name:") | ||||||
|  | 	} | ||||||
|  | 	path = parts[1] | ||||||
|  | 	keyName := parts[2] | ||||||
|  | 	apiPath = path + "/keys/" + keyName + "/" + operation | ||||||
|  |  | ||||||
|  | 	return path, apiPath, nil | ||||||
|  | } | ||||||
|  |  | ||||||
|  | type ImportKeyFunc func(s string, operation string) (path string, apiPath string, err error) | ||||||
|  |  | ||||||
| // error codes: 1: user error, 2: internal computation error, 3: remote api call error | // error codes: 1: user error, 2: internal computation error, 3: remote api call error | ||||||
| func importKey(c *BaseCommand, operation string, flags *FlagSets, args []string) int { | func ImportKey(c *BaseCommand, operation string, pathFunc ImportKeyFunc, flags *FlagSets, args []string) int { | ||||||
| 	// Parse and validate the arguments. | 	// Parse and validate the arguments. | ||||||
| 	if err := flags.Parse(args); err != nil { | 	if err := flags.Parse(args); err != nil { | ||||||
| 		c.UI.Error(err.Error()) | 		c.UI.Error(err.Error()) | ||||||
| @@ -96,14 +111,11 @@ func importKey(c *BaseCommand, operation string, flags *FlagSets, args []string) | |||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.UI.Error(fmt.Sprintf("failed to generate ephemeral key: %v", err)) | 		c.UI.Error(fmt.Sprintf("failed to generate ephemeral key: %v", err)) | ||||||
| 	} | 	} | ||||||
| 	parts := keyPath.FindStringSubmatch(args[0]) | 	path, apiPath, err := pathFunc(args[0], operation) | ||||||
| 	if len(parts) != 3 { | 	if err != nil { | ||||||
| 		c.UI.Error("expected transit path and key name in the form :path:/keys/:name:") | 		c.UI.Error(err.Error()) | ||||||
| 		return 1 | 		return 1 | ||||||
| 	} | 	} | ||||||
| 	path := parts[1] |  | ||||||
| 	keyName := parts[2] |  | ||||||
|  |  | ||||||
| 	keyMaterial := args[1] | 	keyMaterial := args[1] | ||||||
| 	if keyMaterial[0] == '@' { | 	if keyMaterial[0] == '@' { | ||||||
| 		keyMaterialBytes, err := os.ReadFile(keyMaterial[1:]) | 		keyMaterialBytes, err := os.ReadFile(keyMaterial[1:]) | ||||||
| @@ -121,7 +133,7 @@ func importKey(c *BaseCommand, operation string, flags *FlagSets, args []string) | |||||||
| 		return 1 | 		return 1 | ||||||
| 	} | 	} | ||||||
| 	// Fetch the wrapping key | 	// Fetch the wrapping key | ||||||
| 	c.UI.Output("Retrieving transit wrapping key.") | 	c.UI.Output("Retrieving wrapping key.") | ||||||
| 	wrappingKey, err := fetchWrappingKey(c, client, path) | 	wrappingKey, err := fetchWrappingKey(c, client, path) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.UI.Error(fmt.Sprintf("failed to fetch wrapping key: %v", err)) | 		c.UI.Error(fmt.Sprintf("failed to fetch wrapping key: %v", err)) | ||||||
| @@ -138,7 +150,7 @@ func importKey(c *BaseCommand, operation string, flags *FlagSets, args []string) | |||||||
| 		c.UI.Error(fmt.Sprintf("failure wrapping source key: %v", err)) | 		c.UI.Error(fmt.Sprintf("failure wrapping source key: %v", err)) | ||||||
| 		return 2 | 		return 2 | ||||||
| 	} | 	} | ||||||
| 	c.UI.Output("Encrypting ephemeral key with transit wrapping key.") | 	c.UI.Output("Encrypting ephemeral key with wrapping key.") | ||||||
| 	wrappedAESKey, err := rsa.EncryptOAEP( | 	wrappedAESKey, err := rsa.EncryptOAEP( | ||||||
| 		sha256.New(), | 		sha256.New(), | ||||||
| 		rand.Reader, | 		rand.Reader, | ||||||
| @@ -165,9 +177,10 @@ func importKey(c *BaseCommand, operation string, flags *FlagSets, args []string) | |||||||
|  |  | ||||||
| 	data["ciphertext"] = importCiphertext | 	data["ciphertext"] = importCiphertext | ||||||
|  |  | ||||||
| 	c.UI.Output("Submitting wrapped key to Vault transit.") | 	c.UI.Output("Submitting wrapped key.") | ||||||
| 	// Finally, call import | 	// Finally, call import | ||||||
| 	_, err = client.Logical().Write(path+"/keys/"+keyName+"/"+operation, data) |  | ||||||
|  | 	_, err = client.Logical().Write(apiPath, data) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.UI.Error(fmt.Sprintf("failed to call import:%v", err)) | 		c.UI.Error(fmt.Sprintf("failed to call import:%v", err)) | ||||||
| 		return 3 | 		return 3 | ||||||
| @@ -183,7 +196,7 @@ func fetchWrappingKey(c *BaseCommand, client *api.Client, path string) (any, err | |||||||
| 		return nil, fmt.Errorf("error fetching wrapping key: %w", err) | 		return nil, fmt.Errorf("error fetching wrapping key: %w", err) | ||||||
| 	} | 	} | ||||||
| 	if resp == nil { | 	if resp == nil { | ||||||
| 		return nil, fmt.Errorf("transit not mounted at %s: %v", path, err) | 		return nil, fmt.Errorf("no mount found at %s: %v", path, err) | ||||||
| 	} | 	} | ||||||
| 	key, ok := resp.Data["public_key"] | 	key, ok := resp.Data["public_key"] | ||||||
| 	if !ok { | 	if !ok { | ||||||
|   | |||||||
| @@ -54,5 +54,5 @@ func (c *TransitImportVersionCommand) AutocompleteFlags() complete.Flags { | |||||||
| } | } | ||||||
|  |  | ||||||
| func (c *TransitImportVersionCommand) Run(args []string) int { | func (c *TransitImportVersionCommand) Run(args []string) int { | ||||||
| 	return importKey(c.BaseCommand, "import_version", c.Flags(), args) | 	return ImportKey(c.BaseCommand, "import_version", transitImportKeyPath, c.Flags(), args) | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Larroyo
					Larroyo