Compare commits

...

11 Commits

11 changed files with 190 additions and 145 deletions

View File

@ -50,6 +50,13 @@ jobs:
environment:
name: Gamma
steps:
- uses: twingate/github-action@v1
with:
# The Twingate Service Key used to connect Twingate to the proper service
# Learn more about [Twingate Services](https://docs.twingate.com/docs/services)
#
# Required
service-key: ${{ secrets.TWINGATE_GAMMA_SERVICE_KEY }}
- name: Checkout code
uses: actions/checkout@v2
- name: Setup Node.js environment
@ -74,21 +81,21 @@ jobs:
uses: pr-mpt/actions-commit-hash@v2
- name: Download task definition
run: |
aws ecs describe-task-definition --task-definition infisical-core-platform --query taskDefinition > task-definition.json
aws ecs describe-task-definition --task-definition infisical-core-gamma-stage --query taskDefinition > task-definition.json
- name: Render Amazon ECS task definition
id: render-web-container
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: task-definition.json
container-name: infisical-core-platform
container-name: infisical-core
image: infisical/staging_infisical:${{ steps.commit.outputs.short }}
environment-variables: "LOG_LEVEL=info"
- name: Deploy to Amazon ECS service
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.render-web-container.outputs.task-definition }}
service: infisical-core-platform
cluster: infisical-core-platform
service: infisical-core-gamma-stage
cluster: infisical-gamma-stage
wait-for-service-stability: true
production-postgres-deployment:

View File

