mirror of
https://github.com/hashicorp/vault-action.git
synced 2026-04-16 16:55:45 +00:00
parent
2fb925f14c
commit
2e4d53b28d
6 changed files with 268 additions and 41 deletions
101
src/action.js
101
src/action.js
|
|
@ -8,27 +8,13 @@ const { WILDCARD } = require('./constants');
|
|||
|
||||
const { auth: { retrieveToken }, secrets: { getSecrets } } = require('./index');
|
||||
|
||||
const VAULT_TOKEN_STATE = "VAULT_TOKEN";
|
||||
const AUTH_METHODS = ['approle', 'token', 'github', 'jwt', 'kubernetes', 'ldap', 'userpass'];
|
||||
const ENCODING_TYPES = ['base64', 'hex', 'utf8'];
|
||||
|
||||
async function exportSecrets() {
|
||||
|
||||
function getDefaultOptions() {
|
||||
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';
|
||||
|
||||
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();
|
||||
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,
|
||||
|
|
@ -44,6 +30,8 @@ async function exportSecrets() {
|
|||
}
|
||||
}
|
||||
|
||||
const extraHeaders = parseHeadersInput('extraHeaders', { required: false });
|
||||
const vaultNamespace = core.getInput('namespace', { required: false });
|
||||
const tlsSkipVerify = (core.getInput('tlsSkipVerify', { required: false }) || 'false').toLowerCase() != 'false';
|
||||
if (tlsSkipVerify === true) {
|
||||
defaultOptions.https.rejectUnauthorized = false;
|
||||
|
|
@ -56,12 +44,12 @@ async function exportSecrets() {
|
|||
|
||||
const clientCertificateRaw = core.getInput('clientCertificate', { required: false });
|
||||
if (clientCertificateRaw != null) {
|
||||
defaultOptions.https.certificate = Buffer.from(clientCertificateRaw, 'base64').toString();
|
||||
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();
|
||||
defaultOptions.https.key = Buffer.from(clientKeyRaw, 'base64').toString();
|
||||
}
|
||||
|
||||
for (const [headerName, headerValue] of extraHeaders) {
|
||||
|
|
@ -72,13 +60,37 @@ async function exportSecrets() {
|
|||
defaultOptions.headers["X-Vault-Namespace"] = vaultNamespace;
|
||||
}
|
||||
|
||||
return defaultOptions
|
||||
}
|
||||
|
||||
async function exportSecrets() {
|
||||
const exportEnv = core.getInput('exportEnv', { required: false }) != 'false';
|
||||
const revokeToken = core.getInput("revokeToken", { required: false }) !== 'false'
|
||||
const outputToken = (core.getInput('outputToken', { required: false }) || 'false').toLowerCase() != 'false';
|
||||
const exportToken = (core.getInput('exportToken', { required: false }) || 'false').toLowerCase() != 'false';
|
||||
|
||||
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();
|
||||
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 = getDefaultOptions();
|
||||
const vaultToken = await retrieveToken(vaultMethod, got.extend(defaultOptions));
|
||||
core.setSecret(vaultToken)
|
||||
if (revokeToken) {
|
||||
core.saveState(VAULT_TOKEN_STATE, vaultToken)
|
||||
}
|
||||
defaultOptions.headers['X-Vault-Token'] = vaultToken;
|
||||
const client = got.extend(defaultOptions);
|
||||
|
||||
if (outputToken === true) {
|
||||
core.setOutput('vault_token', `${vaultToken}`);
|
||||
core.setOutput('vault_token', `${vaultToken}`);
|
||||
}
|
||||
if (exportToken === true) {
|
||||
core.exportVariable('VAULT_TOKEN', `${vaultToken}`);
|
||||
|
|
@ -134,7 +146,7 @@ async function exportSecrets() {
|
|||
*/
|
||||
function parseSecretsInput(secretsInput) {
|
||||
if (!secretsInput) {
|
||||
return []
|
||||
return []
|
||||
}
|
||||
|
||||
const secrets = secretsInput
|
||||
|
|
@ -219,9 +231,56 @@ function parseHeadersInput(inputKey, inputOptions) {
|
|||
}, new Map());
|
||||
}
|
||||
|
||||
async function revokeToken() {
|
||||
const token = core.getState(VAULT_TOKEN_STATE)
|
||||
if (!token || token === "") {
|
||||
core.debug(`provided token in state (${VAULT_TOKEN_STATE}) is empty. skipping...`)
|
||||
return
|
||||
}
|
||||
core.setSecret(token)
|
||||
|
||||
const defaultOptions = getDefaultOptions();
|
||||
defaultOptions.headers['X-Vault-Token'] = token;
|
||||
const client = got.extend(defaultOptions);
|
||||
try {
|
||||
await revokeClientToken(client)
|
||||
} catch (err) {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {import('got').Got} client
|
||||
*/
|
||||
async function revokeClientToken(client) {
|
||||
const path = "v1/auth/token/revoke-self"
|
||||
/** @type {'json'} */
|
||||
const responseType = 'json';
|
||||
var options = {
|
||||
responseType,
|
||||
};
|
||||
|
||||
core.debug(`Revoking Vault Token from ${path} endpoint`);
|
||||
|
||||
let response;
|
||||
try {
|
||||
response = await client.post(path, options);
|
||||
} catch (err) {
|
||||
if (err instanceof got.HTTPError) {
|
||||
throw Error(`failed to revoke vault token. code: ${err.code}, message: ${err.message}, vaultResponse: ${JSON.stringify(err.response.body)}`)
|
||||
} else {
|
||||
throw err
|
||||
}
|
||||
}
|
||||
core.debug('✔ Vault Token successfully revoked');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
exportSecrets,
|
||||
parseSecretsInput,
|
||||
parseHeadersInput,
|
||||
getDefaultOptions,
|
||||
revokeToken,
|
||||
VAULT_TOKEN_STATE
|
||||
};
|
||||
|
||||
|
|
|
|||
11
src/revoke.js
Normal file
11
src/revoke.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const core = require('@actions/core');
|
||||
const { revokeToken } = require('./action');
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
await revokeToken()
|
||||
} catch (error) {
|
||||
core.setOutput("errorMessage", error.message);
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
})();
|
||||
38
src/revoke.test.js
Normal file
38
src/revoke.test.js
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
jest.mock('got');
|
||||
jest.mock('@actions/core');
|
||||
|
||||
const core = require('@actions/core');
|
||||
const got = require('got');
|
||||
const { when } = require('jest-when');
|
||||
|
||||
const {
|
||||
revokeClientToken
|
||||
} = require('./revoke');
|
||||
|
||||
function mockApiResponse() {
|
||||
const response = ""
|
||||
got.post = jest.fn()
|
||||
got.post.mockReturnValue(response)
|
||||
}
|
||||
function mockApiFailure() {
|
||||
const response = { "errors": ["permission denied"] }
|
||||
got.post = jest.fn()
|
||||
got.post.mockReturnValue(response)
|
||||
}
|
||||
describe("test revoke for token", () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
it("test revoke with success", async () => {
|
||||
mockApiResponse()
|
||||
await revokeClientToken(got)
|
||||
console.log(got.post.mock.calls[0][1])
|
||||
})
|
||||
|
||||
it("test revoke with error", async () => {
|
||||
mockApiFailure()
|
||||
await revokeClientToken(got)
|
||||
console.log(got.post.mock.calls[0][1])
|
||||
})
|
||||
})
|
||||
Loading…
Add table
Add a link
Reference in a new issue