Compare commits

..

189 Commits

Author SHA1 Message Date
JM Faircloth 0b1f582bde simplify more 2023-07-05 16:01:13 -05:00
JM Faircloth 32afcc7f20 remove unused var assignment 2023-07-05 14:33:14 -05:00
JM Faircloth d12e95ba40 remove comment 2023-07-05 13:21:29 -05:00
JM Faircloth 4ef647191c final cleanup 2023-07-05 12:58:41 -05:00
JM Faircloth 1a47f33407 fix test string format 2023-07-05 12:55:58 -05:00
JM Faircloth b239ef37f5 update test check 2023-07-05 12:52:39 -05:00
JM Faircloth ccc7cef6eb fix test string format 2023-07-05 12:49:58 -05:00
JM Faircloth 8e836c6e8e fix ordering of build steps 2023-07-05 12:45:57 -05:00
JM Faircloth a2cae737a3 add debug 2023-07-05 12:43:02 -05:00
JM Faircloth b536ec9ec6 add test cases 2023-07-05 12:39:34 -05:00
JM Faircloth e5605de996 fix lint and pass token to build 2023-07-05 12:30:19 -05:00
JM Faircloth 788264dddd add more tests 2023-07-05 12:25:38 -05:00
JM Faircloth a24b038252 fix secrets stored in JSON format 2023-07-03 15:57:28 -05:00
John-Michael Faircloth e926631bb2 Update to v2.7.1 (#472)
* Update to v2.7.1

* update changelog
2023-07-03 11:09:52 -05:00
John-Michael Faircloth 5213b69445 Revert "fix secrets stored in json format (#466)" (#471)
* Revert "fix secrets stored in json format (#466)"

This reverts commit b9f4d16071.

* fix build: use new Verified Publisher image hashicorp/vault
2023-07-03 10:31:51 -05:00
Robert 357cb9c034 Update to v2.7.0 (#468) 2023-06-21 14:23:47 -05:00
John-Michael Faircloth b9f4d16071 fix secrets stored in json format (#466)
* fix secrets in json format

* fix actionlint

* add more comments and docs

* revert build.yml test

* add test for json

* fix selector

* fix e2e test

* fix e2e test 2

* remove test

* remove isNaN check

* update changelog
2023-06-21 11:55:50 -05:00
Thy Ton 62aa8bb4c4 Fix e2e CI failure from "Test that overwrite env var works in nested action (#453)" (#467)
Fix e2e CI failure VAULT-17057
Co-authored-by: Leonardo Villela <leonardo.villela37@gmail.com>
2023-06-13 11:22:43 -07:00
John-Michael Faircloth ec2980c187 chore: update jira teams and add actionlint (#465)
* chore: update jira teams and add actionlint

* update local test to workflow_dispatch

* define outputs

* try actionlint's skip_outputs setting

* move skip_outputs to action definition in tests

* move skip_outpus to action.yml

* remove skip_outputs

* move outputs

* call actionlint directly to pass args

* try to fix quoting

* truncate ignore

* try escaping

* add comment
2023-06-12 14:58:04 -05:00
John-Michael Faircloth 166100bd2a fix warning about outputToken being an unexpected input (#461) 2023-06-08 10:31:49 -05:00
John-Michael Faircloth dc4f72debb Revert "Test that overwrite env var works in nested action (#453)" (#464)
This reverts commit d27529ebde.
2023-06-08 10:25:58 -05:00
John-Michael Faircloth a87a71c289 ensure we run the build for all branches (#463)
* ensure we run the build for all branches

* remove pull_request_target since push is sufficient

* remove github.ref for checkouts
2023-06-08 09:43:08 -05:00
John-Michael Faircloth bb61006b6d Update to v2.6.0 (#458) 2023-06-07 15:40:06 -05:00
dependabot[bot] 14a4a058b4 chore(deps): bump jsrsasign from 10.6.1 to 10.8.6 (#451)
Bumps [jsrsasign](https://github.com/kjur/jsrsasign) from 10.6.1 to 10.8.6.
- [Release notes](https://github.com/kjur/jsrsasign/releases)
- [Changelog](https://github.com/kjur/jsrsasign/blob/master/ChangeLog.txt)
- [Commits](https://github.com/kjur/jsrsasign/compare/10.6.1...10.8.6)

---
updated-dependencies:
- dependency-name: jsrsasign
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-07 13:54:43 -05:00
Robert 2d9c2b9f1b Update Jira workflow (#456)
* Migrate jira sync to common workflow

---------

Co-authored-by: Tom Proctor <tomhjp@users.noreply.github.com>
2023-05-23 18:51:32 -05:00
Leonardo Villela d27529ebde Test that overwrite env var works in nested action (#453) 2023-05-19 17:54:24 -07:00
TomNorth cd5a8995f3 Scoped token access (#441)
* feat: Always allow scoped access to vault token through outputs
* Make optional instead of always, in case of untrusted steps
---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Thomas <thomas.north@dazn.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-05-19 14:11:33 -04:00
Max Coulombe 72c092c8af * document that the selector uses JSONata (#455) 2023-05-19 13:24:23 -04:00
hashicorp-tsccr[bot] 9c1dce9ef6 Result of tsccr-helper -pin-all-workflows . (#450)
Co-authored-by: hashicorp-tsccr[bot] <hashicorp-tsccr[bot]@users.noreply.github.com>
2023-05-19 10:34:26 -04:00
dependabot[bot] 9866ce3e18 chore(deps): bump jsonata from 2.0.2 to 2.0.3 (#448)
* chore(deps): bump jsonata from 2.0.2 to 2.0.3

Bumps [jsonata](https://github.com/jsonata-js/jsonata) from 2.0.2 to 2.0.3.
- [Release notes](https://github.com/jsonata-js/jsonata/releases)
- [Changelog](https://github.com/jsonata-js/jsonata/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsonata-js/jsonata/compare/v2.0.2...v2.0.3)

---
updated-dependencies:
- dependency-name: jsonata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: maxcoulombe <max.coulombe@hashicorp.com>
2023-05-19 10:29:27 -04:00
Max Wagner 1f5b7d55d8 Set output for the error message (#446)
* Set output for the error message

In the event vault-action throws an error, the error message is
sent to the workflow log when with core.setFailed, but that output
is not accessible to other steps in the workflow.

This change sets an output for the error message, called errorMessage,
than can be read by other steps within the workflow and parsed.

* Add hashicorp/vault-action#446 to improvements in CHANGELOG
2023-05-19 09:21:14 -04:00
Sai Pranav d1655aec40 chore(readme): adding readme for #440 (#444) 2023-05-19 09:17:46 -04:00
Sai Pranav 1d767e3957 Add userpass auth and ldap auth support (#440)
* fix(auth): added approle test in basic integration

* feat(auth): adding userpass and and ldap auth

* chore(changelog): added support for userpass and ldap auth
2023-03-31 17:58:12 -05:00
dependabot[bot] c253c155ba chore(deps-dev): bump jest from 29.4.3 to 29.5.0 (#438)
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 29.4.3 to 29.5.0.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.5.0/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-03-28 10:50:10 -04:00
Max Coulombe 3a9100e7d5 Enhanced "Example Usage" section (#435)
+added documentation on how to use the retrieved secrets
+ added an example of converting the vault-action outputs to json
* fix e2e test setup
2023-03-02 16:06:42 -05:00
Mike Dahl 256bfb9e6a handle undefined response in getSecrets error handler (#431) 2023-03-02 14:39:28 -05:00
Max Coulombe 3bbbc68bd0 Development flow enhancements (#430)
+ added a contribution section to the readme
+ added a local workflow to test changes
+ made the vault token configurable for tests
* bumped action/checkout
2023-02-28 15:28:33 -05:00
dependabot[bot] 74bc2a617b chore(deps): bump jsonata from 1.8.6 to 2.0.2 (#426)
* chore(deps): bump jsonata from 1.8.6 to 2.0.2

Bumps [jsonata](https://github.com/jsonata-js/jsonata) from 1.8.6 to 2.0.2.
- [Release notes](https://github.com/jsonata-js/jsonata/releases)
- [Changelog](https://github.com/jsonata-js/jsonata/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsonata-js/jsonata/compare/v1.8.6...v2.0.2)

---
updated-dependencies:
- dependency-name: jsonata
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* * waiting for result of evaluate to account for jsonata v2 changes

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: maxcoulombe <max.coulombe@hashicorp.com>
2023-02-28 08:29:18 -05:00
dependabot[bot] 76780d43f5 chore(deps-dev): bump @vercel/ncc from 0.36.0 to 0.36.1 (#417)
Bumps [@vercel/ncc](https://github.com/vercel/ncc) from 0.36.0 to 0.36.1.
- [Release notes](https://github.com/vercel/ncc/releases)
- [Commits](https://github.com/vercel/ncc/compare/0.36.0...0.36.1)

---
updated-dependencies:
- dependency-name: "@vercel/ncc"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 17:44:55 -05:00
dependabot[bot] 46540966f1 chore(deps-dev): bump jest from 29.3.1 to 29.4.3 (#425)
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) from 29.3.1 to 29.4.3.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.4.3/packages/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-02-27 17:21:06 -05:00
dependabot[bot] cc5270ec14 chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1 (#422)
* chore(deps): bump http-cache-semantics from 4.1.0 to 4.1.1

Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/compare/v4.1.0...v4.1.1)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

* * built change

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: maxcoulombe <max.coulombe@hashicorp.com>
2023-02-27 17:19:17 -05:00
Austin Gebauer 130d1f5f4f Update to v2.5.0 (#419) 2023-01-26 13:40:24 -08:00
Austin Gebauer d34ee148bc Removes unused devDependencies from package.json (#416)
* Removes unused devDependencies from package.json

* Removes release section of package.json
2023-01-25 09:24:04 -08:00
dependabot[bot] 77bab83f42 chore(deps-dev): bump semantic-release from 19.0.5 to 20.0.4 (#415)
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 19.0.5 to 20.0.4.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v19.0.5...v20.0.4)

---
updated-dependencies:
- dependency-name: semantic-release
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 14:53:51 -08:00
dependabot[bot] 5e3dd4f01b chore(deps-dev): bump @types/jest from 29.2.2 to 29.2.6 (#413)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 29.2.2 to 29.2.6.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-24 14:47:35 -08:00
Kevin Schoonover 7318a98db7 print vault error message on authentication failure (#409)
Co-authored-by: Austin Gebauer <34121980+austingebauer@users.noreply.github.com>
2023-01-23 15:52:40 -08:00
dependabot[bot] b08bc4993d chore(deps): bump minimatch from 3.0.4 to 3.1.2 (#410)
Bumps [minimatch](https://github.com/isaacs/minimatch) from 3.0.4 to 3.1.2.
- [Release notes](https://github.com/isaacs/minimatch/releases)
- [Changelog](https://github.com/isaacs/minimatch/blob/main/changelog.md)
- [Commits](https://github.com/isaacs/minimatch/compare/v3.0.4...v3.1.2)

---
updated-dependencies:
- dependency-name: minimatch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 08:50:47 -08:00
dependabot[bot] 579f9fd8c2 chore(deps): bump json5 from 2.2.1 to 2.2.3 (#404)
Bumps [json5](https://github.com/json5/json5) from 2.2.1 to 2.2.3.
- [Release notes](https://github.com/json5/json5/releases)
- [Changelog](https://github.com/json5/json5/blob/main/CHANGELOG.md)
- [Commits](https://github.com/json5/json5/compare/v2.2.1...v2.2.3)

---
updated-dependencies:
- dependency-name: json5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 08:49:50 -08:00
dependabot[bot] 1226471c04 chore(deps): bump jsrsasign from 10.5.27 to 10.6.1 (#401)
Bumps [jsrsasign](https://github.com/kjur/jsrsasign) from 10.5.27 to 10.6.1.
- [Release notes](https://github.com/kjur/jsrsasign/releases)
- [Changelog](https://github.com/kjur/jsrsasign/blob/master/ChangeLog.txt)
- [Commits](https://github.com/kjur/jsrsasign/compare/10.5.27...10.6.1)

---
updated-dependencies:
- dependency-name: jsrsasign
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 08:48:14 -08:00
dependabot[bot] fdaeeffa26 chore(deps-dev): bump jest and @types/jest (#397)
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) and [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest). These dependencies needed to be updated together.

Updates `jest` from 28.1.1 to 29.3.1
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v29.3.1/packages/jest)

Updates `@types/jest` from 28.1.3 to 29.2.2
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-major
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-01-17 08:46:47 -08:00
Luis (LT) Carbonell 0f409d4023 Add Decoding to Secrets (#408)
* Add decoding to secrets

* remove index.js

* Add test case, and other updates
2023-01-17 10:42:49 -06:00
Austin Gebauer 8fa61e9099 Update to v2.4.3 (#395) 2022-11-08 15:23:50 -08:00
dependabot[bot] 132f1c6930 chore(deps-dev): bump jest-when from 3.5.1 to 3.5.2 (#388)
Bumps [jest-when](https://github.com/timkindberg/jest-when) from 3.5.1 to 3.5.2.
- [Release notes](https://github.com/timkindberg/jest-when/releases)
- [Commits](https://github.com/timkindberg/jest-when/compare/v3.5.1...v3.5.2)

---
updated-dependencies:
- dependency-name: jest-when
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-26 16:28:06 -07:00
dependabot[bot] f558cc7838 chore(deps-dev): bump semantic-release from 19.0.3 to 19.0.5 (#360)
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 19.0.3 to 19.0.5.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v19.0.3...v19.0.5)

---
updated-dependencies:
- dependency-name: semantic-release
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-26 16:25:24 -07:00
dependabot[bot] d0e05af6a3 chore(deps): bump jsrsasign from 10.5.25 to 10.5.27 (#358)
Bumps [jsrsasign](https://github.com/kjur/jsrsasign) from 10.5.25 to 10.5.27.
- [Release notes](https://github.com/kjur/jsrsasign/releases)
- [Changelog](https://github.com/kjur/jsrsasign/blob/master/ChangeLog.txt)
- [Commits](https://github.com/kjur/jsrsasign/compare/10.5.25...10.5.27)

---
updated-dependencies:
- dependency-name: jsrsasign
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-26 16:24:39 -07:00
Theron Voran 1f8e723e55 change example to vault-action@v2 (#391)
so we don't have to keep remembering to update the version tag in the
readme.
2022-10-26 11:48:17 -07:00
vinay-gopalan 32d00a142f update changelog (#386) 2022-10-24 16:46:21 -07:00
Theron Voran 32838a0d48 jira-sync: update the team to applications (#380) 2022-10-13 17:01:10 -07:00
dependabot[bot] ed59bea637 chore(deps-dev): bump @actions/core from 1.9.0 to 1.10.0 (#371)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-13 16:09:15 -07:00
prizov 2537991e61 use node16 in action.yml (#375) 2022-10-13 16:07:48 -07:00
vinay-gopalan 7d98524254 Update to v2.4.2 (#357) 2022-08-15 11:56:52 -07:00
Ben Ash f380d921ae Update jira sync (#354) 2022-08-05 19:11:57 -04:00
Tom Proctor ae2f303e37 Bump got to 11.8.5 (#344) 2022-06-29 17:34:59 +01:00
Tom Proctor 55a11671e0 Retry on 412 status codes (#333)
* Retry on 412 status codes
* Add retry tests
2022-06-29 12:55:26 +01:00
dependabot[bot] ad04ab9377 chore(deps): bump minimist from 1.2.5 to 1.2.6 (#341)
Bumps [minimist](https://github.com/substack/minimist) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/substack/minimist/releases)
- [Commits](https://github.com/substack/minimist/compare/1.2.5...1.2.6)

---
updated-dependencies:
- dependency-name: minimist
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-28 14:39:20 -07:00
Christopher Swenson 2dcbd17a34 Add additional mocks needed for tests in new versions of @actions (#343)
Tests started failing after @actions was bumped in
https://github.com/hashicorp/vault-action/pull/334
2022-06-28 14:35:22 -07:00
dependabot[bot] d2b5111993 chore(deps-dev): bump @actions/core from 1.6.0 to 1.9.0 (#334)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.6.0 to 1.9.0.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

---
updated-dependencies:
- dependency-name: "@actions/core"
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-28 13:57:21 -07:00
dependabot[bot] f5817917e5 chore(deps): bump jsrsasign from 10.5.24 to 10.5.25 (#340)
* chore(deps): bump jsrsasign from 10.5.24 to 10.5.25

Bumps [jsrsasign](https://github.com/kjur/jsrsasign) from 10.5.24 to 10.5.25.
- [Release notes](https://github.com/kjur/jsrsasign/releases)
- [Changelog](https://github.com/kjur/jsrsasign/blob/master/ChangeLog.txt)
- [Commits](https://github.com/kjur/jsrsasign/compare/10.5.24...10.5.25)

---
updated-dependencies:
- dependency-name: jsrsasign
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>

* Use RSA-2048 key instead of 4096

After bumping jest, the RSA signing performance went down by a factor of
2 or so, which causes tests to fail (since they went from ~3 seconds to
~6 seconds to generate a 4096-bit RSA signature). The tests generate
these signatures to fake responses from GitHub.

After replacing the keys with a 2048-bit key, the signing times are now
under a second.

* Revert unnecessary change to test timeout

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Christopher Swenson <christopher.swenson@hashicorp.com>
2022-06-28 13:53:04 -07:00
dependabot[bot] bbf40c0251 chore(deps-dev): bump jest and @types/jest (#330)
Bumps [jest](https://github.com/facebook/jest/tree/HEAD/packages/jest) and [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest). These dependencies needed to be updated together.

Updates `jest` from 26.6.3 to 28.1.1
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/commits/v28.1.1/packages/jest)

Updates `@types/jest` from 27.4.1 to 28.1.1
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: jest
  dependency-type: direct:development
  update-type: version-update:semver-major
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-28 10:54:00 -07:00
dependabot[bot] cebb457349 chore(deps-dev): bump semantic-release from 17.2.3 to 19.0.3 (#331)
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.2.3 to 19.0.3.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v17.2.3...v19.0.3)

---
updated-dependencies:
- dependency-name: semantic-release
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-28 10:46:23 -07:00
Christopher Swenson 6ee4dd3797 Set node version to v16.14.0 to workaround node/npm bug (#335)
Node 16.14.2 thinks the `package-lock.json` is out of sync when it is
not.

https://github.com/npm/cli/issues/4660
2022-06-15 14:50:07 -07:00
dependabot[bot] a502b7fa66 chore(deps): bump jsrsasign from 10.2.0 to 10.5.24 (#329)
Bumps [jsrsasign](https://github.com/kjur/jsrsasign) from 10.2.0 to 10.5.24.
- [Release notes](https://github.com/kjur/jsrsasign/releases)
- [Changelog](https://github.com/kjur/jsrsasign/blob/master/ChangeLog.txt)
- [Commits](https://github.com/kjur/jsrsasign/compare/10.2.0...10.5.24)

---
updated-dependencies:
- dependency-name: jsrsasign
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-06-15 13:08:36 -07:00
Tom Proctor abba4a3980 Update to v2.4.1 (#317) 2022-04-28 16:20:56 +01:00
Tom Proctor e025870ee9 changelog++ 2022-04-28 11:29:22 +01:00
Tom Proctor 4f536680b6 Update default branch 2022-04-28 11:24:54 +01:00
dependabot[bot] fd9b31c94c chore(deps): bump jsonata from 1.8.4 to 1.8.6 (#312)
Bumps [jsonata](https://github.com/jsonata-js/jsonata) from 1.8.4 to 1.8.6.
- [Release notes](https://github.com/jsonata-js/jsonata/releases)
- [Changelog](https://github.com/jsonata-js/jsonata/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsonata-js/jsonata/compare/v1.8.4...v1.8.6)

---
updated-dependencies:
- dependency-name: jsonata
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-28 11:17:38 +01:00
Tom Proctor 8e9712e63b Fix tests from jest-when version bump (#311)
* Fix tests from jest-when version bump
* Delete unused test helpers
* Add workflow_dispatch for tests
2022-04-28 11:12:56 +01:00
dependabot[bot] 25737aea2b chore(deps-dev): bump jest-when from 2.7.2 to 3.5.1 (#294)
Bumps [jest-when](https://github.com/timkindberg/jest-when) from 2.7.2 to 3.5.1.
- [Release notes](https://github.com/timkindberg/jest-when/releases)
- [Commits](https://github.com/timkindberg/jest-when/compare/v2.7.2...v3.5.1)

---
updated-dependencies:
- dependency-name: jest-when
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 13:16:46 +01:00
dependabot[bot] c2b108a1cb chore(deps): bump node-fetch from 2.6.1 to 2.6.7 (#308)
Bumps [node-fetch](https://github.com/node-fetch/node-fetch) from 2.6.1 to 2.6.7.
- [Release notes](https://github.com/node-fetch/node-fetch/releases)
- [Commits](https://github.com/node-fetch/node-fetch/compare/v2.6.1...v2.6.7)

---
updated-dependencies:
- dependency-name: node-fetch
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 13:14:45 +01:00
dependabot[bot] 7c940db65a chore(deps-dev): bump @types/jest from 26.0.23 to 27.4.1 (#297)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.23 to 27.4.1.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

---
updated-dependencies:
- dependency-name: "@types/jest"
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 13:13:45 +01:00
dependabot[bot] d9ec4185f7 chore(deps): bump trim-off-newlines from 1.0.1 to 1.0.3 (#309)
Bumps [trim-off-newlines](https://github.com/stevemao/trim-off-newlines) from 1.0.1 to 1.0.3.
- [Release notes](https://github.com/stevemao/trim-off-newlines/releases)
- [Commits](https://github.com/stevemao/trim-off-newlines/compare/v1.0.1...v1.0.3)

---
updated-dependencies:
- dependency-name: trim-off-newlines
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 13:12:57 +01:00
dependabot[bot] 203b1c7ae9 chore(deps): bump moment from 2.28.0 to 2.29.2 (#304)
Bumps [moment](https://github.com/moment/moment) from 2.28.0 to 2.29.2.
- [Release notes](https://github.com/moment/moment/releases)
- [Changelog](https://github.com/moment/moment/blob/develop/CHANGELOG.md)
- [Commits](https://github.com/moment/moment/compare/2.28.0...2.29.2)

---
updated-dependencies:
- dependency-name: moment
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 13:12:06 +01:00
dependabot[bot] 0580f85e79 chore(deps-dev): bump @types/got from 9.6.11 to 9.6.12 (#266)
Bumps [@types/got](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/got) from 9.6.11 to 9.6.12.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/got)

---
updated-dependencies:
- dependency-name: "@types/got"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-26 13:08:44 +01:00
Tom Proctor b222012f94 changelog++ 2022-04-26 13:08:02 +01:00
Christopher Swenson 40fb8d7236 Write a better error message when secret not found (follow-up) (#306)
* Write a better error message when key not found

* Address additional comments on #182

Co-authored-by: Simon Johansson <simon@simonjohansson.com>
2022-04-20 15:53:24 -07:00
Prakash Manglanathan 4aed62f922 Updated the version in Readme doc. (#303)
The Sample code provided in the readme doesn't work as expected unless the version has been updated to 2.4.0.
2022-04-07 16:42:18 -07:00
Tom Proctor 4679f8aa3c changelog++ 2022-04-07 15:35:48 +01:00
Kamil Domański 2f64a97498 make "role" input optional (#291)
* make "role" input optional

Per Vault documentation it doesn't have to be provided,
and the auth provider's "default_role" parameter is required
precisely for this case.
https://www.vaultproject.io/api/auth/jwt
2022-04-07 15:34:46 +01:00
Tom Proctor 25c4aec690 changelog++ 2022-04-07 14:11:27 +01:00
Kevin Schoonover c14a190aaa feat(): make secrets parameter optional (#299) 2022-04-07 14:10:23 +01:00
Joonas Venäläinen 843e7fa30a fix(docs): Fix bound_audience example url in Github OIDC Token section (#302) 2022-04-04 10:11:40 +01:00
Rosemary Wang 876cdcfdd3 Update docs with GitHub OIDC token configuration (#301) 2022-03-28 09:23:28 -07:00
Tom Proctor 67281159df Update jira action (#274)
* No longer check for Vault team membership
* Tweak jira states and search parameters
2021-11-08 11:16:29 +00:00
Theron Voran 61dd38f491 Update to v2.4.0 (#270) 2021-10-21 11:14:14 -07:00
Gary Frederick 168f9696e8 changelog++ 2021-10-08 10:06:23 -07:00
Alex Kulikovskikh c502100fbe feat(): add support for github provided jwt auth (#257)
* fix: update `privateKeyRaw` condition

* fix: add `contents: read` permission

* fix: get token via `@actions/core`

- Update README
- Switch to use `getIDToken` method for Github token retrieval
- Bump `@actions/core` to 1.6.0
- Add `jwtGithubAudience` input
- Remove unnecessary code

* fix: add description for `jwtGithubAudience`

* fix: move default value for `jwtGithubAudience` to `action.yml`

* docs: fix typo in README & grammar

* test: add tests

* fix: reset `dist/index.js`

* fix: remove default value for `jwtGithubAudience` from `action.yml`

* fix: reset `dist/index.js`

* fix: reset `dist/index.js`
2021-10-08 09:46:21 -07:00
Theron Voran b8c90c7243 changelog++
fix release date
2021-08-23 08:39:55 -07:00
Theron Voran d5a69ceca1 Update to v2.3.1 (#242) 2021-08-23 08:36:01 -07:00
dependabot[bot] f3e4110f8d chore(deps): bump path-parse from 1.0.6 to 1.0.7 (#239) 2021-08-12 19:00:07 +00:00
Theron Voran 8c00f7e4fd set vault license for enterprise tests (#240)
Since hashicorp/vault-enterprise:latest now requires a license to do
anything, we need to set $VAULT_LICENSE when provisioning the
enterprise docker container.
2021-08-12 11:55:16 -07:00
dependabot[bot] ab957d2aec chore(deps): bump normalize-url from 4.5.0 to 4.5.1 (#227) 2021-08-12 17:15:05 +00:00
Theron Voran 0451f06f9f Update to v2.3.0 (#231) 2021-06-23 14:03:57 -07:00
dependabot[bot] b6210c5a51 chore(deps): bump hosted-git-info from 2.8.8 to 2.8.9 (#216)
Bumps [hosted-git-info](https://github.com/npm/hosted-git-info) from 2.8.8 to 2.8.9.
- [Release notes](https://github.com/npm/hosted-git-info/releases)
- [Changelog](https://github.com/npm/hosted-git-info/blob/v2.8.9/CHANGELOG.md)
- [Commits](https://github.com/npm/hosted-git-info/compare/v2.8.8...v2.8.9)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-03 11:10:25 -04:00
Jason O'Donnell 09740f679f changelog++ 2021-06-03 11:01:06 -04:00
Falcon Taylor-Carter 72c7a899ca Implement kubernetes auth / Add customizable auth path (#218)
* Implement kubernetes auth / Add customizable auth path

* Fix typo

* Apply suggestions from code review

Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>

Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>
2021-06-03 10:59:51 -04:00
Jason O'Donnell 0cf3bd6a39 Update version in example doc (#215) 2021-05-06 14:27:48 -04:00
Jason O'Donnell 0723fd7348 Update to v2.2.0 (#214) 2021-05-06 13:51:05 -04:00
dependabot[bot] 0405c26bc9 chore(deps): bump jsonata from 1.8.3 to 1.8.4 (#171)
Bumps [jsonata](https://github.com/jsonata-js/jsonata) from 1.8.3 to 1.8.4.
- [Release notes](https://github.com/jsonata-js/jsonata/releases)
- [Changelog](https://github.com/jsonata-js/jsonata/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsonata-js/jsonata/compare/v1.8.3...v1.8.4)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 13:42:25 -04:00
dependabot[bot] 2eea93c25e chore(deps-dev): bump @actions/core from 1.2.6 to 1.2.7 (#206)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.6 to 1.2.7.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 13:42:11 -04:00
dependabot[bot] 4d9cf786d6 chore(deps-dev): bump @types/jest from 26.0.22 to 26.0.23 (#207)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.22 to 26.0.23.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 13:41:52 -04:00
dependabot[bot] 575042000a chore(deps): bump handlebars from 4.7.6 to 4.7.7 (#213)
Bumps [handlebars](https://github.com/wycats/handlebars.js) from 4.7.6 to 4.7.7.
- [Release notes](https://github.com/wycats/handlebars.js/releases)
- [Changelog](https://github.com/handlebars-lang/handlebars.js/blob/master/release-notes.md)
- [Commits](https://github.com/wycats/handlebars.js/compare/v4.7.6...v4.7.7)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 13:41:29 -04:00
dependabot[bot] cc8ad16bec chore(deps): bump lodash from 4.17.20 to 4.17.21 (#212)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.20 to 4.17.21.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.20...4.17.21)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 13:41:20 -04:00
dependabot[bot] cd1614aee3 chore(deps): bump jsrsasign from 10.1.10 to 10.2.0 (#211)
Bumps [jsrsasign](https://github.com/kjur/jsrsasign) from 10.1.10 to 10.2.0.
- [Release notes](https://github.com/kjur/jsrsasign/releases)
- [Changelog](https://github.com/kjur/jsrsasign/blob/master/ChangeLog.txt)
- [Commits](https://github.com/kjur/jsrsasign/compare/10.1.10...10.2.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-05-06 13:38:52 -04:00
Jason O'Donnell f3a53220d9 changelog++ 2021-05-06 13:24:09 -04:00
Tom Proctor 3526e1be65 Mask each line of multi-line secrets (#208)
* Mask each line of multi-line secrets

* Don't include carriage return characters in masking

* Update CHANGELOG.md
2021-05-05 11:54:07 +01:00
Jason O'Donnell f60544fbda changelog++ 2021-05-04 09:02:26 -04:00
Raman Harnak 9e8f22534f Implement JWT auth (#188) 2021-05-04 08:49:35 -04:00
Maryam Ghanizadeh 8417c61f8a chore(deps): bump marked from 1.2.6 to 2.0.1 security reason (#202) 2021-04-22 16:32:18 -04:00
dependabot[bot] 0cca23248c chore(deps-dev): bump @types/jest from 26.0.19 to 26.0.22 (#201)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.19 to 26.0.22.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 15:46:23 -04:00
dependabot[bot] 8dad53ed10 chore(deps): bump got from 11.8.1 to 11.8.2 (#190)
Bumps [got](https://github.com/sindresorhus/got) from 11.8.1 to 11.8.2.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v11.8.1...v11.8.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-04-22 15:46:05 -04:00
Jason O'Donnell 937d792a86 Add GitHub authentication notice (#200)
* Add GitHub authentication notice

* Typo in perm name
2021-03-26 14:03:15 -04:00
Jason O'Donnell 5e5c06a3c8 Update to v2.1.2 (#180)
* Update to v2.1.2

* Update index.js
2021-01-21 14:45:22 -05:00
Jason O'Donnell ff547e1516 changelog++ 2021-01-11 11:50:03 -05:00
bilucodota 1d4917ec58 - Support complex keys, such as yaml/json files (#173) 2021-01-11 11:47:51 -05:00
Jason O'Donnell 2fb78ab91e Update to v2.1.1 (#168) 2020-12-15 11:55:39 -05:00
dependabot[bot] c261a0ad69 chore(deps): bump ini from 1.3.5 to 1.3.8 (#167)
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 11:17:21 -05:00
Jon L 6ac603b23e bump readme to action v2.1.0 (#157) 2020-12-15 11:12:08 -05:00
dependabot[bot] 638924aeb4 chore(deps-dev): bump @types/jest from 26.0.14 to 26.0.19 (#164)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.14 to 26.0.19.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 11:09:42 -05:00
dependabot[bot] 668d59f96b chore(deps): bump got from 11.7.0 to 11.8.1 (#163)
Bumps [got](https://github.com/sindresorhus/got) from 11.7.0 to 11.8.1.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v11.7.0...v11.8.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 11:09:17 -05:00
dependabot[bot] 208b49ff6c chore(deps-dev): bump semantic-release from 17.1.2 to 17.3.0 (#158)
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.1.2 to 17.3.0.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v17.1.2...v17.3.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 11:08:50 -05:00
dependabot[bot] 53c0f5bcca chore(deps-dev): bump jest from 26.5.0 to 26.6.3 (#150)
Bumps [jest](https://github.com/facebook/jest) from 26.5.0 to 26.6.3.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v26.5.0...v26.6.3)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 11:04:07 -05:00
Jason O'Donnell b4cef0fa36 Revert "chore(deps-dev): bump jest-when from 2.7.2 to 3.0.1 (#147)" (#166)
This reverts commit 7bc0a10ff8.
2020-12-15 11:00:26 -05:00
dependabot[bot] 7bc0a10ff8 chore(deps-dev): bump jest-when from 2.7.2 to 3.0.1 (#147)
Bumps [jest-when](https://github.com/timkindberg/jest-when) from 2.7.2 to 3.0.1.
- [Release notes](https://github.com/timkindberg/jest-when/releases)
- [Commits](https://github.com/timkindberg/jest-when/compare/v2.7.2...v3.0.1)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-12-15 10:15:59 -05:00
Tom Proctor e5febde8e7 Update jira sync github action (#151) 2020-11-18 10:49:47 +00:00
Jason O'Donnell 2ca76a4465 changelog++ 2020-10-06 13:18:57 -04:00
Jason O'Donnell 5b42d37bab Update to v2.0.2 (#133) 2020-10-06 13:15:24 -04:00
dependabot[bot] 124e6eecdb chore(deps-dev): bump jest from 26.4.2 to 26.5.0 (#132)
Bumps [jest](https://github.com/facebook/jest) from 26.4.2 to 26.5.0.
- [Release notes](https://github.com/facebook/jest/releases)
- [Changelog](https://github.com/facebook/jest/blob/master/CHANGELOG.md)
- [Commits](https://github.com/facebook/jest/compare/v26.4.2...v26.5.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-06 11:06:25 -04:00
dependabot[bot] 43c4212f70 chore(deps-dev): bump @types/jest from 26.0.13 to 26.0.14 (#123)
Bumps [@types/jest](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/jest) from 26.0.13 to 26.0.14.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/jest)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 10:33:41 -04:00
dependabot[bot] d4487e4dd0 chore(deps-dev): bump semantic-release from 17.1.1 to 17.1.2 (#124)
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.1.1 to 17.1.2.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v17.1.1...v17.1.2)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 10:32:59 -04:00
dependabot[bot] c7f47bbfcf chore(deps): bump got from 11.6.2 to 11.7.0 (#126)
Bumps [got](https://github.com/sindresorhus/got) from 11.6.2 to 11.7.0.
- [Release notes](https://github.com/sindresorhus/got/releases)
- [Commits](https://github.com/sindresorhus/got/compare/v11.6.2...v11.7.0)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 10:32:32 -04:00
dependabot[bot] 11e2f12f99 chore(deps-dev): bump @actions/core from 1.2.5 to 1.2.6 (#130)
Bumps [@actions/core](https://github.com/actions/toolkit/tree/HEAD/packages/core) from 1.2.5 to 1.2.6.
- [Release notes](https://github.com/actions/toolkit/releases)
- [Changelog](https://github.com/actions/toolkit/blob/main/packages/core/RELEASES.md)
- [Commits](https://github.com/actions/toolkit/commits/HEAD/packages/core)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-01 10:30:25 -04:00
Jason O'Donnell 6c64e41adc changelog++ 2020-10-01 10:23:30 -04:00
ZANCO Bertrand 2f76ad395b Add ability to export Vault Token (#127)
* Add export Vault Token

* Set correct condition for default value

* Add test for exportToken
Fix key with dash

* Restore index.js
2020-10-01 10:22:10 -04:00
Jason O'Donnell 307c9ae581 Add pull_request_target to build action (#129)
* Add pull_request_target to build action

* Remove pull_request from action

* Remove branch specification

* Update as per review
2020-09-23 09:48:24 -04:00
Benjamin Rumble b35ef16370 specify version (#122)
* specify version 

Without specifying `@v2.0.0` GitHub says the string is not the correct format

``` 
The workflow is not valid. .github/workflows/integration_tests.yml (Line: 10, Col: 15): Expected format {org}/{repo}[/path]@ref. Actual 'hashicorp/vault-action',Input string was not in a correct format.```

* v2.0.1

Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>

Co-authored-by: Jason O'Donnell <2160810+jasonodonnell@users.noreply.github.com>
2020-09-22 13:44:55 -04:00
Jason O'Donnell 952d5d48e4 Update to v2.0.1 (#121)
* Update to v2.0.1

* Update changelog
2020-09-15 14:58:16 -04:00
Calvin Leung Huang 649323229c dep: update kind-of to version 6.0.3 (#120) 2020-09-14 14:56:40 -07:00
Calvin Leung Huang 35be1a3b49 dep: update dev dependencies (#119) 2020-09-14 14:45:38 -07:00
Calvin Leung Huang d81a7a778d dep: update jsonata to version 1.8.3 (#118) 2020-09-14 13:01:47 -07:00
dependabot[bot] 3a8d733522 chore(deps-dev): bump @actions/core from 1.2.3 to 1.2.5 (#115) 2020-09-14 19:09:17 +00:00
dependabot[bot] 1db79efc8e chore(deps-dev): bump @types/jest from 25.1.5 to 26.0.13 (#114) 2020-09-14 19:04:49 +00:00
dependabot[bot] 488fc1ce12 chore(deps): bump got from 11.5.1 to 11.6.2 (#117) 2020-09-14 18:58:09 +00:00
dependabot[bot] 722353f009 chore(deps-dev): bump jest from 25.2.7 to 26.4.2 (#116) 2020-09-14 18:54:02 +00:00
Calvin Leung Huang 20a8e522c2 Create dependabot.yml (#113) 2020-09-14 11:45:15 -07:00
dependabot[bot] 35e0a09f2c chore(deps): bump npm from 6.14.4 to 6.14.8 (#112) 2020-09-14 18:17:04 +00:00
dependabot[bot] 5cb0f68cdc chore(deps): bump lodash from 4.17.15 to 4.17.20 (#111)
Bumps [lodash](https://github.com/lodash/lodash) from 4.17.15 to 4.17.20.
- [Release notes](https://github.com/lodash/lodash/releases)
- [Commits](https://github.com/lodash/lodash/compare/4.17.15...4.17.20)

Signed-off-by: dependabot[bot] <support@github.com>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-09-14 11:11:03 -07:00
dependabot[bot] 2977272bf8 chore(deps): bump node-fetch from 2.6.0 to 2.6.1 (#110) 2020-09-14 17:43:06 +00:00
Tom Proctor 2c1083c570 Update Jira sync action versions (#109)
These versions bring a few fixes:
* The action now supports converting the most common bits of markdown syntax into Jira formatting directives
* Replaces Atlassian's comment action, which tries to interpolate bits of text from comments wrapped in {{ github.event_name }} as templates, usually causing an error (in the case I've put there, it will replace the template with `pull_request_target`)
* Remove trailing comma after link that broke the link target Jira selects
2020-09-14 13:06:53 -04:00
Tom Proctor 401e08df03 Add Jira sync workflow (#107) 2020-08-28 10:46:33 +01:00
Jason O'Donnell e27b45646f Update action.yml 2020-08-24 19:22:08 -04:00
Jason O'Donnell 76ca623af3 changelog++ 2020-08-24 19:19:56 -04:00
Jason O'Donnell c8b383ee4e Update doc for TLS (#103) 2020-08-12 11:53:00 -04:00
Jason O'Donnell a214c8c65a changelog++ 2020-08-11 11:14:10 -04:00
Jason O'Donnell f7f0d5a289 Simplify secret request UX (#102)
* Simplify secret UX

* Update doc

* Fix tests
2020-08-11 10:06:28 -04:00
Jason O'Donnell 633de935e7 changelog++ 2020-08-10 11:11:00 -04:00
Jason O'Donnell 5de67274a5 Merge pull request #101 from hashicorp/tlsSkipVerify
Fix bug with tlsSkipVerify and add test coverage
2020-08-07 16:15:00 -04:00
Jason O'Donnell e031fa076e Add missing comment 2020-08-07 15:50:35 -04:00
Jason O'Donnell e5ef6c9c11 Fix bug with tlsSkipVerify and add test coverage 2020-08-07 14:57:44 -04:00
Jason O'Donnell 6cf013b5c0 Merge pull request #100 from hashicorp/build
Update build
2020-08-07 14:41:08 -04:00
Jason O'Donnell 098ee568ed Update build 2020-08-07 14:11:20 -04:00
Jason O'Donnell e055dfd1a4 Update build 2020-08-07 14:02:44 -04:00
Jason O'Donnell b3a02289e7 Merge pull request #97 from hashicorp/tls
Add TLS and mTLS
2020-08-06 14:30:53 -04:00
Jason O'Donnell 683c9b2557 Add toLower and type equality check 2020-08-06 11:10:44 -04:00
Jason O'Donnell e59c94afbe Add TLS and mTLS support 2020-08-05 16:36:27 -04:00
Jason O'Donnell cd01494ae2 Merge pull request #96 from jweissig/patch-1
Updated README.md
2020-08-05 13:12:54 -04:00
Justin Weissig a2d854da53 Updated README.md
Minor spelling and grammar tweaks.
2020-07-31 14:33:00 -07:00
Jim Kalafut d1321fc438 Update README.md 2020-07-31 14:17:53 -07:00
Jim Kalafut 5fb792e09f Merge pull request #95 from hashicorp/readme
Update repository
2020-07-31 14:13:29 -07:00
Jason O'Donnell c63c61cd7e Comment out publish workflow 2020-07-31 16:52:29 -04:00
Jason O'Donnell 944a2bea12 Add CHANGELOG 2020-07-31 16:45:59 -04:00
Jason O'Donnell 2f554c06ec Update README 2020-07-31 16:45:04 -04:00
dependabot-preview[bot] 25ecb3dd46 chore(deps-dev): bump semantic-release from 17.0.4 to 17.0.6 (#46)
Bumps [semantic-release](https://github.com/semantic-release/semantic-release) from 17.0.4 to 17.0.6.
- [Release notes](https://github.com/semantic-release/semantic-release/releases)
- [Commits](https://github.com/semantic-release/semantic-release/compare/v17.0.4...v17.0.6)

Signed-off-by: dependabot-preview[bot] <support@dependabot.com>

Co-authored-by: dependabot-preview[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com>
2020-04-17 10:40:23 -05:00
Richard Simpson 734d76c388 Update issue templates 2020-04-12 00:20:00 -05:00
Richard Simpson 22e3f3e09e fix: mark actions dep as option 2020-04-12 00:10:54 -05:00
Richard Simpson 727494f451 feat: add npm publish and JSONata parsing
BREAKING CHANGE: Switches Key retrieval to use JSONata for parsing.
This allows for more powerful key retrieval and output, but resulted
in a number of related changes to how both key retrieval is done and
how "automatic" output & env keys are generated.

Adds an NPM lib publish to allow consuming parts for other actions that
may want to interact with Vault using vault-action's syntax or parts
without being forced to use the action itself.
2020-04-12 00:00:18 -05:00
Richard Simpson fa68886b06 chore(ci): use standard nodejs shipped with vms 2020-04-11 23:58:16 -05:00
Richard Simpson a7527a3e8a chore: make vault action consumable (#43)
* chore: make vault action consumable

* fix prefixless queries to default to data

* fix the right build entrypoint

* make output more forgiving and shore up selectors

* clarify doc language

* add npmtoken
2020-04-11 23:54:04 -05:00
Richard Simpson 9878eba70a chore: rebuild action 2020-04-07 12:16:27 -05:00
Richard Simpson 83d944ba1a fix: actually allow custom methods 2020-04-07 12:14:02 -05:00
44 changed files with 28064 additions and 19379 deletions
-1
View File
@@ -1 +0,0 @@
ko_fi: richicoder
+23
View File
@@ -0,0 +1,23 @@
---
name: Bug report
about: Create a report to help us improve
title: "[BUG] "
labels: bug
assignees: RichiCoder1
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
The yaml of the `vault-action` step, with any sensitive information masked or removed.
**Expected behavior**
A clear and concise description of what you expected to happen.
**Log Output**
For the most verbose logs, [add a secret called `ACTIONS_STEP_DEBUG` with the value `true`](https://github.com/actions/toolkit/blob/main/docs/action-debugging.md). Then, re-run the workflow if possible and post the *raw logs* for the step here with any sensitive information masked or removed.
**Additional context**
Add any other context about the problem here.
+20
View File
@@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: "[FEAT] "
labels: enhancement
assignees: RichiCoder1
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
+11
View File
@@ -0,0 +1,11 @@
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
version: 2
updates:
- package-ecosystem: "npm" # See documentation for possible values
directory: "/" # Location of package manifests
schedule:
interval: "daily"
+22
View File
@@ -0,0 +1,22 @@
name: Lint GitHub Actions Workflows
on:
push:
paths:
- '.github/workflows/**'
jobs:
actionlint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: "Lint workflow files"
uses: docker://docker.mirror.hashicorp.services/rhysd/actionlint:latest
with:
# Ignore actionlint errors from strict typing for outputs that we use
# in our e2e tests.
# This error occurs because vault-action's outputs are dynamic but
# actionlint expects action.yml to define them.
args: >
-ignore "property \"othersecret\" is not defined in object type"
-ignore "property \"jsonstring\" is not defined in object type"
-ignore "property \"jsonstringmultiline\" is not defined in object type"
+224 -107
View File
@@ -1,171 +1,288 @@
on:
push:
branches:
- master
pull_request:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Use Node.js 10.x
uses: actions/setup-node@v1
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: 10.x
- name: setup npm cache
uses: actions/cache@v1
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: npm install
- name: NPM Install
run: npm ci
- name: npm build
- name: NPM Build
run: npm run build
- name: npm run test
- name: NPM Run Test
run: npm run test
integration:
integrationOSS:
runs-on: ubuntu-latest
services:
vaultBasic:
image: vault:1.2.3
ports:
- 8200/tcp
env:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
options: --cap-add=IPC_LOCK
vaultEnterprise:
image: hashicorp/vault-enterprise:1.3.0_ent
ports:
- 8200/tcp
env:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
options: --cap-add=IPC_LOCK
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: NPM Install
run: npm ci
- name: NPM Build
run: npm run build
- name: NPM Run test;integration:basic
run: npm run test:integration:basic
env:
VAULT_HOST: localhost
VAULT_PORT: 8200
CI: true
integrationEnterprise:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- name: Use Node.js 10.x
uses: actions/setup-node@v1
with:
node-version: 10.x
- name: setup npm cache
uses: actions/cache@v1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: npm install
run: npm ci
- name: npm build
run: npm run build
- name: npm run test:integration:basic
run: npm run test:integration:basic
env:
VAULT_HOST: localhost
VAULT_PORT: ${{ job.services.vaultBasic.ports[8200] }}
CI: true
- name: npm run test:integration:enterprise
run: npm run test:integration:enterprise
env:
VAULT_HOST: localhost
VAULT_PORT: ${{ job.services.vaultEnterprise.ports[8200] }}
CI: true
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault-enterprise
env:
VAULT_LICENSE_CI: ${{ secrets.VAULT_LICENSE_CI }}
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: NPM Install
run: npm ci
- name: NPM Build
run: npm run build
- name: NPM Run test:integration:enterprise
run: npm run test:integration:enterprise
env:
VAULT_HOST: localhost
VAULT_PORT: 8200
CI: true
e2e:
runs-on: ubuntu-latest
services:
vault:
image: vault:1.3.0
ports:
- 8200/tcp
env:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
options: --cap-add=IPC_LOCK
steps:
- uses: actions/checkout@v1
- name: Use Node.js 10.x
uses: actions/setup-node@v1
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: 10.x
- name: setup npm cache
uses: actions/cache@v1
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: npm install
- name: NPM Install
run: npm ci
- name: npm build
- name: NPM Build
run: npm run build
- name: setup vault
- name: Setup Vault
run: node ./integrationTests/e2e/setup.js
env:
VAULT_HOST: localhost
VAULT_PORT: ${{ job.services.vault.ports[8200] }}
- name: use vault action (default K/V version 2)
VAULT_PORT: 8200
- name: Test Vault Action (default KV V2)
uses: ./
id: kv-secrets
with:
url: http://localhost:${{ job.services.vault.ports[8200] }}
url: http://localhost:8200
token: testtoken
secrets: |
test secret ;
test secret | NAMED_SECRET ;
nested/test otherSecret ;
- name: use vault action (custom K/V version 1)
secret/data/test secret ;
secret/data/test secret | NAMED_SECRET ;
secret/data/nested/test otherSecret ;
- name: Test Vault Action (default KV V1)
uses: ./
with:
url: http://localhost:${{ job.services.vault.ports[8200] }}
url: http://localhost:8200
token: testtoken
path: my-secret
kv-version: 1
secrets: |
test altSecret ;
test altSecret | NAMED_ALTSECRET ;
nested/test otherAltSecret ;
- name: use vault action (using cubbyhole engine)
my-secret/test altSecret ;
my-secret/test altSecret | NAMED_ALTSECRET ;
my-secret/nested/test otherAltSecret ;
- name: Test Vault Action (cubbyhole)
uses: ./
with:
url: http://localhost:${{ job.services.vault.ports[8200] }}
url: http://localhost:8200
token: testtoken
secrets: |
/cubbyhole/test foo ;
/cubbyhole/test zip | NAMED_CUBBYSECRET ;
- name: verify
run: npm run test:e2e
# The ordering of these two Test Vault Action Overwrites Env Vars In Subsequent Action steps matters
# They should come before the Verify Vault Action Outputs step
- name: Test Vault Action Overwrites Env Vars In Subsequent Action (part 1/2)
uses: ./
with:
url: http://localhost:8200/
token: testtoken
secrets: |
secret/data/test secret | SUBSEQUENT_TEST_SECRET;
- name: Test Vault Action Overwrites Env Vars In Subsequent Action (part 2/2)
uses: ./
with:
url: http://localhost:8200/
token: testtoken
secrets: |
secret/data/subsequent-test secret | SUBSEQUENT_TEST_SECRET;
- name: Test JSON Secrets
uses: ./
with:
url: http://localhost:8200
token: testtoken
secrets: |
secret/data/test-json-data jsonData;
secret/data/test-json-string jsonString;
secret/data/test-json-string-multiline jsonStringMultiline;
- name: Verify Vault Action Outputs
run: npm run test:integration:e2e
env:
OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets.outputs.otherSecret }}
publish:
if: github.event_name == 'push' && contains(github.ref, 'master')
e2e-tls:
runs-on: ubuntu-latest
needs: [build, integration, e2e]
steps:
- uses: actions/checkout@v1
- name: Use Node.js 10.x
uses: actions/setup-node@v1
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- name: Run docker-compose
run: docker-compose up -d vault-tls
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: 10.x
- name: setup npm cache
uses: actions/cache@v1
node-version: '16.14.0'
- name: Setup NPM Cache
uses: actions/cache@88522ab9f39a2ea568f7027eddc7d8d8bc9d59c8 # v3.3.1
with:
path: ~/.npm
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node-
- name: npm install
run: npm ci
- name: release
if: success() && endsWith(github.ref, 'master')
run: npx semantic-release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: NPM Install
run: npm ci
- name: NPM Build
run: npm run build
- name: Setup Vault
run: node ./integrationTests/e2e-tls/setup.js
env:
VAULT_HOST: localhost
VAULT_PORT: 8200
VAULTCA: ${{ secrets.VAULTCA }}
VAULT_CLIENT_CERT: ${{ secrets.VAULT_CLIENT_CERT }}
VAULT_CLIENT_KEY: ${{ secrets.VAULT_CLIENT_KEY }}
- name: Test Vault Action (default KV V2)
uses: ./
id: kv-secrets-tls
with:
url: https://localhost:8200
token: ${{ env.VAULT_TOKEN }}
caCertificate: ${{ secrets.VAULTCA }}
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
secrets: |
secret/data/test secret ;
secret/data/test secret | NAMED_SECRET ;
secret/data/nested/test otherSecret ;
- name: Test Vault Action (tlsSkipVerify)
uses: ./
with:
url: https://localhost:8200
token: ${{ env.VAULT_TOKEN }}
tlsSkipVerify: true
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
secrets: |
secret/data/tlsSkipVerify skip ;
- name: Test Vault Action (default KV V1)
uses: ./
with:
url: https://localhost:8200
token: ${{ env.VAULT_TOKEN }}
caCertificate: ${{ secrets.VAULTCA }}
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
secrets: |
my-secret/test altSecret ;
my-secret/test altSecret | NAMED_ALTSECRET ;
my-secret/nested/test otherAltSecret ;
- name: Test Vault Action (cubbyhole)
uses: ./
with:
url: https://localhost:8200
token: ${{ env.VAULT_TOKEN }}
secrets: |
/cubbyhole/test foo ;
/cubbyhole/test zip | NAMED_CUBBYSECRET ;
caCertificate: ${{ secrets.VAULTCA }}
clientCertificate: ${{ secrets.VAULT_CLIENT_CERT }}
clientKey: ${{ secrets.VAULT_CLIENT_KEY }}
- name: Verify Vault Action Outputs
run: npm run test:integration:e2e-tls
env:
OTHER_SECRET_OUTPUT: ${{ steps.kv-secrets-tls.outputs.otherSecret }}
+17
View File
@@ -0,0 +1,17 @@
name: JIRA Sync
on:
issues:
types: [opened, closed, deleted, reopened]
pull_request_target:
types: [opened, closed, reopened]
issue_comment: # Also triggers when commenting on a PR from the conversation view
types: [created]
jobs:
sync:
uses: hashicorp/vault-workflows-common/.github/workflows/jira.yaml@main
secrets:
JIRA_SYNC_BASE_URL: ${{ secrets.JIRA_SYNC_BASE_URL }}
JIRA_SYNC_USER_EMAIL: ${{ secrets.JIRA_SYNC_USER_EMAIL }}
JIRA_SYNC_API_TOKEN: ${{ secrets.JIRA_SYNC_API_TOKEN }}
with:
teams-array: '["ecosystem", "applications-eco"]'
+61
View File
@@ -0,0 +1,61 @@
# This is a sample workflow to help test contributions
# Change the branch name, url and token to fit with your own environment
# To run this locally with act use:
# act workflow_dispatch -j local-test
#
# If you have permissions, you can run this workflow via the GitHub UI.
# Otherwise, use 'on: push' instead of 'on: workflow_dispatch'.
# Don't forget to revert the file changes and invalidate any tokens that were
# committed before opening a pull request.
on: workflow_dispatch
name: local-test
jobs:
local-test:
name: local-test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
- uses: actions/setup-node@64ed1c7eab4cce3362f8c340dee64e5eaeef8f7c # v3.6.0
with:
node-version: '16.14.0'
- name: NPM Install
run: npm ci
- name: NPM Build
run: npm run build
- name: Setup Vault
run: node ./integrationTests/e2e/setup.js
env:
VAULT_HOST: localhost
VAULT_PORT: 8200
- name: Import Secrets
id: import-secrets
# use the local changes
uses: ./
# run against a specific version of vault-action
# uses: hashicorp/vault-action@v2.1.2
with:
url: http://localhost:8200
method: token
token: testtoken
secrets: |
secret/data/test-json-string jsonString;
- name: Check Secrets
run: |
touch secrets.json
echo "${{ steps.import-secrets.outputs.jsonString }}" >> secrets.json
- name: Check json file format
run: |
echo
cat secrets.json
jq -c . < secrets.json
+3
View File
@@ -59,3 +59,6 @@ typings/
# next.js build output
.next
# GoLand IDE project files
.idea
+35
View File
@@ -0,0 +1,35 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "node",
"name": "vscode-jest-tests",
"request": "launch",
"args": [
"--runInBand"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
},
{
"type": "node",
"name": "vscode-integration-tests",
"request": "launch",
"args": [
"--runInBand",
"--config=${workspaceFolder}/integrationTests/basic/jest.config.js"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal",
"internalConsoleOptions": "neverOpen",
"disableOptimisticBPs": true,
"program": "${workspaceFolder}/node_modules/jest/bin/jest"
}
]
}
+152
View File
@@ -0,0 +1,152 @@
## Unreleased
* Add changes here
## 2.7.1 (July 3, 2023)
Bugs:
* Revert [GH-466](https://github.com/hashicorp/vault-action/pull/466) which caused a regression in secrets stored as JSON strings [GH-471](https://github.com/hashicorp/vault-action/pull/471)
## 2.7.0 (June 21, 2023)
Bugs:
* Fix a regression that broke support for secrets in JSON format [GH-466](https://github.com/hashicorp/vault-action/pull/466)
Improvements:
* Fix a warning about outputToken being an unexpected input [GH-461](https://github.com/hashicorp/vault-action/pull/461)
## 2.6.0 (June 7, 2023)
Features:
* Add ability to set the `vault_token` output to contain the Vault token after authentication [GH-441](https://github.com/hashicorp/vault-action/pull/441)
* Add support for userpass and ldap authentication methods [GH-440](https://github.com/hashicorp/vault-action/pull/440)
* Define an output, `errorMessage`, for vault-action's error messages so subsequent steps can read the errors [GH-446](https://github.com/hashicorp/vault-action/pull/446)
Bugs:
* Handle undefined response in getSecrets error handler [GH-431](https://github.com/hashicorp/vault-action/pull/431)
## 2.5.0 (Jan 26th, 2023)
Features:
* Adds ability to automatically decode secrets from base64, hex, and utf8 encodings. [GH-408](https://github.com/hashicorp/vault-action/pull/408)
Improvements:
* Improves error messages for Vault authentication failures [GH-409](https://github.com/hashicorp/vault-action/pull/409)
* bump jest from 28.1.1 to 29.3.1 [GH-397](https://github.com/hashicorp/vault-action/pull/397)
* bump @types/jest from 28.1.3 to 29.2.6 [GH-397](https://github.com/hashicorp/vault-action/pull/397), [GH-413](https://github.com/hashicorp/vault-action/pull/413)
* bump jsrsasign from 10.5.27 to 10.6.1 [GH-401](https://github.com/hashicorp/vault-action/pull/401)
* bump json5 from 2.2.1 to 2.2.3 [GH-404](https://github.com/hashicorp/vault-action/pull/404)
* bump minimatch from 3.0.4 to 3.1.2 [GH-410](https://github.com/hashicorp/vault-action/pull/410)
## 2.4.3 (Nov 8th, 2022)
Improvements:
* bump jest-when from 3.5.1 to 3.5.2 [GH-388](https://github.com/hashicorp/vault-action/pull/388)
* bump semantic-release from 19.0.3 to 19.0.5 [GH-360](https://github.com/hashicorp/vault-action/pull/360)
* bump jsrsasign from 10.5.25 to 10.5.27 [GH-358](https://github.com/hashicorp/vault-action/pull/358)
* bump @actions/core from 1.9.0 to 1.10.0 [GH-371](https://github.com/hashicorp/vault-action/pull/371)
* update runtime to node16 for action [GH-375](https://github.com/hashicorp/vault-action/pull/375)
## 2.4.2 (Aug 15, 2022)
Bugs:
* Errors due to replication delay for tokens will now be retried [GH-333](https://github.com/hashicorp/vault-action/pull/333)
Improvements:
* bump got from 11.5.1 to 11.8.5 [GH-344](https://github.com/hashicorp/vault-action/pull/344)
## 2.4.1 (April 28th, 2022)
Improvements:
* Make secrets parameter optional [GH-299](https://github.com/hashicorp/vault-action/pull/299)
* auth/jwt: make "role" input optional [GH-291](https://github.com/hashicorp/vault-action/pull/291)
* Write a better error message when secret not found [GH-306](https://github.com/hashicorp/vault-action/pull/306)
* bump jest-when from 2.7.2 to 3.5.1 [GH-294](https://github.com/hashicorp/vault-action/pull/294)
* bump node-fetch from 2.6.1 to 2.6.7 [GH-308](https://github.com/hashicorp/vault-action/pull/308)
* bump @types/jest from 26.0.23 to 27.4.1 [GH-297](https://github.com/hashicorp/vault-action/pull/297)
* bump trim-off-newlines from 1.0.1 to 1.0.3 [GH-309](https://github.com/hashicorp/vault-action/pull/309)
* bump moment from 2.28.0 to 2.29.2 [GH-304](https://github.com/hashicorp/vault-action/pull/304)
* bump @types/got from 9.6.11 to 9.6.12 [GH-266](https://github.com/hashicorp/vault-action/pull/266)
## 2.4.0 (October 21st, 2021)
Features:
* GitHub provided JWT auth is now supported [GH-257](https://github.com/hashicorp/vault-action/pull/257)
## 2.3.1 (August 23rd, 2021)
Improvements:
* bump normalize-url from 4.5.0 to 4.5.1 [GH-227](https://github.com/hashicorp/vault-action/pull/227)
* bump path-parse from 1.0.6 to 1.0.7 [GH-239](https://github.com/hashicorp/vault-action/pull/239)
## 2.3.0 (June 23rd, 2021)
Features:
* K8s auth method is now supported [GH-218](https://github.com/hashicorp/vault-action/pull/218)
* Custom auth method mount points is configurable [GH-218](https://github.com/hashicorp/vault-action/pull/218)
## 2.2.0 (May 6th, 2021)
Security:
* multi-line secrets are now properly masked in logs [GH-208](https://github.com/hashicorp/vault-action/pull/208)
[CVE-2021-32074](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-32074)
Features:
* JWT auth method is now supported [GH-188](https://github.com/hashicorp/vault-action/pull/188)
## 2.1.2 (January 21st, 2021)
Bugs:
* fixed bug where newlines were being rendered for multi-line secrets [GH-173](https://github.com/hashicorp/vault-action/pull/173)
## 2.1.1 (December 15th, 2020)
Improvements:
* bump jest from 26.5.0 to 26.6.3 [GH-150](https://github.com/hashicorp/vault-action/pull/150)
* bump semantic-release from 17.1.2 to 17.3.0 [GH-158](https://github.com/hashicorp/vault-action/pull/158)
* bump got from 11.7.0 to 11.8.1 [GH-163](https://github.com/hashicorp/vault-action/pull/163)
* bump @types/jest from 26.0.14 to 26.0.19 [GH-164](https://github.com/hashicorp/vault-action/pull/164)
* bump ini from 1.3.5 to 1.3.8 [GH-167](https://github.com/hashicorp/vault-action/pull/167)
## 2.1.0 (October 6th, 2020)
Features:
* Added `exportToken` to share the Vault token as an environment variable [GH-127](https://github.com/hashicorp/vault-action/pull/127)
Security:
* `action/core` updated to 1.2.6 to address minor CVE [GH-130](https://github.com/hashicorp/vault-action/pull/130)
## 2.0.1 (September 15th, 2020)
Improvements:
* bump node-fetch from 2.6.0 to 2.6.1 [GH-110](https://github.com/hashicorp/vault-action/pull/110)
* bump lodash from 4.17.15 to 4.17.20 [GH-111](https://github.com/hashicorp/vault-action/pull/111)
* bump npm from 6.14.4 to 6.14.8 [GH-112](https://github.com/hashicorp/vault-action/pull/112)
* bump @types/jest from 25.1.5 to 26.0.13 [GH-114](https://github.com/hashicorp/vault-action/pull/114)
* bump @actions/core from 1.2.3 to 1.2.5 [GH-115](https://github.com/hashicorp/vault-action/pull/115)
* bump jest from 25.2.7 to 26.4.2 [GH-116](https://github.com/hashicorp/vault-action/pull/116)
* bump got from 11.5.1 to 11.6.2 [GH-117](https://github.com/hashicorp/vault-action/pull/117)
* update jsonata to version 1.8.3 [GH-118](https://github.com/hashicorp/vault-action/pull/118)
* update dev dependencies [GH-119](https://github.com/hashicorp/vault-action/pull/119)
* update kind-of to version 6.0.3 [GH-120](https://github.com/hashicorp/vault-action/pull/120)
## 2.0.0 (August 24th, 2020)
Features:
* Added TLS and mTLS support [GH-97](https://github.com/hashicorp/vault-action/pull/97)
Improvements:
* Breaking change: removed KV specific parameters and generalized secret UX [GH-102](https://github.com/hashicorp/vault-action/pull/102)
Bugs:
+3
View File
@@ -0,0 +1,3 @@
.PHONY: local-test
local-test:
docker compose down; docker-compose up -d vault && act workflow_dispatch -j local-test
+392 -121
View File
@@ -1,25 +1,39 @@
# vault-action
# Vault GitHub Action
---
**Please note**: We take Vault's security and our users' trust very seriously. If you believe you have found a security issue in Vault or this Vault Action, _please responsibly disclose_ by contacting us at [security@hashicorp.com](mailto:security@hashicorp.com).
---
A helper action for easily pulling secrets from HashiCorp Vault™.
By default, this action pulls from [Version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2/) of the K/V Engine. See examples below for how to [use v1](#using-kv-version-1) as well as [other non-K/V engines](#other-secret-engines).
<!-- TOC -->
- [Example Usage](#example-usage)
- [Authentication method](#authentication-method)
- [Key Syntax](#key-syntax)
- [Vault GitHub Action](#vault-github-action)
- [Example Usage](#example-usage)
- [Authentication Methods](#authentication-methods)
- [JWT with GitHub OIDC Tokens](#jwt-with-github-oidc-tokens)
- [AppRole](#approle)
- [Token](#token)
- [GitHub](#github)
- [JWT with OIDC Provider](#jwt-with-oidc-provider)
- [Kubernetes](#kubernetes)
- [Userpass](#userpass)
- [Ldap](#ldap)
- [Other Auth Methods](#other-auth-methods)
- [Key Syntax](#key-syntax)
- [Simple Key](#simple-key)
- [Set Output Variable Name](#set-output-variable-name)
- [Multiple Secrets](#multiple-secrets)
- [Using K/V version 1](#using-kv-version-1)
- [Custom K/V Engine Path](#custom-kv-engine-path)
- [Other Secret Engines](#other-secret-engines)
- [Adding Extra Headers](#adding-extra-headers)
- [Vault Enterprise Features](#vault-enterprise-features)
- [Other Secret Engines](#other-secret-engines)
- [Adding Extra Headers](#adding-extra-headers)
- [HashiCorp Cloud Platform or Vault Enterprise](#hashicorp-cloud-platform-or-vault-enterprise)
- [Namespace](#namespace)
- [Reference](#reference)
- [Masking - Hiding Secrets from Logs](#masking---hiding-secrets-from-logs)
- [Reference](#reference)
- [Masking - Hiding Secrets from Logs](#masking---hiding-secrets-from-logs)
- [Normalization](#normalization)
- [Contributing](#contributing)
<!-- /TOC -->
@@ -32,65 +46,275 @@ jobs:
steps:
# ...
- name: Import Secrets
uses: RichiCoder1/vault-action
id: import-secrets
uses: hashicorp/vault-action@v2
with:
url: https://vault.mycompany.com:8200
token: ${{ secrets.VaultToken }}
token: ${{ secrets.VAULT_TOKEN }}
caCertificate: ${{ secrets.VAULT_CA_CERT }}
secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
ci npm_token
secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
secret/data/ci npm_token
# ...
```
## Authentication method
While most workflows will likely use a vault token, you can also use an `approle` to authenticate with Vault. You can configure which by using the `method` parameter:
- **token**: (by default) you must provide a `token` parameter
Retrieved secrets are available as environment variables or outputs for subsequent steps:
```yaml
...
with:
url: https://vault.mycompany.com:8200
token: ${{ secrets.VaultToken }}
#...
- name: Step following 'Import Secrets'
run: |
ACCESS_KEY_ID = "${{ env.AWS_ACCESS_KEY_ID }}"
SECRET_ACCESS_KEY = "${{ steps.import-secrets.outputs.AWS_SECRET_ACCESS_KEY }}"
# ...
```
- **approle**: you must provide a `roleId` & `secretId` parameter
If your project needs a format other than env vars and step outputs, you can use additional steps to transform them into the desired format.
For example, a common pattern is to save all the secrets in a JSON file:
```yaml
#...
- name: Step following 'Import Secrets'
run: |
touch secrets.json
echo "${{ toJson(steps.import-secrets.outputs) }}" >> secrets.json
# ...
```
Which with our example would yield a file containing:
```json
{
"ACCESS_KEY_ID": "MY_KEY_ID",
"SECRET_ACCESS_KEY": "MY_SECRET_KEY",
"NPM_TOKEN": "MY_NPM_TOKEN"
}
```
Note that all secrets are masked so programs need to read the file themselves otherwise all values will be replaced with a `***` placeholder.
## Authentication Methods
Consider using a [Vault authentication method](https://www.vaultproject.io/docs/auth) such as the JWT auth method with
[GitHub OIDC tokens](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect) or the AppRole auth method. You can configure which by using the `method` parameter.
### JWT with GitHub OIDC Tokens
You can configure trust between a GitHub Actions workflow
and Vault using the
[GitHub's OIDC provider](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect).
Each GitHub Actions workflow receives an auto-generated OIDC token with claims
to establish the identity of the workflow.
__Vault Configuration__
<details>
<summary>Click to toggle instructions for configuring Vault.</summary>
Set up Vault with the [JWT auth method](https://www.vaultproject.io/api/auth/jwt#configure).
Pass the following parameters to your auth method configuration:
- `oidc_discovery_url`: `https://token.actions.githubusercontent.com`
- `bound_issuer`: `https://token.actions.githubusercontent.com`
Configure a [Vault role](https://www.vaultproject.io/api/auth/jwt#create-role) for the auth method.
- `role_type`: `jwt`
- `bound_audiences`: `"https://github.com/<org>"`. Update this parameter if
you change the `aud` claim in the GitHub OIDC token via the
`jwtGithubAudience` parameter in the action config.
- `user_claim`: Set this to a claim name (e.g., `repository`) in the
[GitHub OIDC token](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#understanding-the-oidc-token).
- `bound_claims` OR `bound_subject`: match on [GitHub subject claims](https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#example-subject-claims).
- For wildcard (non-exact) matches, use `bound_claims`.
- `bound_claims_type`: `glob`
- `bound_claims`: JSON object. Maps one or more claim names to corresponding wildcard values.
```json
{"sub": "repo:<orgName>/*"}
```
- For exact matches, use `bound_subject`.
- `bound_claims_type`: `string`
- `bound_subject`: Must exactly match the `sub` claim in the OIDC token.
```plaintext
repo:<orgName/repoName>:ref:refs/heads/branchName
```
</details>
__GitHub Actions Workflow__
In the GitHub Actions workflow, the workflow needs permissions to read contents
and write the ID token.
```yaml
jobs:
retrieve-secret:
permissions:
contents: read
id-token: write
```
In the action, provide the name of the Vault role you created to the `role` parameter.
You can optionally set the `jwtGithubAudience` parameter to change the `aud`
claim from its default.
```yaml
...
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
role: <Vault JWT Auth Role Name>
method: jwt
jwtGithubAudience: sigstore # set the GitHub token's aud claim
```
### AppRole
The [AppRole auth method](https://www.vaultproject.io/docs/auth/approle) allows
your GitHub Actions workflow to authenticate to Vault with a pre-defined role.
Set the role ID and secret ID as GitHub secrets and pass them to the
`roleId` and `secretId` parameters.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: approle
roleId: ${{ secrets.roleId }}
secretId: ${{ secrets.secretId }}
roleId: ${{ secrets.VAULT_ROLE_ID }}
secretId: ${{ secrets.VAULT_SECRET_ID }}
```
- **github**: you must provide the github token as `githubToken`
### Token
For the default method of authenticating to Vault,
use a [Vault token](https://www.vaultproject.io/docs/concepts/tokens).
Set the Vault token as a GitHub secret and pass
it to the `token` parameter.
```yaml
...
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
token: ${{ secrets.VAULT_TOKEN }}
```
### GitHub
The [GitHub auth method](https://www.vaultproject.io/docs/auth/github)
requires `read:org` permissions for authentication. The auto-generated `GITHUB_TOKEN`
created for projects does not have these permissions and GitHub does not allow this
token's permissions to be modified. A new GitHub Token secret must be created with
`read:org` permissions to use this authentication method.
Pass the GitHub token as a GitHub secret into the `githubToken` parameter.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: github
githubToken: ${{ secrets.GITHUB_TOKEN }}
```
If any other method is specified and you provide an `authPayload`, the action will attempt to `POST` to `auth/${method}/login` with the provided payload and parse out the client token.
### JWT with OIDC Provider
You can configure trust between your own OIDC Provider and Vault
with the JWT auth method. Provide a `role` & `jwtPrivateKey` parameters,
additionally you can pass `jwtKeyPassword` & `jwtTtl` parameters
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: jwt
role: <Vault JWT Auth Role Name>
jwtPrivateKey: ${{ secrets.JWT_PRIVATE_KEY }}
jwtKeyPassword: ${{ secrets.JWT_KEY_PASS }}
jwtTtl: 3600 # 1 hour, default value
```
### Kubernetes
Consider the [Kubernetes auth method](https://www.vaultproject.io/docs/auth/kubernetes)
when using self-hosted runners on Kubernetes. You must provide the `role` parameter
for the Vault role associated with the Kubernetes auth method.
You can optionally override the `kubernetesTokenPath` parameter for
custom-mounted serviceAccounts.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: kubernetes
role: <Vault Kubernetes Auth Role Name>
kubernetesTokenPath: /var/run/secrets/kubernetes.io/serviceaccount/token # default token path
```
### Userpass
The [Userpass auth method](https://developer.hashicorp.com/vault/docs/auth/userpass) allows
your GitHub Actions workflow to authenticate to Vault with a username and password.
Set the username and password as GitHub secrets and pass them to the
`username` and `password` parameters.
This is not the same as ldap or okta auth methods.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: userpass
username: ${{ secrets.VAULT_USERNAME }}
password: ${{ secrets.VAULT_PASSWORD }}
```
### Ldap
The [LDAP auth method](https://developer.hashicorp.com/vault/docs/auth/ldap) allows
your GitHub Actions workflow to authenticate to Vault with a username and password inturn verfied with ldap servers.
Set the username and password as GitHub secrets and pass them to the
`username` and `password` parameters.
```yaml
with:
url: https://vault.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: ldap
username: ${{ secrets.VAULT_USERNAME }}
password: ${{ secrets.VAULT_PASSWORD }}
```
### Other Auth Methods
If any other method is specified and you provide an `authPayload`, the action will
attempt to `POST` to `auth/${method}/login` with the provided payload and parse out the client token.
## Key Syntax
The `secrets` parameter is a set of multiple secret requests separated by the `;` character.
Each secret request is comprised of the `path` and the `key` of the desired secret, and optionally the desired Env Var output name.
Each secret request consists of the `path` and the `key` of the desired secret, and optionally the desired Env Var output name.
Note that the selector is using [JSONata](https://docs.jsonata.org/overview.html) and certain characters in keys may need to be escaped.
```raw
{{ Secret Path }} {{ Secret Key }} | {{ Output Variable Name }}
{{ Secret Path }} {{ Secret Key or Selector }} | {{ Env/Output Variable Name }}
```
### Simple Key
To retrieve a key `npmToken` from path `ci` that has value `somelongtoken` from vault you could do:
To retrieve a key `npmToken` from path `secret/data/ci` that has value `somelongtoken` from vault you could do:
```yaml
with:
secrets: ci npmToken
secrets: secret/data/ci npmToken
```
`vault-action` will automatically normalize the given secret selector key, and set the follow as environment variables for the following steps in the current job:
@@ -109,7 +333,6 @@ steps:
# Import config...
- name: Sensitive Operation
run: "my-cli --token '${{ steps.secrets.outputs.npmToken }}'"
```
_**Note:** If you'd like to only use outputs and disable automatic environment variables, you can set the `exportEnv` option to `false`._
@@ -120,7 +343,7 @@ However, if you want to set it to a specific name, say `NPM_TOKEN`, you could do
```yaml
with:
secrets: ci npmToken | NPM_TOKEN
secrets: secret/data/ci npmToken | NPM_TOKEN
```
With that, `vault-action` will now use your requested name and output:
@@ -147,50 +370,18 @@ This action can take multi-line input, so say you had your AWS keys stored in a
```yaml
with:
secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY
secret/data/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
secret/data/ci/aws secretKey | AWS_SECRET_ACCESS_KEY
```
### Using K/V version 1
By default, `vault-action` expects a K/V engine using [version 2](https://www.vaultproject.io/docs/secrets/kv/kv-v2.html).
In order to work with a [v1 engine](https://www.vaultproject.io/docs/secrets/kv/kv-v1/), the `kv-version` parameter may be passed:
```yaml
with:
kv-version: 1
```
## Custom K/V Engine Path
When you enable the K/V Engine, by default it's placed at the path `secret`, so a secret named `ci` will be accessed from `secret/ci`. However, [if you enabled the secrets engine using a custom `path`](https://www.vaultproject.io/docs/commands/secrets/enable/#inlinecode--path-4), you
can pass it as follows:
```yaml
with:
path: my-secrets
secrets: ci npmToken
```
This way, the `ci` secret in the example above will be retrieved from `my-secrets/ci`.
## Other Secret Engines
While this action primarily supports the K/V engine, it is possible to request secrets from other engines in Vault.
Vault Action currently supports retrieving secrets from any engine where secrets
are retrieved via `GET` requests. This means secret engines such as PKI are currently
not supported due to their requirement of sending parameters along with the request
(such as `common_name`).
To do so when specifying the `Secret Path`, just append a leading formard slash (`/`) and specify the path as described in the Vault API documentation.
For example, to retrieve a stored secret from the [`cubbyhole` engine](https://www.vaultproject.io/api-docs/secret/cubbyhole/), assuming you have a stored secret at the path `foo` with the contents:
```json
{
"foo": "bar",
"zip": "zap"
}
```
You could request the contents like so:
For example, to request a secret from the `cubbyhole` secret engine:
```yaml
with:
@@ -218,21 +409,6 @@ steps:
run: "my-cli --token '${{ steps.secrets.outputs.MY_KEY }}'"
```
Secrets pulled from the same `Secret Path` are cached by default. So if you, for example, are using the `aws` engine and retrieve a key, only a single key for a given path is returned.
e.g.:
```yaml
with:
secrets: |
/aws/creds/ci access_key | AWS_ACCESS_KEY_ID ;
/aws/creds/ci secret_key | AWS_SECRET_ACCESS_KEY
```
would work fine.
NOTE: The `Secret Key` is pulled from the `data` property of the response.
## Adding Extra Headers
If you ever need to add extra headers to the vault request, say if you need to authenticate with a firewall, all you need to do is set `extraHeaders`:
@@ -240,58 +416,153 @@ If you ever need to add extra headers to the vault request, say if you need to a
```yaml
with:
secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY
secret/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
secret/ci/aws secretKey | AWS_SECRET_ACCESS_KEY
extraHeaders: |
X-Secure-Id: ${{ secrets.SECURE_ID }}
X-Secure-Secret: ${{ secrets.SECURE_SECRET }}
```
This will automatically add the `x-secure-id` and `x-secure-secret` headers to every request to vault.
This will automatically add the `x-secure-id` and `x-secure-secret` headers to every request to Vault.
## Vault Enterprise Features
## HashiCorp Cloud Platform or Vault Enterprise
If you are using [HCP Vault](https://cloud.hashicorp.com/products/vault)
or Vault Enterprise, you may need additional parameters in
your GitHub Actions workflow.
### Namespace
If you need to retrieve secrets from a specific vault namespace, all that's required is an additional parameter specifying the namespace.
If you need to retrieve secrets from a specific Vault namespace, set the `namespace`
parameter specifying the namespace. In HCP Vault, the namespace defaults to `admin`.
```yaml
steps:
# ...
- name: Import Secrets
uses: RichiCoder1/vault-action
uses: hashicorp/vault-action
with:
url: https://vault-enterprise.mycompany.com:8200
caCertificate: ${{ secrets.VAULT_CA_CERT }}
method: token
token: ${{ secrets.VaultToken }}
namespace: ns1
token: ${{ secrets.VAULT_TOKEN }}
namespace: admin
secrets: |
ci/aws accessKey | AWS_ACCESS_KEY_ID ;
ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
ci npm_token
secret/ci/aws accessKey | AWS_ACCESS_KEY_ID ;
secret/ci/aws secretKey | AWS_SECRET_ACCESS_KEY ;
secret/ci npm_token
```
## Reference
Here is all the inputs available through `with`:
Here are all the inputs available through `with`:
| Input | Description | Default | Required |
| -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
| `url` | The URL for the vault endpoint | | ✔ |
| `secrets` | A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details | | |
| `namespace` | The Vault namespace from which to query secrets. Vault Enterprise only, unset by default | | |
| `path` | The path of a non-default K/V engine | | |
| `kv-version` | The version of the K/V engine to use. | `2` | |
| `method` | The method to use to authenticate with Vault. | `token` | |
| `token` | The Vault Token to be used to authenticate with Vault | | |
| `roleId` | The Role Id for App Role authentication | | |
| `secretId` | The Secret Id for App Role authentication | | |
| `githubToken` | The Github Token to be used to authenticate with Vault | | |
| `authPayload` | The JSON payload to be sent to Vault when using a custom authentication method. | | |
| `extraHeaders` | A string of newline separated extra headers to include on every request. | | |
| `exportEnv` | Whether or not export secrets as environment variables. | `true` | |
| Input | Description | Default | Required |
| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------- |
| `url` | The URL for the vault endpoint | | ✔ |
| `secrets` | A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details | | |
| `namespace` | The Vault namespace from which to query secrets. Vault Enterprise only, unset by default | | |
| `method` | The method to use to authenticate with Vault. | `token` | |
| `role` | Vault role for specified auth method | | |
| `path` | Custom vault path, if the auth method was enabled at a different path | | |
| `token` | The Vault Token to be used to authenticate with Vault | | |
| `roleId` | The Role Id for App Role authentication | | |
| `secretId` | The Secret Id for App Role authentication | | |
| `githubToken` | The Github Token to be used to authenticate with Vault | | |
| `jwtPrivateKey` | Base64 encoded Private key to sign JWT | | |
| `jwtKeyPassword` | Password for key stored in jwtPrivateKey (if needed) | | |
| `jwtGithubAudience` | Identifies the recipient ("aud" claim) that the JWT is intended for |`sigstore`| |
| `jwtTtl` | Time in seconds, after which token expires | | 3600 |
| `kubernetesTokenPath` | The path to the service-account secret with the jwt token for kubernetes based authentication |`/var/run/secrets/kubernetes.io/serviceaccount/token` | |
| `username` | The username of the user to log in to Vault as. Available to both Userpass and LDAP auth methods | | |
| `password` | The password of the user to log in to Vault as. Available to both Userpass and LDAP auth methods | | |
| `authPayload` | The JSON payload to be sent to Vault when using a custom authentication method. | | |
| `extraHeaders` | A string of newline separated extra headers to include on every request. | | |
| `exportEnv` | Whether or not export secrets as environment variables. | `true` | |
| `exportToken` | Whether or not export Vault token as environment variables (i.e VAULT_TOKEN). | `false` | |
| `outputToken` | Whether or not to set the `vault_token` output to contain the Vault token after authentication. | `false` | |
| `caCertificate` | Base64 encoded CA certificate the server certificate was signed with. | | |
| `clientCertificate` | Base64 encoded client certificate the action uses to authenticate with Vault when mTLS is enabled. | | |
| `clientKey` | Base64 encoded client key the action uses to authenticate with Vault when mTLS is enabled. | | |
| `tlsSkipVerify` | When set to true, disables verification of server certificates when testing the action. | `false` | |
## Masking - Hiding Secrets from Logs
This action uses GitHub Action's built-in masking, so all variables will automatically be masked (aka hidden) if printed to the console or to logs.
**This only obscures secrets from output logs.** If someone has the ability to edit your workflows, then they are able to read and therefore write secrets to somewhere else just like normal GitHub Secrets.
## Normalization
To make it simpler to consume certain secrets as env vars, if no Env/Output Var Name is specified `vault-action` will replace and `.` chars with `__`, remove any other non-letter or number characters. If you're concerned about the result, it's recommended to provide an explicit Output Var Key.
## Contributing
If you wish to contribute to this project, the following dependencies are recommended for local development:
- [npm](https://docs.npmjs.com/downloading-and-installing-node-js-and-npm) to install dependencies, build project and run tests
- [docker](https://docs.docker.com/get-docker/) to run the pre-configured vault containers for acceptance tests
- [docker-compose](https://docs.docker.com/compose/) to spin up the pre-configured vault containers for acceptance tests
- [act](https://github.com/nektos/act) to run the vault-action locally
### Build
Use npm to install dependencies and build the project:
```sh
$ npm install && npm run build
```
### Vault test instance
The Github Action needs access to a working Vault instance to function.
Multiple docker configurations are available via the docker-compose.yml file to run containers compatible with the various acceptance test suites.
```sh
$ docker-compose up -d vault # Choose one of: vault, vault-enterprise, vault-tls depending on which tests you would like to run
```
Instead of using one of the dockerized instance, you can also use your own local or remote Vault instance by exporting these environment variables:
```sh
$ export VAULT_HOST=<YOUR VAULT CLUSTER LOCATION> # localhost if undefined
$ export VAULT_PORT=<YOUR VAULT PORT> # 8200 if undefined
$ export VAULT_TOKEN=<YOUR VAULT TOKEN> # testtoken if undefined
```
### Running unit tests
Unit tests can be executed at any time with no dependencies or prior setup.
```sh
$ npm test
```
### Running acceptance tests
With a succesful build to take your local changes into account and a working Vault instance configured, you can now run acceptance tests to validate if any regressions were introduced.
```sh
$ npm run test:integration:basic # Choose one of: basic, enterprise, e2e, e2e-tls
```
### Running the action locally
You can use the [act](https://github.com/nektos/act) command to test your
changes locally.
Edit the ./.github/workflows/local-test.yaml file and add any steps necessary
to test your changes. You may have to additionally edit the Vault url, token
and secret path if you are not using one of the provided containerized
instances. The `local-test` job will call the ./integrationTests/e2e/setup.js
script to bootstrap your local Vault instance with secrets.
Run your feature branch locally:
```sh
act workflow_dispatch -j local-test
```
Or use the provided make target which will also spin up a Vault container:
```sh
make local-test
```
+56 -10
View File
@@ -1,26 +1,25 @@
name: 'Vault Secrets'
description: 'A Github Action that allows you to consume the v2 K/V backend of HashiCorp Vault™ as secure environment variables'
description: 'A Github Action that allows you to consume HashiCorp Vault™ secrets as secure environment variables'
inputs:
url:
description: 'The URL for the vault endpoint'
required: true
secrets:
description: 'A semicolon-separated list of secrets to retrieve. These will automatically be converted to environmental variable keys. See README for more details'
required: true
required: false
namespace:
description: 'The Vault namespace from which to query secrets. Vault Enterprise only, unset by default'
required: false
path:
description: 'The path of a non-default K/V engine'
required: false
kv-version:
description: 'The version of the K/V engine to use.'
default: '2'
required: false
method:
description: 'The method to use to authenticate with Vault.'
default: 'token'
required: false
role:
description: 'Vault role for specified auth method'
required: false
path:
description: 'Custom Vault path, if the auth method was mounted at a different path'
required: false
token:
description: 'The Vault Token to be used to authenticate with Vault'
required: false
@@ -33,6 +32,16 @@ inputs:
githubToken:
description: 'The Github Token to be used to authenticate with Vault'
required: false
kubernetesTokenPath:
description: 'The path to the Kubernetes service account secret'
required: false
default: '/var/run/secrets/kubernetes.io/serviceaccount/token'
username:
description: 'The username of the user to log in to Vault as. Available to both Userpass and LDAP auth methods'
required: false
password:
description: 'The password of the user to log in to Vault as. Available to both Userpass and LDAP auth methods'
required: false
authPayload:
description: 'The JSON payload to be sent to Vault when using a custom authentication method.'
required: false
@@ -43,8 +52,45 @@ inputs:
description: 'Whether or not export secrets as environment variables.'
default: 'true'
required: false
exportToken:
description: 'Whether or not export Vault token as environment variables.'
default: 'false'
required: false
outputToken:
description: 'Whether or not to set the `vault_token` output to contain the Vault token after authentication.'
default: 'false'
required: false
caCertificate:
description: 'Base64 encoded CA certificate to verify the Vault server certificate.'
required: false
clientCertificate:
description: 'Base64 encoded client certificate for mTLS communication with the Vault server.'
required: false
clientKey:
description: 'Base64 encoded client key for mTLS communication with the Vault server.'
required: false
tlsSkipVerify:
description: 'When set to true, disables verification of the Vault server certificate. Setting this to true in production is not recommended.'
required: false
default: 'false'
jwtPrivateKey:
description: 'Base64 encoded Private key to sign JWT'
required: false
jwtKeyPassword:
description: 'Password for key stored in jwtPrivateKey (if needed)'
required: false
jwtGithubAudience:
description: 'Identifies the recipient ("aud" claim) that the JWT is intended for'
required: false
jwtTtl:
description: 'Time in seconds, after which token expires'
required: false
default: 3600
secretEncodingType:
description: 'The encoding type of the secret to decode. If not specified, the secret will not be decoded. Supported values: base64, hex, utf8'
required: false
runs:
using: 'node12'
using: 'node16'
main: 'dist/index.js'
branding:
icon: 'unlock'
+17750 -4799
View File
File diff suppressed because one or more lines are too long
+20 -4
View File
@@ -2,16 +2,32 @@
version: "3.0"
services:
vault:
image: vault:1.3.0
image: hashicorp/vault:latest
environment:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
ports:
- 8200:8200
privileged: true
vault-enterprise:
image: hashicorp/vault-enterprise:1.3.0_ent
image: hashicorp/vault-enterprise:latest
environment:
VAULT_DEV_ROOT_TOKEN_ID: testtoken
VAULT_LICENSE: ${VAULT_LICENSE_CI}
ports:
- 8201:8200
privileged: true
- 8200:8200
privileged: true
vault-tls:
image: hashicorp/vault:latest
hostname: vault-tls
environment:
VAULT_CAPATH: /etc/vault/ca.crt
ports:
- 8200:8200
privileged: true
volumes:
- ${PWD}/integrationTests/e2e-tls/configs:/etc/vault
- vault-data:/var/lib/vault:rw
entrypoint: vault server -config=/etc/vault/config.hcl
volumes:
vault-data:
+134
View File
@@ -0,0 +1,134 @@
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const core = require('@actions/core');
const got = require('got');
const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('authenticate with approle', () => {
let roleId;
let secretId;
beforeAll(async () => {
try {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': vaultToken,
},
});
await got(`${vaultUrl}/v1/secret/data/approle-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUPERSECRET_WITH_APPROLE',
},
},
});
// Enable approle
try {
await got(`${vaultUrl}/v1/sys/auth/approle`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
type: 'approle'
},
});
} catch (error) {
const {response} = error;
if (response.statusCode === 400 && response.body.includes("path is already in use")) {
// Approle might already be enabled from previous test runs
} else {
throw error;
}
}
// Create policies
await got(`${vaultUrl}/v1/sys/policies/acl/test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
"name":"test",
"policy":"path \"auth/approle/*\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"auth/approle/role/my-role/role-id\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\npath \"auth/approle/role/my-role/secret-id\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n\npath \"secret/data/*\" {\n capabilities = [\"list\"]\n}\npath \"secret/metadata/*\" {\n capabilities = [\"list\"]\n}\n\npath \"secret/data/approle-test\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/metadata/approle-test\" {\n capabilities = [\"read\", \"list\"]\n}\n"
},
});
// Create approle
await got(`${vaultUrl}/v1/auth/approle/role/my-role`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
policies: 'test'
},
});
// Get role-id
const roldIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/role-id`, {
headers: {
'X-Vault-Token': vaultToken
},
responseType: 'json',
});
roleId = roldIdResponse.body.data.role_id;
// Get secret-id
const secretIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/secret-id`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
responseType: 'json',
});
secretId = secretIdResponse.body.data.secret_id;
} catch(err) {
console.warn('Create approle', err.response.body);
throw err;
}
});
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('method', expect.anything())
.mockReturnValueOnce('approle');
when(core.getInput)
.calledWith('roleId', expect.anything())
.mockReturnValueOnce(roleId);
when(core.getInput)
.calledWith('secretId', expect.anything())
.mockReturnValueOnce(secretId);
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(secrets);
}
it('authenticate with approle', async() => {
mockInput('secret/data/approle-test secret');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_WITH_APPROLE');
})
});
+72 -41
View File
@@ -8,20 +8,21 @@ const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('integration', () => {
beforeAll(async () => {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
});
await got(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
data: {
@@ -33,21 +34,33 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
data: {
otherSecret: 'OTHERSUPERSECRET',
"other-Secret-dash": 'OTHERSUPERSECRET',
},
}
});
await got(`${vaultUrl}/v1/secret/data/foobar`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
fookv2: 'bar',
},
}
});
// Enable custom secret engine
try {
await got(`${vaultUrl}/v1/sys/mounts/my-secret`, {
await got(`${vaultUrl}/v1/sys/mounts/secret-kv1`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
type: 'kv'
@@ -62,23 +75,33 @@ describe('integration', () => {
}
}
await got(`${vaultUrl}/v1/my-secret/test`, {
await got(`${vaultUrl}/v1/secret-kv1/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
secret: 'CUSTOMSECRET',
}
});
await got(`${vaultUrl}/v1/my-secret/nested/test`, {
await got(`${vaultUrl}/v1/secret-kv1/foobar`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
otherSecret: 'OTHERCUSTOMSECRET',
fookv1: 'bar',
}
});
await got(`${vaultUrl}/v1/secret-kv1/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
"other-Secret-dash": 'OTHERCUSTOMSECRET',
},
});
});
@@ -87,34 +110,30 @@ describe('integration', () => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('url')
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
when(core.getInput)
.calledWith('token')
.mockReturnValueOnce('testtoken');
.calledWith('token', expect.anything())
.mockReturnValueOnce(vaultToken);
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets')
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(secrets);
}
function mockEngineName(name) {
when(core.getInput)
.calledWith('path')
.mockReturnValueOnce(name);
}
it('prints a nice error message when secret not found', async () => {
mockInput(`secret/data/test secret ;
secret/data/test secret | NAMED_SECRET ;
secret/data/notFound kehe | NO_SIR ;`);
function mockVersion(version) {
when(core.getInput)
.calledWith('kv-version')
.mockReturnValueOnce(version);
}
expect(exportSecrets()).rejects.toEqual(Error(`Unable to retrieve result for "secret/data/notFound" because it was not found: {"errors":[]}`));
})
it('get simple secret', async () => {
mockInput('test secret');
mockInput('secret/data/test secret');
await exportSecrets();
@@ -122,7 +141,7 @@ describe('integration', () => {
});
it('re-map secret', async () => {
mockInput('test secret | TEST_KEY');
mockInput('secret/data/test secret | TEST_KEY');
await exportSecrets();
@@ -130,18 +149,18 @@ describe('integration', () => {
});
it('get nested secret', async () => {
mockInput('nested/test otherSecret');
mockInput(`secret/data/nested/test "other-Secret-dash"`);
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET');
expect(core.exportVariable).toBeCalledWith('OTHERSECRETDASH', 'OTHERSUPERSECRET');
});
it('get multiple secrets', async () => {
mockInput(`
test secret ;
test secret | NAMED_SECRET ;
nested/test otherSecret ;`);
secret/data/test secret ;
secret/data/test secret | NAMED_SECRET ;
secret/data/nested/test "other-Secret-dash" ;`);
await exportSecrets();
@@ -149,13 +168,19 @@ describe('integration', () => {
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET');
expect(core.exportVariable).toBeCalledWith('NAMED_SECRET', 'SUPERSECRET');
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERSUPERSECRET');
expect(core.exportVariable).toBeCalledWith('OTHERSECRETDASH', 'OTHERSUPERSECRET');
});
it('leading slash kvv2', async () => {
mockInput('/secret/data/foobar fookv2');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('FOOKV2', 'bar');
});
it('get secret from K/V v1', async () => {
mockInput('test secret');
mockEngineName('my-secret');
mockVersion('1');
mockInput('secret-kv1/test secret');
await exportSecrets();
@@ -163,13 +188,19 @@ describe('integration', () => {
});
it('get nested secret from K/V v1', async () => {
mockInput('nested/test otherSecret');
mockEngineName('my-secret');
mockVersion('1');
mockInput('secret-kv1/nested/test "other-Secret-dash"');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('OTHERSECRET', 'OTHERCUSTOMSECRET');
expect(core.exportVariable).toBeCalledWith('OTHERSECRETDASH', 'OTHERCUSTOMSECRET');
});
it('leading slash kvv1', async () => {
mockInput('/secret-kv1/foobar fookv1');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('FOOKV1', 'bar');
});
describe('generic engines', () => {
@@ -177,7 +208,7 @@ describe('integration', () => {
await got(`${vaultUrl}/v1/cubbyhole/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
foo: "bar",
+256
View File
@@ -0,0 +1,256 @@
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const core = require('@actions/core');
const rsasign = require('jsrsasign');
const {
privateRsaKey,
privateRsaKeyBase64,
publicRsaKey
} = require('./rsa_keys');
const got = require('got');
const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
/**
* Returns Github OIDC response mock
* @param {string} aud Audience claim
* @returns {string}
*/
function mockGithubOIDCResponse(aud= "https://github.com/hashicorp/vault-action") {
const alg = 'RS256';
const header = { alg: alg, typ: 'JWT' };
const now = rsasign.KJUR.jws.IntDate.getNow();
const payload = {
jti: "unique-id",
sub: "repo:hashicorp/vault-action:ref:refs/heads/main",
aud,
ref: "refs/heads/main",
sha: "commit-sha",
repository: "hashicorp/vault-action",
repository_owner: "hashicorp",
run_id: "1",
run_number: "1",
run_attempt: "1",
actor: "github-username",
workflow: "Workflow Name",
head_ref: "",
base_ref: "",
event_name: "push",
ref_type: "branch",
job_workflow_ref: "hashicorp/vault-action/.github/workflows/workflow.yml@refs/heads/main",
iss: 'vault-action',
iat: now,
nbf: now,
exp: now + 3600,
};
const decryptedKey = rsasign.KEYUTIL.getKey(privateRsaKey);
return rsasign.KJUR.jws.JWS.sign(alg, JSON.stringify(header), JSON.stringify(payload), decryptedKey);
}
// The sign call inside this function takes a while to run, so cache the default JWT in a constant.
const defaultGithubJwt = mockGithubOIDCResponse();
describe('jwt auth', () => {
beforeAll(async () => {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': vaultToken,
},
});
try {
await got(`${vaultUrl}/v1/sys/auth/jwt`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
type: 'jwt'
}
});
} catch (error) {
const {response} = error;
if (response.statusCode === 400 && response.body.includes("path is already in use")) {
// Auth method might already be enabled from previous test runs
} else {
throw error;
}
}
await got(`${vaultUrl}/v1/sys/policy/reader`, {
method: 'PUT',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
policy: `
path "*" {
capabilities = ["read"]
}
`
}
});
await got(`${vaultUrl}/v1/auth/jwt/config`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
jwt_validation_pubkeys: publicRsaKey,
default_role: "default"
}
});
await got(`${vaultUrl}/v1/auth/jwt/role/default`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
role_type: 'jwt',
bound_audiences: null,
bound_claims: {
iss: 'vault-action'
},
user_claim: 'iss',
policies: ['reader']
}
});
await got(`${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUPERSECRET',
},
},
});
});
describe('authenticate with private key', () => {
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
when(core.getInput)
.calledWith('method', expect.anything())
.mockReturnValueOnce('jwt');
when(core.getInput)
.calledWith('jwtPrivateKey', expect.anything())
.mockReturnValueOnce(privateRsaKeyBase64);
when(core.getInput)
.calledWith('role', expect.anything())
.mockReturnValueOnce('default');
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce('secret/data/test secret');
});
it('successfully authenticates', async () => {
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET');
});
});
describe('authenticate with Github OIDC', () => {
beforeAll(async () => {
await got(`${vaultUrl}/v1/auth/jwt/role/default-sigstore`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
role_type: 'jwt',
bound_audiences: null,
bound_claims: {
iss: 'vault-action',
aud: 'sigstore',
},
user_claim: 'iss',
policies: ['reader']
}
});
})
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
when(core.getInput)
.calledWith('method', expect.anything())
.mockReturnValueOnce('jwt');
when(core.getInput)
.calledWith('jwtPrivateKey', expect.anything())
.mockReturnValueOnce('');
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce('secret/data/test secret');
});
it('successfully authenticates', async () => {
when(core.getInput)
.calledWith('role', expect.anything())
.mockReturnValueOnce('default');
when(core.getIDToken)
.calledWith(undefined)
.mockReturnValueOnce(defaultGithubJwt);
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET');
});
it('successfully authenticates with `jwtGithubAudience` set to `sigstore`', async () => {
when(core.getInput)
.calledWith('role', expect.anything())
.mockReturnValueOnce('default-sigstore');
when(core.getInput)
.calledWith('jwtGithubAudience', expect.anything())
.mockReturnValueOnce('sigstore');
when(core.getIDToken)
.calledWith(expect.anything())
.mockReturnValueOnce(mockGithubOIDCResponse('sigstore'));
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET');
})
it('successfully authenticates as default role without specifying it', async () => {
when(core.getInput)
.calledWith('role', expect.anything())
.mockReturnValueOnce(null);
when(core.getIDToken)
.calledWith(undefined)
.mockReturnValueOnce(defaultGithubJwt);
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET');
})
});
});
+49
View File
@@ -0,0 +1,49 @@
const privateRsaKey = `
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEArcch89X6VuWj/CQtVfaCXUl0Pcv8IJRgICN8X+3zFNrbiTdh
kTtrOhdkbEU5VaW6aQiCXX5+4C1T2sXXXT682XJhIjKepyX3aY50Fh59pLCciwAK
c5wPy3PVMOhup15u9reiQKxps1SNrqVLyZNjha83qbN9IJvQcMnQghAjjPUeGPMa
MMzG1GOnuPOWIiM2kxqRpbugwwTyuepPnakmfkWqVtMIRprPLY6d3liDIUSSRZ7o
6vbmgeF+9U4DyaimKVNngrmi+mW0OnyH1eJYLrJEY9tZaRF8xraMZiOcBcyAt6S/
TS29HttJ6+zlhcWx34fItEZ8jA5gzhTmspOY8QIDAQABAoIBAQCncXT5qnipOmSk
E4fLiNdcY+aplN+/1Lg6v3acSH8s3SUkNkTA1+wd8WRGHv171VCk3BohVD2UbJib
+H3nzwfQzjFh7jyI+kBHaYfZuE+AXNy54rQvaXSeqWIG2i+k/Y0WFSM2BetjbFmI
qqU3+dive4G69sPeo8RYqV1LtZlLu11j1K1sptcmMi75/cFAB6/uURapNLI978sr
cIaOV2BbLs4Yk7ji4YAtpvL+mky9KF56QAsLspBKgsU/Oxy5FkgFORPlaVn9qBFv
cdXBsZOWFnZ2+F/OA55WJeQoyO8E9l5+N8TGKpzXbmkUWyKiqbN+AviZYrK6KPxi
zXS4SH0xAoGBANUDIhoUTM+u6dJze/i/sSe90k6UOBsSxA8Q7rOfksda4F0JEBPl
l4kbfmMVMEeIJrHNaFRE8r/p/J8sQCg4w/wDB5LEdHGaxf3b2Fbai+QHuElrbOXP
vQ/UOaaMQKFJlwlfOHpbDzXM3bMdGwfT6DCCoQyrAvVRE/x8veXJ9Fl/AoGBANDZ
B+sjiVbIjbWA9debx/QeEjoLB68Pi8DleCOgbtF7c4jJPPDRLz0fce16ePVFpiw4
Mu1E8QQdMTxWY+Y4ERNPwXj//PhD5xDfWYdRJ6IgKFK1bqKIwm7BmJn3WLD2JH2J
mLR0Wfa7M8OmWBbAS8fe5NvubeqERmbMs+f+eeWPAoGAR4Cgvt5XllNhm8o2MB6w
qeV3JfdtCfF3rJMDfXowPAkOTUyQgA1Om7CF8V6YcTqLup13yunGDpPNv+SLuLSt
XPfrX+HgMI5Crd9RNH5x/N52hvavfEkKbrjPjU+BFmLsdzHmdHQCnA2j0c8QVsVU
KIyA4Q66lHxd2CBLYkozYqMCgYAxiZkoPBiifhWm3LzzdF78V3mpTN54tq5Ghed2
Q+KlS6v+4QTUdjnHPMMwOiGgw/GDgZ0KzJSCjk4UasVtYRUjyIIyqj2dwbV4OhIp
V6WX/hqya5ifcuLzlHYW5yWha/EB2fZfr017ibHgkX9Jfjk7YnJUfHyT6OYuEhYG
TEUrnwKBgHztVFIi0vwELwhVCr37pFzKTTmm7G+SYH2hnvL2o3eCNAxSoE8/+vuP
qHxd6MME9OqeuY9s3eimsTuhSxnMN348v3Tr/FnA/VIeEMyDZyPqso1pFylpUnHn
67hv/xqXT3+/MHq6AgVWXjwTgn8XNRDfXmHrBIztq6Kzo/kLmthY
-----END RSA PRIVATE KEY-----
`;
const privateRsaKeyBase64 = Buffer.from(privateRsaKey).toString('base64');
const publicRsaKey = `
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArcch89X6VuWj/CQtVfaC
XUl0Pcv8IJRgICN8X+3zFNrbiTdhkTtrOhdkbEU5VaW6aQiCXX5+4C1T2sXXXT68
2XJhIjKepyX3aY50Fh59pLCciwAKc5wPy3PVMOhup15u9reiQKxps1SNrqVLyZNj
ha83qbN9IJvQcMnQghAjjPUeGPMaMMzG1GOnuPOWIiM2kxqRpbugwwTyuepPnakm
fkWqVtMIRprPLY6d3liDIUSSRZ7o6vbmgeF+9U4DyaimKVNngrmi+mW0OnyH1eJY
LrJEY9tZaRF8xraMZiOcBcyAt6S/TS29HttJ6+zlhcWx34fItEZ8jA5gzhTmspOY
8QIDAQAB
-----END PUBLIC KEY-----
`;
module.exports = {
privateRsaKey,
privateRsaKeyBase64,
publicRsaKey
};
@@ -0,0 +1,116 @@
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const core = require('@actions/core');
const got = require('got');
const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8200'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('authenticate with userpass', () => {
const username = `testUsername`;
const password = `testPassword`;
beforeAll(async () => {
try {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': vaultToken,
},
});
await got(`${vaultUrl}/v1/secret/data/userpass-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUPERSECRET_WITH_USERPASS',
},
},
});
// Enable userpass
try {
await got(`${vaultUrl}/v1/sys/auth/userpass`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
type: 'userpass'
},
});
} catch (error) {
const {response} = error;
if (response.statusCode === 400 && response.body.includes("path is already in use")) {
// Userpass might already be enabled from previous test runs
} else {
throw error;
}
}
// Create policies
await got(`${vaultUrl}/v1/sys/policies/acl/userpass-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
"name":"userpass-test",
"policy":`path \"auth/userpass/*\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"auth/userpass/users/${username}\"\n{\n capabilities = [\"create\", \"read\", \"update\", \"delete\", \"list\"]\n}\n\npath \"secret/data/*\" {\n capabilities = [\"list\"]\n}\npath \"secret/metadata/*\" {\n capabilities = [\"list\"]\n}\n\npath \"secret/data/userpass-test\" {\n capabilities = [\"read\", \"list\"]\n}\npath \"secret/metadata/userpass-test\" {\n capabilities = [\"read\", \"list\"]\n}\n`
},
});
// Create user
await got(`${vaultUrl}/v1/auth/userpass/users/${username}`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken
},
json: {
password: `${password}`,
policies: 'userpass-test'
},
});
} catch(err) {
console.warn('Create user in userpass', err.response.body);
throw err;
}
});
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('method', expect.anything())
.mockReturnValueOnce('userpass');
when(core.getInput)
.calledWith('username', expect.anything())
.mockReturnValueOnce(username);
when(core.getInput)
.calledWith('password', expect.anything())
.mockReturnValueOnce(password);
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
});
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(secrets);
}
it('authenticate with userpass', async() => {
mockInput('secret/data/userpass-test secret');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('SECRET', 'SUPERSECRET_WITH_USERPASS');
})
});
+4
View File
@@ -0,0 +1,4 @@
# e2e tests
This test suite runs `vault-action` as a GitHub Action in the context of a live build, and then verifies that the appropriate environmental variables are set.
These tests are intended to mostly be very simple smoke tests to verify that the action is being compiled and run correctly in context.
+24
View File
@@ -0,0 +1,24 @@
-----BEGIN CERTIFICATE-----
MIIEFjCCAv6gAwIBAgIUe0i7/HGZKvbDb30L9mC99KXFwj8wDQYJKoZIhvcNAQEL
BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKEwlIYXNoaUNvcnAxIzAhBgNVBAsTGlRl
c3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDEyRQcm90b3R5cGUgVGVz
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMjAwODA1MTg1MjAwWhcNMjUwODA0
MTg1MjAwWjCBojELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAU
BgNVBAcTDVNhbiBGcmFuY2lzY28xEjAQBgNVBAoTCUhhc2hpQ29ycDEjMCEGA1UE
CxMaVGVzdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkxLTArBgNVBAMTJFByb3RvdHlw
ZSBUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTCCASIwDQYJKoZIhvcNAQEBBQAD
ggEPADCCAQoCggEBAMrRXuu2+zhBs0pLYEdXIaPc4KoWO3xm2RJdbzy3hfjFybQ8
H/Y6Hi7txjGGSb45xSfXT/RF2srNfs235I+sfB8rrEizNpzkXqOgGa8LKvh2tgBT
BK/jDWsEdDhxmkpFhE69wEW+D5ub7QGnx9jrqLKfwCmUA0utlzcFBk2nRNhRtsrp
CI5YL1VN4coLpgXdvbodzbynPzGHe9R/o9K0Uiz2hgHooyKwhkVYwo0BIAQamLFz
TS7lyeLf0thDOxV31NX8SpSucqRf50WHNk8T/YtKZ9EhlBDT4ybZwwvcC/ocxxcg
1LvB0YweZNjSeO78S4CMh1TFGXnF/xOtGABlIbcCAwEAAaNCMEAwDgYDVR0PAQH/
BAQDAgEGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFABD/NxvYLpo5zVNeD01
r8IIFYlBMA0GCSqGSIb3DQEBCwUAA4IBAQB7TfpIx53gf/oI3mgR6Ciz287WBzFR
OzhJXwHk5J3mx8VC1W8tDRXih2lCLd/f9qDy6LyL/hZcoonev6w9oReuOMBiH6l4
Pf3yq2aDXX0AoGgm75c1m34kY669JLMsHq5+xuUDeeFUMd60w9zVtZfBSumy/sgN
PdjtvThh8sSByocYULs3tuxZDGyQ6GyQcn/xlMrGtmcD5IuX5IXqcKRVlZttykNx
S2ltcR00fekw8WZyPSzMJaP+/Kcq3T2viN02MS6qEycQZoYfEAMdj+A0kjbsZG9D
6J92z78b2DuLAUvZVpynNk/UbpDeqIDy40V3JDmtvrfGUMkMhMqgK/+J
-----END CERTIFICATE-----
@@ -0,0 +1,25 @@
-----BEGIN CERTIFICATE-----
MIIENTCCAx2gAwIBAgIUMu5h1ysA5DlM6lzZFliT2C2n4lEwDQYJKoZIhvcNAQEL
BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKEwlIYXNoaUNvcnAxIzAhBgNVBAsTGlRl
c3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDEyRQcm90b3R5cGUgVGVz
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwIBcNMjAwODA1MTg1MjAwWhgPMjEyMDA3
MTIxODUyMDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzESMBAGA1UEChMJSGFzaGlDb3JwMSMwIQYD
VQQLExpUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOaGFzaGlj
b3JwLnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC/zYDKdDGo
+Qy7eJUNjOe6jpeGvK1lMu5T1Xka+h2ay6WH5gLyrPw5pi582iYpJdHVbplKMywx
LxZv7mAbKNxqdp8UZKy0A3bCuHQqRF8ssXXHufQ8EGxNkLMLJP0e2q39OnrxXekS
8Ct3aJm3V8qkcV3CpVdPNgJh4TSuneCXIxVWjFYSiyHi0/5TRd2D+aQPz12szg5F
mBW4dLzYKHEMlWcjWG8mxtbLyt+jSR1+tSehQx7KndufdfniOWEDBdbeR3yDnZdn
p8DnRWK4oaEI3Sl8tKlDd1Yp+R96aqOEn1tPW6Jy6Vdvk3fCefclbWZ6B9kiJ/1r
gxq7AN7iKmHNAgMBAAGjdTBzMA4GA1UdDwEB/wQEAwIFoDATBgNVHSUEDDAKBggr
BgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRn2mwyk+MPChnLg0iWy1r9
b3JvwTAfBgNVHSMEGDAWgBQAQ/zcb2C6aOc1TXg9Na/CCBWJQTANBgkqhkiG9w0B
AQsFAAOCAQEAOpCy0vHp1Kxgv0VBRrbrwSQLBGP8a1ubVWoeoZQ+EvX9ozqDrHxm
gM4XPYUJlUOOEu0ZRgCW60YK33E1zNKnA1F0/3/rmqMkKnm0BBs/5WzMWtsIBPcU
e0CeJmaRIXnERQMH/svD+RrFo1dcF8rUDIlWez7+xGqoIGBg7v4jEmkZ3HdckcE+
/xvC61YSG8NsJwR/CEcQ8YCyVfgvuS0ukWs4dN15aVDL3Oe61h3bRcGAywOJBrdq
9xaq7ezZp/+lUSkYnatWJBuC/aviH9g9s+gMT0I3fWHh8BB0Ne2txwJ15K/qz5he
TjxFsumrh50aFqjSiEHndtY5UWuGAFLiSw==
-----END CERTIFICATE-----
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAv82AynQxqPkMu3iVDYznuo6XhrytZTLuU9V5Gvodmsulh+YC
8qz8OaYufNomKSXR1W6ZSjMsMS8Wb+5gGyjcanafFGSstAN2wrh0KkRfLLF1x7n0
PBBsTZCzCyT9Htqt/Tp68V3pEvArd2iZt1fKpHFdwqVXTzYCYeE0rp3glyMVVoxW
Eosh4tP+U0Xdg/mkD89drM4ORZgVuHS82ChxDJVnI1hvJsbWy8rfo0kdfrUnoUMe
yp3bn3X54jlhAwXW3kd8g52XZ6fA50ViuKGhCN0pfLSpQ3dWKfkfemqjhJ9bT1ui
culXb5N3wnn3JW1megfZIif9a4MauwDe4iphzQIDAQABAoIBAQCYHJuidAoaTwGZ
ACV9rJzuqD1lvubpFj5KwEcebPPjmtQ5deIqoaQa+D9wBvYyteq3ENKDNRg8HXL2
7B7OC1bbHB5HZxxMW17pSK3gA39Ti52z+zbGF+Q8k6BbG0efG6DW7nUoTOkWeuCN
/6fA7uAoEDxirQwUJuo2xAsq3MyMLwcs95rke9Bly8ABFNaV1oMZq8YT/w8oSc2b
/7WtxXmChHlVYXTcMqzVPqNFqPRixZRWQ+BSHoXmEDviuGd51L4s9D7iXp32TvUx
DMHeS1DFA2en7ZF1uc9VXZeplkkDtVhUe4d6qOqCcUwDFEvMonnyVSa6/FkR5jYZ
2yujTdfhAoGBAM0hGOnmnDnCjADUt4mZlr+Mf0XmdKzEV+hid4CQUvBoTXgjYMvv
c397eNePce7SwSUE1/APERInGUPhRLVFW5q6/34WRtGBbQkT8ByeJANXes4UFZe3
wdNLczWUlSl0G3jTf+Kh3+K5/PtmyxSrAS/9GIk+ibs1mlJOPyVnWqUJAoGBAO9e
WlP9/ruXluvkQyM5ZlnAnZYMsFGzzPx4tkazUjurtqxQoyZ0z+pPItGQ7lOl+pDA
EWiTun66g+Da9uBiBCJUeXiC1ge2p6bT6N194BrYyrWML9hcIL4mqVojUEUmhnSh
6b9h1pC7vFmw5ZFMIIkS60cfBMgQMZxMJN8NuaulAoGBAJM5hwURg90c2ZkbEyPK
PVz7fLlxnxoEzcc3LOf0LeLoKXnpgma8VJwRxXiJNs+fKgrkwAtG9QyfTU3f1412
2zlhr1ASsv9ZMiXKzpHrmpNfbP+NgLXkqFN7mpPBMZGQCMuemPHTFrpGnODfNTB/
T5newIZ4gSgBX+Jk0IOK+47pAoGAeKo6pK6ck9pV5TIbOg18b/AuQG7DD1yxD/CW
CkvpP1VPb8vygrdN/FLKPZRu39IC3qdD31DhKXNCeb5Hx1MBvICS/1INLLRCDVIz
yDvlFgOFJEG3+LxwcQqyQlMc6s8B5pecarKaZDmPODN5dmZG3HKiEicr1OJ878pe
p+aWW1UCgYBmGFbCc1qqlqp+srYGsv3rIgNs5HSfrAjbgY8xh9foMgrYCRAm57gv
01yVxMXWmKA6ReVEu8OTVy9fkuOL/vw2o+C6W4IPZYdvSQoPwd5Lf+AqxEQvFF1m
tT3SZAM3EhQ7tIXdIQHY27SJ1KlUJMrvUq1CiRWiG/MOKf/87JXPog==
-----END RSA PRIVATE KEY-----
@@ -0,0 +1,15 @@
ui = false
disable_mlock = true
listener "tcp" {
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/etc/vault/server.crt"
tls_key_file = "/etc/vault/server.key"
tls_client_ca_file = "/etc/vault/ca.crt"
tls_require_and_verify_client_cert = "true"
}
storage "file" {
path = "/var/lib/vault"
}
@@ -0,0 +1,26 @@
-----BEGIN CERTIFICATE-----
MIIEXjCCA0agAwIBAgIUAswquazrfsyDRvXZwn5718DUhU4wDQYJKoZIhvcNAQEL
BQAwgaIxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
Ew1TYW4gRnJhbmNpc2NvMRIwEAYDVQQKEwlIYXNoaUNvcnAxIzAhBgNVBAsTGlRl
c3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MS0wKwYDVQQDEyRQcm90b3R5cGUgVGVz
dCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwIBcNMjAwODA1MTg1MjAwWhgPMjEyMDA3
MTIxODUyMDBaMIGMMQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEW
MBQGA1UEBxMNU2FuIEZyYW5jaXNjbzESMBAGA1UEChMJSGFzaGlDb3JwMSMwIQYD
VQQLExpUZXN0IENlcnRpZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOaGFzaGlj
b3JwLnRlc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC0azdZsrPC
5Rv8nRxVJnLi+oZgCJYgzhCDiEbYqt1QK1gqNXp0ml5ck6ycj0drwzHzrrX+xcPV
5FcNKH3RFyon9XkzjwaXkMv6IkgvH6/jQ1dDW9kWBf3Io3Y59wnD/YaIzNK0CYJS
fRNdsZb4InH8gh+RL33+FeysgJwXG1TVA4tTUj7DQxDE0cDd9UD+C9Yx7OWiUjC1
IjqdFPusX1nziKYjeI5/UiCmOUGqJJRoMPonuzuGIj9GdmBKmga64OfeZFqn4f6a
ay61VnGCwZ24VniUwYElsFbcF2Nv9WqnrOeQlHOsYN68VMqHzaYPqE6SPa6mO5mI
/tmpXrDG3Y+RAgMBAAGjgZ0wgZowDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoG
CCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFE03/UTs2ZmJpWHSmstt
hDngW6F3MB8GA1UdIwQYMBaAFABD/NxvYLpo5zVNeD01r8IIFYlBMCUGA1UdEQQe
MByCCWxvY2FsaG9zdIIJdmF1bHQtdGxzhwR/AAABMA0GCSqGSIb3DQEBCwUAA4IB
AQCzarBGJium5oZDSSP5GqxpS13QP2onEen6I1k2eRdcOqtbfNdQ20RJrb4dfNkE
Dc09KWVlZAn+hYge2KKTXJ+4ltIC9V1LvquyWipNczOT1ve0H9gt3Wm88LdESqI5
HOx43pIaa3cWXBlbzrFmT1SASYm1V5Oo1mXzpUukGokHLLmAz36VVuJGbD0BxYke
5MefG4tNT1SsMsIqVvGxI9NiVs7YTdJu81MctSYK5snsEKnYdi9N7CHOk3bdDpeC
v2Vo7XBk3s4sBMGmnJO+1JOcRFJioooEFkqNyQmg3atfInysVbreKS5KtWNTaCPm
yI55plW8ga5ucja2VX3WbwAO
-----END CERTIFICATE-----
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAtGs3WbKzwuUb/J0cVSZy4vqGYAiWIM4Qg4hG2KrdUCtYKjV6
dJpeXJOsnI9Ha8Mx8661/sXD1eRXDSh90RcqJ/V5M48Gl5DL+iJILx+v40NXQ1vZ
FgX9yKN2OfcJw/2GiMzStAmCUn0TXbGW+CJx/IIfkS99/hXsrICcFxtU1QOLU1I+
w0MQxNHA3fVA/gvWMezlolIwtSI6nRT7rF9Z84imI3iOf1IgpjlBqiSUaDD6J7s7
hiI/RnZgSpoGuuDn3mRap+H+mmsutVZxgsGduFZ4lMGBJbBW3Bdjb/Vqp6znkJRz
rGDevFTKh82mD6hOkj2upjuZiP7ZqV6wxt2PkQIDAQABAoIBAQCvK0HsVvLtkSCh
HbF6gwAcnHyHFQ8d/rRN4KxYhVynD85j/NRODer8G20F/J6tZDFFlSWinUTMkQxr
/BpcPg9yCIWKp50Q30cMLujCyBMvphw9jBmzplGG0h5hnRbgMXDDtYoFvw3HJST+
XQRlGpxtO7GGdwPvBD5sJdpnHOQ6g7qIYKmlHM99kHU8vr0VghqZAYxEh8RpnYez
NLra+7ep+Zp1pFIniU6B8ohyL3OArbQ65qYrZYriAEI5HeEk0RhjewcPsV56LwbS
CncTVS/dNYgk1zRIvytmbDVD3v/4lLvnpIWeKVdk7p1aGJeCdpLeWNvDLX0Ws67r
QeZQizwhAoGBAMwAIA6+HPsx+8dhNbN3ydX8YU6uUfSeshhshIFZPIYL2vrKyAHU
/GAYVzYJH/cU0IvlLJlLdQuiZkOXEX87tgdfmM/o4Qdl12RR0BvU0Cae0txtzNrP
yTdfZqDhTz/V8jOAXUNA5oQA45Y3rI7JES8hBd1F9WFOH2WINp344GzlAoGBAOJo
SgmRE24VcnfUMqrBpwZBdBrTxDQyTagvd+MuGomIQfcE2Y4rr1eIuJJ0HF5/eYxc
DZRO/LVP9tQ8ozXi2tdmgUdKC79O2edmdOCWW5of464R/TLcM5B4SmS36RtdE3qJ
ig4fcUmsJ15MAGpkXLMh5YSD/N3TmcnURtx06Fk9AoGATi+mGcBnnybzFuF9EYHR
y7/lE6DgLF8+ZvoAdwralY2pqgFaUslsyO/LTRyGMc66d0OoqkAvZfwiMbmOrTMX
ew/6o4Tf6lPwD7UDjAcul/67VlyG7T5CIoTf8r0oAJFhOLf0BrizINiuYX6JFlid
y3BerQYJG/gzNFjWhglDCrkCgYA+3wUISRAjNrN10ShMwL/3/b8XIA1RDVMBTEU3
gfr+jCb9SIx9bWYgoafXi4TBPRbswjdHIvQMCWuankgYU6m/vQhTWp2Of4AFQS9d
moNPdmGMWhR8xidPjAfklimWXq9lDMKYj2SvN64rAmHvKXWQjO4mcVyL4RHIuTkA
STqoZQKBgQDKHd8F6tjZHEFolmjS5l682g7zVTpBhozezJ/RqYvhJh5ew1pXoD/O
Zu9iMfHoDjR4ZUXq6aeLUj/oIt1AsjwaGChOLLAvFbvePgS9XkYkwIlaxS4efAya
+CQE/JmY/a1/c2MDLNMCEXvUqX68pv6iDF8pfn+i4tn0omYqgfUlCA==
-----END RSA PRIVATE KEY-----
+14
View File
@@ -0,0 +1,14 @@
describe('e2e-tls', () => {
it('verify', () => {
expect(process.env.SECRET).toBe("SUPERSECRET");
expect(process.env.NAMED_SECRET).toBe("SUPERSECRET");
expect(process.env.OTHERSECRET).toBe("OTHERSUPERSECRET");
expect(process.env.OTHER_SECRET_OUTPUT).toBe("OTHERSUPERSECRET");
expect(process.env.ALTSECRET).toBe("CUSTOMSECRET");
expect(process.env.NAMED_ALTSECRET).toBe("CUSTOMSECRET");
expect(process.env.OTHERALTSECRET).toBe("OTHERCUSTOMSECRET");
expect(process.env.FOO).toBe("bar");
expect(process.env.NAMED_CUBBYSECRET).toBe("zap");
expect(process.env.SKIP).toBe("true");
});
});
+3
View File
@@ -0,0 +1,3 @@
module.exports = {
verbose: true
};
+197
View File
@@ -0,0 +1,197 @@
const got = require('got');
const core = require('@actions/core');
const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
const caCertificateRaw = `${process.env.VAULTCA}`;
const clientCertificateRaw = `${process.env.VAULT_CLIENT_CERT}`;
const clientKeyRaw = `${process.env.VAULT_CLIENT_KEY}`;
(async () => {
try {
var caCertificate = Buffer.from(caCertificateRaw, 'base64').toString();
if (caCertificate == null) {
throw Error("VAULTCA env not set.")
}
var clientCertificate = Buffer.from(clientCertificateRaw, 'base64').toString();
if (clientCertificate == null) {
throw Error("VAULT_CLIENT_CERT env not set.")
}
var clientKey = Buffer.from(clientKeyRaw, 'base64').toString();
if (clientKey == null) {
throw Error("VAULT_CLIENT_KEY env not set.")
}
// Init
const {body} = await got(`https://${vaultUrl}/v1/sys/init`, {
method: 'POST',
json: {
secret_shares: 1,
secret_threshold: 1,
},
responseType: 'json',
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
}
});
if (body.keys_base64.length != 1) {
throw Error("No unseal key found after init.")
}
var unseal = body.keys_base64[0];
if (body.root_token == "") {
throw Error("No root token found after init.")
}
var rootToken = body.root_token;
core.exportVariable('VAULT_TOKEN', rootToken);
core.setSecret(rootToken)
// Unseal
await got(`https://${vaultUrl}/v1/sys/unseal`, {
method: 'POST',
json: {
key: unseal,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
}
});
await got(`https://${vaultUrl}/v1/sys/mounts/secret`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
type: 'kv-v2'
}
});
await got(`https://${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
data: {
secret: 'SUPERSECRET',
},
},
});
await got(`https://${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
data: {
otherSecret: 'OTHERSUPERSECRET',
},
}
});
await got(`https://${vaultUrl}/v1/secret/data/tlsSkipVerify`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
data: {
skip: 'true',
},
}
});
await got(`https://${vaultUrl}/v1/sys/mounts/my-secret`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
type: 'kv'
}
});
await got(`https://${vaultUrl}/v1/my-secret/test`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
altSecret: 'CUSTOMSECRET',
}
});
await got(`https://${vaultUrl}/v1/my-secret/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
otherAltSecret: 'OTHERCUSTOMSECRET',
},
});
await got(`https://${vaultUrl}/v1/cubbyhole/test`, {
method: 'POST',
headers: {
'X-Vault-Token': rootToken,
},
https: {
certificateAuthority: caCertificate,
certificate: clientCertificate,
key: clientKey,
},
json: {
foo: 'bar',
zip: 'zap',
},
});
} catch (error) {
console.log(error);
process.exit(1);
}
})();
+7
View File
@@ -9,5 +9,12 @@ describe('e2e', () => {
expect(process.env.OTHERALTSECRET).toBe("OTHERCUSTOMSECRET");
expect(process.env.FOO).toBe("bar");
expect(process.env.NAMED_CUBBYSECRET).toBe("zap");
expect(process.env.SUBSEQUENT_TEST_SECRET).toBe("SUBSEQUENT_TEST_SECRET");
expect(process.env.JSONSTRING).toBe('{"x":1,"y":"qux"}');
expect(process.env.JSONSTRINGMULTILINE).toBe('{"x": 1, "y": "q\\nux"}');
let result = JSON.stringify('{"x":1,"y":"qux"}');
result = result.substring(1, result.length - 1);
expect(process.env.JSONDATA).toBe(result);
});
});
+60 -7
View File
@@ -1,20 +1,23 @@
const got = require('got');
const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
const vaultToken = `${process.env.VAULT_TOKEN}` === undefined ? `${process.env.VAULT_TOKEN}` : "testtoken";
const jsonStringMultiline = '{"x": 1, "y": "q\\nux"}';
(async () => {
try {
// Verify Connection
await got(`http://${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
});
await got(`http://${vaultUrl}/v1/secret/data/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
data: {
@@ -26,7 +29,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/secret/data/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
data: {
@@ -35,10 +38,48 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
}
});
await got(`http://${vaultUrl}/v1/secret/data/test-json-string`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
// this is stored in Vault as a string
jsonString: '{"x":1,"y":"qux"}',
},
},
});
await got(`http://${vaultUrl}/v1/secret/data/test-json-data`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
// this is stored in Vault as a map
jsonData: {"x":1,"y":"qux"},
},
},
});
await got(`http://${vaultUrl}/v1/secret/data/test-json-string-multiline`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
jsonStringMultiline,
},
},
});
await got(`http://${vaultUrl}/v1/sys/mounts/my-secret`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
type: 'kv'
@@ -48,7 +89,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/my-secret/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
altSecret: 'CUSTOMSECRET',
@@ -58,7 +99,7 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/my-secret/nested/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
otherAltSecret: 'OTHERCUSTOMSECRET',
@@ -68,13 +109,25 @@ const vaultUrl = `${process.env.VAULT_HOST}:${process.env.VAULT_PORT}`;
await got(`http://${vaultUrl}/v1/cubbyhole/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
json: {
foo: 'bar',
zip: 'zap',
},
});
await got(`http://${vaultUrl}/v1/secret/data/subsequent-test`, {
method: 'POST',
headers: {
'X-Vault-Token': vaultToken,
},
json: {
data: {
secret: 'SUBSEQUENT_TEST_SECRET',
},
},
});
} catch (error) {
console.log(error);
process.exit(1);
+29 -44
View File
@@ -8,6 +8,7 @@ const { when } = require('jest-when');
const { exportSecrets } = require('../../src/action');
const vaultUrl = `http://${process.env.VAULT_HOST || 'localhost'}:${process.env.VAULT_PORT || '8201'}`;
const vaultToken = `${process.env.VAULT_TOKEN || 'testtoken'}`
describe('integration', () => {
beforeAll(async () => {
@@ -15,7 +16,7 @@ describe('integration', () => {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
});
@@ -43,20 +44,20 @@ describe('integration', () => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('url')
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
when(core.getInput)
.calledWith('token')
.mockReturnValueOnce('testtoken');
.calledWith('token', expect.anything())
.mockReturnValueOnce(vaultToken);
when(core.getInput)
.calledWith('namespace')
.calledWith('namespace', expect.anything())
.mockReturnValueOnce('ns1');
});
it('get simple secret', async () => {
mockInput('test secret');
mockInput('secret/data/test secret');
await exportSecrets();
@@ -64,7 +65,7 @@ describe('integration', () => {
});
it('re-map secret', async () => {
mockInput('test secret | TEST_KEY');
mockInput('secret/data/test secret | TEST_KEY');
await exportSecrets();
@@ -72,7 +73,7 @@ describe('integration', () => {
});
it('get nested secret', async () => {
mockInput('nested/test otherSecret');
mockInput('secret/data/nested/test otherSecret');
await exportSecrets();
@@ -81,9 +82,9 @@ describe('integration', () => {
it('get multiple secrets', async () => {
mockInput(`
test secret ;
test secret | NAMED_SECRET ;
nested/test otherSecret ;`);
secret/data/test secret ;
secret/data/test secret | NAMED_SECRET ;
secret/data/nested/test otherSecret ;`);
await exportSecrets();
@@ -95,9 +96,7 @@ describe('integration', () => {
});
it('get secret from K/V v1', async () => {
mockInput('test secret');
mockEngineName('my-secret');
mockVersion('1');
mockInput('my-secret/test secret');
await exportSecrets();
@@ -105,9 +104,7 @@ describe('integration', () => {
});
it('get nested secret from K/V v1', async () => {
mockInput('nested/test otherSecret');
mockEngineName('my-secret');
mockVersion('1');
mockInput('my-secret/nested/test otherSecret');
await exportSecrets();
@@ -123,7 +120,7 @@ describe('authenticate with approle', () => {
// Verify Connection
await got(`${vaultUrl}/v1/secret/config`, {
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
},
});
@@ -141,7 +138,7 @@ describe('authenticate with approle', () => {
await got(`${vaultUrl}/v1/sys/auth/approle`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2',
},
json: {
@@ -161,7 +158,7 @@ describe('authenticate with approle', () => {
await got(`${vaultUrl}/v1/sys/policies/acl/test`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2',
},
json: {
@@ -174,7 +171,7 @@ describe('authenticate with approle', () => {
await got(`${vaultUrl}/v1/auth/approle/role/my-role`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2',
},
json: {
@@ -185,7 +182,7 @@ describe('authenticate with approle', () => {
// Get role-id
const roldIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/role-id`, {
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2',
},
responseType: 'json',
@@ -196,7 +193,7 @@ describe('authenticate with approle', () => {
const secretIdResponse = await got(`${vaultUrl}/v1/auth/approle/role/my-role/secret-id`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': 'ns2',
},
responseType: 'json',
@@ -215,21 +212,21 @@ describe('authenticate with approle', () => {
.calledWith('method', expect.anything())
.mockReturnValueOnce('approle');
when(core.getInput)
.calledWith('roleId')
.calledWith('roleId', expect.anything())
.mockReturnValueOnce(roleId);
when(core.getInput)
.calledWith('secretId')
.calledWith('secretId', expect.anything())
.mockReturnValueOnce(secretId);
when(core.getInput)
.calledWith('url')
.calledWith('url', expect.anything())
.mockReturnValueOnce(`${vaultUrl}`);
when(core.getInput)
.calledWith('namespace')
.calledWith('namespace', expect.anything())
.mockReturnValueOnce('ns2');
});
it('authenticate with approle', async() => {
mockInput('test secret');
mockInput('secret/data/test secret');
await exportSecrets();
@@ -242,7 +239,7 @@ async function enableNamespace(name) {
await got(`${vaultUrl}/v1/sys/namespaces/${name}`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
}
});
} catch (error) {
@@ -260,7 +257,7 @@ async function enableEngine(path, namespace, version) {
await got(`${vaultUrl}/v1/sys/mounts/${path}`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': namespace,
},
json: { type: 'kv', config: {}, options: { version }, generate_signing_key: true },
@@ -281,7 +278,7 @@ async function writeSecret(engine, path, namespace, version, data) {
await got(`${vaultUrl}/v1/${secretPath}`, {
method: 'POST',
headers: {
'X-Vault-Token': 'testtoken',
'X-Vault-Token': vaultToken,
'X-Vault-Namespace': namespace,
},
json: secretPayload
@@ -290,18 +287,6 @@ async function writeSecret(engine, path, namespace, version, data) {
function mockInput(secrets) {
when(core.getInput)
.calledWith('secrets')
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(secrets);
}
function mockEngineName(name) {
when(core.getInput)
.calledWith('path')
.mockReturnValueOnce(name);
}
function mockVersion(version) {
when(core.getInput)
.calledWith('kv-version')
.mockReturnValueOnce(version);
}
+7438 -14051
View File
File diff suppressed because it is too large Load Diff
+22 -23
View File
@@ -4,24 +4,20 @@
"description": "A Github Action that allows you to consume vault secrets as secure environment variables.",
"main": "dist/index.js",
"scripts": {
"build": "ncc build src/index.js -o dist",
"build": "ncc build src/entry.js -o dist",
"test": "jest",
"test:integration:basic": "jest -c integrationTests/basic/jest.config.js",
"test:integration:enterprise": "jest -c integrationTests/enterprise/jest.config.js",
"test:e2e": "jest -c integrationTests/e2e/jest.config.js"
},
"release": {
"branch": "master",
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/github"
],
"ci": false
"test:integration:e2e": "jest -c integrationTests/e2e/jest.config.js",
"test:integration:e2e-tls": "jest -c integrationTests/e2e-tls/jest.config.js"
},
"files": [
"src/**/*",
"dist/**/*"
],
"repository": {
"type": "git",
"url": "git+https://github.com/RichiCoder1/vault-action.git"
"url": "git+https://github.com/hashicorp/vault-action.git"
},
"keywords": [
"hashicorp",
@@ -31,22 +27,25 @@
"github-actions",
"javascript"
],
"author": "Richard Simpson <richardsimpson@outlook.com>",
"author": "HashiCorp",
"license": "MIT",
"bugs": {
"url": "https://github.com/RichiCoder1/vault-action/issues"
"url": "https://github.com/hashicorp/vault-action/issues"
},
"homepage": "https://github.com/RichiCoder1/vault-action#readme",
"homepage": "https://github.com/hashicorp/vault-action#readme",
"dependencies": {
"got": "^10.2.2",
"@actions/core": "^1.2.3"
"got": "^11.8.5",
"jsonata": "^2.0.3",
"jsrsasign": "^10.8.6"
},
"peerDependencies": {
"@actions/core": ">=1 <2"
},
"devDependencies": {
"@types/got": "^9.6.9",
"@types/jest": "^25.1.3",
"@zeit/ncc": "^0.22.0",
"jest": "^25.1.0",
"jest-when": "^2.7.0",
"semantic-release": "^17.0.4"
"@actions/core": "^1.10.0",
"@vercel/ncc": "^0.36.1",
"jest": "^29.5.0",
"jest-when": "^3.5.2",
"mock-http-server": "^1.4.5"
}
}
+101 -105
View File
@@ -2,32 +2,63 @@
const core = require('@actions/core');
const command = require('@actions/core/lib/command');
const got = require('got').default;
const { retrieveToken } = require('./auth');
const jsonata = require('jsonata');
const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index');
const AUTH_METHODS = ['approle', 'token', 'github'];
const VALID_KV_VERSION = [-1, 1, 2];
const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes', 'ldap', 'userpass'];
const ENCODING_TYPES = ['base64', 'hex', 'utf8'];
async function exportSecrets() {
const vaultUrl = core.getInput('url', { required: true });
const vaultNamespace = core.getInput('namespace', { required: false });
const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
const outputToken = (core.getInput('outputToken', { required: false }) || 'false').toLowerCase() != 'false';
const exportToken = (core.getInput('exportToken', { required: false }) || 'false').toLowerCase() != 'false';
let enginePath = core.getInput('path', { required: false });
/** @type {number | string} */
let kvVersion = core.getInput('kv-version', { required: false });
const secretsInput = core.getInput('secrets', { required: true });
const secretsInput = core.getInput('secrets', { required: false });
const secretRequests = parseSecretsInput(secretsInput);
const secretEncodingType = core.getInput('secretEncodingType', { required: false });
const vaultMethod = (core.getInput('method', { required: false }) || 'token').toLowerCase();
if (!AUTH_METHODS.includes(vaultMethod)) {
throw Error(`Sorry, the authentication method ${vaultMethod} is not currently supported.`);
const authPayload = core.getInput('authPayload', { required: false });
if (!AUTH_METHODS.includes(vaultMethod) && !authPayload) {
throw Error(`Sorry, the provided authentication method ${vaultMethod} is not currently supported and no custom authPayload was provided.`);
}
const defaultOptions = {
prefixUrl: vaultUrl,
headers: {}
headers: {},
https: {},
retry: {
statusCodes: [
...got.defaults.options.retry.statusCodes,
// Vault returns 412 when the token in use hasn't yet been replicated
// to the performance replica queried. See issue #332.
412,
]
}
}
const tlsSkipVerify = (core.getInput('tlsSkipVerify', { required: false }) || 'false').toLowerCase() != 'false';
if (tlsSkipVerify === true) {
defaultOptions.https.rejectUnauthorized = false;
}
const caCertificateRaw = core.getInput('caCertificate', { required: false });
if (caCertificateRaw != null) {
defaultOptions.https.certificateAuthority = Buffer.from(caCertificateRaw, 'base64').toString();
}
const clientCertificateRaw = core.getInput('clientCertificate', { required: false });
if (clientCertificateRaw != null) {
defaultOptions.https.certificate = Buffer.from(clientCertificateRaw, 'base64').toString();
}
const clientKeyRaw = core.getInput('clientKey', { required: false });
if (clientKeyRaw != null) {
defaultOptions.https.key = Buffer.from(clientKeyRaw, 'base64').toString();
}
for (const [headerName, headerValue] of extraHeaders) {
@@ -38,78 +69,60 @@ async function exportSecrets() {
defaultOptions.headers["X-Vault-Namespace"] = vaultNamespace;
}
const vaultToken = await retrieveToken(vaultMethod, got.extend(defaultOptions));
core.setSecret(vaultToken)
defaultOptions.headers['X-Vault-Token'] = vaultToken;
const client = got.extend(defaultOptions);
const vaultToken = await retrieveToken(vaultMethod, client);
if (!enginePath) {
enginePath = 'secret';
if (outputToken === true) {
core.setOutput('vault_token', `${vaultToken}`);
}
if (exportToken === true) {
core.exportVariable('VAULT_TOKEN', `${vaultToken}`);
}
if (!kvVersion) {
kvVersion = 2;
}
kvVersion = +kvVersion;
const requests = secretRequests.map(request => {
const { path, selector } = request;
return request;
});
if (Number.isNaN(kvVersion) || !VALID_KV_VERSION.includes(kvVersion)) {
throw Error(`You must provide a valid K/V version (${VALID_KV_VERSION.slice(1).join(', ')}). Input: "${kvVersion}"`);
}
const results = await getSecrets(requests, client);
const responseCache = new Map();
for (const secretRequest of secretRequests) {
const { secretPath, outputVarName, envVarName, secretSelector, isJSONPath } = secretRequest;
const requestOptions = {
headers: {
'X-Vault-Token': vaultToken
},
};
for (const [headerName, headerValue] of extraHeaders) {
requestOptions.headers[headerName] = headerValue;
}
for (const result of results) {
// Output the result
if (vaultNamespace != null) {
requestOptions.headers["X-Vault-Namespace"] = vaultNamespace;
}
var value = result.value;
const request = result.request;
const cachedResponse = result.cachedResponse;
let requestPath = `v1`;
const kvRequest = !secretPath.startsWith('/')
if (!kvRequest) {
requestPath += secretPath;
} else {
requestPath += (kvVersion === 2)
? `/${enginePath}/data/${secretPath}`
: `/${enginePath}/${secretPath}`;
}
let body;
if (responseCache.has(requestPath)) {
body = responseCache.get(requestPath);
if (cachedResponse) {
core.debug(' using cached response');
} else {
const result = await client.get(requestPath, requestOptions);
body = result.body;
responseCache.set(requestPath, body);
}
let dataDepth = isJSONPath === true ? 0 : kvRequest === false ? 1 : kvVersion;
// if a secret is encoded, decode it
if (ENCODING_TYPES.includes(secretEncodingType)) {
value = Buffer.from(value, secretEncodingType).toString();
}
const secretData = getResponseData(body, dataDepth);
const value = selectData(secretData, secretSelector, isJSONPath);
command.issue('add-mask', value);
for (const line of value.replace(/\r/g, '').split('\n')) {
if (line.length > 0) {
core.setSecret(line);
}
}
if (exportEnv) {
core.exportVariable(envVarName, `${value}`);
core.exportVariable(request.envVarName, `${value}`);
}
core.setOutput(outputVarName, `${value}`);
core.debug(`${secretPath} => outputs.${outputVarName}${exportEnv ? ` | env.${envVarName}` : ''}`);
core.setOutput(request.outputVarName, `${value}`);
core.debug(`${request.path} => outputs.${request.outputVarName}${exportEnv ? ` | env.${request.envVarName}` : ''}`);
}
};
/** @typedef {Object} SecretRequest
* @property {string} secretPath
/** @typedef {Object} SecretRequest
* @property {string} path
* @property {string} envVarName
* @property {string} outputVarName
* @property {string} secretSelector
* @property {boolean} isJSONPath
* @property {string} selector
*/
/**
@@ -117,6 +130,10 @@ async function exportSecrets() {
* @param {string} secretsInput
*/
function parseSecretsInput(secretsInput) {
if (!secretsInput) {
return []
}
const secrets = secretsInput
.split(';')
.filter(key => !!key)
@@ -126,12 +143,12 @@ function parseSecretsInput(secretsInput) {
/** @type {SecretRequest[]} */
const output = [];
for (const secret of secrets) {
let path = secret;
let pathSpec = secret;
let outputVarName = null;
const renameSigilIndex = secret.lastIndexOf('|');
if (renameSigilIndex > -1) {
path = secret.substring(0, renameSigilIndex).trim();
pathSpec = secret.substring(0, renameSigilIndex).trim();
outputVarName = secret.substring(renameSigilIndex + 1).trim();
if (outputVarName.length < 1) {
@@ -139,7 +156,7 @@ function parseSecretsInput(secretsInput) {
}
}
const pathParts = path
const pathParts = pathSpec
.split(/\s+/)
.map(part => part.trim())
.filter(part => part.length !== 0);
@@ -148,64 +165,44 @@ function parseSecretsInput(secretsInput) {
throw Error(`You must provide a valid path and key. Input: "${secret}"`);
}
const [secretPath, secretSelector] = pathParts;
const [path, selectorQuoted] = pathParts;
const isJSONPath = secretSelector.includes('.');
/** @type {any} */
const selectorAst = jsonata(selectorQuoted).ast();
const selector = selectorQuoted.replace(new RegExp('"', 'g'), '');
if (isJSONPath && !outputVarName) {
if ((selectorAst.type !== "path" || selectorAst.steps[0].stages) && selectorAst.type !== "string" && !outputVarName) {
throw Error(`You must provide a name for the output key when using json selectors. Input: "${secret}"`);
}
let envVarName = outputVarName;
if (!outputVarName) {
outputVarName = secretSelector;
envVarName = normalizeOutputKey(outputVarName);
outputVarName = normalizeOutputKey(selector);
envVarName = normalizeOutputKey(selector, true);
}
output.push({
secretPath,
path,
envVarName,
outputVarName,
secretSelector,
isJSONPath
selector
});
}
return output;
}
/**
* Parses a JSON response and returns the secret data
* @param {string} responseBody
* @param {number} dataLevel
*/
function getResponseData(responseBody, dataLevel) {
let secretData = JSON.parse(responseBody);
for (let i = 0; i < dataLevel; i++) {
secretData = secretData['data'];
}
return secretData;
}
/**
* Parses a JSON response and returns the secret data
* @param {Object} data
* @param {string} selector
*/
function selectData(data, selector, isJSONPath) {
if (!isJSONPath) {
return data[selector];
}
// TODO: JSONPath
}
/**
* Replaces any forward-slash characters to
* Replaces any dot chars to __ and removes non-ascii charts
* @param {string} dataKey
* @param {boolean=} isEnvVar
*/
function normalizeOutputKey(dataKey) {
return dataKey.replace('/', '__').replace(/[^\w-]/, '').toUpperCase();
function normalizeOutputKey(dataKey, isEnvVar = false) {
let outputKey = dataKey
.replace('.', '__').replace(new RegExp('-', 'g'), '').replace(/[^\p{L}\p{N}_-]/gu, '');
if (isEnvVar) {
outputKey = outputKey.toUpperCase();
}
return outputKey;
}
/**
@@ -236,7 +233,6 @@ function parseHeadersInput(inputKey, inputOptions) {
module.exports = {
exportSecrets,
parseSecretsInput,
parseResponse: getResponseData,
normalizeOutputKey,
parseHeadersInput
};
+231 -51
View File
@@ -2,12 +2,12 @@ jest.mock('got');
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
const command = require('@actions/core/lib/command');
const core = require('@actions/core');
const got = require('got');
const {
exportSecrets,
parseSecretsInput,
parseResponse,
parseHeadersInput
} = require('./action');
@@ -17,11 +17,10 @@ describe('parseSecretsInput', () => {
it('parses simple secret', () => {
const output = parseSecretsInput('test key');
expect(output).toContainEqual({
secretPath: 'test',
secretSelector: 'key',
path: 'test',
selector: 'key',
outputVarName: 'key',
envVarName: 'KEY',
isJSONPath: false
envVarName: 'KEY'
});
});
@@ -49,10 +48,10 @@ describe('parseSecretsInput', () => {
expect(output).toHaveLength(2);
expect(output[0]).toMatchObject({
secretPath: 'first',
path: 'first',
});
expect(output[1]).toMatchObject({
secretPath: 'second',
path: 'second',
});
});
@@ -78,7 +77,7 @@ describe('parseSecretsInput', () => {
expect(output).toHaveLength(3);
expect(output[0]).toMatchObject({
secretPath: 'first',
path: 'first',
});
expect(output[1]).toMatchObject({
outputVarName: 'b',
@@ -88,13 +87,13 @@ describe('parseSecretsInput', () => {
outputVarName: 'SOME_C',
envVarName: 'SOME_C',
});
})
});
});
describe('parseHeaders', () => {
it('parses simple header', () => {
when(core.getInput)
.calledWith('extraHeaders')
.calledWith('extraHeaders', undefined)
.mockReturnValueOnce('TEST: 1');
const result = parseHeadersInput('extraHeaders');
expect(Array.from(result)).toContainEqual(['test', '1']);
@@ -102,7 +101,7 @@ describe('parseHeaders', () => {
it('parses simple header with whitespace', () => {
when(core.getInput)
.calledWith('extraHeaders')
.calledWith('extraHeaders', undefined)
.mockReturnValueOnce(`
TEST: 1
`);
@@ -112,7 +111,7 @@ describe('parseHeaders', () => {
it('parses multiple headers', () => {
when(core.getInput)
.calledWith('extraHeaders')
.calledWith('extraHeaders', undefined)
.mockReturnValueOnce(`
TEST: 1
FOO: bAr
@@ -124,74 +123,41 @@ describe('parseHeaders', () => {
it('parses null response', () => {
when(core.getInput)
.calledWith('extraHeaders')
.calledWith('extraHeaders', undefined)
.mockReturnValueOnce(null);
const result = parseHeadersInput('extraHeaders');
expect(Array.from(result)).toHaveLength(0);
});
})
describe('parseResponse', () => {
// https://www.vaultproject.io/api/secret/kv/kv-v1.html#sample-response
it('parses K/V version 1 response', () => {
const response = JSON.stringify({
data: {
foo: 'bar'
}
})
const output = parseResponse(response, 1);
expect(output).toEqual({
foo: 'bar'
});
});
// https://www.vaultproject.io/api/secret/kv/kv-v2.html#read-secret-version
it('parses K/V version 2 response', () => {
const response = JSON.stringify({
data: {
data: {
foo: 'bar'
}
}
})
const output = parseResponse(response, 2);
expect(output).toEqual({
foo: 'bar'
});
});
});
describe('exportSecrets', () => {
beforeEach(() => {
jest.resetAllMocks();
when(core.getInput)
.calledWith('url')
.calledWith('url', expect.anything())
.mockReturnValueOnce('http://vault:8200');
when(core.getInput)
.calledWith('token')
.calledWith('token', expect.anything())
.mockReturnValueOnce('EXAMPLE');
});
function mockInput(key) {
when(core.getInput)
.calledWith('secrets')
.calledWith('secrets', expect.anything())
.mockReturnValueOnce(key);
}
function mockVersion(version) {
when(core.getInput)
.calledWith('kv-version')
.calledWith('kv-version', expect.anything())
.mockReturnValueOnce(version);
}
function mockExtraHeaders(headerString) {
when(core.getInput)
.calledWith('extraHeaders')
.calledWith('extraHeaders', expect.anything())
.mockReturnValueOnce(headerString);
}
@@ -212,6 +178,23 @@ describe('exportSecrets', () => {
}
}
function mockExportToken(doExport) {
when(core.getInput)
.calledWith('exportToken', expect.anything())
.mockReturnValueOnce(doExport);
}
function mockOutputToken(doOutput) {
when(core.getInput)
.calledWith('outputToken', expect.anything())
.mockReturnValueOnce(doOutput);
}
function mockEncodeType(doEncode) {
when(core.getInput)
.calledWith('secretEncodingType', expect.anything())
.mockReturnValueOnce(doEncode);
}
it('simple secret retrieval', async () => {
mockInput('test key');
mockVaultData({
@@ -224,6 +207,83 @@ describe('exportSecrets', () => {
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('encoded secret retrieval', async () => {
mockInput('test key');
mockVaultData({
key: 'MQ=='
});
mockEncodeType('base64');
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', '1');
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('JSON data secret retrieval', async () => {
const jsonData = {"x":1,"y":2};
// for secrets stored in Vault as pure JSON, we call stringify twice
// and remove the added surrounding quotes
let result = JSON.stringify(JSON.stringify(jsonData));
result = result.substring(1, result.length - 1);
mockInput('test key');
mockVaultData({
key: jsonData,
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', result);
expect(core.setOutput).toBeCalledWith('key', result);
});
it('JSON string secret retrieval', async () => {
const jsonString = '{"x":1,"y":2}';
mockInput('test key');
mockVaultData({
key: jsonString,
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', jsonString);
expect(core.setOutput).toBeCalledWith('key', jsonString);
});
it('multi-line JSON string secret retrieval', async () => {
const jsonString = `
{
"x":1,
"y":"bar"
}
`;
mockInput('test key');
mockVaultData({
key: jsonString,
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY', jsonString);
expect(core.setOutput).toBeCalledWith('key', jsonString);
});
it('intl secret retrieval', async () => {
mockInput('测试 测试');
mockVaultData({
测试: 1
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('测试', '1');
expect(core.setOutput).toBeCalledWith('测试', '1');
});
it('mapped secret retrieval', async () => {
mockInput('test key|TEST_NAME');
mockVaultData({
@@ -267,4 +327,124 @@ describe('exportSecrets', () => {
expect(core.exportVariable).toBeCalledWith('KEY', '1');
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('nested secret retrieval', async () => {
mockInput('test key.value');
mockVaultData({
key: { value: 1 }
});
await exportSecrets();
expect(core.exportVariable).toBeCalledWith('KEY__VALUE', '1');
expect(core.setOutput).toBeCalledWith('key__value', '1');
});
it('export Vault token', async () => {
mockInput('test key');
mockVaultData({
key: 1
});
mockExportToken("true")
await exportSecrets();
expect(core.exportVariable).toBeCalledTimes(2);
expect(core.exportVariable).toBeCalledWith('VAULT_TOKEN', 'EXAMPLE');
expect(core.exportVariable).toBeCalledWith('KEY', '1');
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('not export Vault token', async () => {
mockInput('test key');
mockVaultData({
key: 1
});
mockExportToken("false")
await exportSecrets();
expect(core.exportVariable).toBeCalledTimes(1);
expect(core.exportVariable).toBeCalledWith('KEY', '1');
expect(core.setOutput).toBeCalledWith('key', '1');
});
it('single-line secret gets masked', async () => {
mockInput('test key');
mockVaultData({
key: 'secret'
});
mockExportToken("false")
await exportSecrets();
expect(core.setSecret).toBeCalledTimes(2);
expect(core.setSecret).toBeCalledWith('secret');
expect(core.setOutput).toBeCalledWith('key', 'secret');
})
it('multi-line secret', async () => {
const multiLineString = `ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU
GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3
Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA
NrRFi9wrf+M7Q==`;
mockInput('test key');
mockVaultData({
key: multiLineString
});
mockExportToken("false")
await exportSecrets();
expect(core.setSecret).toBeCalledTimes(5); // 1 for each non-empty line + VAULT_TOKEN
expect(core.setSecret).toBeCalledWith("ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAklOUpkDHrfHY17SbrmTIpNLTGK9Tjom/BWDSU");
expect(core.setSecret).toBeCalledWith("GPl+nafzlHDTYW7hdI4yZ5ew18JH4JW9jbhUFrviQzM7xlELEVf4h9lFX5QVkbPppSwg0cda3");
expect(core.setSecret).toBeCalledWith("Pbv7kOdJ/MTyBlWXFCR+HAo3FXRitBqxiX1nKhXpHAZsMciLq8V6RjsNAQwdsdMFvSlVK/7XA");
expect(core.setSecret).toBeCalledWith("NrRFi9wrf+M7Q==");
expect(core.setOutput).toBeCalledWith('key', multiLineString);
})
it('multi-line secret gets masked for each non-empty line', async () => {
const multiLineString = `a multi-line string
with blank lines
`
mockInput('test key');
mockVaultData({
key: multiLineString
});
mockExportToken("false")
await exportSecrets();
expect(core.setSecret).toBeCalledTimes(3); // 1 for each non-empty line.
expect(core.setSecret).toBeCalledWith('a multi-line string');
expect(core.setSecret).toBeCalledWith('with blank lines');
expect(core.setOutput).toBeCalledWith('key', multiLineString);
})
it('export only Vault token, no secrets', async () => {
mockExportToken("true")
await exportSecrets();
expect(core.exportVariable).toBeCalledTimes(1);
expect(core.exportVariable).toBeCalledWith('VAULT_TOKEN', 'EXAMPLE');
})
it('output only Vault token, no secrets', async () => {
mockOutputToken("true")
await exportSecrets();
expect(core.setOutput).toBeCalledTimes(1);
expect(core.setOutput).toBeCalledWith('vault_token', 'EXAMPLE');
})
});
+84 -6
View File
@@ -1,22 +1,64 @@
// @ts-check
const core = require('@actions/core');
const rsasign = require('jsrsasign');
const fs = require('fs');
const { default: got } = require('got');
const defaultKubernetesTokenPath = '/var/run/secrets/kubernetes.io/serviceaccount/token'
/***
* Authenticate with Vault and retrieve a Vault token that can be used for requests.
* @param {string} method
* @param {import('got').Got} client
*/
async function retrieveToken(method, client) {
let path = core.getInput('path', { required: false }) || method;
path = `v1/auth/${path}/login`
switch (method) {
case 'approle': {
const vaultRoleId = core.getInput('roleId', { required: true });
const vaultSecretId = core.getInput('secretId', { required: true });
return await getClientToken(client, method, { role_id: vaultRoleId, secret_id: vaultSecretId });
return await getClientToken(client, method, path, { role_id: vaultRoleId, secret_id: vaultSecretId });
}
case 'github': {
const githubToken = core.getInput('githubToken', { required: true });
return await getClientToken(client, method, { token: githubToken });
return await getClientToken(client, method, path, { token: githubToken });
}
case 'jwt': {
/** @type {string} */
let jwt;
const role = core.getInput('role', { required: false });
const privateKeyRaw = core.getInput('jwtPrivateKey', { required: false });
const privateKey = Buffer.from(privateKeyRaw, 'base64').toString();
const keyPassword = core.getInput('jwtKeyPassword', { required: false });
const tokenTtl = core.getInput('jwtTtl', { required: false }) || '3600'; // 1 hour
const githubAudience = core.getInput('jwtGithubAudience', { required: false });
if (!privateKey) {
jwt = await core.getIDToken(githubAudience)
} else {
jwt = generateJwt(privateKey, keyPassword, Number(tokenTtl));
}
return await getClientToken(client, method, path, { jwt: jwt, role: role });
}
case 'kubernetes': {
const role = core.getInput('role', { required: true })
const tokenPath = core.getInput('kubernetesTokenPath', { required: false }) || defaultKubernetesTokenPath
const data = fs.readFileSync(tokenPath, 'utf8')
if (!(role && data) && data != "") {
throw new Error("Role Name must be set and a kubernetes token must set")
}
return await getClientToken(client, method, path, { jwt: data, role: role })
}
case 'userpass':
case 'ldap': {
const username = core.getInput('username', { required: true });
const password = core.getInput('password', { required: true });
path = path + `/${username}`
return await getClientToken(client, method, path, { password: password })
}
default: {
if (!method || method === 'token') {
return core.getInput('token', { required: true });
@@ -26,19 +68,46 @@ async function retrieveToken(method, client) {
if (!payload) {
throw Error('When using a custom authentication method, you must provide the payload');
}
return await getClientToken(client, method, JSON.parse(payload.trim()));
return await getClientToken(client, method, path, JSON.parse(payload.trim()));
}
}
}
}
/***
* Generates signed Json Web Token with specified private key and ttl
* @param {string} privateKey
* @param {string} keyPassword
* @param {number} ttl
*/
function generateJwt(privateKey, keyPassword, ttl) {
const alg = 'RS256';
const header = { alg: alg, typ: 'JWT' };
const now = rsasign.KJUR.jws.IntDate.getNow();
const payload = {
iss: 'vault-action',
iat: now,
nbf: now,
exp: now + ttl,
event: process.env.GITHUB_EVENT_NAME,
workflow: process.env.GITHUB_WORKFLOW,
sha: process.env.GITHUB_SHA,
actor: process.env.GITHUB_ACTOR,
repository: process.env.GITHUB_REPOSITORY,
ref: process.env.GITHUB_REF
};
const decryptedKey = rsasign.KEYUTIL.getKey(privateKey, keyPassword);
return rsasign.KJUR.jws.JWS.sign(alg, JSON.stringify(header), JSON.stringify(payload), decryptedKey);
}
/***
* Call the appropriate login endpoint and parse out the token in the response.
* @param {import('got').Got} client
* @param {string} method
* @param {string} path
* @param {any} payload
*/
async function getClientToken(client, method, payload) {
async function getClientToken(client, method, path, payload) {
/** @type {'json'} */
const responseType = 'json';
var options = {
@@ -46,10 +115,19 @@ async function getClientToken(client, method, payload) {
responseType,
};
core.debug(`Retrieving Vault Token from v1/auth/${method}/login endpoint`);
core.debug(`Retrieving Vault Token from ${path} endpoint`);
/** @type {import('got').Response<VaultLoginResponse>} */
const response = await client.post(`v1/auth/${method}/login`, options);
let response;
try {
response = await client.post(`${path}`, options);
} catch (err) {
if (err instanceof got.HTTPError) {
throw Error(`failed to retrieve vault token. code: ${err.code}, message: ${err.message}, vaultResponse: ${JSON.stringify(err.response.body)}`)
} else {
throw err
}
}
if (response && response.body && response.body.auth && response.body.auth.client_token) {
core.debug('✔ Vault Token successfully retrieved');
+88
View File
@@ -0,0 +1,88 @@
jest.mock('got');
jest.mock('@actions/core');
jest.mock('@actions/core/lib/command');
jest.mock('fs', () => ({
stat: jest.fn().mockResolvedValue(null),
promises: {
access: jest.fn().mockResolvedValue(null),
}
}));
const core = require('@actions/core');
const got = require('got');
const fs = require("fs")
const { when } = require('jest-when');
const {
retrieveToken
} = require('./auth');
function mockInput(name, key) {
when(core.getInput)
.calledWith(name, expect.anything())
.mockReturnValueOnce(key);
}
function mockApiResponse() {
const response = { body: { auth: { client_token: testToken, renewable: true, policies: [], accessor: "accessor" } } }
got.post = jest.fn()
got.post.mockReturnValue(response)
}
const testToken = "testoken";
describe("test retrival for token", () => {
beforeEach(() => {
jest.resetAllMocks();
});
it("test retrival with approle", async () => {
const method = 'approle'
mockApiResponse()
const testRoleId = "testRoleId"
const testSecretId = "testSecretId"
mockInput("roleId", testRoleId)
mockInput("secretId", testSecretId)
const token = await retrieveToken(method, got)
expect(token).toEqual(testToken)
const payload = got.post.mock.calls[0][1].json
expect(payload).toEqual({ role_id: testRoleId, secret_id: testSecretId })
const url = got.post.mock.calls[0][0]
expect(url).toContain('approle')
})
it("test retrival with github token", async () => {
const method = 'github'
mockApiResponse()
const githubToken = "githubtoken"
mockInput("githubToken", githubToken)
const token = await retrieveToken(method, got)
expect(token).toEqual(testToken)
const payload = got.post.mock.calls[0][1].json
expect(payload).toEqual({ token: githubToken })
const url = got.post.mock.calls[0][0]
expect(url).toContain('github')
})
it("test retrival with kubernetes", async () => {
const method = 'kubernetes'
const jwtToken = "someJwtToken"
const testRole = "testRole"
const testTokenPath = "testTokenPath"
const testPath = 'differentK8sPath'
mockApiResponse()
mockInput("kubernetesTokenPath", testTokenPath)
mockInput("role", testRole)
mockInput("path", testPath)
fs.readFileSync = jest.fn()
fs.readFileSync.mockReturnValueOnce(jwtToken)
const token = await retrieveToken(method, got)
expect(token).toEqual(testToken)
const payload = got.post.mock.calls[0][1].json
expect(payload).toEqual({ jwt: jwtToken, role: testRole })
const url = got.post.mock.calls[0][0]
expect(url).toContain('differentK8sPath')
})
})
+11
View File
@@ -0,0 +1,11 @@
const core = require('@actions/core');
const { exportSecrets } = require('./action');
(async () => {
try {
await core.group('Get Vault Secrets', exportSecrets);
} catch (error) {
core.setOutput("errorMessage", error.message);
core.setFailed(error.message);
}
})();
+6 -9
View File
@@ -1,10 +1,7 @@
const core = require('@actions/core');
const { exportSecrets } = require('./action');
const auth = require('./auth');
const secrets = require('./secrets');
(async () => {
try {
await core.group('Get Vault Secrets', exportSecrets);
} catch (error) {
core.setFailed(error.message);
}
})();
module.exports = {
auth,
secrets
};
+69
View File
@@ -0,0 +1,69 @@
jest.mock('@actions/core');
const core = require('@actions/core');
const ServerMock = require("mock-http-server");
const { exportSecrets } = require("./action");
const { when } = require('jest-when');
describe('exportSecrets retries', () => {
var server = new ServerMock({ host: "127.0.0.1", port: 0 });
var calls = 0;
beforeEach((done) => {
calls = 0;
jest.resetAllMocks();
when(core.getInput)
.calledWith('token', expect.anything())
.mockReturnValueOnce('EXAMPLE');
when(core.getInput)
.calledWith('secrets', expect.anything())
.mockReturnValueOnce("kv/mysecret key");
server.start(() => {
expect(server.getHttpPort()).not.toBeNull();
when(core.getInput)
.calledWith('url', expect.anything())
.mockReturnValueOnce('http://127.0.0.1:' + server.getHttpPort());
done();
});
});
afterEach((done) => {
server.stop(done);
});
function mockStatusCodes(statusCodes) {
server.on({
path: '/v1/kv/mysecret',
reply: {
status: function() {
let status = statusCodes[calls];
calls += 1;
return status;
},
headers: { "content-type": "application/json" },
body: function() {
return JSON.stringify({ data: {"key": "value"} })
}
}
});
}
it('retries on 412 status code', (done) => {
mockStatusCodes([412, 200])
exportSecrets().then(() => {
expect(calls).toEqual(2);
done();
});
});
it('retries on 500 status code', (done) => {
mockStatusCodes([500, 200])
exportSecrets().then(() => {
expect(calls).toEqual(2);
done();
});
});
});
+140
View File
@@ -0,0 +1,140 @@
const jsonata = require("jsonata");
/**
* @typedef {Object} SecretRequest
* @property {string} path
* @property {string} selector
*/
/**
* @template {SecretRequest} TRequest
* @typedef {Object} SecretResponse
* @property {TRequest} request
* @property {string} value
* @property {boolean} cachedResponse
*/
/**
* @template TRequest
* @param {Array<TRequest>} secretRequests
* @param {import('got').Got} client
* @return {Promise<SecretResponse<TRequest>[]>}
*/
async function getSecrets(secretRequests, client) {
const responseCache = new Map();
const results = [];
for (const secretRequest of secretRequests) {
let { path, selector } = secretRequest;
const requestPath = `v1/${path}`;
let body;
let cachedResponse = false;
if (responseCache.has(requestPath)) {
body = responseCache.get(requestPath);
cachedResponse = true;
} else {
try {
const result = await client.get(requestPath);
body = result.body;
responseCache.set(requestPath, body);
} catch (error) {
const {response} = error;
if (response?.statusCode === 404) {
throw Error(`Unable to retrieve result for "${path}" because it was not found: ${response.body.trim()}`)
}
throw error
}
}
if (!selector.match(/.*[\.].*/)) {
selector = '"' + selector + '"'
}
selector = "data." + selector
body = JSON.parse(body)
if (body.data["data"] != undefined) {
selector = "data." + selector
}
const value = await selectData(body, selector);
results.push({
request: secretRequest,
value,
cachedResponse
});
}
return results;
}
/**
* Uses a Jsonata selector retrieve a bit of data from the result
* @param {object} data
* @param {string} selector
*/
async function selectData(data, selector) {
const ata = jsonata(selector);
let d = await ata.evaluate(data);
console.log(selector);
// If we have a Javascript Object, then this data was stored in Vault as
// pure JSON (not a JSON string). We will capture that before we stringify it.
const storedAsJSONData = isObject(d);
result = JSON.stringify(d);
// Compat for custom engines
if (!result && ((ata.ast().type === "path" && ata.ast()['steps'].length === 1) || ata.ast().type === "string") && selector !== 'data' && 'data' in data) {
result = JSON.stringify(await jsonata(`data.${selector}`).evaluate(data));
} else if (!result) {
throw Error(`Unable to retrieve result for ${selector}. No match data was found. Double check your Key or Selector.`);
}
if (result.startsWith(`"`)) {
// we need to strip the beginning and ending quotes otherwise it will
// always successfully parse as a JSON string
// result = result.substring(1, result.length - 1);
// if (!isJSONString(result)) {
// // add the quotes back so we can parse it into a Javascript object
// // to allow support for multi-line secrets. See https://github.com/hashicorp/vault-action/issues/160
// result = `"${result}"`
console.log(" =>>> PARSING")
result = JSON.parse(result);
// }
} else {
console.log('does not start with quote')
// Support secrets stored in Vault as pure JSON.
// See https://github.com/hashicorp/vault-action/issues/194 and https://github.com/hashicorp/vault-action/pull/173
result = JSON.stringify(result);
result = result.substring(1, result.length - 1);
}
return result;
}
/**
* isOjbect returns true if target is a Javascript object
* @param {Type} target
*/
function isObject(target) {
return typeof target === 'object' && target !== null;
}
/**
* isJSONString returns true if target parses as a valid JSON string
* @param {Type} target
*/
function isJSONString(target) {
if (typeof target !== "string"){
return false;
}
try {
JSON.parse(target);
} catch (e) {
return false;
}
return true;
}
module.exports = {
getSecrets,
selectData
}