@ -101,33 +101,51 @@ export const getUserPrivateKey = async (
password: string,
user: Pick<
TUserEncryptionKeys,
"protectedKeyTag" | "protectedKey" | "protectedKeyIV" | "encryptedPrivateKey" | "iv" | "salt" | "tag"
| "protectedKeyTag"
| "protectedKey"
| "protectedKeyIV"
| "encryptedPrivateKey"
| "iv"
| "salt"
| "tag"
| "encryptionVersion"
>
) => {
const derivedKey = await argon2.hash(password, {
salt: Buffer.from(user.salt),
memoryCost: 65536,
timeCost: 3,
parallelism: 1,
hashLength: 32,
type: argon2.argon2id,
raw: true
});
if (!derivedKey) throw new Error("Failed to derive key from password");
const key = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.protectedKey as string,
iv: user.protectedKeyIV as string,
tag: user.protectedKeyTag as string,
key: derivedKey
});
if (user.encryptionVersion === 1) {
return decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key: password.slice(0, 32).padStart(32 + (password.slice(0, 32).length - new Blob([password]).size), "0")
});
}
if (user.encryptionVersion === 2 && user.protectedKey && user.protectedKeyIV && user.protectedKeyTag) {
const derivedKey = await argon2.hash(password, {
salt: Buffer.from(user.salt),
memoryCost: 65536,
timeCost: 3,
parallelism: 1,
hashLength: 32,
type: argon2.argon2id,
raw: true
});
if (!derivedKey) throw new Error("Failed to derive key from password");
const key = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.protectedKey,
iv: user.protectedKeyIV,
tag: user.protectedKeyTag,
key: derivedKey
});
const privateKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key: Buffer.from(key, "hex")
});
return privateKey;
const privateKey = decryptSymmetric128BitHexKeyUTF8({
ciphertext: user.encryptedPrivateKey,
iv: user.iv,
tag: user.tag,
key: Buffer.from(key, "hex")
});
return privateKey;
}
throw new Error(`GetUserPrivateKey: Encryption version not found`);
};
export const buildUserProjectKey = async (privateKey: string, publickey: string) => {

View File

@ -9,6 +9,7 @@ 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 { logger } from "@app/lib/logger";
import { getServerCfg } from "@app/services/super-admin/super-admin-service";
import { TTokenDALFactory } from "../auth-token/auth-token-dal";
@ -258,7 +259,13 @@ export const authLoginServiceFactory = ({
});
// from password decrypt the private key
if (password) {
const privateKey = await getUserPrivateKey(password, userEnc);
const privateKey = await getUserPrivateKey(password, userEnc).catch((err) => {
logger.error(
err,
`loginExchangeClientProof: private key generation failed for [userId=${user.id}] and [email=${user.email}] `
);
return "";
});
const hashedPassword = await bcrypt.hash(password, cfg.BCRYPT_SALT_ROUND);
const { iv, tag, ciphertext, encoding } = infisicalSymmetricEncypt(privateKey);
await userDAL.updateUserEncryptionByUserId(userEnc.userId, {

View File

@ -165,7 +165,8 @@ export const authSignupServiceFactory = ({
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
tag: encryptedPrivateKeyTag,
encryptionVersion: 2
});
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(privateKey);
const updateduser = await authDAL.transaction(async (tx) => {
@ -325,7 +326,8 @@ export const authSignupServiceFactory = ({
protectedKeyTag,
encryptedPrivateKey,
iv: encryptedPrivateKeyIV,
tag: encryptedPrivateKeyTag
tag: encryptedPrivateKeyTag,
encryptionVersion: 2
});
const { tag, encoding, ciphertext, iv } = infisicalSymmetricEncypt(privateKey);
const updateduser = await authDAL.transaction(async (tx) => {

View File

@ -98,6 +98,7 @@ export const superAdminServiceFactory = ({
if (existingUser) throw new BadRequestError({ name: "Admin sign up", message: "User already exist" });
const privateKey = await getUserPrivateKey(password, {
encryptionVersion: 2,
salt,
protectedKey,
protectedKeyIV,

View File

@ -25,6 +25,10 @@ Used to configure platform-specific security and operational settings
https://app.infisical.com).
</ParamField>
<ParamField query="TELEMETRY_ENABLED" type="string" default="true" optional>
Telemetry helps us improve Infisical but if you want to dsiable it you may set this to `false`.
</ParamField>
## Data Layer
The platform utilizes Postgres to persist all of its data and Redis for caching and backgroud tasks

View File

@ -26,4 +26,4 @@ export const createNotification = (
type: myProps?.type || "info",
});
export const NotificationContainer = () => <ToastContainer hideProgressBar />;
export const NotificationContainer = () => <ToastContainer pauseOnHover toastClassName="border border-mineshaft-500" style={{ width: "400px" }} />;

View File

@ -11,7 +11,6 @@ import {
FormControl,
Input,
ModalClose,
SecretInput,
Select,
SelectItem
} from "@app/components/v2";
@ -125,7 +124,7 @@ export const AddShareSecretForm = ({
};
return (
<form className="flex w-full flex-col items-center" onSubmit={handleSubmit(onFormSubmit)}>
<div className={`${!inModal && "border border-mineshaft-600 bg-mineshaft-800 p-4"}`}>
<div className={`${!inModal && "border border-mineshaft-600 bg-mineshaft-800 rounded-md p-6"}`}>
<div className="mb-4">
<Controller
control={control}
@ -137,25 +136,25 @@ export const AddShareSecretForm = ({
isError={Boolean(error)}
errorText={error?.message}
>
<SecretInput
isVisible
<textarea
placeholder="Enter sensitive data to share via an encrypted link..."
{...field}
containerClassName="py-1.5 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/50 border border-mineshaft-600 bg-mineshaft-900 px-2 min-h-[70px]"
className="py-1.5 w-full h-40 placeholder:text-mineshaft-400 rounded-md transition-all group-hover:mr-2 text-bunker-300 hover:border-primary-400/30 focus:border-primary-400/50 outline-none border border-mineshaft-600 bg-mineshaft-900 px-2 min-h-[70px]"
/>
</FormControl>
)}
/>
</div>
<div className="flex w-full flex-row justify-center">
<div className="w-2/7 flex">
<div className="hidden sm:block sm:w-2/6 flex">
<Controller
control={control}
name="expiresAfterViews"
defaultValue={6}
defaultValue={1}
render={({ field, fieldState: { error } }) => (
<FormControl
className="mb-4 w-full"
label="Expires After Views"
label="Expires after Views"
isError={Boolean(error)}
errorText="Please enter a valid number of views"
>
@ -164,16 +163,16 @@ export const AddShareSecretForm = ({
)}
/>
</div>
<div className="w-1/7 flex items-center justify-center px-2">
<div className="hidden sm:flex sm:w-1/7 items-center justify-center px-2 mx-auto">
<p className="px-4 text-sm text-gray-400">OR</p>
</div>
<div className="w-4/7 flex">
<div className="flex w-full">
<div className="flex w-2/5 w-full justify-center">
<div className="w-full sm:w-3/6 flex justify-end">
<div className="flex justify-start">
<div className="flex w-full pr-2 justify-center">
<Controller
control={control}
name="expiresInValue"
defaultValue={6}
defaultValue={10}
render={({ field, fieldState: { error } }) => (
<FormControl
label="Expires after Time"
@ -185,7 +184,7 @@ export const AddShareSecretForm = ({
)}
/>
</div>
<div className="flex w-3/5 w-full justify-center">
<div className="flex justify-center">
<Controller
control={control}
name="expiresInUnit"
@ -196,7 +195,7 @@ export const AddShareSecretForm = ({
defaultValue={field.value}
{...field}
onValueChange={(e) => onChange(e)}
className="w-full"
className="w-full border border-mineshaft-600"
>
{expirationUnitsAndActions.map(({ unit }) => (
<SelectItem value={unit} key={unit}>
@ -211,7 +210,7 @@ export const AddShareSecretForm = ({
</div>
</div>
</div>
<div className={`flex items-center ${!inModal && "justify-left pt-1"}`}>
<div className={`flex items-center ${!inModal && "justify-left pt-2"}`}>
<Button className="mr-4" type="submit" isDisabled={isSubmitting} isLoading={isSubmitting}>
{inModal ? "Create" : "Share Secret"}
</Button>

View File

@ -15,9 +15,9 @@ export const ViewAndCopySharedSecret = ({
copyUrlToClipboard: () => void;
}) => {
return (
<div className={`flex w-full justify-center ${!inModal ? "mx-auto max-w-[40rem]" : ""}`}>
<div className={`${!inModal ? "border border-mineshaft-600 bg-mineshaft-800 p-4" : ""}`}>
<div className="my-2 flex items-center justify-end rounded-md bg-white/[0.07] p-2 text-base text-gray-400">
<div className={`flex w-full justify-center px-6 ${!inModal ? "mx-auto max-w-2xl" : ""}`}>
<div className={`${!inModal ? "border border-mineshaft-600 bg-mineshaft-800 rounded-md p-4" : ""}`}>
<div className="my-2 flex items-center justify-end rounded-md border border-mineshaft-500 bg-mineshaft-700 p-2 text-base text-gray-400">
<p className="mr-4 break-all">{newSharedSecret}</p>
<IconButton
ariaLabel="copy icon"

View File

@ -3,7 +3,7 @@ import Head from "next/head";
import Image from "next/image";
import Link from "next/link";
import { useRouter } from "next/router";
import { faArrowRight, faPlus } from "@fortawesome/free-solid-svg-icons";
import { faArrowRight } from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { decryptSymmetric } from "@app/components/utilities/cryptography/crypto";
@ -54,107 +54,114 @@ export const ShareSecretPublicPage = ({ isNewSession }: { isNewSession: boolean
navigator.clipboard.writeText(decryptedSecret);
setIsUrlCopied(true);
};
const { popUp, handlePopUpOpen, handlePopUpToggle } = usePopUp(["createSharedSecret"] as const);
const { popUp, handlePopUpToggle } = usePopUp(["createSharedSecret"] as const);
return (
<div className="h-screen bg-gradient-to-tr from-mineshaft-700 to-bunker-800 text-gray-200">
<div className="h-screen dark:[color-scheme:dark] flex flex-col overflow-y-auto bg-gradient-to-tr from-mineshaft-700 to-bunker-800 text-gray-200">
<Head>
<title>Secret Shared | Infisical</title>
<link rel="icon" href="/infisical.ico" />
</Head>
<div className="h-screen w-full flex-col items-center justify-center dark:[color-scheme:dark]">
<div className="mb-4 flex justify-center pt-8 md:pt-16">
<Link href="https://infisical.com">
<Image
src="/images/gradientLogo.svg"
height={90}
width={120}
alt="Infisical logo"
className="cursor-pointer"
/>
</Link>
</div>
<h1 className="mt-6 mb-4 bg-gradient-to-b from-white to-bunker-200 bg-clip-text px-4 text-center text-2xl font-medium text-transparent">
{id ? "Someone shared a secret on Infisical with you." : "Share Secrets with Infisical"}
</h1>
<div className="m-auto mt-8 flex w-full max-w-xl justify-center px-4">
{id && (
<SecretTable
isLoading={isLoading}
decryptedSecret={decryptedSecret}
isUrlCopied={isUrlCopied}
copyUrlToClipboard={copyUrlToClipboard}
/>
)}
</div>
{isNewSession && (
<AddShareSecretModal
popUp={popUp}
handlePopUpToggle={handlePopUpToggle}
inModal={false}
isPublic
/>
)}
<div className="m-auto my-6 flex w-full max-w-xl justify-center px-8 px-4 sm:my-8">
<div className="w-full border-t border-mineshaft-600" />
</div>
<div className="m-auto max-w-xl px-4">
{!isNewSession && (
<div className="flex flex-1 flex-col items-center justify-center px-4 pb-4">
<Button
className="bg-mineshaft-700 text-bunker-200"
colorSchema="primary"
variant="outline_bg"
size="sm"
onClick={() => {
handlePopUpOpen("createSharedSecret");
}}
leftIcon={<FontAwesomeIcon icon={faPlus} />}
>
Share your own Secret
</Button>
</div>
)}
<div className="m-auto mb-8 flex flex max-w-xl flex-col justify-center gap-2 rounded-md border border-primary-500/30 bg-primary/5 p-6">
<p className="pb-2 font-semibold text-mineshaft-100 md:pb-4 md:text-xl">
Safe, Secure, & Open Source
</p>
<p className="md:text-md text-sm">
Infisical is the #1 {" "}
<a
href="https://github.com/infisical/infisical"
target="_blank"
rel="noopener noreferrer"
className="text-primary underline"
>
open source
</a>{" "}
secrets management platform for developers. <br className="hidden md:inline" />
<div className="pb-2" />
Infisical Secret Sharing uses end-to-end encrypted architecture to ensure that your secrets are truly private, even from our servers.
</p>
<div className="w-full flex flex-grow items-center justify-center dark:[color-scheme:dark]">
<div className="relative">
<div className="mb-4 flex justify-center pt-8">
<Link href="https://infisical.com">
<span className="mt-4 cursor-pointer duration-200 hover:text-primary">
Learn More <FontAwesomeIcon icon={faArrowRight} />
</span>
<Image
src="/images/gradientLogo.svg"
height={90}
width={120}
alt="Infisical logo"
className="cursor-pointer"
/>
</Link>
</div>
<div className="w-full flex justify-center">
<h1 className={`${id ? "max-w-sm mb-4": "max-w-md mt-4 mb-6"} bg-gradient-to-b from-white to-bunker-200 bg-clip-text px-4 text-center text-3xl font-medium text-transparent`}>
{id ? "Someone shared a secret on Infisical with you" : "Share a secret with Infisical"}
</h1>
</div>
<div className="m-auto mt-4 flex w-full max-w-2xl justify-center px-6">
{id && (
<SecretTable
isLoading={isLoading}
decryptedSecret={decryptedSecret}
isUrlCopied={isUrlCopied}
copyUrlToClipboard={copyUrlToClipboard}
/>
)}
</div>
{isNewSession && (
<div className="px-0 sm:px-6">
<AddShareSecretModal
popUp={popUp}
handlePopUpToggle={handlePopUpToggle}
inModal={false}
isPublic
/>
</div>
)}
{!isNewSession && (
<div className="flex flex-1 flex-col items-center justify-center px-6 pt-4">
<a
href="https://share.infisical.com/"
target="_blank"
rel="noopener noreferrer"
className="mt-3 w-full text-sm font-normal leading-[1.2rem] text-mineshaft-300 hover:text-mineshaft-100"
>
<Button
className="bg-mineshaft-700 text-bunker-200 w-full py-3"
colorSchema="primary"
variant="outline_bg"
size="sm"
onClick={() => {}}
rightIcon={<FontAwesomeIcon icon={faArrowRight} className="pl-2" />}
>
Share your own Secret
</Button>
</a>
</div>
)}
<div className="m-auto my-6 flex w-full max-w-xl justify-center px-8 px-4 sm:my-8">
<div className="w-full border-t border-mineshaft-600" />
</div>
<div className="flex flex-col justify-center items-center m-auto max-w-2xl px-6">
<div className="m-auto mb-12 flex flex max-w-2xl w-full flex-col justify-center rounded-md border border-primary-500/30 bg-primary/5 p-6 pt-5">
<p className="pb-2 font-semibold text-mineshaft-100 md:pb-3 text-lg md:text-xl w-full">
Open source <span className="bg-clip-text text-transparent bg-gradient-to-tr from-yellow-500 to-primary-500">secret management</span> for developers
</p>
<div className="flex flex-col sm:flex-row gap-x-4">
<p className="md:text-md text-md">
<a
href="https://github.com/infisical/infisical"
target="_blank"
rel="noopener noreferrer"
className="bg-clip-text text-transparent bg-gradient-to-tr from-yellow-500 to-primary-500 text-bold"
>
Infisical
</a>{" "} is the all-in-one secret management platform to securely manage secrets, configs, and certificates across your team and infrastructure.
</p>
<Link href="https://infisical.com">
<span className="mt-4 border border-mineshaft-400/40 w-[17.5rem] h-min py-2 px-3 rounded-md bg-mineshaft-600 cursor-pointer duration-200 hover:text-white hover:border-primary/60 hover:bg-primary/20">
Try Infisical <FontAwesomeIcon icon={faArrowRight} className="pl-1"/>
</span>
</Link>
</div>
</div>
</div>
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} isPublic inModal />
</div>
<div className="bottom-0 flex w-full items-center justify-center bg-mineshaft-600 p-2 sm:absolute">
<p className="text-center text-sm text-mineshaft-300">
© 2024{" "}
<a className="text-primary" href="https://infisical.com">
Infisical
</a>
. All rights reserved.
<br />
156 2nd st, 3rd Floor, San Francisco, California, 94105, United States. 🇺🇸
</p>
</div>
<AddShareSecretModal popUp={popUp} handlePopUpToggle={handlePopUpToggle} isPublic inModal />
</div>
<div className="mt-auto flex w-full items-center justify-center bg-mineshaft-600 p-2">
<p className="text-center text-sm text-mineshaft-300">
© 2024{" "}
<a className="text-primary" href="https://infisical.com">
Infisical
</a>
. All rights reserved.
<br />
156 2nd st, 3rd Floor, San Francisco, California, 94105, United States. 🇺🇸
</p>
</div>
</div>
);

View File

@ -16,7 +16,7 @@ export const SecretTable = ({
isUrlCopied,
copyUrlToClipboard
}: Props) => (
<div className="flex w-full items-center justify-center rounded border border-solid border-mineshaft-700 bg-mineshaft-800 p-2">
<div className="flex w-full items-center justify-center rounded-md border border-solid border-mineshaft-700 bg-mineshaft-800 p-2">
{isLoading && <div className="bg-mineshaft-800 text-center text-bunker-400">Loading...</div>}
{!isLoading && !decryptedSecret && (
<Tr>
@ -37,7 +37,7 @@ export const SecretTable = ({
colorSchema="primary"
ariaLabel="copy to clipboard"
onClick={copyUrlToClipboard}
className="mx-1 flex max-h-8 items-center rounded"
className="mx-1 flex max-h-8 items-center rounded absolute top-1 sm:top-2 right-0 sm:right-5"
size="xs"
>
<FontAwesomeIcon className="pr-2" icon={isUrlCopied ? faCheck : faCopy} /> Copy