mirror of
https://github.com/awatertrevi/infisical.git
synced 2026-09-22 13:39:35 +00:00
Finish preliminary Vault integration, made docs for Vault and Checkly
This commit is contained in:
@@ -86,47 +86,53 @@ export const saveIntegrationAccessToken = async (
|
||||
// TODO: check if access token is valid for each integration
|
||||
|
||||
let integrationAuth;
|
||||
const {
|
||||
workspaceId,
|
||||
accessId,
|
||||
accessToken,
|
||||
integration
|
||||
}: {
|
||||
workspaceId: string;
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
integration: string;
|
||||
} = req.body;
|
||||
const {
|
||||
workspaceId,
|
||||
accessId,
|
||||
accessToken,
|
||||
url,
|
||||
namespace,
|
||||
integration
|
||||
}: {
|
||||
workspaceId: string;
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
url: string;
|
||||
namespace: string;
|
||||
integration: string;
|
||||
} = req.body;
|
||||
|
||||
const bot = await Bot.findOne({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
isActive: true
|
||||
});
|
||||
const bot = await Bot.findOne({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
isActive: true
|
||||
});
|
||||
|
||||
if (!bot) throw new Error('Bot must be enabled to save integration access token');
|
||||
if (!bot) throw new Error('Bot must be enabled to save integration access token');
|
||||
|
||||
integrationAuth = await IntegrationAuth.findOneAndUpdate({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration
|
||||
}, {
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration,
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8
|
||||
}, {
|
||||
new: true,
|
||||
upsert: true
|
||||
});
|
||||
integrationAuth = await IntegrationAuth.findOneAndUpdate({
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration
|
||||
}, {
|
||||
workspace: new Types.ObjectId(workspaceId),
|
||||
integration,
|
||||
url,
|
||||
namespace,
|
||||
algorithm: ALGORITHM_AES_256_GCM,
|
||||
keyEncoding: ENCODING_SCHEME_UTF8
|
||||
}, {
|
||||
new: true,
|
||||
upsert: true
|
||||
});
|
||||
|
||||
// encrypt and save integration access details
|
||||
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
|
||||
integrationAuthId: integrationAuth._id.toString(),
|
||||
accessId,
|
||||
accessToken,
|
||||
accessExpiresAt: undefined
|
||||
});
|
||||
// encrypt and save integration access details
|
||||
integrationAuth = await IntegrationService.setIntegrationAuthAccess({
|
||||
integrationAuthId: integrationAuth._id.toString(),
|
||||
accessId,
|
||||
accessToken,
|
||||
accessExpiresAt: undefined
|
||||
});
|
||||
|
||||
if (!integrationAuth) throw new Error('Failed to save integration access token');
|
||||
if (!integrationAuth) throw new Error('Failed to save integration access token');
|
||||
|
||||
return res.status(200).send({
|
||||
integrationAuth
|
||||
|
||||
@@ -57,6 +57,7 @@ export const createIntegration = async (req: Request, res: Response) => {
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(200).send({
|
||||
integration,
|
||||
});
|
||||
|
||||
@@ -36,7 +36,8 @@ import {
|
||||
INTEGRATION_TRAVISCI_API_URL,
|
||||
INTEGRATION_SUPABASE_API_URL,
|
||||
INTEGRATION_CHECKLY,
|
||||
INTEGRATION_CHECKLY_API_URL
|
||||
INTEGRATION_CHECKLY_API_URL,
|
||||
INTEGRATION_HASHICORP_VAULT
|
||||
} from "../variables";
|
||||
import { standardRequest} from '../config/request';
|
||||
|
||||
@@ -200,6 +201,15 @@ const syncSecrets = async ({
|
||||
accessToken,
|
||||
});
|
||||
break;
|
||||
case INTEGRATION_HASHICORP_VAULT:
|
||||
await syncSecretsHashiCorpVault({
|
||||
integration,
|
||||
integrationAuth,
|
||||
secrets,
|
||||
accessId,
|
||||
accessToken
|
||||
});
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1762,5 +1772,65 @@ const syncSecretsCheckly = async ({
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Sync/push [secrets] to HashiCorp Vault path
|
||||
* @param {Object} obj
|
||||
* @param {IIntegration} obj.integration - integration details
|
||||
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
|
||||
* @param {String} obj.accessToken - access token for HashiCorp Vault integration
|
||||
*/
|
||||
const syncSecretsHashiCorpVault = async ({
|
||||
integration,
|
||||
integrationAuth,
|
||||
secrets,
|
||||
accessId,
|
||||
accessToken,
|
||||
}: {
|
||||
integration: IIntegration;
|
||||
integrationAuth: IIntegrationAuth;
|
||||
secrets: any;
|
||||
accessId: string | null;
|
||||
accessToken: string;
|
||||
}) => {
|
||||
if (!accessId) return;
|
||||
|
||||
interface LoginAppRoleRes {
|
||||
auth: {
|
||||
client_token: string;
|
||||
}
|
||||
}
|
||||
|
||||
// get Vault client token (could be optimized)
|
||||
const { data }: { data: LoginAppRoleRes } = await standardRequest.post(
|
||||
`${integrationAuth.url}/v1/auth/approle/login`,
|
||||
{
|
||||
"role_id": accessId,
|
||||
"secret_id": accessToken
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"X-Vault-Namespace": integrationAuth.namespace
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
const clientToken = data.auth.client_token;
|
||||
|
||||
await standardRequest.post(
|
||||
`${integrationAuth.url}/v1/${integration.app}/data/${integration.path}`,
|
||||
{
|
||||
data: secrets
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
"Authorization": `Bearer ${accessToken}`,
|
||||
"Accept": "application/json",
|
||||
"Content-Type": "application/json",
|
||||
"X-Vault-Token": clientToken,
|
||||
"X-Vault-Namespace": integrationAuth.namespace
|
||||
},
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
export { syncSecrets };
|
||||
|
||||
@@ -3,6 +3,7 @@ import { ErrorRequestHandler } from 'express';
|
||||
import { InternalServerError } from '../utils/errors';
|
||||
import { getLogger } from '../utils/logger';
|
||||
import RequestError, { LogLevel } from '../utils/requestError';
|
||||
import { getNodeEnv } from '../config';
|
||||
|
||||
export const requestErrorHandler: ErrorRequestHandler = async (
|
||||
error: RequestError | Error,
|
||||
@@ -12,6 +13,11 @@ export const requestErrorHandler: ErrorRequestHandler = async (
|
||||
) => {
|
||||
if (res.headersSent) return next();
|
||||
|
||||
if (await getNodeEnv() !== "production") {
|
||||
/* eslint-disable no-console */
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
//TODO: Find better way to type check for error. In current setting you need to cast type to get the functions and variables from RequestError
|
||||
if (!(error instanceof RequestError)) {
|
||||
error = InternalServerError({
|
||||
|
||||
@@ -14,7 +14,8 @@ import {
|
||||
INTEGRATION_CIRCLECI,
|
||||
INTEGRATION_TRAVISCI,
|
||||
INTEGRATION_SUPABASE,
|
||||
INTEGRATION_CHECKLY
|
||||
INTEGRATION_CHECKLY,
|
||||
INTEGRATION_HASHICORP_VAULT
|
||||
} from "../variables";
|
||||
|
||||
export interface IIntegration {
|
||||
@@ -47,7 +48,8 @@ export interface IIntegration {
|
||||
| 'circleci'
|
||||
| 'travisci'
|
||||
| 'supabase'
|
||||
| 'checkly';
|
||||
| 'checkly'
|
||||
| 'hashicorp-vault';
|
||||
integrationAuth: Types.ObjectId;
|
||||
}
|
||||
|
||||
@@ -133,7 +135,8 @@ const integrationSchema = new Schema<IIntegration>(
|
||||
INTEGRATION_CIRCLECI,
|
||||
INTEGRATION_TRAVISCI,
|
||||
INTEGRATION_SUPABASE,
|
||||
INTEGRATION_CHECKLY
|
||||
INTEGRATION_CHECKLY,
|
||||
INTEGRATION_HASHICORP_VAULT
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
INTEGRATION_CIRCLECI,
|
||||
INTEGRATION_TRAVISCI,
|
||||
INTEGRATION_SUPABASE,
|
||||
INTEGRATION_HASHICORP_VAULT,
|
||||
ALGORITHM_AES_256_GCM,
|
||||
ENCODING_SCHEME_UTF8,
|
||||
ENCODING_SCHEME_BASE64
|
||||
@@ -25,6 +26,8 @@ export interface IIntegrationAuth extends Document {
|
||||
integration: 'heroku' | 'vercel' | 'netlify' | 'github' | 'gitlab' | 'render' | 'railway' | 'flyio' | 'azure-key-vault' | 'circleci' | 'travisci' | 'supabase' | 'aws-parameter-store' | 'aws-secret-manager' | 'checkly';
|
||||
teamId: string;
|
||||
accountId: string;
|
||||
url: string;
|
||||
namespace: string;
|
||||
refreshCiphertext?: string;
|
||||
refreshIV?: string;
|
||||
refreshTag?: string;
|
||||
@@ -62,7 +65,8 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
|
||||
INTEGRATION_FLYIO,
|
||||
INTEGRATION_CIRCLECI,
|
||||
INTEGRATION_TRAVISCI,
|
||||
INTEGRATION_SUPABASE
|
||||
INTEGRATION_SUPABASE,
|
||||
INTEGRATION_HASHICORP_VAULT
|
||||
],
|
||||
required: true,
|
||||
},
|
||||
@@ -70,6 +74,14 @@ const integrationAuthSchema = new Schema<IIntegrationAuth>(
|
||||
// vercel-specific integration param
|
||||
type: String,
|
||||
},
|
||||
url: {
|
||||
// for any self-hosted integrations (e.g. self-hosted hashicorp-vault)
|
||||
type: String
|
||||
},
|
||||
namespace: {
|
||||
// hashicorp-vault-specific integration param
|
||||
type: String
|
||||
},
|
||||
accountId: {
|
||||
// netlify-specific integration param
|
||||
type: String,
|
||||
|
||||
@@ -15,7 +15,7 @@ import {
|
||||
import { body, param } from 'express-validator';
|
||||
import { integrationController } from '../../controllers/v1';
|
||||
|
||||
router.post( // new: add new integration for integration auth
|
||||
router.post(
|
||||
'/',
|
||||
requireAuth({
|
||||
acceptedAuthModes: [AUTH_MODE_JWT, AUTH_MODE_API_KEY]
|
||||
|
||||
@@ -57,6 +57,8 @@ router.post(
|
||||
body('workspaceId').exists().trim().notEmpty(),
|
||||
body('accessId').trim(),
|
||||
body('accessToken').exists().trim().notEmpty(),
|
||||
body('url').trim(),
|
||||
body('namespace').trim(),
|
||||
body('integration').exists().trim().notEmpty(),
|
||||
validateRequest,
|
||||
requireAuth({
|
||||
|
||||
@@ -23,6 +23,7 @@ export const INTEGRATION_CIRCLECI = "circleci";
|
||||
export const INTEGRATION_TRAVISCI = "travisci";
|
||||
export const INTEGRATION_SUPABASE = 'supabase';
|
||||
export const INTEGRATION_CHECKLY = 'checkly';
|
||||
export const INTEGRATION_HASHICORP_VAULT = 'hashicorp-vault';
|
||||
export const INTEGRATION_SET = new Set([
|
||||
INTEGRATION_AZURE_KEY_VAULT,
|
||||
INTEGRATION_HEROKU,
|
||||
@@ -35,7 +36,8 @@ export const INTEGRATION_SET = new Set([
|
||||
INTEGRATION_CIRCLECI,
|
||||
INTEGRATION_TRAVISCI,
|
||||
INTEGRATION_SUPABASE,
|
||||
INTEGRATION_CHECKLY
|
||||
INTEGRATION_CHECKLY,
|
||||
INTEGRATION_HASHICORP_VAULT
|
||||
]);
|
||||
|
||||
// integration types
|
||||
@@ -202,6 +204,15 @@ export const getIntegrationOptions = async () => {
|
||||
clientId: '',
|
||||
docsLink: ''
|
||||
},
|
||||
{
|
||||
name: 'HashiCorp Vault',
|
||||
slug: 'hashicorp-vault',
|
||||
image: 'Vault.png',
|
||||
isAvailable: true,
|
||||
type: 'pat',
|
||||
clientId: '',
|
||||
docsLink: ''
|
||||
},
|
||||
{
|
||||
name: 'Google Cloud Platform',
|
||||
slug: 'gcp',
|
||||
|
||||
Reference in New Issue
Block a user