diff --git a/README.md b/README.md
index a5ed5c621..e3e0ae521 100644
--- a/README.md
+++ b/README.md
@@ -48,25 +48,26 @@
## Introduction
-**[Infisical](https://infisical.com)** is the open source secret management platform that teams use to centralize their secrets like API keys, database credentials, and configurations.
+**[Infisical](https://infisical.com)** is the open source secret management platform that teams use to centralize their application configuration and secrets like API keys and database credentials as well as manage their internal PKI.
-We're on a mission to make secret management more accessible to everyone, not just security teams, and that means redesigning the entire developer experience from ground up.
+We're on a mission to make security tooling more accessible to everyone, not just security teams, and that means redesigning the entire developer experience from ground up.
## Features
-- **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.).
-- **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand.
-- **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development and CI/CD.
-- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** to perform CRUD operation on secrets, users, projects, and any other resource in Infisical.
-- **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like [GitHub](https://infisical.com/docs/integrations/cicd/githubactions), [Vercel](https://infisical.com/docs/integrations/cloud/vercel), [AWS](https://infisical.com/docs/integrations/cloud/aws-secret-manager), and tools like [Terraform](https://infisical.com/docs/integrations/frameworks/terraform), [Ansible](https://infisical.com/docs/integrations/platforms/ansible), and more.
+- **[User-friendly dashboard](https://infisical.com/docs/documentation/platform/project)** to manage secrets across projects and environments (e.g. development, production, etc.).
+- **[Client SDKs](https://infisical.com/docs/sdks/overview)** to fetch secrets for your apps and infrastructure on demand.
+- **[Infisical CLI](https://infisical.com/docs/cli/overview)** to fetch and inject secrets into any framework in local development and CI/CD.
+- **[Infisical API](https://infisical.com/docs/api-reference/overview/introduction)** to perform CRUD operation on secrets, users, projects, and any other resource in Infisical.
+- **[Native integrations](https://infisical.com/docs/integrations/overview)** with platforms like [GitHub](https://infisical.com/docs/integrations/cicd/githubactions), [Vercel](https://infisical.com/docs/integrations/cloud/vercel), [AWS](https://infisical.com/docs/integrations/cloud/aws-secret-manager), and tools like [Terraform](https://infisical.com/docs/integrations/frameworks/terraform), [Ansible](https://infisical.com/docs/integrations/platforms/ansible), and more.
- **[Infisical Kubernetes operator](https://infisical.com/docs/documentation/getting-started/kubernetes)** to managed secrets in k8s, automatically reload deployments, and more.
-- **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)** to inject secrets into your applications without modifying any code logic.
+- **[Infisical Agent](https://infisical.com/docs/infisical-agent/overview)** to inject secrets into your applications without modifying any code logic.
- **[Self-hosting and on-prem](https://infisical.com/docs/self-hosting/overview)** to get complete control over your data.
-- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery](https://infisical.com/docs/documentation/platform/pit-recovery)** to version every secret and project state.
-- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project.
-- **[Role-based Access Controls](https://infisical.com/docs/documentation/platform/role-based-access-controls)** to create permission sets on any resource in Infisica and assign those to user or machine identities.
+- **[Secret versioning](https://infisical.com/docs/documentation/platform/secret-versioning)** and **[Point-in-Time Recovery](https://infisical.com/docs/documentation/platform/pit-recovery)** to version every secret and project state.
+- **[Audit logs](https://infisical.com/docs/documentation/platform/audit-logs)** to record every action taken in a project.
+- **[Role-based Access Controls](https://infisical.com/docs/documentation/platform/role-based-access-controls)** to create permission sets on any resource in Infisica and assign those to user or machine identities.
- **[Simple on-premise deployments](https://infisical.com/docs/self-hosting/overview)** to AWS, Digital Ocean, and more.
-- **[Secret Scanning and Leak Prevention](https://infisical.com/docs/cli/scanning-overview)** to prevent secrets from leaking to git.
+- **[Internal PKI](https://infisical.com/docs/documentation/platform/pki/private-ca)** to create Private CA hierarchies and start issuing and managing X.509 digital certificates.
+- **[Secret Scanning and Leak Prevention](https://infisical.com/docs/cli/scanning-overview)** to prevent secrets from leaking to git.
And much more.
@@ -74,9 +75,9 @@ And much more.
Check out the [Quickstart Guides](https://infisical.com/docs/getting-started/introduction)
-| Use Infisical Cloud | Deploy Infisical on premise |
-| ------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| The fastest and most reliable way to get started with Infisical is signing up for free to [Infisical Cloud](https://app.infisical.com/login). | View all [deployment options](https://infisical.com/docs/self-hosting/overview) |
+| Use Infisical Cloud | Deploy Infisical on premise |
+| ------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ |
+| The fastest and most reliable way to get started with Infisical is signing up for free to [Infisical Cloud](https://app.infisical.com/login). | View all [deployment options](https://infisical.com/docs/self-hosting/overview) |
### Run Infisical locally
diff --git a/backend/src/db/migrations/20240609133400_private-key-handoff.ts b/backend/src/db/migrations/20240609133400_private-key-handoff.ts
new file mode 100644
index 000000000..9741c6029
--- /dev/null
+++ b/backend/src/db/migrations/20240609133400_private-key-handoff.ts
@@ -0,0 +1,61 @@
+import { Knex } from "knex";
+
+import { TableName } from "../schemas";
+
+export async function up(knex: Knex): Promise {
+ const doesPasswordFieldExist = await knex.schema.hasColumn(TableName.UserEncryptionKey, "hashedPassword");
+ const doesPrivateKeyFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKey"
+ );
+ const doesPrivateKeyIVFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKeyIV"
+ );
+ const doesPrivateKeyTagFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKeyTag"
+ );
+ const doesPrivateKeyEncodingFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKeyEncoding"
+ );
+ if (await knex.schema.hasTable(TableName.UserEncryptionKey)) {
+ await knex.schema.alterTable(TableName.UserEncryptionKey, (t) => {
+ if (!doesPasswordFieldExist) t.string("hashedPassword");
+ if (!doesPrivateKeyFieldExist) t.text("serverEncryptedPrivateKey");
+ if (!doesPrivateKeyIVFieldExist) t.text("serverEncryptedPrivateKeyIV");
+ if (!doesPrivateKeyTagFieldExist) t.text("serverEncryptedPrivateKeyTag");
+ if (!doesPrivateKeyEncodingFieldExist) t.text("serverEncryptedPrivateKeyEncoding");
+ });
+ }
+}
+
+export async function down(knex: Knex): Promise {
+ const doesPasswordFieldExist = await knex.schema.hasColumn(TableName.UserEncryptionKey, "hashedPassword");
+ const doesPrivateKeyFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKey"
+ );
+ const doesPrivateKeyIVFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKeyIV"
+ );
+ const doesPrivateKeyTagFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKeyTag"
+ );
+ const doesPrivateKeyEncodingFieldExist = await knex.schema.hasColumn(
+ TableName.UserEncryptionKey,
+ "serverEncryptedPrivateKeyEncoding"
+ );
+ if (await knex.schema.hasTable(TableName.UserEncryptionKey)) {
+ await knex.schema.alterTable(TableName.UserEncryptionKey, (t) => {
+ if (doesPasswordFieldExist) t.dropColumn("hashedPassword");
+ if (doesPrivateKeyFieldExist) t.dropColumn("serverEncryptedPrivateKey");
+ if (doesPrivateKeyIVFieldExist) t.dropColumn("serverEncryptedPrivateKeyIV");
+ if (doesPrivateKeyTagFieldExist) t.dropColumn("serverEncryptedPrivateKeyTag");
+ if (doesPrivateKeyEncodingFieldExist) t.dropColumn("serverEncryptedPrivateKeyEncoding");
+ });
+ }
+}
diff --git a/backend/src/db/schemas/user-encryption-keys.ts b/backend/src/db/schemas/user-encryption-keys.ts
index 693b73b4c..fd9d21a9d 100644
--- a/backend/src/db/schemas/user-encryption-keys.ts
+++ b/backend/src/db/schemas/user-encryption-keys.ts
@@ -21,7 +21,12 @@ export const UserEncryptionKeysSchema = z.object({
tag: z.string(),
salt: z.string(),
verifier: z.string(),
- userId: z.string().uuid()
+ userId: z.string().uuid(),
+ hashedPassword: z.string().nullable().optional(),
+ serverEncryptedPrivateKey: z.string().nullable().optional(),
+ serverEncryptedPrivateKeyIV: z.string().nullable().optional(),
+ serverEncryptedPrivateKeyTag: z.string().nullable().optional(),
+ serverEncryptedPrivateKeyEncoding: z.string().nullable().optional()
});
export type TUserEncryptionKeys = z.infer;
diff --git a/backend/src/ee/services/ldap-config/ldap-config-service.ts b/backend/src/ee/services/ldap-config/ldap-config-service.ts
index dd49bd0ae..8027f5907 100644
--- a/backend/src/ee/services/ldap-config/ldap-config-service.ts
+++ b/backend/src/ee/services/ldap-config/ldap-config-service.ts
@@ -73,7 +73,13 @@ type TLdapConfigServiceFactoryDep = {
>;
userDAL: Pick<
TUserDALFactory,
- "create" | "findOne" | "transaction" | "updateById" | "findUserEncKeyByUserIdsBatch" | "find"
+ | "create"
+ | "findOne"
+ | "transaction"
+ | "updateById"
+ | "findUserEncKeyByUserIdsBatch"
+ | "find"
+ | "findUserEncKeyByUserId"
>;
userAliasDAL: Pick;
permissionService: Pick;
@@ -592,12 +598,14 @@ export const ldapConfigServiceFactory = ({
});
const isUserCompleted = Boolean(user.isAccepted);
+ const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
const providerAuthToken = jwt.sign(
{
authTokenType: AuthTokenType.PROVIDER_TOKEN,
userId: user.id,
username: user.username,
+ hasExchangedPrivateKey: Boolean(userEnc?.serverEncryptedPrivateKey),
...(user.email && { email: user.email, isEmailVerified: user.isEmailVerified }),
firstName,
lastName,
diff --git a/backend/src/ee/services/saml-config/saml-config-service.ts b/backend/src/ee/services/saml-config/saml-config-service.ts
index 5d7b7ec3b..3cc51e1c2 100644
--- a/backend/src/ee/services/saml-config/saml-config-service.ts
+++ b/backend/src/ee/services/saml-config/saml-config-service.ts
@@ -41,7 +41,10 @@ import { TCreateSamlCfgDTO, TGetSamlCfgDTO, TSamlLoginDTO, TUpdateSamlCfgDTO } f
type TSamlConfigServiceFactoryDep = {
samlConfigDAL: Pick;
- userDAL: Pick;
+ userDAL: Pick<
+ TUserDALFactory,
+ "create" | "findOne" | "transaction" | "updateById" | "findById" | "findUserEncKeyByUserId"
+ >;
userAliasDAL: Pick;
orgDAL: Pick<
TOrgDALFactory,
@@ -452,6 +455,7 @@ export const samlConfigServiceFactory = ({
await licenseService.updateSubscriptionOrgMemberCount(organization.id);
const isUserCompleted = Boolean(user.isAccepted);
+ const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
const providerAuthToken = jwt.sign(
{
authTokenType: AuthTokenType.PROVIDER_TOKEN,
@@ -464,6 +468,7 @@ export const samlConfigServiceFactory = ({
organizationId: organization.id,
organizationSlug: organization.slug,
authMethod: authProvider,
+ hasExchangedPrivateKey: Boolean(userEnc?.serverEncryptedPrivateKey),
authType: UserAliasType.SAML,
isUserCompleted,
...(relayState
diff --git a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts
index 92c6b611d..4092bf356 100644
--- a/backend/src/ee/services/secret-snapshot/snapshot-dal.ts
+++ b/backend/src/ee/services/secret-snapshot/snapshot-dal.ts
@@ -331,8 +331,8 @@ export const snapshotDALFactory = (db: TDbClient) => {
* Prunes excess snapshots from the database to ensure only a specified number of recent snapshots are retained for each folder.
*
* This function operates in three main steps:
- * 1. Pruning snapshots from root/non-versioned folders.
- * 2. Pruning snapshots from versioned folders.
+ * 1. Pruning snapshots from current folders.
+ * 2. Pruning snapshots from non-current folders (versioned ones).
* 3. Removing orphaned snapshots that do not belong to any existing folder or folder version.
*
* The function processes snapshots in batches, determined by the `PRUNE_FOLDER_BATCH_SIZE` constant,
@@ -350,7 +350,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
try {
let uuidOffset = "00000000-0000-0000-0000-000000000000";
- // cleanup snapshots from root/non-versioned folders
+ // cleanup snapshots from current folders
// eslint-disable-next-line no-constant-condition, no-unreachable-loop
while (true) {
const folderBatch = await db(TableName.SecretFolder)
@@ -382,12 +382,11 @@ export const snapshotDALFactory = (db: TDbClient) => {
.join(TableName.Environment, `${TableName.Environment}.id`, `${TableName.SecretFolder}.envId`)
.join(TableName.Project, `${TableName.Project}.id`, `${TableName.Environment}.projectId`)
.join("snapshot_cte", "snapshot_cte.id", `${TableName.Snapshot}.id`)
- .whereNull(`${TableName.SecretFolder}.parentId`)
.whereRaw(`snapshot_cte.row_num > ${TableName.Project}."pitVersionLimit"`)
.delete();
} catch (err) {
logger.error(
- `Failed to prune snapshots from root/non-versioned folders in range ${batchEntries[0]}:${
+ `Failed to prune snapshots from current folders in range ${batchEntries[0]}:${
batchEntries[batchEntries.length - 1]
}`
);
@@ -399,7 +398,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
}
}
- // cleanup snapshots from versioned folders
+ // cleanup snapshots from non-current folders
uuidOffset = "00000000-0000-0000-0000-000000000000";
// eslint-disable-next-line no-constant-condition
while (true) {
@@ -440,7 +439,7 @@ export const snapshotDALFactory = (db: TDbClient) => {
.delete();
} catch (err) {
logger.error(
- `Failed to prune snapshots from versioned folders in range ${batchEntries[0]}:${
+ `Failed to prune snapshots from non-current folders in range ${batchEntries[0]}:${
batchEntries[batchEntries.length - 1]
}`
);
diff --git a/backend/src/lib/config/env.ts b/backend/src/lib/config/env.ts
index 80a2111fc..f4da71293 100644
--- a/backend/src/lib/config/env.ts
+++ b/backend/src/lib/config/env.ts
@@ -29,7 +29,7 @@ const envSchema = z
DB_USER: zpStr(z.string().describe("Postgres database username").optional()),
DB_PASSWORD: zpStr(z.string().describe("Postgres database password").optional()),
DB_NAME: zpStr(z.string().describe("Postgres database name").optional()),
-
+ BCRYPT_SALT_ROUND: z.number().default(12),
NODE_ENV: z.enum(["development", "test", "production"]).default("production"),
SALT_ROUNDS: z.coerce.number().default(10),
INITIAL_ORGANIZATION_NAME: zpStr(z.string().optional()),
diff --git a/backend/src/lib/crypto/srp.ts b/backend/src/lib/crypto/srp.ts
index bc29cdb3f..8d7ea656a 100644
--- a/backend/src/lib/crypto/srp.ts
+++ b/backend/src/lib/crypto/srp.ts
@@ -6,7 +6,7 @@ import tweetnacl from "tweetnacl-util";
import { TUserEncryptionKeys } from "@app/db/schemas";
-import { decryptSymmetric, encryptAsymmetric, encryptSymmetric } from "./encryption";
+import { decryptSymmetric128BitHexKeyUTF8, encryptAsymmetric, encryptSymmetric } from "./encryption";
export const generateSrpServerKey = async (salt: string, verifier: string) => {
// eslint-disable-next-line new-cap
@@ -97,7 +97,13 @@ export const generateUserSrpKeys = async (email: string, password: string) => {
};
};
-export const getUserPrivateKey = async (password: string, user: TUserEncryptionKeys) => {
+export const getUserPrivateKey = async (
+ password: string,
+ user: Pick<
+ TUserEncryptionKeys,
+ "protectedKeyTag" | "protectedKey" | "protectedKeyIV" | "encryptedPrivateKey" | "iv" | "salt" | "tag"
+ >
+) => {
const derivedKey = await argon2.hash(password, {
salt: Buffer.from(user.salt),
memoryCost: 65536,
@@ -108,17 +114,18 @@ export const getUserPrivateKey = async (password: string, user: TUserEncryptionK
raw: true
});
if (!derivedKey) throw new Error("Failed to derive key from password");
- const key = decryptSymmetric({
- ciphertext: user.protectedKey!,
- iv: user.protectedKeyIV!,
- tag: user.protectedKeyTag!,
- key: derivedKey.toString("base64")
+ const key = decryptSymmetric128BitHexKeyUTF8({
+ ciphertext: user.protectedKey as string,
+ iv: user.protectedKeyIV as string,
+ tag: user.protectedKeyTag as string,
+ key: derivedKey
});
- const privateKey = decryptSymmetric({
+
+ const privateKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
- key
+ key: Buffer.from(key, "hex")
});
return privateKey;
};
diff --git a/backend/src/server/routes/v1/admin-router.ts b/backend/src/server/routes/v1/admin-router.ts
index d8f64b86f..ea701c828 100644
--- a/backend/src/server/routes/v1/admin-router.ts
+++ b/backend/src/server/routes/v1/admin-router.ts
@@ -80,6 +80,7 @@ export const registerAdminRouter = async (server: FastifyZodProvider) => {
schema: {
body: z.object({
email: z.string().email().trim(),
+ password: z.string().trim(),
firstName: z.string().trim(),
lastName: z.string().trim().optional(),
protectedKey: z.string().trim(),
diff --git a/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts b/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts
index d20ea0edc..227345916 100644
--- a/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts
+++ b/backend/src/server/routes/v1/identity-kubernetes-auth-router.ts
@@ -198,7 +198,7 @@ export const registerIdentityKubernetesRouter = async (server: FastifyZodProvide
}),
response: {
200: z.object({
- identityKubernetesAuth: IdentityKubernetesAuthsSchema
+ identityKubernetesAuth: IdentityKubernetesAuthResponseSchema
})
}
},
diff --git a/backend/src/server/routes/v1/password-router.ts b/backend/src/server/routes/v1/password-router.ts
index a8ef3fb77..c94e5d4cb 100644
--- a/backend/src/server/routes/v1/password-router.ts
+++ b/backend/src/server/routes/v1/password-router.ts
@@ -51,7 +51,8 @@ export const registerPasswordRouter = async (server: FastifyZodProvider) => {
encryptedPrivateKeyIV: z.string().trim(),
encryptedPrivateKeyTag: z.string().trim(),
salt: z.string().trim(),
- verifier: z.string().trim()
+ verifier: z.string().trim(),
+ password: z.string().trim()
}),
response: {
200: z.object({
diff --git a/backend/src/server/routes/v1/sso-router.ts b/backend/src/server/routes/v1/sso-router.ts
index 60bbec7db..3d8d02e6f 100644
--- a/backend/src/server/routes/v1/sso-router.ts
+++ b/backend/src/server/routes/v1/sso-router.ts
@@ -259,4 +259,50 @@ export const registerSsoRouter = async (server: FastifyZodProvider) => {
);
}
});
+
+ server.route({
+ url: "/token-exchange",
+ method: "POST",
+ schema: {
+ body: z.object({
+ providerAuthToken: z.string(),
+ email: z.string()
+ })
+ },
+ handler: async (req, res) => {
+ const userAgent = req.headers["user-agent"];
+ if (!userAgent) throw new Error("user agent header is required");
+
+ const data = await server.services.login.oauth2TokenExchange({
+ email: req.body.email,
+ ip: req.realIp,
+ userAgent,
+ providerAuthToken: req.body.providerAuthToken
+ });
+
+ if (data.isMfaEnabled) {
+ return { mfaEnabled: true, token: data.token } as const; // for discriminated union
+ }
+
+ void res.setCookie("jid", data.token.refresh, {
+ httpOnly: true,
+ path: "/",
+ sameSite: "strict",
+ secure: appCfg.HTTPS_ENABLED
+ });
+
+ return {
+ mfaEnabled: false,
+ encryptionVersion: data.user.encryptionVersion,
+ token: data.token.access,
+ publicKey: data.user.publicKey,
+ encryptedPrivateKey: data.user.encryptedPrivateKey,
+ iv: data.user.iv,
+ tag: data.user.tag,
+ protectedKey: data.user.protectedKey || null,
+ protectedKeyIV: data.user.protectedKeyIV || null,
+ protectedKeyTag: data.user.protectedKeyTag || null
+ } as const;
+ }
+ });
};
diff --git a/backend/src/server/routes/v1/user-router.ts b/backend/src/server/routes/v1/user-router.ts
index 3d9f531b9..b9269d66e 100644
--- a/backend/src/server/routes/v1/user-router.ts
+++ b/backend/src/server/routes/v1/user-router.ts
@@ -19,7 +19,23 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
schema: {
response: {
200: z.object({
- user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))
+ user: UsersSchema.merge(
+ UserEncryptionKeysSchema.pick({
+ clientPublicKey: true,
+ serverPrivateKey: true,
+ encryptionVersion: true,
+ protectedKey: true,
+ protectedKeyIV: true,
+ protectedKeyTag: true,
+ publicKey: true,
+ encryptedPrivateKey: true,
+ iv: true,
+ tag: true,
+ salt: true,
+ verifier: true,
+ userId: true
+ })
+ )
})
}
},
@@ -30,6 +46,26 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
}
});
+ server.route({
+ method: "GET",
+ url: "/private-key",
+ config: {
+ rateLimit: readLimit
+ },
+ schema: {
+ response: {
+ 200: z.object({
+ privateKey: z.string()
+ })
+ }
+ },
+ onRequest: verifyAuth([AuthMode.JWT], { requireOrg: false }),
+ handler: async (req) => {
+ const privateKey = await server.services.user.getUserPrivateKey(req.permission.id);
+ return { privateKey };
+ }
+ });
+
server.route({
method: "GET",
url: "/:userId/unlock",
diff --git a/backend/src/server/routes/v2/user-router.ts b/backend/src/server/routes/v2/user-router.ts
index 1f15008c7..21dd32021 100644
--- a/backend/src/server/routes/v2/user-router.ts
+++ b/backend/src/server/routes/v2/user-router.ts
@@ -255,7 +255,23 @@ export const registerUserRouter = async (server: FastifyZodProvider) => {
description: "Retrieve the current user on the request",
response: {
200: z.object({
- user: UsersSchema.merge(UserEncryptionKeysSchema.omit({ verifier: true }))
+ user: UsersSchema.merge(
+ UserEncryptionKeysSchema.pick({
+ clientPublicKey: true,
+ serverPrivateKey: true,
+ encryptionVersion: true,
+ protectedKey: true,
+ protectedKeyIV: true,
+ protectedKeyTag: true,
+ publicKey: true,
+ encryptedPrivateKey: true,
+ iv: true,
+ tag: true,
+ salt: true,
+ verifier: true,
+ userId: true
+ })
+ )
})
}
},
diff --git a/backend/src/server/routes/v3/login-router.ts b/backend/src/server/routes/v3/login-router.ts
index 4c7df5612..61a0c74e5 100644
--- a/backend/src/server/routes/v3/login-router.ts
+++ b/backend/src/server/routes/v3/login-router.ts
@@ -81,7 +81,8 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
email: z.string().trim(),
providerAuthToken: z.string().trim().optional(),
clientProof: z.string().trim(),
- captchaToken: z.string().trim().optional()
+ captchaToken: z.string().trim().optional(),
+ password: z.string().optional()
}),
response: {
200: z.discriminatedUnion("mfaEnabled", [
@@ -112,7 +113,8 @@ export const registerLoginRouter = async (server: FastifyZodProvider) => {
ip: req.realIp,
userAgent,
providerAuthToken: req.body.providerAuthToken,
- clientProof: req.body.clientProof
+ clientProof: req.body.clientProof,
+ password: req.body.password
});
if (data.isMfaEnabled) {
diff --git a/backend/src/server/routes/v3/signup-router.ts b/backend/src/server/routes/v3/signup-router.ts
index ac43df36d..59131464a 100644
--- a/backend/src/server/routes/v3/signup-router.ts
+++ b/backend/src/server/routes/v3/signup-router.ts
@@ -102,7 +102,8 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
verifier: z.string().trim(),
organizationName: z.string().trim().min(1),
providerAuthToken: z.string().trim().optional().nullish(),
- attributionSource: z.string().trim().optional()
+ attributionSource: z.string().trim().optional(),
+ password: z.string()
}),
response: {
200: z.object({
@@ -167,6 +168,7 @@ export const registerSignupRouter = async (server: FastifyZodProvider) => {
schema: {
body: z.object({
email: z.string().email().trim(),
+ password: z.string(),
firstName: z.string().trim(),
lastName: z.string().trim().optional(),
protectedKey: z.string().trim(),
diff --git a/backend/src/services/auth/auth-fns.ts b/backend/src/services/auth/auth-fns.ts
index ecbf73a48..e8574a8b9 100644
--- a/backend/src/services/auth/auth-fns.ts
+++ b/backend/src/services/auth/auth-fns.ts
@@ -15,10 +15,10 @@ export const validateProviderAuthToken = (providerToken: string, username?: stri
if (decodedToken.username !== username) throw new Error("Invalid auth credentials");
if (decodedToken.organizationId) {
- return { orgId: decodedToken.organizationId, authMethod: decodedToken.authMethod };
+ return { orgId: decodedToken.organizationId, authMethod: decodedToken.authMethod, userName: decodedToken.username };
}
- return { authMethod: decodedToken.authMethod, orgId: null };
+ return { authMethod: decodedToken.authMethod, orgId: null, userName: decodedToken.username };
};
export const validateSignUpAuthorization = (token: string, userId: string, validate = true) => {
diff --git a/backend/src/services/auth/auth-login-service.ts b/backend/src/services/auth/auth-login-service.ts
index d6d99f682..3a85b6996 100644
--- a/backend/src/services/auth/auth-login-service.ts
+++ b/backend/src/services/auth/auth-login-service.ts
@@ -1,3 +1,4 @@
+import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { TUsers, UserDeviceSchema } from "@app/db/schemas";
@@ -5,6 +6,8 @@ import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
import { getConfig } from "@app/lib/config/env";
import { request } from "@app/lib/config/request";
import { generateSrpServerKey, srpCheckClientProof } from "@app/lib/crypto";
+import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
+import { getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError, DatabaseError, UnauthorizedError } from "@app/lib/errors";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
@@ -19,6 +22,7 @@ import {
TLoginClientProofDTO,
TLoginGenServerPublicKeyDTO,
TOauthLoginDTO,
+ TOauthTokenExchangeDTO,
TVerifyMfaTokenDTO
} from "./auth-login-type";
import { AuthMethod, AuthModeJwtTokenPayload, AuthModeMfaJwtTokenPayload, AuthTokenType } from "./auth-type";
@@ -101,7 +105,7 @@ export const authLoginServiceFactory = ({
user: TUsers;
ip: string;
userAgent: string;
- organizationId: string | undefined;
+ organizationId?: string;
authMethod: AuthMethod;
}) => {
const cfg = getConfig();
@@ -178,7 +182,8 @@ export const authLoginServiceFactory = ({
ip,
userAgent,
providerAuthToken,
- captchaToken
+ captchaToken,
+ password
}: TLoginClientProofDTO) => {
const appCfg = getConfig();
@@ -251,14 +256,29 @@ export const authLoginServiceFactory = ({
throw new Error("Failed to authenticate. Try again?");
}
- await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
- serverPrivateKey: null,
- clientPublicKey: null
- });
-
await userDAL.updateById(userEnc.userId, {
consecutiveFailedPasswordAttempts: 0
});
+ // from password decrypt the private key
+ if (password) {
+ const privateKey = await getUserPrivateKey(password, userEnc);
+ const hashedPassword = await bcrypt.hash(password, cfg.BCRYPT_SALT_ROUND);
+ const { iv, tag, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
+ await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
+ serverPrivateKey: null,
+ clientPublicKey: null,
+ hashedPassword,
+ serverEncryptedPrivateKey: ciphertext,
+ serverEncryptedPrivateKeyIV: iv,
+ serverEncryptedPrivateKeyTag: tag,
+ serverEncryptedPrivateKeyEncoding: encoding
+ });
+ } else {
+ await userDAL.updateUserEncryptionByUserId(userEnc.userId, {
+ serverPrivateKey: null,
+ clientPublicKey: null
+ });
+ }
// send multi factor auth token if they it enabled
if (userEnc.isMfaEnabled && userEnc.email) {
@@ -502,8 +522,14 @@ export const authLoginServiceFactory = ({
authMethods: [authMethod],
isGhost: false
});
+ } else {
+ const isLinkingRequired = !user?.authMethods?.includes(authMethod);
+ if (isLinkingRequired) {
+ user = await userDAL.updateById(user.id, { authMethods: [...(user.authMethods || []), authMethod] });
+ }
}
- const isLinkingRequired = !user?.authMethods?.includes(authMethod);
+
+ const userEnc = await userDAL.findUserEncKeyByUserId(user.id);
const isUserCompleted = user.isAccepted;
const providerAuthToken = jwt.sign(
{
@@ -514,9 +540,9 @@ export const authLoginServiceFactory = ({
isEmailVerified: user.isEmailVerified,
firstName: user.firstName,
lastName: user.lastName,
+ hasExchangedPrivateKey: Boolean(userEnc?.serverEncryptedPrivateKey),
authMethod,
isUserCompleted,
- isLinkingRequired,
...(callbackPort
? {
callbackPort
@@ -528,10 +554,71 @@ export const authLoginServiceFactory = ({
expiresIn: appCfg.JWT_PROVIDER_AUTH_LIFETIME
}
);
-
return { isUserCompleted, providerAuthToken };
};
+ /**
+ * Handles OAuth2 token exchange for user login with private key handoff.
+ *
+ * The process involves exchanging a provider's authorization token for an Infisical access token.
+ * The provider token is returned to the client, who then sends it back to obtain the Infisical access token.
+ *
+ * This approach is used instead of directly sending the access token for the following reasons:
+ * 1. To facilitate easier logic changes from SRP OAuth to simple OAuth.
+ * 2. To avoid attaching the access token to the URL, which could be logged. The provider token has a very short lifespan, reducing security risks.
+ */
+ const oauth2TokenExchange = async ({ userAgent, ip, providerAuthToken, email }: TOauthTokenExchangeDTO) => {
+ const decodedProviderToken = validateProviderAuthToken(providerAuthToken, email);
+
+ const appCfg = getConfig();
+ const { authMethod, userName } = decodedProviderToken;
+ if (!userName) throw new BadRequestError({ message: "Missing user name" });
+ const organizationId =
+ (isAuthMethodSaml(authMethod) || authMethod === AuthMethod.LDAP) && decodedProviderToken.orgId
+ ? decodedProviderToken.orgId
+ : undefined;
+
+ const userEnc = await userDAL.findUserEncKeyByUsername({
+ username: email
+ });
+ if (!userEnc) throw new BadRequestError({ message: "Invalid token" });
+ if (!userEnc.serverEncryptedPrivateKey)
+ throw new BadRequestError({ message: "Key handoff incomplete. Please try logging in again." });
+ // send multi factor auth token if they it enabled
+ if (userEnc.isMfaEnabled && userEnc.email) {
+ enforceUserLockStatus(Boolean(userEnc.isLocked), userEnc.temporaryLockDateEnd);
+
+ const mfaToken = jwt.sign(
+ {
+ authMethod,
+ authTokenType: AuthTokenType.MFA_TOKEN,
+ userId: userEnc.userId
+ },
+ appCfg.AUTH_SECRET,
+ {
+ expiresIn: appCfg.JWT_MFA_LIFETIME
+ }
+ );
+
+ await sendUserMfaCode({
+ userId: userEnc.userId,
+ email: userEnc.email
+ });
+
+ return { isMfaEnabled: true, token: mfaToken } as const;
+ }
+
+ const token = await generateUserTokens({
+ user: { ...userEnc, id: userEnc.userId },
+ ip,
+ userAgent,
+ authMethod,
+ organizationId
+ });
+
+ return { token, isMfaEnabled: false, user: userEnc } as const;
+ };
+
/*
* logout user by incrementing the version by 1 meaning any old session will become invalid
* as there number is behind
@@ -545,6 +632,7 @@ export const authLoginServiceFactory = ({
loginExchangeClientProof,
logout,
oauth2Login,
+ oauth2TokenExchange,
resendMfaToken,
verifyMfaToken,
selectOrganization,
diff --git a/backend/src/services/auth/auth-login-type.ts b/backend/src/services/auth/auth-login-type.ts
index 4f73ec996..db57d730e 100644
--- a/backend/src/services/auth/auth-login-type.ts
+++ b/backend/src/services/auth/auth-login-type.ts
@@ -13,6 +13,7 @@ export type TLoginClientProofDTO = {
ip: string;
userAgent: string;
captchaToken?: string;
+ password?: string;
};
export type TVerifyMfaTokenDTO = {
@@ -31,3 +32,10 @@ export type TOauthLoginDTO = {
authMethod: AuthMethod;
callbackPort?: string;
};
+
+export type TOauthTokenExchangeDTO = {
+ providerAuthToken: string;
+ ip: string;
+ userAgent: string;
+ email: string;
+};
diff --git a/backend/src/services/auth/auth-password-service.ts b/backend/src/services/auth/auth-password-service.ts
index a400c297b..0e6558966 100644
--- a/backend/src/services/auth/auth-password-service.ts
+++ b/backend/src/services/auth/auth-password-service.ts
@@ -1,3 +1,4 @@
+import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { SecretEncryptionAlgo, SecretKeyEncoding } from "@app/db/schemas";
@@ -57,7 +58,8 @@ export const authPaswordServiceFactory = ({
encryptedPrivateKeyTag,
salt,
verifier,
- tokenVersionId
+ tokenVersionId,
+ password
}: TChangePasswordDTO) => {
const userEnc = await userDAL.findUserEncKeyByUserId(userId);
if (!userEnc) throw new Error("Failed to find user");
@@ -76,6 +78,8 @@ export const authPaswordServiceFactory = ({
);
if (!isValidClientProof) throw new Error("Failed to authenticate. Try again?");
+ const appCfg = getConfig();
+ const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
await userDAL.updateUserEncryptionByUserId(userId, {
encryptionVersion: 2,
protectedKey,
@@ -87,7 +91,8 @@ export const authPaswordServiceFactory = ({
salt,
verifier,
serverPrivateKey: null,
- clientPublicKey: null
+ clientPublicKey: null,
+ hashedPassword
});
if (tokenVersionId) {
diff --git a/backend/src/services/auth/auth-password-type.ts b/backend/src/services/auth/auth-password-type.ts
index cf2aac08d..a52374506 100644
--- a/backend/src/services/auth/auth-password-type.ts
+++ b/backend/src/services/auth/auth-password-type.ts
@@ -10,6 +10,7 @@ export type TChangePasswordDTO = {
salt: string;
verifier: string;
tokenVersionId?: string;
+ password: string;
};
export type TResetPasswordViaBackupKeyDTO = {
diff --git a/backend/src/services/auth/auth-signup-service.ts b/backend/src/services/auth/auth-signup-service.ts
index 528cb44fa..8cf2c9d34 100644
--- a/backend/src/services/auth/auth-signup-service.ts
+++ b/backend/src/services/auth/auth-signup-service.ts
@@ -1,3 +1,4 @@
+import bcrypt from "bcrypt";
import jwt from "jsonwebtoken";
import { OrgMembershipStatus, TableName } from "@app/db/schemas";
@@ -6,6 +7,8 @@ import { TUserGroupMembershipDALFactory } from "@app/ee/services/group/user-grou
import { TLicenseServiceFactory } from "@app/ee/services/license/license-service";
import { isAuthMethodSaml } from "@app/ee/services/permission/permission-fns";
import { getConfig } from "@app/lib/config/env";
+import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
+import { getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError } from "@app/lib/errors";
import { isDisposableEmail } from "@app/lib/validator";
import { TGroupProjectDALFactory } from "@app/services/group-project/group-project-dal";
@@ -119,6 +122,7 @@ export const authSignupServiceFactory = ({
const completeEmailAccountSignup = async ({
email,
+ password,
firstName,
lastName,
providerAuthToken,
@@ -137,6 +141,7 @@ export const authSignupServiceFactory = ({
userAgent,
authorization
}: TCompleteAccountSignupDTO) => {
+ const appCfg = getConfig();
const user = await userDAL.findOne({ username: email });
if (!user || (user && user.isAccepted)) {
throw new Error("Failed to complete account for complete user");
@@ -152,6 +157,17 @@ export const authSignupServiceFactory = ({
validateSignUpAuthorization(authorization, user.id);
}
+ const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
+ const privateKey = await getUserPrivateKey(password, {
+ salt,
+ protectedKey,
+ protectedKeyIV,
+ protectedKeyTag,
+ encryptedPrivateKey,
+ iv: encryptedPrivateKeyIV,
+ tag: encryptedPrivateKeyTag
+ });
+ const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(privateKey);
const updateduser = await authDAL.transaction(async (tx) => {
const us = await userDAL.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
if (!us) throw new Error("User not found");
@@ -166,7 +182,12 @@ export const authSignupServiceFactory = ({
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
- tag: encryptedPrivateKeyTag
+ tag: encryptedPrivateKeyTag,
+ hashedPassword,
+ serverEncryptedPrivateKeyEncoding: encoding,
+ serverEncryptedPrivateKeyTag: tag,
+ serverEncryptedPrivateKeyIV: iv,
+ serverEncryptedPrivateKey: ciphertext
},
tx
);
@@ -227,7 +248,6 @@ export const authSignupServiceFactory = ({
userId: updateduser.info.id
});
if (!tokenSession) throw new Error("Failed to create token");
- const appCfg = getConfig();
const accessToken = jwt.sign(
{
@@ -265,6 +285,7 @@ export const authSignupServiceFactory = ({
ip,
salt,
email,
+ password,
verifier,
firstName,
publicKey,
@@ -295,6 +316,18 @@ export const authSignupServiceFactory = ({
name: "complete account invite"
});
+ const appCfg = getConfig();
+ const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
+ const privateKey = await getUserPrivateKey(password, {
+ salt,
+ protectedKey,
+ protectedKeyIV,
+ protectedKeyTag,
+ encryptedPrivateKey,
+ iv: encryptedPrivateKeyIV,
+ tag: encryptedPrivateKeyTag
+ });
+ const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(privateKey);
const updateduser = await authDAL.transaction(async (tx) => {
const us = await userDAL.updateById(user.id, { firstName, lastName, isAccepted: true }, tx);
if (!us) throw new Error("User not found");
@@ -310,7 +343,12 @@ export const authSignupServiceFactory = ({
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
- tag: encryptedPrivateKeyTag
+ tag: encryptedPrivateKeyTag,
+ hashedPassword,
+ serverEncryptedPrivateKeyEncoding: encoding,
+ serverEncryptedPrivateKeyTag: tag,
+ serverEncryptedPrivateKeyIV: iv,
+ serverEncryptedPrivateKey: ciphertext
},
tx
);
@@ -343,7 +381,6 @@ export const authSignupServiceFactory = ({
userId: updateduser.info.id
});
if (!tokenSession) throw new Error("Failed to create token");
- const appCfg = getConfig();
const accessToken = jwt.sign(
{
diff --git a/backend/src/services/auth/auth-signup-type.ts b/backend/src/services/auth/auth-signup-type.ts
index a37a1cd96..9cd70f8c7 100644
--- a/backend/src/services/auth/auth-signup-type.ts
+++ b/backend/src/services/auth/auth-signup-type.ts
@@ -1,5 +1,6 @@
export type TCompleteAccountSignupDTO = {
email: string;
+ password: string;
firstName: string;
lastName?: string;
protectedKey: string;
@@ -21,6 +22,7 @@ export type TCompleteAccountSignupDTO = {
export type TCompleteAccountInviteDTO = {
email: string;
+ password: string;
firstName: string;
lastName?: string;
protectedKey: string;
diff --git a/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts b/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts
index 8ee8c36bd..f1e1c6be0 100644
--- a/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts
+++ b/backend/src/services/identity-kubernetes-auth/identity-kubernetes-auth-service.ts
@@ -442,7 +442,34 @@ export const identityKubernetesAuthServiceFactory = ({
const updatedKubernetesAuth = await identityKubernetesAuthDAL.updateById(identityKubernetesAuth.id, updateQuery);
- return { ...updatedKubernetesAuth, orgId: identityMembershipOrg.orgId };
+ const updatedCACert =
+ updatedKubernetesAuth.encryptedCaCert && updatedKubernetesAuth.caCertIV && updatedKubernetesAuth.caCertTag
+ ? decryptSymmetric({
+ ciphertext: updatedKubernetesAuth.encryptedCaCert,
+ iv: updatedKubernetesAuth.caCertIV,
+ tag: updatedKubernetesAuth.caCertTag,
+ key
+ })
+ : "";
+
+ const updatedTokenReviewerJwt =
+ updatedKubernetesAuth.encryptedTokenReviewerJwt &&
+ updatedKubernetesAuth.tokenReviewerJwtIV &&
+ updatedKubernetesAuth.tokenReviewerJwtTag
+ ? decryptSymmetric({
+ ciphertext: updatedKubernetesAuth.encryptedTokenReviewerJwt,
+ iv: updatedKubernetesAuth.tokenReviewerJwtIV,
+ tag: updatedKubernetesAuth.tokenReviewerJwtTag,
+ key
+ })
+ : "";
+
+ return {
+ ...updatedKubernetesAuth,
+ orgId: identityMembershipOrg.orgId,
+ caCert: updatedCACert,
+ tokenReviewerJwt: updatedTokenReviewerJwt
+ };
};
const getKubernetesAuth = async ({
diff --git a/backend/src/services/super-admin/super-admin-service.ts b/backend/src/services/super-admin/super-admin-service.ts
index bec8f3f37..f1d931b20 100644
--- a/backend/src/services/super-admin/super-admin-service.ts
+++ b/backend/src/services/super-admin/super-admin-service.ts
@@ -1,6 +1,10 @@
+import bcrypt from "bcrypt";
+
import { TSuperAdmin, TSuperAdminUpdate } from "@app/db/schemas";
import { TKeyStoreFactory } from "@app/keystore/keystore";
import { getConfig } from "@app/lib/config/env";
+import { infisicalSymmetricEncypt } from "@app/lib/crypto/encryption";
+import { getUserPrivateKey } from "@app/lib/crypto/srp";
import { BadRequestError } from "@app/lib/errors";
import { TAuthLoginFactory } from "../auth/auth-login-service";
@@ -77,6 +81,7 @@ export const superAdminServiceFactory = ({
firstName,
salt,
email,
+ password,
verifier,
publicKey,
protectedKey,
@@ -92,6 +97,17 @@ export const superAdminServiceFactory = ({
const existingUser = await userDAL.findOne({ email });
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
+ const privateKey = await getUserPrivateKey(password, {
+ salt,
+ protectedKey,
+ protectedKeyIV,
+ protectedKeyTag,
+ encryptedPrivateKey,
+ iv: encryptedPrivateKeyIV,
+ tag: encryptedPrivateKeyTag
+ });
+ const hashedPassword = await bcrypt.hash(password, appCfg.BCRYPT_SALT_ROUND);
+ const { iv, tag, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
const userInfo = await userDAL.transaction(async (tx) => {
const newUser = await userDAL.create(
{
@@ -119,7 +135,12 @@ export const superAdminServiceFactory = ({
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag,
verifier,
- userId: newUser.id
+ userId: newUser.id,
+ hashedPassword,
+ serverEncryptedPrivateKey: ciphertext,
+ serverEncryptedPrivateKeyIV: iv,
+ serverEncryptedPrivateKeyTag: tag,
+ serverEncryptedPrivateKeyEncoding: encoding
},
tx
);
diff --git a/backend/src/services/super-admin/super-admin-types.ts b/backend/src/services/super-admin/super-admin-types.ts
index e586946f2..e444c8843 100644
--- a/backend/src/services/super-admin/super-admin-types.ts
+++ b/backend/src/services/super-admin/super-admin-types.ts
@@ -1,5 +1,6 @@
export type TAdminSignUpDTO = {
email: string;
+ password: string;
publicKey: string;
salt: string;
lastName?: string;
diff --git a/backend/src/services/user/user-service.ts b/backend/src/services/user/user-service.ts
index 693078fbd..4ee8bdc1f 100644
--- a/backend/src/services/user/user-service.ts
+++ b/backend/src/services/user/user-service.ts
@@ -1,3 +1,5 @@
+import { SecretKeyEncoding } from "@app/db/schemas";
+import { infisicalSymmetricDecrypt } from "@app/lib/crypto/encryption";
import { BadRequestError } from "@app/lib/errors";
import { TAuthTokenServiceFactory } from "@app/services/auth-token/auth-token-service";
import { TokenType } from "@app/services/auth-token/auth-token-types";
@@ -230,6 +232,21 @@ export const userServiceFactory = ({
);
};
+ const getUserPrivateKey = async (userId: string) => {
+ const user = await userDAL.findUserEncKeyByUserId(userId);
+ if (!user?.serverEncryptedPrivateKey || !user.serverEncryptedPrivateKeyIV || !user.serverEncryptedPrivateKeyTag) {
+ throw new BadRequestError({ message: "Private key not found. Please login again" });
+ }
+ const privateKey = infisicalSymmetricDecrypt({
+ ciphertext: user.serverEncryptedPrivateKey,
+ tag: user.serverEncryptedPrivateKeyTag,
+ iv: user.serverEncryptedPrivateKeyIV,
+ keyEncoding: user.serverEncryptedPrivateKeyEncoding as SecretKeyEncoding
+ });
+
+ return privateKey;
+ };
+
return {
sendEmailVerificationCode,
verifyEmailVerificationCode,
@@ -240,6 +257,7 @@ export const userServiceFactory = ({
getMe,
createUserAction,
getUserAction,
- unlockUser
+ unlockUser,
+ getUserPrivateKey
};
};
diff --git a/cli/go.mod b/cli/go.mod
index 6a1da8c6d..a2144b674 100644
--- a/cli/go.mod
+++ b/cli/go.mod
@@ -10,6 +10,7 @@ require (
github.com/fatih/semgroup v1.2.0
github.com/gitleaks/go-gitdiff v0.8.0
github.com/h2non/filetype v1.1.3
+ github.com/infisical/go-sdk v0.2.0
github.com/mattn/go-isatty v0.0.14
github.com/muesli/ansi v0.0.0-20221106050444-61f0cd9a192a
github.com/muesli/mango-cobra v1.2.0
@@ -22,23 +23,48 @@ require (
github.com/rs/zerolog v1.26.1
github.com/spf13/cobra v1.6.1
github.com/spf13/viper v1.8.1
- github.com/stretchr/testify v1.8.1
- golang.org/x/crypto v0.14.0
- golang.org/x/term v0.13.0
+ github.com/stretchr/testify v1.9.0
+ golang.org/x/crypto v0.23.0
+ golang.org/x/term v0.20.0
gopkg.in/yaml.v2 v2.4.0
)
require (
+ cloud.google.com/go/auth v0.5.1 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
+ cloud.google.com/go/compute/metadata v0.3.0 // indirect
+ cloud.google.com/go/iam v1.1.8 // indirect
github.com/alessio/shellescape v1.4.1 // indirect
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef // indirect
+ github.com/aws/aws-sdk-go-v2 v1.27.2 // indirect
+ github.com/aws/aws-sdk-go-v2/config v1.27.18 // indirect
+ github.com/aws/aws-sdk-go-v2/credentials v1.17.18 // indirect
+ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
+ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
+ github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
+ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
+ github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
+ github.com/aws/smithy-go v1.20.2 // indirect
github.com/chzyer/readline v1.5.1 // indirect
github.com/danieljoos/wincred v1.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
+ github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
+ github.com/go-logr/logr v1.4.1 // indirect
+ github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/errors v0.20.2 // indirect
github.com/go-openapi/strfmt v0.21.3 // indirect
github.com/godbus/dbus/v5 v5.1.0 // indirect
+ github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
+ github.com/golang/protobuf v1.5.4 // indirect
+ github.com/google/s2a-go v0.1.7 // indirect
+ github.com/googleapis/enterprise-certificate-proxy v0.3.2 // indirect
+ github.com/googleapis/gax-go/v2 v2.12.4 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/magiconair/properties v1.8.5 // indirect
@@ -59,17 +85,30 @@ require (
github.com/subosito/gotenv v1.2.0 // indirect
github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect
go.mongodb.org/mongo-driver v1.10.0 // indirect
- golang.org/x/net v0.17.0 // indirect
- golang.org/x/sync v0.1.0 // indirect
- golang.org/x/sys v0.13.0 // indirect
- golang.org/x/text v0.13.0 // indirect
+ go.opencensus.io v0.24.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 // indirect
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
+ go.opentelemetry.io/otel v1.24.0 // indirect
+ go.opentelemetry.io/otel/metric v1.24.0 // indirect
+ go.opentelemetry.io/otel/trace v1.24.0 // indirect
+ golang.org/x/net v0.25.0 // indirect
+ golang.org/x/oauth2 v0.21.0 // indirect
+ golang.org/x/sync v0.7.0 // indirect
+ golang.org/x/sys v0.20.0 // indirect
+ golang.org/x/text v0.15.0 // indirect
+ golang.org/x/time v0.5.0 // indirect
+ google.golang.org/api v0.183.0 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect
+ google.golang.org/grpc v1.64.0 // indirect
+ google.golang.org/protobuf v1.34.1 // indirect
gopkg.in/ini.v1 v1.62.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/fatih/color v1.13.0
- github.com/go-resty/resty/v2 v2.10.0
+ github.com/go-resty/resty/v2 v2.13.1
github.com/inconshreveable/mousetrap v1.0.1 // indirect
github.com/jedib0t/go-pretty v4.3.0+incompatible
github.com/manifoldco/promptui v0.9.0
diff --git a/cli/go.sum b/cli/go.sum
index ff3030a9c..3784ce4d8 100644
--- a/cli/go.sum
+++ b/cli/go.sum
@@ -18,15 +18,23 @@ cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmW
cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
+cloud.google.com/go/auth v0.5.1 h1:0QNO7VThG54LUzKiQxv8C6x1YX7lUrzlAa1nVLF8CIw=
+cloud.google.com/go/auth v0.5.1/go.mod h1:vbZT8GjzDf3AVqCcQmqeeM32U9HBFc32vVVAbwDsa6s=
+cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
+cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
+cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc=
+cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
+cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
+cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
@@ -49,6 +57,32 @@ github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmV
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef h1:46PFijGLmAjMPwCCCo7Jf0W6f9slllCkkv7vyc1yOSg=
github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
+github.com/aws/aws-sdk-go-v2 v1.27.2 h1:pLsTXqX93rimAOZG2FIYraDQstZaaGVVN4tNw65v0h8=
+github.com/aws/aws-sdk-go-v2 v1.27.2/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
+github.com/aws/aws-sdk-go-v2/config v1.27.18 h1:wFvAnwOKKe7QAyIxziwSKjmer9JBMH1vzIL6W+fYuKk=
+github.com/aws/aws-sdk-go-v2/config v1.27.18/go.mod h1:0xz6cgdX55+kmppvPm2IaKzIXOheGJhAufacPJaXZ7c=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.18 h1:D/ALDWqK4JdY3OFgA2thcPO1c9aYTT5STS/CvnkqY1c=
+github.com/aws/aws-sdk-go-v2/credentials v1.17.18/go.mod h1:JuitCWq+F5QGUrmMPsk945rop6bB57jdscu+Glozdnc=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 h1:dDgptDO9dxeFkXy+tEgVkzSClHZje/6JkPW5aZyEvrQ=
+github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5/go.mod h1:gjvE2KBUgUQhcv89jqxrIxH9GaKs1JbZzWejj/DaHGA=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 h1:cy8ahBJuhtM8GTTSyOkfy6WVPV1IE+SS5/wfXUYuulw=
+github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9/go.mod h1:CZBXGLaJnEZI6EVNcPd7a6B5IC5cA/GkRWtu9fp3S6Y=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 h1:A4SYk07ef04+vxZToz9LWvAXl9LW0NClpPpMsi31cz0=
+github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9/go.mod h1:5jJcHuwDagxN+ErjQ3PU3ocf6Ylc/p9x+BLO/+X4iXw=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
+github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
+github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 h1:o4T+fKxA3gTMcluBNZZXE9DNaMkJuUL1O3mffCUjoJo=
+github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11/go.mod h1:84oZdJ+VjuJKs9v1UTC9NaodRZRseOXCTgku+vQJWR8=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 h1:gEYM2GSpr4YNWc6hCd5nod4+d4kd9vWIAWrmGuLdlMw=
+github.com/aws/aws-sdk-go-v2/service/sso v1.20.11/go.mod h1:gVvwPdPNYehHSP9Rs7q27U1EU+3Or2ZpXvzAYJNh63w=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 h1:iXjh3uaH3vsVcnyZX7MqCoCfcyxIrVE9iOQruRaWPrQ=
+github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5/go.mod h1:5ZXesEuy/QcO0WUnt+4sDkxhdXRHTu2yG0uCSH8B6os=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF5e2mfxHCg7ZVMYmk=
+github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk=
+github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
+github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM=
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
@@ -97,6 +131,8 @@ github.com/fatih/color v1.13.0 h1:8LOYc1KYPPmyKMuN8QV2DNRWNbLo6LZ0iLs8+mlH53w=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/semgroup v1.2.0 h1:h/OLXwEM+3NNyAdZEpMiH1OzfplU09i2qXPVThGZvyg=
github.com/fatih/semgroup v1.2.0/go.mod h1:1KAD4iIYfXjE4U13B48VM4z9QUwV5Tt8O4rS879kgm8=
+github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
+github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
@@ -105,12 +141,17 @@ github.com/gitleaks/go-gitdiff v0.8.0/go.mod h1:pKz0X4YzCKZs30BL+weqBIG7mx0jl4tF
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
+github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
+github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
+github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
+github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
+github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-openapi/errors v0.20.2 h1:dxy7PGTqEh94zj2E3h1cUmQQWiM1+aeCROfAr02EmK8=
github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M=
github.com/go-openapi/strfmt v0.21.3 h1:xwhj5X6CjXEZZHMWy1zKJxvW9AfHC9pkyUjLvHtKG7o=
github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg=
-github.com/go-resty/resty/v2 v2.10.0 h1:Qla4W/+TMmv0fOeeRqzEpXPLfTUnR5HZ1+lGs+CkiCo=
-github.com/go-resty/resty/v2 v2.10.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A=
+github.com/go-resty/resty/v2 v2.13.1 h1:x+LHXBI2nMB1vqndymf26quycC4aggYJ7DECYbiz03g=
+github.com/go-resty/resty/v2 v2.13.1/go.mod h1:GznXlLxkq6Nh4sU59rPmUw3VtgpO3aS96ORAI6Q7d+0=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=
github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
@@ -119,6 +160,8 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfU
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
+github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -144,6 +187,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
+github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
+github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -157,8 +202,9 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
+github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -175,11 +221,18 @@ github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
+github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
+github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
+github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
+github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
@@ -210,6 +263,8 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
+github.com/infisical/go-sdk v0.2.0 h1:n1/KNdYpeQavSqVwC9BfeV8VRzf3N2X9zO1tzQOSj5Q=
+github.com/infisical/go-sdk v0.2.0/go.mod h1:vHTDVw3k+wfStXab513TGk1n53kaKF2xgLqpw/xvtl4=
github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo=
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -330,8 +385,9 @@ github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
+github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
+github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
@@ -340,8 +396,9 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
+github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
+github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4=
@@ -372,6 +429,18 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
+go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
+go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk=
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw=
+go.opentelemetry.io/otel v1.24.0 h1:0LAOdjNmQeSTzGBzduGe/rU4tZhMwL5rWgtp9Ku5Jfo=
+go.opentelemetry.io/otel v1.24.0/go.mod h1:W7b9Ozg4nkF5tWI5zsXkaKKDjdVjpD4oAt9Qi/MArHo=
+go.opentelemetry.io/otel/metric v1.24.0 h1:6EhoGWWK28x1fbpA4tYTOWBkPefTDQnb8WSGXlc88kI=
+go.opentelemetry.io/otel/metric v1.24.0/go.mod h1:VYhLe1rFfxuTXLgj4CBiyz+9WYBA8pNGJgDcSFRKBco=
+go.opentelemetry.io/otel/trace v1.24.0 h1:CsKnnL4dUAr/0llH9FKuc698G04IrpWV0MQA/Y1YELI=
+go.opentelemetry.io/otel/trace v1.24.0/go.mod h1:HPc3Xr/cOApsBI154IU0OI0HJexz+aw5uPdbs3UCjNU=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
@@ -385,8 +454,9 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211215165025-cf75a172585e/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
-golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
+golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
+golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
+golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -465,8 +535,9 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
-golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
+golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
+golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
+golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -479,6 +550,8 @@ golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ
golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210402161424-2e8d93401602/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
+golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs=
+golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -491,8 +564,9 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
+golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -546,14 +620,16 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
-golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
+golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
-golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
+golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
+golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw=
+golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -565,13 +641,14 @@ golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
-golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
+golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk=
+golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=
-golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
+golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
+golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -652,6 +729,8 @@ google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjR
google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
google.golang.org/api v0.44.0/go.mod h1:EBOGZqzyhtvMDoxwS97ctnh0zUmYY6CxqXsc1AvkYD8=
+google.golang.org/api v0.183.0 h1:PNMeRDwo1pJdgNcFQ9GstuLe/noWKIc89pRWRLMvLwE=
+google.golang.org/api v0.183.0/go.mod h1:q43adC5/pHoSZTx5h2mSmdF7NcyfW9JuDyIOJAgS9ZQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -700,6 +779,10 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D
google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e h1:SkdGTrROJl2jRGT/Fxv5QUf9jtdKCQh4KQJXbXVLAi0=
+google.golang.org/genproto/googleapis/api v0.0.0-20240521202816-d264139d666e/go.mod h1:LweJcLbyVij6rCex8YunD8DYR5VDonap/jYl3ZRxcIU=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -720,6 +803,8 @@ google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAG
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
+google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY=
+google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -732,6 +817,8 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg=
+google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
diff --git a/cli/packages/api/api.go b/cli/packages/api/api.go
index 01f29a03a..a56f4c24e 100644
--- a/cli/packages/api/api.go
+++ b/cli/packages/api/api.go
@@ -490,7 +490,7 @@ func CallUniversalAuthLogin(httpClient *resty.Client, request UniversalAuthLogin
return universalAuthLoginResponse, nil
}
-func CallUniversalAuthRefreshAccessToken(httpClient *resty.Client, request UniversalAuthRefreshRequest) (UniversalAuthRefreshResponse, error) {
+func CallMachineIdentityRefreshAccessToken(httpClient *resty.Client, request UniversalAuthRefreshRequest) (UniversalAuthRefreshResponse, error) {
var universalAuthRefreshResponse UniversalAuthRefreshResponse
response, err := httpClient.
R().
@@ -500,11 +500,11 @@ func CallUniversalAuthRefreshAccessToken(httpClient *resty.Client, request Unive
Post(fmt.Sprintf("%v/v1/auth/token/renew", config.INFISICAL_URL))
if err != nil {
- return UniversalAuthRefreshResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unable to complete api request [err=%s]", err)
+ return UniversalAuthRefreshResponse{}, fmt.Errorf("CallMachineIdentityRefreshAccessToken: Unable to complete api request [err=%s]", err)
}
if response.IsError() {
- return UniversalAuthRefreshResponse{}, fmt.Errorf("CallUniversalAuthRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
+ return UniversalAuthRefreshResponse{}, fmt.Errorf("CallMachineIdentityRefreshAccessToken: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
}
return universalAuthRefreshResponse, nil
diff --git a/cli/packages/api/model.go b/cli/packages/api/model.go
index 56b9807f7..0fffe0520 100644
--- a/cli/packages/api/model.go
+++ b/cli/packages/api/model.go
@@ -233,6 +233,7 @@ type GetLoginOneV2Response struct {
type GetLoginTwoV2Request struct {
Email string `json:"email"`
ClientProof string `json:"clientProof"`
+ Password string `json:"password"`
}
type GetLoginTwoV2Response struct {
diff --git a/cli/packages/cmd/agent.go b/cli/packages/cmd/agent.go
index f2b05d1f0..e29895570 100644
--- a/cli/packages/cmd/agent.go
+++ b/cli/packages/cmd/agent.go
@@ -20,6 +20,7 @@ import (
"text/template"
"time"
+ infisicalSdk "github.com/infisical/go-sdk"
"github.com/rs/zerolog/log"
"gopkg.in/yaml.v2"
@@ -59,9 +60,26 @@ type UniversalAuth struct {
RemoveClientSecretOnRead bool `yaml:"remove_client_secret_on_read"`
}
-type OAuthConfig struct {
- ClientID string `yaml:"client-id"`
- ClientSecret string `yaml:"client-secret"`
+type KubernetesAuth struct {
+ IdentityID string `yaml:"identity-id"`
+ ServiceAccountToken string `yaml:"service-account-token"`
+}
+
+type AzureAuth struct {
+ IdentityID string `yaml:"identity-id"`
+}
+
+type GcpIdTokenAuth struct {
+ IdentityID string `yaml:"identity-id"`
+}
+
+type GcpIamAuth struct {
+ IdentityID string `yaml:"identity-id"`
+ ServiceAccountKey string `yaml:"service-account-key"`
+}
+
+type AwsIamAuth struct {
+ IdentityID string `yaml:"identity-id"`
}
type Sink struct {
@@ -87,15 +105,6 @@ type Template struct {
} `yaml:"config"`
}
-func newAgentTemplateChannels(templates []Template) map[string]chan bool {
- // we keep each destination as an identifier for various channel
- templateChannel := make(map[string]chan bool)
- for _, template := range templates {
- templateChannel[template.DestinationPath] = make(chan bool)
- }
- return templateChannel
-}
-
type DynamicSecretLease struct {
LeaseID string
ExpireAt time.Time
@@ -256,6 +265,14 @@ func WriteBytesToFile(data *bytes.Buffer, outputPath string) error {
return err
}
+func ParseAuthConfig(authConfigFile []byte, destination interface{}) error {
+ if err := yaml.Unmarshal(authConfigFile, destination); err != nil {
+ return err
+ }
+
+ return nil
+}
+
func ParseAgentConfig(configFile []byte) (*Config, error) {
var rawConfig struct {
Infisical InfisicalConfig `yaml:"infisical"`
@@ -283,36 +300,13 @@ func ParseAgentConfig(configFile []byte) (*Config, error) {
config := &Config{
Infisical: rawConfig.Infisical,
Auth: AuthConfig{
- Type: rawConfig.Auth.Type,
+ Type: rawConfig.Auth.Type,
+ Config: rawConfig.Auth.Config,
},
Sinks: rawConfig.Sinks,
Templates: rawConfig.Templates,
}
- // Marshal and then unmarshal the config based on the type
- configBytes, err := yaml.Marshal(rawConfig.Auth.Config)
- if err != nil {
- return nil, err
- }
-
- switch rawConfig.Auth.Type {
- case "universal-auth":
- var tokenConfig UniversalAuth
- if err := yaml.Unmarshal(configBytes, &tokenConfig); err != nil {
- return nil, err
- }
-
- config.Auth.Config = tokenConfig
- case "oauth": // aws, gcp, k8s service account, etc
- var oauthConfig OAuthConfig
- if err := yaml.Unmarshal(configBytes, &oauthConfig); err != nil {
- return nil, err
- }
- config.Auth.Config = oauthConfig
- default:
- return nil, fmt.Errorf("unknown auth type: %s", rawConfig.Auth.Type)
- }
-
return config, nil
}
@@ -337,7 +331,7 @@ func dynamicSecretTemplateFunction(accessToken string, dynamicSecretManager *Dyn
return func(args ...string) (map[string]interface{}, error) {
argLength := len(args)
if argLength != 4 && argLength != 5 {
- return nil, fmt.Errorf("Invalid arguments found for dynamic-secret function. Check template %i", templateId)
+ return nil, fmt.Errorf("invalid arguments found for dynamic-secret function. Check template %d", templateId)
}
projectSlug, envSlug, secretPath, slug, ttl := args[0], args[1], args[2], args[3], ""
@@ -421,32 +415,54 @@ func ProcessBase64Template(templateId int, encodedTemplate string, data interfac
}
type AgentManager struct {
- accessToken string
- accessTokenTTL time.Duration
- accessTokenMaxTTL time.Duration
- accessTokenFetchedTime time.Time
- accessTokenRefreshedTime time.Time
- mutex sync.Mutex
- filePaths []Sink // Store file paths if needed
- templates []Template
- dynamicSecretLeases *DynamicSecretLeaseManager
- clientIdPath string
- clientSecretPath string
- newAccessTokenNotificationChan chan bool
- removeClientSecretOnRead bool
- cachedClientSecret string
- exitAfterAuth bool
+ accessToken string
+ accessTokenTTL time.Duration
+ accessTokenMaxTTL time.Duration
+ accessTokenFetchedTime time.Time
+ accessTokenRefreshedTime time.Time
+ mutex sync.Mutex
+ filePaths []Sink // Store file paths if needed
+ templates []Template
+ dynamicSecretLeases *DynamicSecretLeaseManager
+
+ authConfigBytes []byte
+ authStrategy util.AuthStrategyType
+
+ newAccessTokenNotificationChan chan bool
+ removeUniversalAuthClientSecretOnRead bool
+ cachedUniversalAuthClientSecret string
+ exitAfterAuth bool
+
+ infisicalClient infisicalSdk.InfisicalClientInterface
}
-func NewAgentManager(fileDeposits []Sink, templates []Template, clientIdPath string, clientSecretPath string, newAccessTokenNotificationChan chan bool, removeClientSecretOnRead bool, exitAfterAuth bool) *AgentManager {
+type NewAgentMangerOptions struct {
+ FileDeposits []Sink
+ Templates []Template
+
+ AuthConfigBytes []byte
+ AuthStrategy util.AuthStrategyType
+
+ NewAccessTokenNotificationChan chan bool
+ ExitAfterAuth bool
+}
+
+func NewAgentManager(options NewAgentMangerOptions) *AgentManager {
+
return &AgentManager{
- filePaths: fileDeposits,
- templates: templates,
- clientIdPath: clientIdPath,
- clientSecretPath: clientSecretPath,
- newAccessTokenNotificationChan: newAccessTokenNotificationChan,
- removeClientSecretOnRead: removeClientSecretOnRead,
- exitAfterAuth: exitAfterAuth,
+ filePaths: options.FileDeposits,
+ templates: options.Templates,
+
+ authConfigBytes: options.AuthConfigBytes,
+ authStrategy: options.AuthStrategy,
+
+ newAccessTokenNotificationChan: options.NewAccessTokenNotificationChan,
+ exitAfterAuth: options.ExitAfterAuth,
+
+ infisicalClient: infisicalSdk.NewInfisicalClient(infisicalSdk.Config{
+ SiteUrl: config.INFISICAL_URL,
+ UserAgent: api.USER_AGENT, // ? Should we perhaps use a different user agent for the Agent for better analytics?
+ }),
}
}
@@ -469,52 +485,164 @@ func (tm *AgentManager) GetToken() string {
return tm.accessToken
}
+func (tm *AgentManager) FetchUniversalAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, e error) {
+
+ var universalAuthConfig UniversalAuth
+ if err := ParseAuthConfig(tm.authConfigBytes, &universalAuthConfig); err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err)
+ }
+
+ clientID, err := util.GetEnvVarOrFileContent(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME, universalAuthConfig.ClientIDPath)
+ if err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get client id: %v", err)
+ }
+
+ clientSecret, err := util.GetEnvVarOrFileContent("INFISICAL_UNIVERSAL_CLIENT_SECRET", universalAuthConfig.ClientSecretPath)
+ if err != nil {
+ if len(tm.cachedUniversalAuthClientSecret) == 0 {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get client secret: %v", err)
+ }
+ clientSecret = tm.cachedUniversalAuthClientSecret
+ }
+
+ tm.cachedUniversalAuthClientSecret = clientSecret
+ if tm.removeUniversalAuthClientSecretOnRead {
+ defer os.Remove(universalAuthConfig.ClientSecretPath)
+ }
+
+ return tm.infisicalClient.Auth().UniversalAuthLogin(clientID, clientSecret)
+
+}
+
+func (tm *AgentManager) FetchKubernetesAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) {
+
+ var kubernetesAuthConfig KubernetesAuth
+ if err := ParseAuthConfig(tm.authConfigBytes, &kubernetesAuthConfig); err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err)
+ }
+
+ identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, kubernetesAuthConfig.IdentityID)
+ if err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err)
+ }
+
+ serviceAccountTokenPath := os.Getenv(util.INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_NAME)
+ if serviceAccountTokenPath == "" {
+ serviceAccountTokenPath = kubernetesAuthConfig.ServiceAccountToken
+ if serviceAccountTokenPath == "" {
+ serviceAccountTokenPath = "/var/run/secrets/kubernetes.io/serviceaccount/token"
+ }
+ }
+
+ return tm.infisicalClient.Auth().KubernetesAuthLogin(identityId, serviceAccountTokenPath)
+
+}
+
+func (tm *AgentManager) FetchAzureAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) {
+
+ var azureAuthConfig AzureAuth
+ if err := ParseAuthConfig(tm.authConfigBytes, &azureAuthConfig); err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err)
+ }
+
+ identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, azureAuthConfig.IdentityID)
+ if err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err)
+ }
+
+ return tm.infisicalClient.Auth().AzureAuthLogin(identityId)
+
+}
+
+func (tm *AgentManager) FetchGcpIdTokenAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) {
+
+ var gcpIdTokenAuthConfig GcpIdTokenAuth
+ if err := ParseAuthConfig(tm.authConfigBytes, &gcpIdTokenAuthConfig); err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err)
+ }
+
+ identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, gcpIdTokenAuthConfig.IdentityID)
+ if err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err)
+ }
+
+ return tm.infisicalClient.Auth().GcpIdTokenAuthLogin(identityId)
+
+}
+
+func (tm *AgentManager) FetchGcpIamAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) {
+
+ var gcpIamAuthConfig GcpIamAuth
+ if err := ParseAuthConfig(tm.authConfigBytes, &gcpIamAuthConfig); err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err)
+ }
+
+ identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, gcpIamAuthConfig.IdentityID)
+ if err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err)
+ }
+
+ serviceAccountKeyPath := os.Getenv(util.INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_NAME)
+ if serviceAccountKeyPath == "" {
+ // we don't need to read this file, because the service account key path is directly read inside the sdk
+ serviceAccountKeyPath = gcpIamAuthConfig.ServiceAccountKey
+ if serviceAccountKeyPath == "" {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("gcp service account key path not found")
+ }
+ }
+
+ return tm.infisicalClient.Auth().GcpIamAuthLogin(identityId, serviceAccountKeyPath)
+
+}
+
+func (tm *AgentManager) FetchAwsIamAuthAccessToken() (credential infisicalSdk.MachineIdentityCredential, err error) {
+
+ var awsIamAuthConfig AwsIamAuth
+ if err := ParseAuthConfig(tm.authConfigBytes, &awsIamAuthConfig); err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to parse auth config due to error: %v", err)
+ }
+
+ identityId, err := util.GetEnvVarOrFileContent(util.INFISICAL_MACHINE_IDENTITY_ID_NAME, awsIamAuthConfig.IdentityID)
+
+ if err != nil {
+ return infisicalSdk.MachineIdentityCredential{}, fmt.Errorf("unable to get identity id: %v", err)
+ }
+
+ return tm.infisicalClient.Auth().AwsIamAuthLogin(identityId)
+
+}
+
// Fetches a new access token using client credentials
func (tm *AgentManager) FetchNewAccessToken() error {
- clientID := os.Getenv(util.INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME)
- if clientID == "" {
- clientIDAsByte, err := ReadFile(tm.clientIdPath)
- if err != nil {
- return fmt.Errorf("unable to read client id from file path '%s' due to error: %v", tm.clientIdPath, err)
- }
- clientID = string(clientIDAsByte)
+
+ authStrategies := map[util.AuthStrategyType]func() (credential infisicalSdk.MachineIdentityCredential, e error){
+ util.AuthStrategy.UNIVERSAL_AUTH: tm.FetchUniversalAuthAccessToken,
+ util.AuthStrategy.KUBERNETES_AUTH: tm.FetchKubernetesAuthAccessToken,
+ util.AuthStrategy.AZURE_AUTH: tm.FetchAzureAuthAccessToken,
+ util.AuthStrategy.GCP_ID_TOKEN_AUTH: tm.FetchGcpIdTokenAuthAccessToken,
+ util.AuthStrategy.GCP_IAM_AUTH: tm.FetchGcpIamAuthAccessToken,
+ util.AuthStrategy.AWS_IAM_AUTH: tm.FetchAwsIamAuthAccessToken,
}
- clientSecret := os.Getenv("INFISICAL_UNIVERSAL_CLIENT_SECRET")
- if clientSecret == "" {
- clientSecretAsByte, err := ReadFile(tm.clientSecretPath)
- if err != nil {
- if len(tm.cachedClientSecret) == 0 {
- return fmt.Errorf("unable to read client secret from file and no cached client secret found: %v", err)
- } else {
- clientSecretAsByte = []byte(tm.cachedClientSecret)
- }
- }
- clientSecret = string(clientSecretAsByte)
+ if _, ok := authStrategies[tm.authStrategy]; !ok {
+ return fmt.Errorf("auth strategy %s not found", tm.authStrategy)
}
- // remove client secret after first read
- if tm.removeClientSecretOnRead {
- os.Remove(tm.clientSecretPath)
- }
+ credential, err := authStrategies[tm.authStrategy]()
- // save as cache in memory
- tm.cachedClientSecret = clientSecret
-
- loginResponse, err := util.UniversalAuthLogin(clientID, clientSecret)
if err != nil {
return err
}
- accessTokenTTL := time.Duration(loginResponse.AccessTokenTTL * int(time.Second))
- accessTokenMaxTTL := time.Duration(loginResponse.AccessTokenMaxTTL * int(time.Second))
+ accessTokenTTL := time.Duration(credential.ExpiresIn * int64(time.Second))
+ accessTokenMaxTTL := time.Duration(credential.AccessTokenMaxTTL * int64(time.Second))
if accessTokenTTL <= time.Duration(5)*time.Second {
- util.PrintErrorMessageAndExit("At this this, agent does not support refresh of tokens with 5 seconds or less ttl. Please increase access token ttl and try again")
+ util.PrintErrorMessageAndExit("At this time, agent does not support refresh of tokens with 5 seconds or less ttl. Please increase access token ttl and try again")
}
tm.accessTokenFetchedTime = time.Now()
- tm.SetToken(loginResponse.AccessToken, accessTokenTTL, accessTokenMaxTTL)
+ tm.SetToken(credential.AccessToken, accessTokenTTL, accessTokenMaxTTL)
return nil
}
@@ -527,7 +655,7 @@ func (tm *AgentManager) RefreshAccessToken() error {
SetRetryWaitTime(5 * time.Second)
accessToken := tm.GetToken()
- response, err := api.CallUniversalAuthRefreshAccessToken(httpClient, api.UniversalAuthRefreshRequest{AccessToken: accessToken})
+ response, err := api.CallMachineIdentityRefreshAccessToken(httpClient, api.UniversalAuthRefreshRequest{AccessToken: accessToken})
if err != nil {
return err
}
@@ -564,6 +692,7 @@ func (tm *AgentManager) ManageTokenLifecycle() {
continue
}
} else if time.Now().After(accessTokenMaxTTLExpiresInTime) {
+ // case: token has reached max ttl and we should re-authenticate entirely (cannot refresh)
log.Info().Msgf("token has reached max ttl, attempting to re authenticate...")
err := tm.FetchNewAccessToken()
if err != nil {
@@ -574,6 +703,7 @@ func (tm *AgentManager) ManageTokenLifecycle() {
continue
}
} else {
+ // case: token ttl has expired, but the token is still within max ttl, so we can refresh
log.Info().Msgf("attempting to refresh existing token...")
err := tm.RefreshAccessToken()
if err != nil {
@@ -770,18 +900,33 @@ var agentCmd = &cobra.Command{
return
}
- if agentConfig.Auth.Type != "universal-auth" {
- util.PrintErrorMessageAndExit("Only auth type of 'universal-auth' is supported at this time")
- }
+ authMethodValid, authStrategy := util.IsAuthMethodValid(agentConfig.Auth.Type)
- configUniversalAuthType := agentConfig.Auth.Config.(UniversalAuth)
+ if !authMethodValid {
+ util.PrintErrorMessageAndExit(fmt.Sprintf("The auth method '%s' is not supported.", agentConfig.Auth.Type))
+ }
tokenRefreshNotifier := make(chan bool)
sigChan := make(chan os.Signal, 1)
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
filePaths := agentConfig.Sinks
- tm := NewAgentManager(filePaths, agentConfig.Templates, configUniversalAuthType.ClientIDPath, configUniversalAuthType.ClientSecretPath, tokenRefreshNotifier, configUniversalAuthType.RemoveClientSecretOnRead, agentConfig.Infisical.ExitAfterAuth)
+
+ configBytes, err := yaml.Marshal(agentConfig.Auth.Config)
+ if err != nil {
+ log.Error().Msgf("unable to marshal auth config because %v", err)
+ return
+ }
+
+ tm := NewAgentManager(NewAgentMangerOptions{
+ FileDeposits: filePaths,
+ Templates: agentConfig.Templates,
+ AuthConfigBytes: configBytes,
+ NewAccessTokenNotificationChan: tokenRefreshNotifier,
+ ExitAfterAuth: agentConfig.Infisical.ExitAfterAuth,
+ AuthStrategy: authStrategy,
+ })
+
tm.dynamicSecretLeases = NewDynamicSecretLeaseManager(sigChan)
go tm.ManageTokenLifecycle()
diff --git a/cli/packages/cmd/login.go b/cli/packages/cmd/login.go
index 61e24b12f..a81a903a8 100644
--- a/cli/packages/cmd/login.go
+++ b/cli/packages/cmd/login.go
@@ -539,6 +539,7 @@ func getFreshUserCredentials(email string, password string) (*api.GetLoginOneV2R
loginTwoResponseResult, err := api.CallLogin2V2(httpClient, api.GetLoginTwoV2Request{
Email: email,
ClientProof: hex.EncodeToString(srpM1),
+ Password: password,
})
if err != nil {
diff --git a/cli/packages/cmd/token.go b/cli/packages/cmd/token.go
index 3e5d42765..4e568cb85 100644
--- a/cli/packages/cmd/token.go
+++ b/cli/packages/cmd/token.go
@@ -39,7 +39,7 @@ var tokenRenewCmd = &cobra.Command{
util.PrintErrorMessageAndExit("You are trying to renew a service token. You can only renew universal auth access tokens.")
}
- renewedAccessToken, err := util.RenewUniversalAuthAccessToken(token)
+ renewedAccessToken, err := util.RenewMachineIdentityAccessToken(token)
if err != nil {
util.HandleError(err, "Unable to renew token")
diff --git a/cli/packages/util/auth.go b/cli/packages/util/auth.go
new file mode 100644
index 000000000..75ffe9344
--- /dev/null
+++ b/cli/packages/util/auth.go
@@ -0,0 +1,42 @@
+package util
+
+type AuthStrategyType string
+
+var AuthStrategy = struct {
+ UNIVERSAL_AUTH AuthStrategyType
+ KUBERNETES_AUTH AuthStrategyType
+ AZURE_AUTH AuthStrategyType
+ GCP_ID_TOKEN_AUTH AuthStrategyType
+ GCP_IAM_AUTH AuthStrategyType
+ AWS_IAM_AUTH AuthStrategyType
+}{
+ UNIVERSAL_AUTH: "universal-auth",
+ KUBERNETES_AUTH: "kubernetes",
+ AZURE_AUTH: "azure",
+ GCP_ID_TOKEN_AUTH: "gcp-id-token",
+ GCP_IAM_AUTH: "gcp-iam",
+ AWS_IAM_AUTH: "aws-iam",
+}
+
+var AVAILABLE_AUTH_STRATEGIES = []AuthStrategyType{
+ AuthStrategy.UNIVERSAL_AUTH,
+ AuthStrategy.KUBERNETES_AUTH,
+ AuthStrategy.AZURE_AUTH,
+ AuthStrategy.GCP_ID_TOKEN_AUTH,
+ AuthStrategy.GCP_IAM_AUTH,
+ AuthStrategy.AWS_IAM_AUTH,
+}
+
+func IsAuthMethodValid(authMethod string) (isValid bool, strategy AuthStrategyType) {
+
+ if authMethod == "user" {
+ return true, ""
+ }
+
+ for _, strategy := range AVAILABLE_AUTH_STRATEGIES {
+ if string(strategy) == authMethod {
+ return true, strategy
+ }
+ }
+ return false, ""
+}
diff --git a/cli/packages/util/constants.go b/cli/packages/util/constants.go
index 311a4b0d9..bff3c3ab0 100644
--- a/cli/packages/util/constants.go
+++ b/cli/packages/util/constants.go
@@ -1,20 +1,32 @@
package util
const (
- CONFIG_FILE_NAME = "infisical-config.json"
- CONFIG_FOLDER_NAME = ".infisical"
- INFISICAL_DEFAULT_API_URL = "https://app.infisical.com/api"
- INFISICAL_DEFAULT_URL = "https://app.infisical.com"
- INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
- INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
+ CONFIG_FILE_NAME = "infisical-config.json"
+ CONFIG_FOLDER_NAME = ".infisical"
+ INFISICAL_DEFAULT_API_URL = "https://app.infisical.com/api"
+ INFISICAL_DEFAULT_URL = "https://app.infisical.com"
+ INFISICAL_WORKSPACE_CONFIG_FILE_NAME = ".infisical.json"
+ INFISICAL_TOKEN_NAME = "INFISICAL_TOKEN"
+ INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN"
+
+ // Universal Auth
INFISICAL_UNIVERSAL_AUTH_CLIENT_ID_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_ID"
INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET_NAME = "INFISICAL_UNIVERSAL_AUTH_CLIENT_SECRET"
- INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN_NAME = "INFISICAL_UNIVERSAL_AUTH_ACCESS_TOKEN"
- SECRET_TYPE_PERSONAL = "personal"
- SECRET_TYPE_SHARED = "shared"
- KEYRING_SERVICE_NAME = "infisical"
- PERSONAL_SECRET_TYPE_NAME = "personal"
- SHARED_SECRET_TYPE_NAME = "shared"
+
+ // Kubernetes auth
+ INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_NAME = "INFISICAL_KUBERNETES_SERVICE_ACCOUNT_TOKEN_PATH"
+
+ // GCP Auth
+ INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH_NAME = "INFISICAL_GCP_IAM_SERVICE_ACCOUNT_KEY_FILE_PATH"
+
+ // Generic env variable used for auth methods that require a machine identity ID
+ INFISICAL_MACHINE_IDENTITY_ID_NAME = "INFISICAL_MACHINE_IDENTITY_ID"
+
+ SECRET_TYPE_PERSONAL = "personal"
+ SECRET_TYPE_SHARED = "shared"
+ KEYRING_SERVICE_NAME = "infisical"
+ PERSONAL_SECRET_TYPE_NAME = "personal"
+ SHARED_SECRET_TYPE_NAME = "shared"
SERVICE_TOKEN_IDENTIFIER = "service-token"
UNIVERSAL_AUTH_TOKEN_IDENTIFIER = "universal-auth-token"
diff --git a/cli/packages/util/helper.go b/cli/packages/util/helper.go
index 9e5052530..b8eb66027 100644
--- a/cli/packages/util/helper.go
+++ b/cli/packages/util/helper.go
@@ -123,7 +123,7 @@ func UniversalAuthLogin(clientId string, clientSecret string) (api.UniversalAuth
return tokenResponse, nil
}
-func RenewUniversalAuthAccessToken(accessToken string) (string, error) {
+func RenewMachineIdentityAccessToken(accessToken string) (string, error) {
httpClient := resty.New()
httpClient.SetRetryCount(10000).
@@ -134,7 +134,7 @@ func RenewUniversalAuthAccessToken(accessToken string) (string, error) {
AccessToken: accessToken,
}
- tokenResponse, err := api.CallUniversalAuthRefreshAccessToken(httpClient, request)
+ tokenResponse, err := api.CallMachineIdentityRefreshAccessToken(httpClient, request)
if err != nil {
return "", err
}
@@ -246,3 +246,30 @@ func AppendAPIEndpoint(address string) string {
}
return address + "/api"
}
+
+func ReadFileAsString(filePath string) (string, error) {
+ fileBytes, err := os.ReadFile(filePath)
+
+ if err != nil {
+ return "", err
+ }
+
+ return string(fileBytes), nil
+
+}
+
+func GetEnvVarOrFileContent(envName string, filePath string) (string, error) {
+ // First check if the environment variable is set
+ if envVarValue := os.Getenv(envName); envVarValue != "" {
+ return envVarValue, nil
+ }
+
+ // If it's not set, try to read the file
+ fileContent, err := ReadFileAsString(filePath)
+
+ if err != nil {
+ return "", fmt.Errorf("unable to read file content from file path '%s' [err=%v]", filePath, err)
+ }
+
+ return fileContent, nil
+}
diff --git a/company/documentation/getting-started/introduction.mdx b/company/documentation/getting-started/introduction.mdx
index 0f414c62a..55b483194 100644
--- a/company/documentation/getting-started/introduction.mdx
+++ b/company/documentation/getting-started/introduction.mdx
@@ -4,59 +4,63 @@ sidebarTitle: "What is Infisical?"
description: "An Introduction to the Infisical secret management platform."
---
-Infisical is an [open-source](https://github.com/infisical/infisical) secret management platform for developers.
-It provides capabilities for storing, managing, and syncing application configuration and secrets like API keys, database
-credentials, and certificates across infrastructure. In addition, Infisical prevents secrets leaks to git and enables secure
-sharing of secrets among engineers.
+**[Infisical](https://infisical.com)** is the open source secret management platform that developers use to centralize their application configuration and secrets like API keys and database credentials as well as manage their internal PKI. In addition, developers use Infisical to prevent secrets leaks to git and securely share secrets amongst engineers.
Start managing secrets securely with [Infisical Cloud](https://app.infisical.com) or learn how to [host Infisical](/self-hosting/overview) yourself.
-
- Get started with Infisical Cloud in just a few minutes.
-
-
- Self-host Infisical on your own infrastructure.
-
+
+ Get started with Infisical Cloud in just a few minutes.
+
+
+ Self-host Infisical on your own infrastructure.
+
-## Why Infisical?
+## Why Infisical?
+
+Infisical helps developers achieve secure centralized secret management and provides all the tools to easily manage secrets in various environments and infrastructure components. In particular, here are some of the most common points that developers mention after adopting Infisical:
-Infisical helps developers achieve secure centralized secret management and provides all the tools to easily manage secrets in various environments and infrastructure components. In particular, here are some of the most common points that developers mention after adopting Infisical:
- Streamlined **local development** processes (switching .env files to [Infisical CLI](/cli/commands/run) and removing secrets from developer machines).
-- **Best-in-class developer experience** with an easy-to-use [Web Dashboard](/documentation/platform/project).
-- Simple secret management inside **[CI/CD pipelines](/integrations/cicd/githubactions)** and staging environments.
-- Secure and compliant secret management practices in **[production environments](/sdks/overview)**.
+- **Best-in-class developer experience** with an easy-to-use [Web Dashboard](/documentation/platform/project).
+- Simple secret management inside **[CI/CD pipelines](/integrations/cicd/githubactions)** and staging environments.
+- Secure and compliant secret management practices in **[production environments](/sdks/overview)**.
- **Facilitated workflows** around [secret change management](/documentation/platform/pr-workflows), [access requests](/documentation/platform/access-controls/access-requests), [temporary access provisioning](/documentation/platform/access-controls/temporary-access), and more.
- **Improved security posture** thanks to [secret scanning](/cli/scanning-overview), [granular access control policies](/documentation/platform/access-controls/overview), [automated secret rotation](https://infisical.com/docs/documentation/platform/secret-rotation/overview), and [dynamic secrets](/documentation/platform/dynamic-secrets/overview) capabilities.
-## How does Infisical work?
+## How does Infisical work?
-To make secret management effortless and secure, Infisical follows a certain structure for enabling secret management workflows as defined below.
+To make secret management effortless and secure, Infisical follows a certain structure for enabling secret management workflows as defined below.
-**Identities** in Infisical are users or machine which have a certain set of roles and permissions assigned to them. Such identities are able to manage secrets in various **Clients** throughout the entire infrastructure. To do that, identities have to verify themselves through one of the available **Authentication Methods**.
+**Identities** in Infisical are users or machine which have a certain set of roles and permissions assigned to them. Such identities are able to manage secrets in various **Clients** throughout the entire infrastructure. To do that, identities have to verify themselves through one of the available **Authentication Methods**.
-As a result, the 3 main concepts that are important to understand are:
-- **[Identities](/documentation/platform/identities/overview)**: users or machines with a set permissions assigned to them.
+As a result, the 3 main concepts that are important to understand are:
+
+- **[Identities](/documentation/platform/identities/overview)**: users or machines with a set permissions assigned to them.
- **[Clients](/integrations/platforms/kubernetes)**: Infisical-developed tools for managing secrets in various infrastructure components (e.g., [Kubernetes Operator](/integrations/platforms/kubernetes), [Infisical Agent](/integrations/platforms/infisical-agent), [CLI](/cli/usage), [SDKs](/sdks/overview), [API](/api-reference/overview/introduction), [Web Dashboard](/documentation/platform/organization)).
- **[Authentication Methods](/documentation/platform/identities/universal-auth)**: ways for Identities to authenticate inside different clients (e.g., SAML SSO for Web Dashboard, Universal Auth for Infisical Agent, etc.).
-## How to get started with Infisical?
+## How to get started with Infisical?
Depending on your use case, it might be helpful to look into some of the resources and guides provided below.
-
+
Inject secrets into any application process/environment.
Fetch secrets with any programming language on demand.
-
+
Inject secrets into Docker containers.
This field is required when using any of the following auth types: `kubernetes`, `azure`, `gcp-id-token`, `gcp-iam`, or `aws-iam`. |
+| `auth.config.service-account-token` | Path to the Kubernetes service account token to use (optional)
Default: `/var/run/secrets/kubernetes.io/serviceaccount/token` |
+| `auth.config.service-account-key` | Path to your GCP service account key file. This field is required when using `gcp-iam` auth type.