├── .github ├── dependabot.yml └── workflows │ ├── ensure-docs-compiled.yaml │ ├── notify-integration-release-via-manual.yaml │ ├── notify-integration-release-via-tag.yaml │ └── release.yml ├── .gitignore ├── .goreleaser.yml ├── .web-docs ├── README.md ├── components │ └── data-source │ │ └── sshkey │ │ └── README.md ├── metadata.hcl └── scripts │ └── compile-to-webdocs.sh ├── GNUmakefile ├── README.md ├── docs ├── README.md └── datasources │ └── sshkey.mdx ├── go.mod ├── go.sum ├── main.go └── sshkey ├── data.go ├── data.hcl2spec.go ├── data_acc_test.go ├── data_test.go ├── ed25519.go ├── rsa.go ├── sshkey.go └── test-fixtures ├── ed25519.pkr.hcl └── rsa.pkr.hcl /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | version: 2 7 | updates: 8 | - package-ecosystem: "gomod" # See documentation for possible values 9 | directory: "/" # Location of package manifests 10 | schedule: 11 | interval: "daily" 12 | - package-ecosystem: "github-actions" 13 | # Workflow files stored in the default location of `.github/workflows`. (You don't need to specify `/.github/workflows` for `directory`. You can use `directory: "/"`.) 14 | directory: "/" 15 | schedule: 16 | interval: "weekly" 17 | -------------------------------------------------------------------------------- /.github/workflows/ensure-docs-compiled.yaml: -------------------------------------------------------------------------------- 1 | name: Ensure Docs are Compiled 2 | on: 3 | push: 4 | jobs: 5 | ensure-docs-compiled: 6 | runs-on: ubuntu-latest 7 | steps: 8 | - name: Checkout 🛎 9 | uses: actions/checkout@v4 10 | - uses: actions/setup-go@v5 11 | with: 12 | go-version: 1.22 13 | - shell: bash 14 | run: make generate 15 | - shell: bash 16 | run: | 17 | if [[ -z "$(git status -s)" ]]; then 18 | echo "OK" 19 | else 20 | echo "Docs have been updated, but the compiled docs have not been committed." 21 | echo "Run 'make generate', and commit the result to resolve this error." 22 | exit 1 23 | fi 24 | -------------------------------------------------------------------------------- /.github/workflows/notify-integration-release-via-manual.yaml: -------------------------------------------------------------------------------- 1 | # Manual release workflow is used for deploying documentation updates 2 | # on the specified branch without making an official plugin release. 3 | name: Notify Integration Release (Manual) 4 | on: 5 | workflow_dispatch: 6 | inputs: 7 | version: 8 | description: "The release version (semver)" 9 | default: 1.0.0 10 | required: false 11 | branch: 12 | description: "A branch or SHA" 13 | default: 'main' 14 | required: false 15 | jobs: 16 | notify-release: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - name: Checkout this repo 20 | uses: actions/checkout@v4 21 | with: 22 | ref: ${{ github.event.inputs.branch }} 23 | # Ensure that Docs are Compiled 24 | - uses: actions/setup-go@v5 25 | with: 26 | go-version: 1.22 27 | - shell: bash 28 | run: make generate 29 | - shell: bash 30 | run: | 31 | if [[ -z "$(git status -s)" ]]; then 32 | echo "OK" 33 | else 34 | echo "Docs have been updated, but the compiled docs have not been committed." 35 | echo "Run 'make generate', and commit the result to resolve this error." 36 | exit 1 37 | fi 38 | # Perform the Release 39 | - name: Checkout integration-release-action 40 | uses: actions/checkout@v4 41 | with: 42 | repository: hashicorp/integration-release-action 43 | path: ./integration-release-action 44 | - name: Notify Release 45 | uses: ./integration-release-action 46 | with: 47 | # The integration identifier will be used by the Packer team to register the integration 48 | # the expected format is packer// 49 | integration_identifier: "packer/hashicorp/scaffolding" 50 | release_version: ${{ github.event.inputs.version }} 51 | release_sha: ${{ github.event.inputs.branch }} 52 | github_token: ${{ secrets.GITHUB_TOKEN }} 53 | -------------------------------------------------------------------------------- /.github/workflows/notify-integration-release-via-tag.yaml: -------------------------------------------------------------------------------- 1 | name: Notify Integration Release (Tag) 2 | on: 3 | push: 4 | tags: 5 | - '*.*.*' # Proper releases 6 | jobs: 7 | strip-version: 8 | runs-on: ubuntu-latest 9 | outputs: 10 | packer-version: ${{ steps.strip.outputs.packer-version }} 11 | steps: 12 | - name: Strip leading v from version tag 13 | id: strip 14 | env: 15 | REF: ${{ github.ref_name }} 16 | run: | 17 | echo "packer-version=$(echo "$REF" | sed -E 's/v?([0-9]+\.[0-9]+\.[0-9]+)/\1/')" >> "$GITHUB_OUTPUT" 18 | notify-release: 19 | needs: 20 | - strip-version 21 | runs-on: ubuntu-latest 22 | steps: 23 | - name: Checkout this repo 24 | uses: actions/checkout@v4 25 | with: 26 | ref: ${{ github.ref }} 27 | # Ensure that Docs are Compiled 28 | - uses: actions/setup-go@v5 29 | with: 30 | go-version: 1.22 31 | - shell: bash 32 | run: make generate 33 | - shell: bash 34 | run: | 35 | if [[ -z "$(git status -s)" ]]; then 36 | echo "OK" 37 | else 38 | echo "Docs have been updated, but the compiled docs have not been committed." 39 | echo "Run 'make generate', and commit the result to resolve this error." 40 | exit 1 41 | fi 42 | # Perform the Release 43 | - name: Checkout integration-release-action 44 | uses: actions/checkout@v4 45 | with: 46 | repository: hashicorp/integration-release-action 47 | path: ./integration-release-action 48 | - name: Notify Release 49 | uses: ./integration-release-action 50 | with: 51 | integration_identifier: 'packer/ivoronin/sshkey' 52 | release_version: ${{ github.ref_name }} 53 | release_sha: ${{ github.ref }} 54 | github_token: ${{ secrets.GITHUB_TOKEN }} 55 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | # This GitHub action can publish assets for release when a tag is created. 2 | # Currently its setup to run on any tag that matches the pattern "v*" (ie. v0.1.0). 3 | # 4 | # This uses an action (hashicorp/ghaction-import-gpg) that assumes you set your 5 | # private key in the `GPG_PRIVATE_KEY` secret and passphrase in the `GPG_PASSPHRASE` 6 | # secret. If you would rather own your own GPG handling, please fork this action 7 | # or use an alternative one for key handling. 8 | # 9 | # You will need to pass the `--batch` flag to `gpg` in your signing step 10 | # in `goreleaser` to indicate this is being used in a non-interactive mode. 11 | # 12 | name: release 13 | on: 14 | push: 15 | tags: 16 | - 'v*' 17 | jobs: 18 | goreleaser: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v4 23 | - name: Unshallow 24 | run: git fetch --prune --unshallow 25 | - name: Set up Go 26 | uses: actions/setup-go@v5 27 | with: 28 | go-version: 1.22 29 | - name: Describe plugin 30 | id: plugin_describe 31 | run: echo "::set-output name=api_version::$(go run . describe | jq -r '.api_version')" 32 | - name: Import GPG key 33 | id: import_gpg 34 | uses: crazy-max/ghaction-import-gpg@v6 35 | with: 36 | gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }} 37 | passphrase: ${{ secrets.GPG_PASSPHRASE }} 38 | - name: Run GoReleaser 39 | uses: goreleaser/goreleaser-action@v6 40 | with: 41 | version: latest 42 | args: release --clean 43 | env: 44 | GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} 45 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 46 | API_VERSION: ${{ steps.plugin_describe.outputs.api_version }} 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | packer_cache 2 | crash.log 3 | packer-plugin-sshkey 4 | .docs 5 | docs.zip 6 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # This is an example goreleaser.yaml file with some sane defaults. 2 | # Make sure to check the documentation at http://goreleaser.com 3 | env: 4 | - CGO_ENABLED=0 5 | before: 6 | hooks: 7 | # We strongly recommend running tests to catch any regression before release. 8 | # Even though, this an optional step. 9 | - go test ./... 10 | # As part of the release doc files are included as a separate deliverable for 11 | # consumption by Packer.io. To include a separate docs.zip uncomment the following command. 12 | - make ci-release-docs 13 | # Check plugin compatibility with required version of the Packer SDK 14 | - make plugin-check 15 | builds: 16 | # A separated build to run the packer-plugins-check only once for a linux_amd64 binary 17 | - 18 | id: plugin-check 19 | mod_timestamp: '{{ .CommitTimestamp }}' 20 | flags: 21 | - -trimpath # removes all file system paths from the compiled executable 22 | ldflags: 23 | - '-s -w -X {{ .ModulePath }}/version.Version={{.Version}} -X {{ .ModulePath }}/version.VersionPrerelease= ' 24 | goos: 25 | - linux 26 | goarch: 27 | - amd64 28 | binary: '{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}' 29 | - 30 | mod_timestamp: '{{ .CommitTimestamp }}' 31 | flags: 32 | - -trimpath # removes all file system paths from the compiled executable 33 | ldflags: 34 | - '-s -w -X {{ .ModulePath }}/version.Version={{.Version}} -X {{ .ModulePath }}/version.VersionPrerelease= ' 35 | goos: 36 | - freebsd 37 | - windows 38 | - linux 39 | - darwin 40 | goarch: 41 | - amd64 42 | - '386' 43 | - arm 44 | - arm64 45 | ignore: 46 | - goos: darwin 47 | goarch: '386' 48 | - goos: linux 49 | goarch: amd64 50 | binary: '{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}' 51 | archives: 52 | - format: zip 53 | files: 54 | - none* 55 | name_template: '{{ .ProjectName }}_v{{ .Version }}_{{ .Env.API_VERSION }}_{{ .Os }}_{{ .Arch }}' 56 | checksum: 57 | name_template: '{{ .ProjectName }}_v{{ .Version }}_SHA256SUMS' 58 | algorithm: sha256 59 | signs: 60 | - artifacts: checksum 61 | args: 62 | # if you are using this is in a GitHub action or some other automated pipeline, you 63 | # need to pass the batch flag to indicate its not interactive. 64 | - "--batch" 65 | - "--local-user" 66 | - "{{ .Env.GPG_FINGERPRINT }}" 67 | - "--output" 68 | - "${signature}" 69 | - "--detach-sign" 70 | - "${artifact}" 71 | release: 72 | # If you want to manually examine the release before its live, uncomment this line: 73 | # draft: true 74 | # As part of the release doc files are included as a separate deliverable for consumption by Packer.io. 75 | # To include a separate docs.zip uncomment the extra_files config and the docs.zip command hook above. 76 | extra_files: 77 | - glob: ./docs.zip 78 | 79 | changelog: 80 | disable: true 81 | -------------------------------------------------------------------------------- /.web-docs/README.md: -------------------------------------------------------------------------------- 1 | The SSHkey plugin can be used for generating SSH keys for configuring private key authentication 2 | 3 | ### Installation 4 | 5 | To install this plugin, copy and paste this code into your Packer configuration, then run [`packer init`](https://www.packer.io/docs/commands/init). 6 | 7 | ```hcl 8 | packer { 9 | required_plugins { 10 | sshkey = { 11 | source = "github.com/ivoronin/sshkey" 12 | version = "~> 1" 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | Alternatively, you can use `packer plugins install` to manage installation of this plugin. 19 | 20 | ```sh 21 | $ packer plugins install github.com/ivoronin/sshkey 22 | ``` 23 | 24 | ### Components 25 | 26 | #### Builders 27 | 28 | - [sshkey](/packer/integrations/ivoronin/sshkey/latest/components/data-source/sshkey) - Data source used to generate SSH keys 29 | 30 | ### Example Usage 31 | 32 | ```hcl 33 | packer { 34 | required_plugins { 35 | sshkey = { 36 | version = "~> 1" 37 | source = "github.com/ivoronin/sshkey" 38 | } 39 | } 40 | } 41 | 42 | data "sshkey" "install" { 43 | } 44 | 45 | source "qemu" "install" { 46 | ssh_username = "root" 47 | ssh_private_key_file = data.sshkey.install.private_key_path 48 | ssh_clear_authorized_keys = true 49 | http_content = { 50 | "/preseed.cfg" = templatefile("preseed.cfg.pkrtpl", { 51 | "ssh_public_key" : data.sshkey.install.public_key 52 | }) 53 | } 54 | <...> 55 | } 56 | 57 | build { 58 | sources = ["source.qemu.install"] 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /.web-docs/components/data-source/sshkey/README.md: -------------------------------------------------------------------------------- 1 | Type: `sshkey` 2 | 3 | Data source used to generate SSH keys 4 | 5 | ## Parameters and output 6 | 7 | ### Optional 8 | 9 | - `name` (string) - Key name, **must be unique** across `sshkey` datasource instances. Defaults to `packer`. 10 | - `type` (string) - Key type, must be either `rsa` or `ed25519`. Defaults to `rsa`. 11 | 12 | ## Output data 13 | 14 | - `public_key` (string) - SSH public key in "ssh-rsa ..." format 15 | - `private_key_path` (string) - Path to SSH private key 16 | 17 | ## Notes 18 | 19 | - Private key is cached in `PACKER_CACHE_DIR` (by default `packer_cache` directory is used). If you delete cached private key it will be regenerated on the next run. 20 | - Packer 1.7.3 or later is required 21 | -------------------------------------------------------------------------------- /.web-docs/metadata.hcl: -------------------------------------------------------------------------------- 1 | # For full specification on the configuration of this file visit: 2 | # https://github.com/hashicorp/integration-template#metadata-configuration 3 | integration { 4 | name = "SSH Key" 5 | description = "The SSHkey plugin can be used for generating SSH keys for configuring private key authentication." 6 | identifier = "packer/ivoronin/sshkey" 7 | component { 8 | type = "data-source" 9 | name = "SSH Key" 10 | slug = "sshkey" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /.web-docs/scripts/compile-to-webdocs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Converts the folder name that the component documentation file 4 | # is stored in into the integration slug of the component. 5 | componentTypeFromFolderName() { 6 | if [[ "$1" = "builders" ]]; then 7 | echo "builder" 8 | elif [[ "$1" = "provisioners" ]]; then 9 | echo "provisioner" 10 | elif [[ "$1" = "post-processors" ]]; then 11 | echo "post-processor" 12 | elif [[ "$1" = "datasources" ]]; then 13 | echo "data-source" 14 | else 15 | echo "" 16 | fi 17 | } 18 | 19 | # $1: The content to adjust links 20 | # $2: The organization of the integration 21 | rewriteLinks() { 22 | local result="$1" 23 | local organization="$2" 24 | 25 | urlSegment="([^/]+)" 26 | urlAnchor="(#[^/]+)" 27 | 28 | # Rewrite Component Index Page links to the Integration root page. 29 | # 30 | # (\1) (\2) (\3) 31 | # /packer/plugins/datasources/amazon#anchor-tag--> 32 | # /packer/integrations/hashicorp/amazon#anchor-tag 33 | local find="\(\/packer\/plugins\/$urlSegment\/$urlSegment$urlAnchor?\)" 34 | local replace="\(\/packer\/integrations\/$organization\/\2\3\)" 35 | result="$(echo "$result" | sed -E "s/$find/$replace/g")" 36 | 37 | 38 | # Rewrite Component links to the Integration component page 39 | # 40 | # (\1) (\2) (\3) (\4) 41 | # /packer/plugins/datasources/amazon/parameterstore#anchor-tag --> 42 | # /packer/integrations/{organization}/amazon/latest/components/datasources/parameterstore 43 | local find="\(\/packer\/plugins\/$urlSegment\/$urlSegment\/$urlSegment$urlAnchor?\)" 44 | local replace="\(\/packer\/integrations\/$organization\/\2\/latest\/components\/\1\/\3\4\)" 45 | result="$(echo "$result" | sed -E "s/$find/$replace/g")" 46 | 47 | # Rewrite the Component URL segment from the Packer Plugin format 48 | # to the Integrations format 49 | result="$(echo "$result" \ 50 | | sed "s/\/datasources\//\/data-source\//g" \ 51 | | sed "s/\/builders\//\/builder\//g" \ 52 | | sed "s/\/post-processors\//\/post-processor\//g" \ 53 | | sed "s/\/provisioners\//\/provisioner\//g" \ 54 | )" 55 | 56 | echo "$result" 57 | } 58 | 59 | # $1: Docs Dir 60 | # $2: Web Docs Dir 61 | # $3: Component File 62 | # $4: The org of the integration 63 | processComponentFile() { 64 | local docsDir="$1" 65 | local webDocsDir="$2" 66 | local componentFile="$3" 67 | 68 | local escapedDocsDir="$(echo "$docsDir" | sed 's/\//\\\//g' | sed 's/\./\\\./g')" 69 | local componentTypeAndSlug="$(echo "$componentFile" | sed "s/$escapedDocsDir\///g" | sed 's/\.mdx//g')" 70 | 71 | # Parse out the Component Slug & Component Type 72 | local componentSlug="$(echo "$componentTypeAndSlug" | cut -d'/' -f 2)" 73 | local componentType="$(componentTypeFromFolderName "$(echo "$componentTypeAndSlug" | cut -d'/' -f 1)")" 74 | if [[ "$componentType" = "" ]]; then 75 | echo "Failed to process '$componentFile', unexpected folder name." 76 | echo "Documentation for components must be stored in one of:" 77 | echo "builders, provisioners, post-processors, datasources" 78 | exit 1 79 | fi 80 | 81 | 82 | # Calculate the location of where this file will ultimately go 83 | local webDocsFolder="$webDocsDir/components/$componentType/$componentSlug" 84 | mkdir -p "$webDocsFolder" 85 | local webDocsFile="$webDocsFolder/README.md" 86 | local webDocsFileTmp="$webDocsFolder/README.md.tmp" 87 | 88 | # Copy over the file to its webDocsFile location 89 | cp "$componentFile" "$webDocsFile" 90 | 91 | # Remove the Header 92 | local lastMetadataLine="$(grep -n -m 2 '^\-\-\-' "$componentFile" | tail -n1 | cut -d':' -f1)" 93 | cat "$webDocsFile" | tail -n +"$(($lastMetadataLine+2))" > "$webDocsFileTmp" 94 | mv "$webDocsFileTmp" "$webDocsFile" 95 | 96 | # Remove the top H1, as this will be added automatically on the web 97 | cat "$webDocsFile" | tail -n +3 > "$webDocsFileTmp" 98 | mv "$webDocsFileTmp" "$webDocsFile" 99 | 100 | # Rewrite Links 101 | rewriteLinks "$(cat "$webDocsFile")" "$4" > "$webDocsFileTmp" 102 | mv "$webDocsFileTmp" "$webDocsFile" 103 | } 104 | 105 | # Compiles the Packer SDC compiled docs folder down 106 | # to a integrations-compliant folder (web docs) 107 | # 108 | # $1: The directory of the plugin 109 | # $2: The directory of the SDC compiled docs files 110 | # $3: The output directory to place the web-docs files 111 | # $4: The org of the integration 112 | compileWebDocs() { 113 | local docsDir="$1/$2" 114 | local webDocsDir="$1/$3" 115 | 116 | echo "Compiling MDX docs in '$2' to Markdown in '$3'..." 117 | # Create the web-docs directory if it hasn't already been created 118 | mkdir -p "$webDocsDir" 119 | 120 | # Copy the README over 121 | cp "$docsDir/README.md" "$webDocsDir/README.md" 122 | 123 | # Process all MDX component files (exclude index files, which are unsupported) 124 | for file in $(find "$docsDir" | grep "$docsDir/.*/.*\.mdx" | grep --invert-match "index.mdx"); do 125 | processComponentFile "$docsDir" "$webDocsDir" "$file" "$4" 126 | done 127 | } 128 | 129 | compileWebDocs "$1" "$2" "$3" "$4" 130 | -------------------------------------------------------------------------------- /GNUmakefile: -------------------------------------------------------------------------------- 1 | NAME=sshkey 2 | BINARY=packer-plugin-${NAME} 3 | PLUGIN_FQN="$(shell grep -E '^module' 40 | } 41 | 42 | build { 43 | sources = ["source.qemu.install"] 44 | } 45 | ``` 46 | 47 | Starting from version 1.7, Packer supports a new `packer init` command allowing 48 | automatic installation of Packer plugins. Read the 49 | [Packer documentation](https://www.packer.io/docs/commands/init) for more information. 50 | 51 | > [!NOTE] 52 | > Packer 1.8.5 has a [-evaluate-datasources](https://developer.hashicorp.com/packer/docs/commands/validate#evaluate-datasources) option which solves the `ssh_private_key_file is invalid` issue. Run: `packer validate test.pkr.hcl -evaluate-datasources` 53 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | The SSHkey plugin can be used for generating SSH keys for configuring private key authentication 2 | 3 | ### Installation 4 | 5 | To install this plugin, copy and paste this code into your Packer configuration, then run [`packer init`](https://www.packer.io/docs/commands/init). 6 | 7 | ```hcl 8 | packer { 9 | required_plugins { 10 | sshkey = { 11 | source = "github.com/ivoronin/sshkey" 12 | version = "~> 1" 13 | } 14 | } 15 | } 16 | ``` 17 | 18 | Alternatively, you can use `packer plugins install` to manage installation of this plugin. 19 | 20 | ```sh 21 | $ packer plugins install github.com/ivoronin/sshkey 22 | ``` 23 | 24 | ### Components 25 | 26 | #### Builders 27 | 28 | - [sshkey](/packer/integrations/ivoronin/sshkey/latest/components/data-source/sshkey) - Data source used to generate SSH keys 29 | 30 | ### Example Usage 31 | 32 | ```hcl 33 | packer { 34 | required_plugins { 35 | sshkey = { 36 | version = "~> 1" 37 | source = "github.com/ivoronin/sshkey" 38 | } 39 | } 40 | } 41 | 42 | data "sshkey" "install" { 43 | } 44 | 45 | source "qemu" "install" { 46 | ssh_username = "root" 47 | ssh_private_key_file = data.sshkey.install.private_key_path 48 | ssh_clear_authorized_keys = true 49 | http_content = { 50 | "/preseed.cfg" = templatefile("preseed.cfg.pkrtpl", { 51 | "ssh_public_key" : data.sshkey.install.public_key 52 | }) 53 | } 54 | <...> 55 | } 56 | 57 | build { 58 | sources = ["source.qemu.install"] 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /docs/datasources/sshkey.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | description: Datasource used to generate SSH keys 3 | page_title: SSH Key - Datasources 4 | nav_title: SSH Key 5 | --- 6 | 7 | # SSH Key 8 | 9 | Type: `sshkey` 10 | 11 | Data source used to generate SSH keys 12 | 13 | ## Parameters and output 14 | 15 | ### Optional 16 | 17 | - `name` (string) - Key name, **must be unique** across `sshkey` datasource instances. Defaults to `packer`. 18 | - `type` (string) - Key type, must be either `rsa` or `ed25519`. Defaults to `rsa`. 19 | 20 | ## Output data 21 | 22 | - `public_key` (string) - SSH public key in "ssh-rsa ..." format 23 | - `private_key_path` (string) - Path to SSH private key 24 | 25 | ## Notes 26 | 27 | - Private key is cached in `PACKER_CACHE_DIR` (by default `packer_cache` directory is used). If you delete cached private key it will be regenerated on the next run. 28 | - Packer 1.7.3 or later is required 29 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/ivoronin/packer-plugin-sshkey 2 | 3 | go 1.22.7 4 | 5 | require ( 6 | github.com/hashicorp/hcl/v2 v2.22.0 7 | github.com/hashicorp/packer-plugin-sdk v0.5.4 8 | github.com/zclconf/go-cty v1.15.0 9 | golang.org/x/crypto v0.27.0 10 | ) 11 | 12 | require ( 13 | github.com/agext/levenshtein v1.2.3 // indirect 14 | github.com/apparentlymart/go-textseg/v13 v13.0.0 // indirect 15 | github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect 16 | github.com/armon/go-metrics v0.4.1 // indirect 17 | github.com/aws/aws-sdk-go v1.55.5 // indirect 18 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect 19 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 20 | github.com/fatih/color v1.17.0 // indirect 21 | github.com/go-jose/go-jose/v4 v4.0.4 // indirect 22 | github.com/google/uuid v1.6.0 // indirect 23 | github.com/hashicorp/consul/api v1.29.4 // indirect 24 | github.com/hashicorp/errwrap v1.1.0 // indirect 25 | github.com/hashicorp/go-cleanhttp v0.5.2 // indirect 26 | github.com/hashicorp/go-getter/v2 v2.2.3 // indirect 27 | github.com/hashicorp/go-hclog v1.6.3 // indirect 28 | github.com/hashicorp/go-immutable-radix v1.3.1 // indirect 29 | github.com/hashicorp/go-multierror v1.1.1 // indirect 30 | github.com/hashicorp/go-retryablehttp v0.7.7 // indirect 31 | github.com/hashicorp/go-rootcerts v1.0.2 // indirect 32 | github.com/hashicorp/go-safetemp v1.0.0 // indirect 33 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 // indirect 34 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 // indirect 35 | github.com/hashicorp/go-sockaddr v1.0.6 // indirect 36 | github.com/hashicorp/go-version v1.7.0 // indirect 37 | github.com/hashicorp/golang-lru v1.0.2 // indirect 38 | github.com/hashicorp/hcl v1.0.0 // indirect 39 | github.com/hashicorp/serf v0.10.1 // indirect 40 | github.com/hashicorp/vault/api v1.15.0 // indirect 41 | github.com/hashicorp/yamux v0.1.1 // indirect 42 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 // indirect 43 | github.com/jmespath/go-jmespath v0.4.0 // indirect 44 | github.com/klauspost/compress v1.17.9 // indirect 45 | github.com/mattn/go-colorable v0.1.13 // indirect 46 | github.com/mattn/go-isatty v0.0.20 // indirect 47 | github.com/mitchellh/go-homedir v1.1.0 // indirect 48 | github.com/mitchellh/go-testing-interface v1.14.1 // indirect 49 | github.com/mitchellh/go-wordwrap v1.0.1 // indirect 50 | github.com/mitchellh/iochan v1.0.0 // indirect 51 | github.com/mitchellh/mapstructure v1.5.0 // indirect 52 | github.com/mitchellh/reflectwalk v1.0.2 // indirect 53 | github.com/ryanuber/go-glob v1.0.0 // indirect 54 | github.com/ugorji/go/codec v1.2.12 // indirect 55 | github.com/ulikunitz/xz v0.5.12 // indirect 56 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect 57 | golang.org/x/mod v0.21.0 // indirect 58 | golang.org/x/net v0.29.0 // indirect 59 | golang.org/x/sync v0.8.0 // indirect 60 | golang.org/x/sys v0.25.0 // indirect 61 | golang.org/x/text v0.18.0 // indirect 62 | golang.org/x/time v0.6.0 // indirect 63 | golang.org/x/tools v0.25.0 // indirect 64 | ) 65 | 66 | replace github.com/zclconf/go-cty => github.com/nywilken/go-cty v1.13.3 // added by packer-sdc fix as noted in github.com/hashicorp/packer-plugin-sdk/issues/187 67 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= 2 | github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= 3 | github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 4 | github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 5 | github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= 6 | github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 7 | github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= 8 | github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= 9 | github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= 10 | github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY= 11 | github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= 12 | github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 13 | github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= 14 | github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= 15 | github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= 16 | github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 17 | github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= 18 | github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= 19 | github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= 20 | github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= 21 | github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 22 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 23 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d h1:xDfNPAt8lFiC1UJrqV3uuy861HCTo708pDMbjHHdCas= 24 | github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d/go.mod h1:6QX/PXZ00z/TKoufEY6K/a0k6AhaJrQKdFe6OfVXsa4= 25 | github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= 26 | github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 27 | github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 28 | github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 29 | github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= 30 | github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= 31 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 32 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 33 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 34 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 35 | github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= 36 | github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= 37 | github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= 38 | github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4= 39 | github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI= 40 | github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E= 41 | github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc= 42 | github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 43 | github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= 44 | github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= 45 | github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= 46 | github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 47 | github.com/go-test/deep v1.0.3 h1:ZrJSEWsXzPOxaZnFteGEfooLba+ju3FYIbOrS+rQd68= 48 | github.com/go-test/deep v1.0.3/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= 49 | github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 50 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 51 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 52 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 53 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 54 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 55 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 56 | github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= 57 | github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= 58 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 59 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 60 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 61 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 62 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 63 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 64 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 65 | github.com/hashicorp/consul/api v1.29.4 h1:P6slzxDLBOxUSj3fWo2o65VuKtbtOXFi7TSSgtXutuE= 66 | github.com/hashicorp/consul/api v1.29.4/go.mod h1:HUlfw+l2Zy68ceJavv2zAyArl2fqhGWnMycyt56sBgg= 67 | github.com/hashicorp/consul/proto-public v0.6.2 h1:+DA/3g/IiKlJZb88NBn0ZgXrxJp2NlvCZdEyl+qxvL0= 68 | github.com/hashicorp/consul/proto-public v0.6.2/go.mod h1:cXXbOg74KBNGajC+o8RlA502Esf0R9prcoJgiOX/2Tg= 69 | github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= 70 | github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= 71 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 72 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 73 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 74 | github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= 75 | github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= 76 | github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= 77 | github.com/hashicorp/go-getter/v2 v2.2.3 h1:6CVzhT0KJQHqd9b0pK3xSP0CM/Cv+bVhk+jcaRJ2pGk= 78 | github.com/hashicorp/go-getter/v2 v2.2.3/go.mod h1:hp5Yy0GMQvwWVUmwLs3ygivz1JSLI323hdIE9J9m7TY= 79 | github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k= 80 | github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 81 | github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 82 | github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= 83 | github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= 84 | github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 85 | github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= 86 | github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= 87 | github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= 88 | github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= 89 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 90 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 91 | github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= 92 | github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU= 93 | github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk= 94 | github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= 95 | github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= 96 | github.com/hashicorp/go-safetemp v1.0.0 h1:2HR189eFNrjHQyENnQMMpCiBAsRxzbTMIgBhEyExpmo= 97 | github.com/hashicorp/go-safetemp v1.0.0/go.mod h1:oaerMy3BhqiTbVye6QuFhFtIceqFoDHxNAB65b+Rj1I= 98 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8 h1:iBt4Ew4XEGLfh6/bPk4rSYmuZJGizr6/x/AEizP0CQc= 99 | github.com/hashicorp/go-secure-stdlib/parseutil v0.1.8/go.mod h1:aiJI+PIApBRQG7FZTEBx5GiiX+HbOHilUdNxUZi4eV0= 100 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts= 101 | github.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4= 102 | github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= 103 | github.com/hashicorp/go-sockaddr v1.0.6 h1:RSG8rKU28VTUTvEKghe5gIhIQpv8evvNpnDEyqO4u9I= 104 | github.com/hashicorp/go-sockaddr v1.0.6/go.mod h1:uoUUmtwU7n9Dv3O4SNLeFvg0SxQ3lyjsj6+CCykpaxI= 105 | github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= 106 | github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 107 | github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 108 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 109 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 110 | github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= 111 | github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 112 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 113 | github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= 114 | github.com/hashicorp/golang-lru v1.0.2/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= 115 | github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= 116 | github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= 117 | github.com/hashicorp/hcl/v2 v2.22.0 h1:hkZ3nCtqeJsDhPRFz5EA9iwcG1hNWGePOTw6oyul12M= 118 | github.com/hashicorp/hcl/v2 v2.22.0/go.mod h1:62ZYHrXgPoX8xBnzl8QzbWq4dyDsDtfCRgIq1rbJEvA= 119 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 120 | github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= 121 | github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= 122 | github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= 123 | github.com/hashicorp/packer-plugin-sdk v0.5.4 h1:5Bl5DMEa//G4gBNcl842JopM9L4KSSsxpvB4W1lEwIA= 124 | github.com/hashicorp/packer-plugin-sdk v0.5.4/go.mod h1:ALm0ZIK3c/F4iOqPNi7xVuHTgrR5dxzOK+DhFN5DHj4= 125 | github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= 126 | github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= 127 | github.com/hashicorp/vault/api v1.15.0 h1:O24FYQCWwhwKnF7CuSqP30S51rTV7vz1iACXE/pj5DA= 128 | github.com/hashicorp/vault/api v1.15.0/go.mod h1:+5YTO09JGn0u+b6ySD/LLVf8WkJCPLAL2Vkmrn2+CM8= 129 | github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE= 130 | github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= 131 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869 h1:IPJ3dvxmJ4uczJe5YQdrYB16oTJlGSC/OyZDqUk9xX4= 132 | github.com/jehiah/go-strftime v0.0.0-20171201141054-1d33003b3869/go.mod h1:cJ6Cj7dQo+O6GJNiMx+Pa94qKj+TG8ONdKHgMNIyyag= 133 | github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= 134 | github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 135 | github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= 136 | github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= 137 | github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= 138 | github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 139 | github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= 140 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 141 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 142 | github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= 143 | github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= 144 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 145 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 146 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 147 | github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= 148 | github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= 149 | github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 150 | github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= 151 | github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= 152 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 153 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 154 | github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= 155 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= 156 | github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= 157 | github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= 158 | github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= 159 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 160 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 161 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 162 | github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= 163 | github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= 164 | github.com/miekg/dns v1.1.41 h1:WMszZWJG0XmzbK9FEmzH2TVcqYzFesusSIB41b8KHxY= 165 | github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= 166 | github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= 167 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 168 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 169 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 170 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 171 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= 172 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= 173 | github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= 174 | github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= 175 | github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= 176 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 177 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 178 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 179 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 180 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 181 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 182 | github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 183 | github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 184 | github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= 185 | github.com/nywilken/go-cty v1.13.3 h1:03U99oXf3j3g9xgqAE3YGpixCjM8Mg09KZ0Ji9LzX0o= 186 | github.com/nywilken/go-cty v1.13.3/go.mod h1:YKQzy/7pZ7iq2jNFzy5go57xdxdWoLLpaEp4u238AE0= 187 | github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 188 | github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= 189 | github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= 190 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 191 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 192 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 193 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 194 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 195 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 196 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 197 | github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= 198 | github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= 199 | github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= 200 | github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= 201 | github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= 202 | github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= 203 | github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 204 | github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 205 | github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= 206 | github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= 207 | github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= 208 | github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= 209 | github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= 210 | github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= 211 | github.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk= 212 | github.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= 213 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= 214 | github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= 215 | github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= 216 | github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= 217 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 218 | github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 219 | github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= 220 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 221 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 222 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 223 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 224 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 225 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 226 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 227 | github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= 228 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 229 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 230 | github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= 231 | github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 232 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= 233 | github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= 234 | golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 235 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 236 | golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= 237 | golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= 238 | golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= 239 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= 240 | golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= 241 | golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= 242 | golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= 243 | golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 244 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 245 | golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 246 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 247 | golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 248 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 249 | golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= 250 | golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= 251 | golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= 252 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 253 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 254 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 255 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 256 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 257 | golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= 258 | golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 259 | golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 260 | golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 261 | golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 262 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 263 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 264 | golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 265 | golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 266 | golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 267 | golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 268 | golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 269 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 270 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 271 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 272 | golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 273 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 274 | golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 275 | golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 276 | golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 277 | golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 278 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 279 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 280 | golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= 281 | golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 282 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 283 | golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= 284 | golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= 285 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 286 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 287 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 288 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 289 | golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= 290 | golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 291 | golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= 292 | golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 293 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 294 | golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 295 | golang.org/x/tools v0.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= 296 | golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= 297 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 298 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 299 | google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b h1:+YaDE2r2OG8t/z5qmsh7Y+XXwCbvadxxZ0YY6mTdrVA= 300 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405 h1:AB/lmRny7e2pLhFEYIbl5qkDAUt2h0ZRO4wGPhZf+ik= 301 | google.golang.org/genproto/googleapis/rpc v0.0.0-20231030173426-d783a09b4405/go.mod h1:67X1fPuzjcrkymZzZV1vvkFeTn2Rvc6lYF9MYFGCcwE= 302 | google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= 303 | google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 304 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 305 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 306 | gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 307 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 308 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 309 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 310 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 311 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 312 | gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 313 | gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10= 314 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 315 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 316 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 317 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/hashicorp/packer-plugin-sdk/plugin" 8 | "github.com/hashicorp/packer-plugin-sdk/version" 9 | "github.com/ivoronin/packer-plugin-sshkey/sshkey" 10 | ) 11 | 12 | var ( 13 | Version = "1.2.2" 14 | VersionPrerelease = "" 15 | PluginVersion = version.NewPluginVersion(Version, VersionPrerelease, "") 16 | ) 17 | 18 | func main() { 19 | pps := plugin.NewSet() 20 | pps.RegisterDatasource(plugin.DEFAULT_NAME, new(sshkey.Datasource)) 21 | pps.SetVersion(PluginVersion) 22 | err := pps.Run() 23 | if err != nil { 24 | fmt.Fprintln(os.Stderr, err.Error()) 25 | os.Exit(1) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /sshkey/data.go: -------------------------------------------------------------------------------- 1 | //go:generate packer-sdc mapstructure-to-hcl2 -type Config,DatasourceOutput 2 | package sshkey 3 | 4 | import ( 5 | "errors" 6 | "github.com/hashicorp/hcl/v2/hcldec" 7 | "github.com/hashicorp/packer-plugin-sdk/hcl2helper" 8 | "github.com/hashicorp/packer-plugin-sdk/packer" 9 | "github.com/hashicorp/packer-plugin-sdk/template/config" 10 | "github.com/zclconf/go-cty/cty" 11 | "os" 12 | "strings" 13 | ) 14 | 15 | type Config struct { 16 | Name string `mapstructure:"name"` 17 | Type string `mapstructure:"type"` 18 | } 19 | 20 | type Datasource struct { 21 | config Config 22 | } 23 | 24 | type DatasourceOutput struct { 25 | PrivateKeyPath string `mapstructure:"private_key_path"` 26 | PublicKey string `mapstructure:"public_key"` 27 | } 28 | 29 | func (d *Datasource) Configure(raws ...interface{}) error { 30 | if err := config.Decode(&d.config, nil, raws...); err != nil { 31 | return err 32 | } 33 | 34 | if d.config.Name == "" { 35 | d.config.Name = "packer" 36 | } 37 | 38 | if d.config.Type == "" { 39 | d.config.Type = "rsa" 40 | } 41 | 42 | return nil 43 | } 44 | 45 | func (d *Datasource) ConfigSpec() hcldec.ObjectSpec { 46 | return d.config.FlatMapstructure().HCL2Spec() 47 | } 48 | 49 | func (d *Datasource) OutputSpec() hcldec.ObjectSpec { 50 | return (&DatasourceOutput{}).FlatMapstructure().HCL2Spec() 51 | } 52 | 53 | func (d *Datasource) Execute() (cty.Value, error) { 54 | var err error 55 | var key SSHKey 56 | 57 | nv := cty.NullVal(cty.EmptyObject) 58 | 59 | switch d.config.Type { 60 | case "rsa": 61 | key = new(RSAKey) 62 | case "ed25519": 63 | key = new(ED25519Key) 64 | default: 65 | return nv, errors.New("unsupported key type") 66 | } 67 | 68 | keyTag := strings.ReplaceAll(d.config.Name, string(os.PathSeparator), "_") 69 | keyName := "ssh_private_key_" + keyTag + "_" + d.config.Type + ".pem" 70 | 71 | keyPath, err := packer.CachePath(keyName) 72 | if err != nil { 73 | return cty.NullVal(cty.EmptyObject), err 74 | } 75 | 76 | pem, err := os.ReadFile(keyPath) 77 | if err == nil { 78 | if err = key.FromPEM(pem); err != nil { 79 | return nv, err 80 | } 81 | } else if errors.Is(err, os.ErrNotExist) { 82 | if err = key.Generate(); err != nil { 83 | return nv, err 84 | } 85 | 86 | pem, err = key.ToPEM() 87 | if err != nil { 88 | return nv, err 89 | } 90 | if err = os.WriteFile(keyPath, pem, 0600); err != nil { 91 | return nv, err 92 | } 93 | } else { 94 | return nv, err 95 | } 96 | 97 | pubKeyStr, err := key.Public() 98 | if err != nil { 99 | return nv, err 100 | } 101 | 102 | output := DatasourceOutput{ 103 | PrivateKeyPath: keyPath, 104 | PublicKey: pubKeyStr + " " + keyTag, 105 | } 106 | 107 | return hcl2helper.HCL2ValueFromConfig(output, d.OutputSpec()), nil 108 | } 109 | -------------------------------------------------------------------------------- /sshkey/data.hcl2spec.go: -------------------------------------------------------------------------------- 1 | // Code generated by "packer-sdc mapstructure-to-hcl2"; DO NOT EDIT. 2 | 3 | package sshkey 4 | 5 | import ( 6 | "github.com/hashicorp/hcl/v2/hcldec" 7 | "github.com/zclconf/go-cty/cty" 8 | ) 9 | 10 | // FlatConfig is an auto-generated flat version of Config. 11 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 12 | type FlatConfig struct { 13 | Name *string `mapstructure:"name" cty:"name" hcl:"name"` 14 | Type *string `mapstructure:"type" cty:"type" hcl:"type"` 15 | } 16 | 17 | // FlatMapstructure returns a new FlatConfig. 18 | // FlatConfig is an auto-generated flat version of Config. 19 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 20 | func (*Config) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 21 | return new(FlatConfig) 22 | } 23 | 24 | // HCL2Spec returns the hcl spec of a Config. 25 | // This spec is used by HCL to read the fields of Config. 26 | // The decoded values from this spec will then be applied to a FlatConfig. 27 | func (*FlatConfig) HCL2Spec() map[string]hcldec.Spec { 28 | s := map[string]hcldec.Spec{ 29 | "name": &hcldec.AttrSpec{Name: "name", Type: cty.String, Required: false}, 30 | "type": &hcldec.AttrSpec{Name: "type", Type: cty.String, Required: false}, 31 | } 32 | return s 33 | } 34 | 35 | // FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. 36 | // Where the contents of a field with a `mapstructure:,squash` tag are bubbled up. 37 | type FlatDatasourceOutput struct { 38 | PrivateKeyPath *string `mapstructure:"private_key_path" cty:"private_key_path" hcl:"private_key_path"` 39 | PublicKey *string `mapstructure:"public_key" cty:"public_key" hcl:"public_key"` 40 | } 41 | 42 | // FlatMapstructure returns a new FlatDatasourceOutput. 43 | // FlatDatasourceOutput is an auto-generated flat version of DatasourceOutput. 44 | // Where the contents a fields with a `mapstructure:,squash` tag are bubbled up. 45 | func (*DatasourceOutput) FlatMapstructure() interface{ HCL2Spec() map[string]hcldec.Spec } { 46 | return new(FlatDatasourceOutput) 47 | } 48 | 49 | // HCL2Spec returns the hcl spec of a DatasourceOutput. 50 | // This spec is used by HCL to read the fields of DatasourceOutput. 51 | // The decoded values from this spec will then be applied to a FlatDatasourceOutput. 52 | func (*FlatDatasourceOutput) HCL2Spec() map[string]hcldec.Spec { 53 | s := map[string]hcldec.Spec{ 54 | "private_key_path": &hcldec.AttrSpec{Name: "private_key_path", Type: cty.String, Required: false}, 55 | "public_key": &hcldec.AttrSpec{Name: "public_key", Type: cty.String, Required: false}, 56 | } 57 | return s 58 | } 59 | -------------------------------------------------------------------------------- /sshkey/data_acc_test.go: -------------------------------------------------------------------------------- 1 | package sshkey 2 | 3 | import ( 4 | _ "embed" 5 | "fmt" 6 | "github.com/hashicorp/packer-plugin-sdk/acctest" 7 | "os/exec" 8 | "testing" 9 | ) 10 | 11 | //go:embed test-fixtures/rsa.pkr.hcl 12 | var testDatasourceTemplateRSA string 13 | 14 | //go:embed test-fixtures/ed25519.pkr.hcl 15 | var testDatasourceTemplateED25519 string 16 | 17 | func check(buildCommand *exec.Cmd, logfile string) error { 18 | if buildCommand.ProcessState != nil { 19 | if buildCommand.ProcessState.ExitCode() != 0 { 20 | return fmt.Errorf("Bad exit code. Logfile: %s", logfile) 21 | } 22 | } 23 | return nil 24 | } 25 | 26 | func TestAccSSHKeyManager(t *testing.T) { 27 | testGenerateRSA := &acctest.PluginTestCase{ 28 | Name: "sshkey_datasource_create_rsa", 29 | Template: testDatasourceTemplateRSA, 30 | Check: check, 31 | } 32 | testLoadRSA := &acctest.PluginTestCase{ 33 | Name: "sshkey_datasource_load_rsa", 34 | Template: testDatasourceTemplateRSA, 35 | Check: check, 36 | } 37 | testGenerateED25519 := &acctest.PluginTestCase{ 38 | Name: "sshkey_datasource_create_ed25519", 39 | Template: testDatasourceTemplateED25519, 40 | Check: check, 41 | } 42 | testLoadED25519 := &acctest.PluginTestCase{ 43 | Name: "sshkey_datasource_load_ed25519", 44 | Template: testDatasourceTemplateED25519, 45 | Check: check, 46 | } 47 | acctest.TestPlugin(t, testGenerateRSA) 48 | acctest.TestPlugin(t, testLoadRSA) 49 | acctest.TestPlugin(t, testGenerateED25519) 50 | acctest.TestPlugin(t, testLoadED25519) 51 | } 52 | -------------------------------------------------------------------------------- /sshkey/data_test.go: -------------------------------------------------------------------------------- 1 | package sshkey 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestDatasource(t *testing.T) { 8 | d := Datasource{config: Config{}} 9 | err := d.Configure(nil) 10 | if err != nil { 11 | t.Fatalf("Failed to configure datasource") 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /sshkey/ed25519.go: -------------------------------------------------------------------------------- 1 | package sshkey 2 | 3 | import ( 4 | "crypto/ed25519" 5 | "crypto/x509" 6 | "encoding/pem" 7 | "errors" 8 | ) 9 | 10 | const ED25519_BLOCK_TYPE = "PRIVATE KEY" 11 | 12 | type ED25519Key struct { 13 | SSHKey 14 | key ed25519.PrivateKey 15 | } 16 | 17 | func (k *ED25519Key) Generate() error { 18 | var err error 19 | _, k.key, err = ed25519.GenerateKey(nil) 20 | if err == nil { 21 | return err 22 | } 23 | 24 | return nil 25 | } 26 | 27 | func (k *ED25519Key) FromPEM(bytes []byte) error { 28 | var err error 29 | block, _ := pem.Decode(bytes) 30 | if block == nil || block.Type != ED25519_BLOCK_TYPE { 31 | return errors.New("unable to decode PEM") 32 | } 33 | 34 | key, err := x509.ParsePKCS8PrivateKey(block.Bytes) 35 | k.key = key.(ed25519.PrivateKey) 36 | if err != nil { 37 | return err 38 | } 39 | return nil 40 | } 41 | 42 | func (k *ED25519Key) ToPEM() ([]byte, error) { 43 | der, err := x509.MarshalPKCS8PrivateKey(k.key) 44 | if err != nil { 45 | return nil, err 46 | } 47 | block := pem.Block{ 48 | Type: ED25519_BLOCK_TYPE, 49 | Bytes: der, 50 | } 51 | pem := pem.EncodeToMemory(&block) 52 | return pem, nil 53 | } 54 | 55 | func (k *ED25519Key) Public() (string, error) { 56 | return publicKeyStringFor(k.key.Public()) 57 | } 58 | -------------------------------------------------------------------------------- /sshkey/rsa.go: -------------------------------------------------------------------------------- 1 | package sshkey 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/rsa" 6 | "crypto/x509" 7 | "encoding/pem" 8 | "errors" 9 | ) 10 | 11 | const RSA_BITS = 2048 12 | const RSA_BLOCK_TYPE = "RSA PRIVATE KEY" 13 | 14 | type RSAKey struct { 15 | SSHKey 16 | key *rsa.PrivateKey 17 | } 18 | 19 | func (k *RSAKey) Generate() error { 20 | var err error 21 | k.key, err = rsa.GenerateKey(rand.Reader, RSA_BITS) 22 | if err == nil { 23 | return err 24 | } 25 | 26 | return nil 27 | } 28 | 29 | func (k *RSAKey) FromPEM(bytes []byte) error { 30 | var err error 31 | block, _ := pem.Decode(bytes) 32 | if block == nil || block.Type != RSA_BLOCK_TYPE { 33 | return errors.New("unable to decode PEM") 34 | } 35 | 36 | k.key, err = x509.ParsePKCS1PrivateKey(block.Bytes) 37 | if err != nil { 38 | return err 39 | } 40 | return nil 41 | } 42 | 43 | func (k *RSAKey) ToPEM() ([]byte, error) { 44 | der := x509.MarshalPKCS1PrivateKey(k.key) 45 | block := pem.Block{ 46 | Type: RSA_BLOCK_TYPE, 47 | Bytes: der, 48 | } 49 | pem := pem.EncodeToMemory(&block) 50 | return pem, nil 51 | } 52 | 53 | func (k *RSAKey) Public() (string, error) { 54 | return publicKeyStringFor(k.key.Public()) 55 | } 56 | -------------------------------------------------------------------------------- /sshkey/sshkey.go: -------------------------------------------------------------------------------- 1 | package sshkey 2 | 3 | import ( 4 | "crypto" 5 | "golang.org/x/crypto/ssh" 6 | "strings" 7 | ) 8 | 9 | type SSHKey interface { 10 | Generate() error 11 | ToPEM() ([]byte, error) 12 | FromPEM(bytes []byte) error 13 | Public() (string, error) 14 | } 15 | 16 | func publicKeyStringFor(privKey crypto.PrivateKey) (string, error) { 17 | pubKey, err := ssh.NewPublicKey(privKey) 18 | if err != nil { 19 | return "", err 20 | } 21 | 22 | bytes := ssh.MarshalAuthorizedKey(pubKey) 23 | str := strings.TrimRight(string(bytes[:]), "\r\n") 24 | 25 | return str, nil 26 | } 27 | -------------------------------------------------------------------------------- /sshkey/test-fixtures/ed25519.pkr.hcl: -------------------------------------------------------------------------------- 1 | data "sshkey" "example" { 2 | type = "ed25519" 3 | } 4 | 5 | source "null" "example" { 6 | communicator = "none" 7 | } 8 | 9 | build { 10 | sources = ["sources.null.example"] 11 | provisioner "shell-local" { 12 | inline = [ 13 | "echo PUBLIC KEY:", 14 | "echo ${data.sshkey.example.public_key}", 15 | "echo PRIVATE KEY \\(${data.sshkey.example.private_key_path}\\):", 16 | "cat \"${data.sshkey.example.private_key_path}\"", 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /sshkey/test-fixtures/rsa.pkr.hcl: -------------------------------------------------------------------------------- 1 | data "sshkey" "example" { 2 | } 3 | 4 | source "null" "example" { 5 | communicator = "none" 6 | } 7 | 8 | build { 9 | sources = ["sources.null.example"] 10 | provisioner "shell-local" { 11 | inline = [ 12 | "echo PUBLIC KEY:", 13 | "echo ${data.sshkey.example.public_key}", 14 | "echo PRIVATE KEY \\(${data.sshkey.example.private_key_path}\\):", 15 | "cat \"${data.sshkey.example.private_key_path}\"", 16 | ] 17 | } 18 | } 19 | --------------------------------------------------------------------------------