diff --git a/.env.example b/.env.example
index 989a285e33..ec832f9e9d 100644
--- a/.env.example
+++ b/.env.example
@@ -9,13 +9,13 @@ ENCRYPTION_KEY=replace_with_lengthy_secure_hex
JWT_SIGNUP_SECRET=replace_with_lengthy_secure_hex
JWT_REFRESH_SECRET=replace_with_lengthy_secure_hex
JWT_AUTH_SECRET=replace_with_lengthy_secure_hex
+JWT_SERVICE_SECRET=replace_with_lengthy_secure_hex
# JWT lifetime
# Optional lifetimes for JWT tokens expressed in seconds or a string
# describing a time span (e.g. 60, "2 days", "10h", "7d")
JWT_AUTH_LIFETIME=
JWT_REFRESH_LIFETIME=
-JWT_SERVICE_SECRET=
JWT_SIGNUP_LIFETIME=
# Optional lifetimes for OTP expressed in seconds
diff --git a/.github/workflows/release_docker_k8_operator.yaml b/.github/workflows/release_docker_k8_operator.yaml
index 01aa3b6250..788d414b6e 100644
--- a/.github/workflows/release_docker_k8_operator.yaml
+++ b/.github/workflows/release_docker_k8_operator.yaml
@@ -26,13 +26,4 @@ jobs:
context: k8-operator
push: true
platforms: linux/amd64,linux/arm64
- tags: infisical/kubernetes-operator:latest
-
- - uses: actions/setup-go@v2
-
- - name: Upload CRD manifest
- uses: svenstaro/upload-release-action@v2
- with:
- repo_token: ${{ secrets.GITHUB_TOKEN }}
- file: dist/install-secrets-operator.yaml
- tag: ${{ github.ref }}
\ No newline at end of file
+ tags: infisical/kubernetes-operator:latest
\ No newline at end of file
diff --git a/.goreleaser.yaml b/.goreleaser.yaml
index e839733970..9ac38524bc 100644
--- a/.goreleaser.yaml
+++ b/.goreleaser.yaml
@@ -81,6 +81,7 @@ nfpms:
- rpm
- deb
- apk
+ - archlinux
bindir: /usr/bin
scoop:
bucket:
diff --git a/.prettierrc b/.prettierrc
deleted file mode 100644
index fa81e63deb..0000000000
--- a/.prettierrc
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "semi": true,
- "trailingComma": "none",
- "singleQuote": true,
- "printWidth": 80,
- "useTabs": false
-}
diff --git a/README.md b/README.md
index ba76997f1f..caf79191e6 100644
--- a/README.md
+++ b/README.md
@@ -27,6 +27,9 @@
+
+
+
@@ -92,12 +95,6 @@ Not sure where to get started? You can:
We're currently in Public Alpha.
-## 🚨 Stay Up-to-Date
-
-Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of new features are coming very quickly. Watch **releases** of this repository to be notified about future updates:
-
-
-
## 🔌 Integrations
We're currently setting the foundation and building [integrations](https://infisical.com/docs/integrations/overview) so secrets can be synced everywhere. Any help is welcome! :)
@@ -131,10 +128,14 @@ We're currently setting the foundation and building [integrations](https://infis
- 🔜 Vercel (https://github.com/Infisical/infisical/issues/60)
+
+ ✔️ Vercel
+
- 🔜 GitLab CI/CD
+
+ ✔️ Kubernetes
+
🔜 Fly.io
@@ -156,10 +157,10 @@ We're currently setting the foundation and building [integrations](https://infis
🔜 GCP
- 🔜 Kubernetes
+ 🔜 GitLab CI/CD (https://github.com/Infisical/infisical/issues/134)
- 🔜 CircleCI
+ 🔜 CircleCI (https://github.com/Infisical/infisical/issues/91)
@@ -192,7 +193,7 @@ We're currently setting the foundation and building [integrations](https://infis
🔜 Supabase
- 🔜 Serverless
+ 🔜 Render (https://github.com/Infisical/infisical/issues/132)
@@ -302,6 +303,12 @@ This repo is entirely MIT licensed, with the exception of the `ee` directory whi
Looking to report a security vulnerability? Please don't post about it in GitHub issue. Instead, refer to our [SECURITY.md](./SECURITY.md) file.
+## 🚨 Stay Up-to-Date
+
+Infisical officially launched as v.1.0 on November 21st, 2022. However, a lot of new features are coming very quickly. Watch **releases** of this repository to be notified about future updates:
+
+
+
## 🦸 Contributors
[//]: contributor-faces
@@ -310,4 +317,4 @@ Looking to report a security vulnerability? Please don't post about it in GitHub
-
+
diff --git a/backend/.eslintrc b/backend/.eslintrc
index 7fe1989012..c1ca1a1eba 100644
--- a/backend/.eslintrc
+++ b/backend/.eslintrc
@@ -1,11 +1,10 @@
{
"parser": "@typescript-eslint/parser",
- "plugins": ["@typescript-eslint", "prettier"],
+ "plugins": ["@typescript-eslint"],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
- "plugin:@typescript-eslint/recommended",
- "prettier"
+ "plugin:@typescript-eslint/recommended"
],
"rules": {
"no-console": 2
diff --git a/backend/Dockerfile b/backend/Dockerfile
index ccc76e66e5..85b7204fec 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -4,12 +4,12 @@ WORKDIR /app
COPY package.json package-lock.json ./
-RUN npm ci --only-production
+RUN npm ci --only-production --ignore-scripts
COPY . .
HEALTHCHECK --interval=10s --timeout=3s --start-period=10s \
- CMD node healthcheck.js
+ CMD node healthcheck.js
CMD ["npm", "run", "start"]
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 8330bfefc2..693f055a65 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -52,13 +52,10 @@
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"eslint": "^8.26.0",
- "eslint-config-prettier": "^8.5.0",
- "eslint-plugin-prettier": "^4.2.1",
"install": "^0.13.0",
"jest": "^29.3.1",
"nodemon": "^2.0.19",
"npm": "^8.19.3",
- "prettier": "^2.7.1",
"ts-node": "^10.9.1"
}
},
@@ -3073,12 +3070,6 @@
"@types/node": "*"
}
},
- "node_modules/@types/prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
- "dev": true
- },
"node_modules/@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@@ -3930,6 +3921,7 @@
"dependencies": {
"anymatch": "~3.1.2",
"braces": "~3.0.2",
+ "fsevents": "~2.3.2",
"glob-parent": "~5.1.2",
"is-binary-path": "~2.1.0",
"is-glob": "~4.0.1",
@@ -4431,39 +4423,6 @@
"url": "https://opencollective.com/eslint"
}
},
- "node_modules/eslint-config-prettier": {
- "version": "8.5.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
- "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
- "dev": true,
- "bin": {
- "eslint-config-prettier": "bin/cli.js"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
- }
- },
- "node_modules/eslint-plugin-prettier": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
- "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
- "dev": true,
- "dependencies": {
- "prettier-linter-helpers": "^1.0.0"
- },
- "engines": {
- "node": ">=12.0.0"
- },
- "peerDependencies": {
- "eslint": ">=7.28.0",
- "prettier": ">=2.0.0"
- },
- "peerDependenciesMeta": {
- "eslint-config-prettier": {
- "optional": true
- }
- }
- },
"node_modules/eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -4781,12 +4740,6 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
- "node_modules/fast-diff": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
- "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
- "dev": true
- },
"node_modules/fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
@@ -5180,6 +5133,7 @@
"minimist": "^1.2.5",
"neo-async": "^2.6.0",
"source-map": "^0.6.1",
+ "uglify-js": "^3.1.4",
"wordwrap": "^1.0.0"
},
"bin": {
@@ -5837,6 +5791,7 @@
"@types/node": "*",
"anymatch": "^3.0.3",
"fb-watchman": "^2.0.0",
+ "fsevents": "^2.3.2",
"graceful-fs": "^4.2.9",
"jest-regex-util": "^29.2.0",
"jest-util": "^29.3.1",
@@ -6053,7 +6008,6 @@
"@jest/transform": "^29.3.1",
"@jest/types": "^29.3.1",
"@types/babel__traverse": "^7.0.6",
- "@types/prettier": "^2.1.5",
"babel-preset-current-node-syntax": "^1.0.0",
"chalk": "^4.0.0",
"expect": "^29.3.1",
@@ -6641,9 +6595,11 @@
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-4.11.0.tgz",
"integrity": "sha512-9l9n4Nk2BYZzljW3vHah3Z0rfS5npKw6ktnkmFgTcnzaXH1DRm3pDl6VMHu84EVb1lzmSaJC4OzWZqTkB5i2wg==",
"dependencies": {
+ "@aws-sdk/credential-providers": "^3.186.0",
"bson": "^4.7.0",
"denque": "^2.1.0",
"mongodb-connection-string-url": "^2.5.4",
+ "saslprep": "^1.0.3",
"socks": "^2.7.1"
},
"engines": {
@@ -6873,9 +6829,6 @@
},
"bin": {
"nopt": "bin/nopt.js"
- },
- "engines": {
- "node": "*"
}
},
"node_modules/normalize-path": {
@@ -7729,6 +7682,7 @@
"inBundle": true,
"license": "MIT",
"dependencies": {
+ "@colors/colors": "1.5.0",
"string-width": "^4.2.0"
},
"engines": {
@@ -8524,6 +8478,7 @@
"inBundle": true,
"license": "MIT",
"dependencies": {
+ "encoding": "^0.1.13",
"minipass": "^3.1.6",
"minipass-sized": "^1.0.3",
"minizlib": "^2.1.2"
@@ -9897,33 +9852,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
- "dev": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/prettier-linter-helpers": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
- "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
- "dev": true,
- "dependencies": {
- "fast-diff": "^1.1.2"
- },
- "engines": {
- "node": ">=6.0.0"
- }
- },
"node_modules/pretty-format": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
@@ -13854,12 +13782,6 @@
"@types/node": "*"
}
},
- "@types/prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ri0UmynRRvZiiUJdiz38MmIblKK+oH30MztdBVR95dv/Ubw6neWSb8u1XpRb72L4qsZOhz+L+z9JD40SJmfWow==",
- "dev": true
- },
"@types/qs": {
"version": "6.9.7",
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.7.tgz",
@@ -14860,22 +14782,6 @@
}
}
},
- "eslint-config-prettier": {
- "version": "8.5.0",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.5.0.tgz",
- "integrity": "sha512-obmWKLUNCnhtQRKc+tmnYuQl0pFU1ibYJQ5BGhTVB08bHe9wC8qUeG7c08dj9XX+AuPj1YSGSQIHl1pnDHZR0Q==",
- "dev": true,
- "requires": {}
- },
- "eslint-plugin-prettier": {
- "version": "4.2.1",
- "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-4.2.1.tgz",
- "integrity": "sha512-f/0rXLXUt0oFYs8ra4w49wYZBG5GKZpAYsJSm6rnYL5uVDjd+zowwMwVZHnAjf4edNrKpCDYfXDgmRE/Ak7QyQ==",
- "dev": true,
- "requires": {
- "prettier-linter-helpers": "^1.0.0"
- }
- },
"eslint-scope": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz",
@@ -15098,12 +15004,6 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
- "fast-diff": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
- "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
- "dev": true
- },
"fast-glob": {
"version": "3.2.12",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz",
@@ -16033,7 +15933,6 @@
"@jest/transform": "^29.3.1",
"@jest/types": "^29.3.1",
"@types/babel__traverse": "^7.0.6",
- "@types/prettier": "^2.1.5",
"babel-preset-current-node-syntax": "^1.0.0",
"chalk": "^4.0.0",
"expect": "^29.3.1",
@@ -18718,21 +18617,6 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
- "prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
- "dev": true
- },
- "prettier-linter-helpers": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
- "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
- "dev": true,
- "requires": {
- "fast-diff": "^1.1.2"
- }
- },
"pretty-format": {
"version": "29.3.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.3.1.tgz",
diff --git a/backend/package.json b/backend/package.json
index a2c5830480..119a79073a 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -34,12 +34,12 @@
"version": "1.0.0",
"main": "src/index.js",
"scripts": {
+ "prepare": "cd .. && npm install",
"start": "npm run build && node build/index.js",
"dev": "nodemon",
- "build": "rimraf ./build && tsc && cp -R ./src/templates ./src/json ./build",
+ "build": "rimraf ./build && tsc && cp -R ./src/templates ./build",
"lint": "eslint . --ext .ts",
"lint-and-fix": "eslint . --ext .ts --fix",
- "prettier-format": "prettier --config .prettierrc 'src/**/*.ts' --write",
"lint-staged": "lint-staged"
},
"repository": {
@@ -66,13 +66,10 @@
"@typescript-eslint/eslint-plugin": "^5.40.1",
"@typescript-eslint/parser": "^5.40.1",
"eslint": "^8.26.0",
- "eslint-config-prettier": "^8.5.0",
- "eslint-plugin-prettier": "^4.2.1",
"install": "^0.13.0",
"jest": "^29.3.1",
"nodemon": "^2.0.19",
"npm": "^8.19.3",
- "prettier": "^2.7.1",
"ts-node": "^10.9.1"
}
}
diff --git a/backend/src/config/index.ts b/backend/src/config/index.ts
index aa02d08a18..99c72ab3d4 100644
--- a/backend/src/config/index.ts
+++ b/backend/src/config/index.ts
@@ -18,9 +18,8 @@ const CLIENT_ID_GITHUB =
process.env.CLIENT_ID_GITHUB! || 'e787fc24bcec43ecd5d5';
const CLIENT_SECRET_VERCEL = process.env.CLIENT_SECRET_VERCEL!;
const CLIENT_SECRET_NETLIFY = process.env.CLIENT_SECRET_NETLIFY!;
-const CLIENT_SECRET_GITHUB =
- process.env.CLIENT_SECRET_GITHUB! ||
- '407f32da788f63559abd662c6de08bb2911ca8ae';
+const CLIENT_SECRET_GITHUB = process.env.CLIENT_SECRET_GITHUB! || '407f32da788f63559abd662c6de08bb2911ca8ae';
+const CLIENT_SLUG_VERCEL= process.env.CLIENT_SLUG_VERCEL!;
const POSTHOG_HOST = process.env.POSTHOG_HOST! || 'https://app.posthog.com';
const POSTHOG_PROJECT_API_KEY =
process.env.POSTHOG_PROJECT_API_KEY! ||
@@ -63,6 +62,7 @@ export {
CLIENT_SECRET_VERCEL,
CLIENT_SECRET_NETLIFY,
CLIENT_SECRET_GITHUB,
+ CLIENT_SLUG_VERCEL,
POSTHOG_HOST,
POSTHOG_PROJECT_API_KEY,
PRIVATE_KEY,
diff --git a/backend/src/helpers/integration.ts b/backend/src/helpers/integration.ts
index 9aaff9741a..ccfd72a535 100644
--- a/backend/src/helpers/integration.ts
+++ b/backend/src/helpers/integration.ts
@@ -53,12 +53,12 @@ const handleOAuthExchangeHelper = async ({
if (!bot) throw new Error('Bot must be enabled for OAuth2 code-token exchange');
// exchange code for access and refresh tokens
- let res = await exchangeCode({
+ const res = await exchangeCode({
integration,
code
});
- let update: Update = {
+ const update: Update = {
workspace: workspaceId,
integration
}
@@ -138,7 +138,7 @@ const syncIntegrationsHelper = async ({
// to that integration
for await (const integration of integrations) {
// get workspace, environment (shared) secrets
- const secrets = await BotService.getSecrets({
+ const secrets = await BotService.getSecrets({ // issue here?
workspaceId: integration.workspace.toString(),
environment: integration.environment
});
diff --git a/backend/src/integrations/sync.ts b/backend/src/integrations/sync.ts
index 363e96f34d..9c6b66674c 100644
--- a/backend/src/integrations/sync.ts
+++ b/backend/src/integrations/sync.ts
@@ -64,14 +64,13 @@ const syncSecrets = async ({
});
break;
case INTEGRATION_GITHUB:
- await syncSecretsNetlify({
+ await syncSecretsGitHub({
integration,
integrationAuth,
secrets,
accessToken
});
break;
- }
} catch (err) {
Sentry.setUser(null);
Sentry.captureException(err);
@@ -137,157 +136,149 @@ const syncSecretsHeroku = async ({
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
*/
const syncSecretsVercel = async ({
- integration,
- secrets,
- accessToken
+ integration,
+ secrets,
+ accessToken
}: {
- integration: IIntegration;
- secrets: any;
- accessToken: string;
+ integration: IIntegration,
+ secrets: any;
+ accessToken: string;
}) => {
- interface VercelSecret {
- id?: string;
- type: string;
- key: string;
- value: string;
- target: string[];
- }
-
- try {
- // Get all (decrypted) secrets back from Vercel in
- // decrypted format
- const params = new URLSearchParams({
- decrypt: 'true'
- });
-
- const res = (
- await Promise.all(
- (
- await axios.get(
- `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`,
+
+ interface VercelSecret {
+ id?: string;
+ type: string;
+ key: string;
+ value: string;
+ target: string[];
+ }
+
+ try {
+ // Get all (decrypted) secrets back from Vercel in
+ // decrypted format
+ const params = new URLSearchParams({
+ decrypt: "true"
+ });
+
+ const res = (await Promise.all((await axios.get(
+ `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env`,
{
- params,
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
+ params,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
}
- )
- ).data.envs
- .filter((secret: VercelSecret) =>
- secret.target.includes(integration.target)
- )
- .map(
- async (secret: VercelSecret) =>
- (
- await axios.get(
- `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
- {
+ ))
+ .data
+ .envs
+ .filter((secret: VercelSecret) => secret.target.includes(integration.target))
+ .map(async (secret: VercelSecret) => (await axios.get(
+ `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
+ {
headers: {
- Authorization: `Bearer ${accessToken}`
+ Authorization: `Bearer ${accessToken}`
}
- }
- )
- ).data
- )
- )
- ).reduce(
- (obj: any, secret: any) => ({
- ...obj,
- [secret.key]: secret
- }),
- {}
- );
-
- const updateSecrets: VercelSecret[] = [];
- const deleteSecrets: VercelSecret[] = [];
- const newSecrets: VercelSecret[] = [];
-
- // Identify secrets to create
- Object.keys(secrets).map((key) => {
- if (!(key in res)) {
- // case: secret has been created
- newSecrets.push({
- key: key,
- value: secrets[key],
- type: 'encrypted',
- target: [integration.target]
+
+ }
+ )).data)
+ )).reduce((obj: any, secret: any) => ({
+ ...obj,
+ [secret.key]: secret
+ }), {});
+
+ const updateSecrets: VercelSecret[] = [];
+ const deleteSecrets: VercelSecret[] = [];
+ const newSecrets: VercelSecret[] = [];
+
+ // Identify secrets to create
+ Object.keys(secrets).map((key) => {
+ if (!(key in res)) {
+ // case: secret has been created
+ newSecrets.push({
+ key: key,
+ value: secrets[key],
+ type: 'encrypted',
+ target: [integration.target]
+ });
+ }
});
- }
- });
-
- // Identify secrets to update and delete
- Object.keys(res).map((key) => {
- if (key in secrets) {
- if (res[key].value !== secrets[key]) {
- // case: secret value has changed
- updateSecrets.push({
- id: res[key].id,
- key: key,
- value: secrets[key],
- type: 'encrypted',
- target: [integration.target]
- });
- }
- } else {
- // case: secret has been deleted
- deleteSecrets.push({
- id: res[key].id,
- key: key,
- value: res[key].value,
- type: 'encrypted',
- target: [integration.target]
+
+ // Identify secrets to update and delete
+ Object.keys(res).map((key) => {
+ if (key in secrets) {
+ if (res[key].value !== secrets[key]) {
+ // case: secret value has changed
+ updateSecrets.push({
+ id: res[key].id,
+ key: key,
+ value: secrets[key],
+ type: 'encrypted',
+ target: [integration.target]
+ });
+ }
+ } else {
+ // case: secret has been deleted
+ deleteSecrets.push({
+ id: res[key].id,
+ key: key,
+ value: res[key].value,
+ type: 'encrypted',
+ target: [integration.target],
+ });
+ }
});
- }
- });
- // Sync/push new secrets
- if (newSecrets.length > 0) {
- await axios.post(
- `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`,
- newSecrets,
- {
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
+ // Sync/push new secrets
+ if (newSecrets.length > 0) {
+ await axios.post(
+ `${INTEGRATION_VERCEL_API_URL}/v10/projects/${integration.app}/env`,
+ newSecrets,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
}
- );
- }
- // Sync/push updated secrets
- if (updateSecrets.length > 0) {
- updateSecrets.forEach(async (secret: VercelSecret) => {
- const { id, ...updatedSecret } = secret;
- await axios.patch(
- `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
- updatedSecret,
- {
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- }
- );
- });
- }
+ // Sync/push updated secrets
+ if (updateSecrets.length > 0) {
+ updateSecrets.forEach(async (secret: VercelSecret) => {
+ const {
+ id,
+ ...updatedSecret
+ } = secret;
+ await axios.patch(
+ `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
+ updatedSecret,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ });
+ }
- // Delete secrets
- if (deleteSecrets.length > 0) {
- deleteSecrets.forEach(async (secret: VercelSecret) => {
- await axios.delete(
- `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
- {
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- }
- );
- });
+ // Delete secrets
+ if (deleteSecrets.length > 0) {
+ deleteSecrets.forEach(async (secret: VercelSecret) => {
+ await axios.delete(
+ `${INTEGRATION_VERCEL_API_URL}/v9/projects/${integration.app}/env/${secret.id}`,
+ {
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ });
+ }
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to sync secrets to Vercel');
}
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to sync secrets to Vercel');
- }
-};
+}
/**
* Sync/push [secrets] to Netlify site [app]
@@ -297,165 +288,211 @@ const syncSecretsVercel = async ({
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
*/
const syncSecretsNetlify = async ({
- integration,
- integrationAuth,
- secrets,
- accessToken
+ integration,
+ integrationAuth,
+ secrets,
+ accessToken
}: {
- integration: IIntegration;
- integrationAuth: IIntegrationAuth;
- secrets: any;
- accessToken: string;
+ integration: IIntegration;
+ integrationAuth: IIntegrationAuth;
+ secrets: any;
+ accessToken: string;
}) => {
- try {
- const getParams = new URLSearchParams({
- context_name: integration.context,
- site_id: integration.siteId
- });
+ try {
- const res = (
- await axios.get(
- `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
- {
- params: getParams,
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
+ interface NetlifyValue {
+ id?: string;
+ context: string; // 'dev' | 'branch-deploy' | 'deploy-preview' | 'production',
+ value: string;
}
- )
- ).data.reduce(
- (obj: any, secret: any) => ({
- ...obj,
- [secret.key]: secret.values[0].value
- }),
- {}
- );
-
- interface UpdateNetlifySecret {
- key: string;
- context: string;
- value: string;
- }
-
- interface DeleteNetlifySecret {
- key: string;
- }
-
- interface NewNetlifySecretValue {
- value: string;
- context: string;
- }
-
- interface NewNetlifySecret {
- key: string;
- values: NewNetlifySecretValue[];
- }
-
- const updateSecrets: UpdateNetlifySecret[] = [];
- const deleteSecrets: DeleteNetlifySecret[] = [];
- const newSecrets: NewNetlifySecret[] = [];
-
- // Identify secrets to create
- Object.keys(secrets).map((key) => {
- if (!(key in res)) {
- // case: secret has been created
- newSecrets.push({
- key: key,
- values: [
+
+ interface NetlifySecret {
+ key: string;
+ values: NetlifyValue[];
+ }
+
+ interface NetlifySecretsRes {
+ [index: string]: NetlifySecret;
+ }
+
+ const getParams = new URLSearchParams({
+ context_name: 'all', // integration.context or all
+ site_id: integration.siteId
+ });
+
+ const res = (await axios.get(
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
{
- value: secrets[key], // include id?
- context: integration.context
+ params: getParams,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ ))
+ .data
+ .reduce((obj: any, secret: any) => ({
+ ...obj,
+ [secret.key]: secret
+ }), {});
+
+ const newSecrets: NetlifySecret[] = []; // createEnvVars
+ const deleteSecrets: string[] = []; // deleteEnvVar
+ const deleteSecretValues: NetlifySecret[] = []; // deleteEnvVarValue
+ const updateSecrets: NetlifySecret[] = []; // setEnvVarValue
+
+ // identify secrets to create and update
+ Object.keys(secrets).map((key) => {
+ if (!(key in res)) {
+ // case: Infisical secret does not exist in Netlify -> create secret
+ newSecrets.push({
+ key,
+ values: [{
+ value: secrets[key],
+ context: integration.context
+ }]
+ });
+ } else {
+ // case: Infisical secret exists in Netlify
+ const contexts = res[key].values
+ .reduce((obj: any, value: NetlifyValue) => ({
+ ...obj,
+ [value.context]: value
+ }), {});
+
+ if (integration.context in contexts) {
+ // case: Netlify secret value exists in integration context
+ if (secrets[key] !== contexts[integration.context].value) {
+ // case: Infisical and Netlify secret values are different
+ // -> update Netlify secret context and value
+ updateSecrets.push({
+ key,
+ values: [{
+ context: integration.context,
+ value: secrets[key]
+ }]
+ });
+ }
+ } else {
+ // case: Netlify secret value does not exist in integration context
+ // -> add the new Netlify secret context and value
+ updateSecrets.push({
+ key,
+ values: [{
+ context: integration.context,
+ value: secrets[key]
+ }]
+ });
+ }
+ }
+ })
+
+ // identify secrets to delete
+ // TODO: revise (patch case where 1 context was deleted but others still there
+ Object.keys(res).map((key) => {
+ // loop through each key's context
+ if (!(key in secrets)) {
+ // case: Netlify secret does not exist in Infisical
+
+ const numberOfValues = res[key].values.length;
+
+ res[key].values.forEach((value: NetlifyValue) => {
+ if (value.context === integration.context) {
+ if (numberOfValues <= 1) {
+ // case: Netlify secret value has less than 1 context -> delete secret
+ deleteSecrets.push(key);
+ } else {
+ // case: Netlify secret value has more than 1 context -> delete secret value context
+ deleteSecretValues.push({
+ key,
+ values: [{
+ id: value.id,
+ context: integration.context,
+ value: value.value
+ }]
+ });
+ }
+ }
+ });
}
- ]
});
- }
- });
- // Identify secrets to update and delete
- Object.keys(res).map((key) => {
- if (key in secrets) {
- if (res[key] !== secrets[key]) {
- // case: secret value has changed
- updateSecrets.push({
- key: key,
- context: integration.context,
- value: secrets[key]
- });
- }
- } else {
- // case: secret has been deleted
- deleteSecrets.push({
- key
+ const syncParams = new URLSearchParams({
+ site_id: integration.siteId
});
- }
- });
- const syncParams = new URLSearchParams({
- site_id: integration.siteId
- });
+ if (newSecrets.length > 0) {
+ await axios.post(
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
+ newSecrets,
+ {
+ params: syncParams,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ }
- // Sync/push new secrets
- if (newSecrets.length > 0) {
- await axios.post(
- `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env`,
- newSecrets,
- {
- params: syncParams,
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
+ if (updateSecrets.length > 0) {
+ updateSecrets.forEach(async (secret: NetlifySecret) => {
+ await axios.patch(
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
+ {
+ context: secret.values[0].context,
+ value: secret.values[0].value
+ },
+ {
+ params: syncParams,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ });
}
- );
- }
- // Sync/push updated secrets
- if (updateSecrets.length > 0) {
- updateSecrets.forEach(async (secret: UpdateNetlifySecret) => {
- await axios.patch(
- `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
- {
- context: secret.context,
- value: secret.value
- },
- {
- params: syncParams,
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- }
- );
- });
- }
+ if (deleteSecrets.length > 0) {
+ deleteSecrets.forEach(async (key: string) => {
+ await axios.delete(
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${key}`,
+ {
+ params: syncParams,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ });
+ }
- // Delete secrets
- if (deleteSecrets.length > 0) {
- deleteSecrets.forEach(async (secret: DeleteNetlifySecret) => {
- await axios.delete(
- `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}`,
- {
- params: syncParams,
- headers: {
- Authorization: `Bearer ${accessToken}`
- }
- }
- );
- });
+ if (deleteSecretValues.length > 0) {
+ deleteSecretValues.forEach(async (secret: NetlifySecret) => {
+ await axios.delete(
+ `${INTEGRATION_NETLIFY_API_URL}/api/v1/accounts/${integrationAuth.accountId}/env/${secret.key}/value/${secret.values[0].id}`,
+ {
+ params: syncParams,
+ headers: {
+ Authorization: `Bearer ${accessToken}`
+ }
+ }
+ );
+ });
+ }
+ } catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
+ throw new Error('Failed to sync secrets to Heroku');
}
- } catch (err) {
- Sentry.setUser(null);
- Sentry.captureException(err);
- throw new Error('Failed to sync secrets to Heroku');
- }
-};
+}
/**
- * Sync/push [secrets] to Github site [app]
+ * Sync/push [secrets] to GitHub site [app]
* @param {Object} obj
* @param {IIntegration} obj.integration - integration details
* @param {IIntegrationAuth} obj.integrationAuth - integration auth details
* @param {Object} obj.secrets - secrets to push to integration (object where keys are secret keys and values are secret values)
*/
-const syncSecretsGithub = async ({
+const syncSecretsGitHub = async ({
integration,
integrationAuth,
secrets,
diff --git a/backend/src/routes/password.ts b/backend/src/routes/password.ts
index 955e532a0b..8032cba83d 100644
--- a/backend/src/routes/password.ts
+++ b/backend/src/routes/password.ts
@@ -27,7 +27,6 @@ router.post(
passwordController.changePassword
);
-// NEW
router.post(
'/email/password-reset',
passwordLimiter,
@@ -36,7 +35,6 @@ router.post(
passwordController.emailPasswordReset
);
-// NEW
router.post(
'/email/password-reset-verify',
passwordLimiter,
@@ -46,7 +44,6 @@ router.post(
passwordController.emailPasswordResetVerify
);
-// NEW
router.get(
'/backup-private-key',
passwordLimiter,
@@ -68,7 +65,6 @@ router.post(
passwordController.createBackupPrivateKey
);
-// NEW
router.post(
'/password-reset',
requireSignupAuth,
diff --git a/backend/src/utils/crypto.ts b/backend/src/utils/crypto.ts
index 585dae51d2..28f96b0cfb 100644
--- a/backend/src/utils/crypto.ts
+++ b/backend/src/utils/crypto.ts
@@ -1,6 +1,7 @@
import nacl from 'tweetnacl';
import util from 'tweetnacl-util';
import AesGCM from './aes-gcm';
+import * as Sentry from '@sentry/node';
/**
* Return new base64, NaCl, public-private key pair.
@@ -47,6 +48,8 @@ const encryptAsymmetric = ({
util.decodeBase64(privateKey)
);
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform asymmetric encryption');
}
@@ -86,6 +89,8 @@ const decryptAsymmetric = ({
util.decodeBase64(privateKey)
);
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform asymmetric decryption');
}
@@ -112,6 +117,8 @@ const encryptSymmetric = ({
iv = obj.iv;
tag = obj.tag;
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform symmetric encryption');
}
@@ -147,6 +154,8 @@ const decryptSymmetric = ({
try {
plaintext = AesGCM.decrypt(ciphertext, iv, tag, key);
} catch (err) {
+ Sentry.setUser(null);
+ Sentry.captureException(err);
throw new Error('Failed to perform symmetric decryption');
}
diff --git a/backend/src/variables/integration.ts b/backend/src/variables/integration.ts
index f67b57b59f..2e0a8182b6 100644
--- a/backend/src/variables/integration.ts
+++ b/backend/src/variables/integration.ts
@@ -1,6 +1,7 @@
import {
CLIENT_ID_HEROKU,
- CLIENT_ID_NETLIFY
+ CLIENT_ID_NETLIFY,
+ CLIENT_SLUG_VERCEL
} from '../config';
// integrations
@@ -49,6 +50,7 @@ const INTEGRATION_OPTIONS = [
isAvailable: true,
type: 'vercel',
clientId: '',
+ clientSlug: CLIENT_SLUG_VERCEL,
docsLink: ''
},
{
diff --git a/cli/packages/cmd/run.go b/cli/packages/cmd/run.go
index ec5210b1fd..7518fe98df 100644
--- a/cli/packages/cmd/run.go
+++ b/cli/packages/cmd/run.go
@@ -8,6 +8,7 @@ import (
"os"
"os/exec"
"os/signal"
+ "runtime"
"strings"
"syscall"
@@ -19,12 +20,38 @@ import (
// runCmd represents the run command
var runCmd = &cobra.Command{
+ Example: `
+ infisical run --env=dev -- npm run dev
+ infisical run --command "first-command && second-command; more-commands..."
+ `,
Use: "run [any infisical run command flags] -- [your application start command]",
Short: "Used to inject environments variables into your application process",
DisableFlagsInUseLine: true,
- Example: "infisical run --env=prod -- npm run dev",
- Args: cobra.MinimumNArgs(1),
PreRun: toggleDebug,
+ Args: func(cmd *cobra.Command, args []string) error {
+ // Check if the --command flag has been set
+ commandFlagSet := cmd.Flags().Changed("command")
+
+ // If the --command flag has been set, check if a value was provided
+ if commandFlagSet {
+ command := cmd.Flag("command").Value.String()
+ if command == "" {
+ return fmt.Errorf("you need to provide a command after the flag --command")
+ }
+
+ // If the --command flag has been set, args should not be provided
+ if len(args) > 0 {
+ return fmt.Errorf("you cannot set any arguments after --command flag. --command only takes a string command")
+ }
+ } else {
+ // If the --command flag has not been set, at least one arg should be provided
+ if len(args) == 0 {
+ return fmt.Errorf("at least one argument is required after the run command, received %d", len(args))
+ }
+ }
+
+ return nil
+ },
Run: func(cmd *cobra.Command, args []string) {
envName, err := cmd.Flags().GetString("env")
if err != nil {
@@ -54,10 +81,23 @@ var runCmd = &cobra.Command{
}
if shouldExpandSecrets {
- secretsWithSubstitutions := util.SubstituteSecrets(secrets)
- execCmd(args[0], args[1:], secretsWithSubstitutions)
+ secrets = util.SubstituteSecrets(secrets)
+ }
+
+ if cmd.Flags().Changed("command") {
+ command := cmd.Flag("command").Value.String()
+ err = executeMultipleCommandWithEnvs(command, secrets)
+ if err != nil {
+ log.Errorf("Something went wrong when executing your command [error=%s]", err)
+ return
+ }
} else {
- execCmd(args[0], args[1:], secrets)
+ err = executeSingleCommandWithEnvs(args, secrets)
+ if err != nil {
+ log.Errorf("Something went wrong when executing your command [error=%s]", err)
+ return
+ }
+ return
}
},
@@ -68,22 +108,51 @@ func init() {
runCmd.Flags().StringP("env", "e", "dev", "Set the environment (dev, prod, etc.) from which your secrets should be pulled from")
runCmd.Flags().String("projectId", "", "The project ID from which your secrets should be pulled from")
runCmd.Flags().Bool("expand", true, "Parse shell parameter expansions in your secrets")
+ runCmd.Flags().StringP("command", "c", "", "chained commands to execute (e.g. \"npm install && npm run dev; echo ...\")")
}
-// Credit: inspired by AWS Valut
-func execCmd(command string, args []string, envs []models.SingleEnvironmentVariable) error {
- numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(envs))
-
+// Will execute a single command and pass in the given secrets into the process
+func executeSingleCommandWithEnvs(args []string, secrets []models.SingleEnvironmentVariable) error {
+ command := args[0]
+ argsForCommand := args[1:]
+ numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets))
log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected)
- log.Debugf("executing command: %s %s \n", command, strings.Join(args, " "))
- log.Debugln("Secrets injected:", envs)
+ log.Debugf("executing command: %s %s \n", command, strings.Join(argsForCommand, " "))
+ log.Debugln("Secrets injected:", secrets)
+
+ cmd := exec.Command(command, argsForCommand...)
+ cmd.Stdin = os.Stdin
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Env = getAllEnvs(secrets)
+
+ return execCmd(cmd)
+}
+
+func executeMultipleCommandWithEnvs(fullCommand string, secrets []models.SingleEnvironmentVariable) error {
+ shell := [2]string{"sh", "-c"}
+ if runtime.GOOS == "windows" {
+ shell = [2]string{"cmd", "/C"}
+ } else {
+ shell[0] = os.Getenv("SHELL")
+ }
- cmd := exec.Command(command, args...)
+ cmd := exec.Command(shell[0], shell[1], fullCommand)
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
- cmd.Env = getAllEnvs(envs)
+ cmd.Env = getAllEnvs(secrets)
+ numberOfSecretsInjected := fmt.Sprintf("\u2713 Injected %v Infisical secrets into your application process successfully", len(secrets))
+ log.Infof("\x1b[%dm%s\x1b[0m", 32, numberOfSecretsInjected)
+ log.Debugf("executing command: %s %s %s \n", shell[0], shell[1], fullCommand)
+ log.Debugln("Secrets injected:", secrets)
+
+ return execCmd(cmd)
+}
+
+// Credit: inspired by AWS Valut
+func execCmd(cmd *exec.Cmd) error {
sigChannel := make(chan os.Signal, 1)
signal.Notify(sigChannel)
@@ -100,7 +169,7 @@ func execCmd(command string, args []string, envs []models.SingleEnvironmentVaria
if err := cmd.Wait(); err != nil {
_ = cmd.Process.Signal(os.Kill)
- return fmt.Errorf("Failed to wait for command termination: %v", err)
+ return fmt.Errorf("failed to wait for command termination: %v", err)
}
waitStatus := cmd.ProcessState.Sys().(syscall.WaitStatus)
diff --git a/docs/cli/commands/export.mdx b/docs/cli/commands/export.mdx
index fd58868ff8..b80fb74703 100644
--- a/docs/cli/commands/export.mdx
+++ b/docs/cli/commands/export.mdx
@@ -30,4 +30,7 @@ infisical export --format=csv > secrets.csv
# Export variables to a JSON file
infisical export --format=json > secrets.json
+
+# Export variables to a YAML file
+infisical export --format=yaml > secrets.yaml
```
diff --git a/docs/cli/commands/run.mdx b/docs/cli/commands/run.mdx
index 7fb2076129..2c65ef53e9 100644
--- a/docs/cli/commands/run.mdx
+++ b/docs/cli/commands/run.mdx
@@ -2,9 +2,25 @@
title: "infisical run"
---
-```bash
-infisical run [options] -- [your application start command]
-```
+
+
+ ```bash
+ infisical run [options] -- [your application start command]
+
+ # Example
+ infisical run [options] -- npm run dev
+ ```
+
+
+
+ ```bash
+ infisical run [options] --command [string command]
+
+ # Example
+ infisical run [options] --command "npm run bootstrap && npm run dev start; other-bash-command"
+ ```
+
+
## Description
@@ -15,5 +31,6 @@ Inject environment variables from the platform into an application process.
| Option | Description | Default value |
| -------------- | ----------------------------------------------------------------------------------------------------------- | ------------- |
| `--env` | Used to set the environment that secrets are pulled from. Accepted values: `dev`, `staging`, `test`, `prod` | `dev` |
-| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | `None` |
+| `--projectId` | Used to link a local project to the platform (required only if injecting via the service token method) | None |
| `--expand` | Parse shell parameter expansions in your secrets (e.g., `${DOMAIN}`) | `true` |
+| `--command` | Pass secrets into chained commands (e.g., `"first-command && second-command; more-commands..."`) | None |
diff --git a/docs/contributing/developing.mdx b/docs/contributing/developing.mdx
index 3be1a1b6bf..25a1f72ee1 100644
--- a/docs/contributing/developing.mdx
+++ b/docs/contributing/developing.mdx
@@ -23,7 +23,7 @@ Mandatory variables in the `.env` file:
1. Keys and JWT variables
-
+
The `.env.example` has these variables empty, you can self generate the `JWT and ENCRYPTION_KEY` with this [32-byte random hex strings generator](https://www.browserling.com/tools/random-hex).
diff --git a/docs/images/integrations-heroku-auth.png b/docs/images/integrations-heroku-auth.png
new file mode 100644
index 0000000000..da9b4cf529
Binary files /dev/null and b/docs/images/integrations-heroku-auth.png differ
diff --git a/docs/images/integrations-heroku.png b/docs/images/integrations-heroku.png
new file mode 100644
index 0000000000..cc225f2860
Binary files /dev/null and b/docs/images/integrations-heroku.png differ
diff --git a/docs/images/integrations-netlify-auth.png b/docs/images/integrations-netlify-auth.png
new file mode 100644
index 0000000000..fe25d7acf2
Binary files /dev/null and b/docs/images/integrations-netlify-auth.png differ
diff --git a/docs/images/integrations-netlify.png b/docs/images/integrations-netlify.png
new file mode 100644
index 0000000000..60261043e3
Binary files /dev/null and b/docs/images/integrations-netlify.png differ
diff --git a/docs/images/integrations-vercel-auth.png b/docs/images/integrations-vercel-auth.png
new file mode 100644
index 0000000000..d8f3d2d185
Binary files /dev/null and b/docs/images/integrations-vercel-auth.png differ
diff --git a/docs/images/integrations-vercel.png b/docs/images/integrations-vercel.png
new file mode 100644
index 0000000000..f3a814c7a3
Binary files /dev/null and b/docs/images/integrations-vercel.png differ
diff --git a/docs/images/integrations.png b/docs/images/integrations.png
new file mode 100644
index 0000000000..88b0c7aec9
Binary files /dev/null and b/docs/images/integrations.png differ
diff --git a/docs/integrations/cicd/circleci.mdx b/docs/integrations/cicd/circleci.mdx
new file mode 100644
index 0000000000..7ded52d8aa
--- /dev/null
+++ b/docs/integrations/cicd/circleci.mdx
@@ -0,0 +1,5 @@
+---
+title: "Circle CI"
+---
+
+Coming soon.
diff --git a/docs/integrations/cloud/flyio.mdx b/docs/integrations/cloud/flyio.mdx
new file mode 100644
index 0000000000..b53a524041
--- /dev/null
+++ b/docs/integrations/cloud/flyio.mdx
@@ -0,0 +1,5 @@
+---
+title: "Fly.io"
+---
+
+Coming soon.
diff --git a/docs/integrations/cloud/heroku.mdx b/docs/integrations/cloud/heroku.mdx
index 5f0debd3e4..e16cd0d540 100644
--- a/docs/integrations/cloud/heroku.mdx
+++ b/docs/integrations/cloud/heroku.mdx
@@ -1,26 +1,29 @@
---
title: "Heroku"
-description: "With this integration, you can automatically sync your secrets to Heroku as soon as you update secrets in Infisical."
---
-## Instructions
+Prerequisites:
-### Step 1: Open the integrations console
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
-Open the Infisical Dashboard. Choose the project in which you want to set up the intergation. Go to the integrations tab in the left sidebar.
+## Navigate to your project's integrations tab
-### Step 2: Authenticate with Heroku
+
-Click on "Heroku" tile. Log in if required and provide the necessary permissions to Infisical. You will afterwards be redirected back to the integrations page.
+## Authorize Infisical for Heroku
-Note: during an integration with Heroku, for security reasons, it is impossible to maintain end-to-end encryption. In theory, this lets Infisical decrypt yor environment variables. In practice, we can assure you that this will never be done, and it allows us to protect your secrets from bad actors online. With any questions, reach out support@infisical.com.
+Press on the Heroku tile and grant Infisical access to your Heroku account.
-### Step 3: Start integration
+
-Choose a Heroku App that you want to sync the secrets to, and the Infisical project environment that you want to sync the secrets from. Start the integration.
+
+ If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
+ Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
+
-The integration should now show status 'In Sync'. Every time you edit secrets, they will be automatically pushed to Heroku.
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which Heroku app and press start integration to start syncing secrets to Heroku.
+
+
-
- If you need to update your integration, you will have to delete the current one and create a new one.
-
diff --git a/docs/integrations/cloud/netlify.mdx b/docs/integrations/cloud/netlify.mdx
new file mode 100644
index 0000000000..e78f013686
--- /dev/null
+++ b/docs/integrations/cloud/netlify.mdx
@@ -0,0 +1,32 @@
+---
+title: "Netlify"
+---
+
+
+ Infisical integrates with Netlify's new environment variable experience. If your site uses Netlify's old environment variable experience, you'll have to upgrade it to the new one to use this integration.
+
+
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+
+## Navigate to your project's integrations tab
+
+
+
+## Authorize Infisical for Netlify
+
+Press on the Netlify tile and grant Infisical access to your Netlify account.
+
+
+
+
+ If this is your project's first cloud integration, then you'll have to grant Infisical access to your project's environment variables.
+ Although this step breaks E2EE, it's necessary for Infisical to sync the environment variables to the cloud platform.
+
+
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which Netlify app and context. Lastly, press start integration to start syncing secrets to Netlify.
+
+
\ No newline at end of file
diff --git a/docs/integrations/cloud/render.mdx b/docs/integrations/cloud/render.mdx
new file mode 100644
index 0000000000..895bf01d17
--- /dev/null
+++ b/docs/integrations/cloud/render.mdx
@@ -0,0 +1,5 @@
+---
+title: "Render"
+---
+
+Coming soon.
diff --git a/docs/integrations/cloud/vercel.mdx b/docs/integrations/cloud/vercel.mdx
index eb09203b57..59b416c446 100644
--- a/docs/integrations/cloud/vercel.mdx
+++ b/docs/integrations/cloud/vercel.mdx
@@ -2,4 +2,22 @@
title: "Vercel"
---
-Coming soon.
+Prerequisites:
+
+- Set up and add envars to [Infisical Cloud](https://app.infisical.com)
+
+## Navigate to your project's integrations tab
+
+
+
+## Authorize Infisical for Vercel
+
+Press on the Vercel tile and grant Infisical access to your Vercel account.
+
+
+
+## Start integration
+
+Select which Infisical environment secrets you want to sync to which Vercel app and environment. Lastly, press start integration to start syncing secrets to Vercel.
+
+
\ No newline at end of file
diff --git a/docs/integrations/overview.mdx b/docs/integrations/overview.mdx
index 9323f8917e..fb8a5d6314 100644
--- a/docs/integrations/overview.mdx
+++ b/docs/integrations/overview.mdx
@@ -1,5 +1,5 @@
---
-title: "Overview"
+title: 'Overview'
---
Integrations allow environment variables to be synced from Infisical into your local development workflow, CI/CD pipelines, and production infrastructure.
@@ -10,18 +10,10 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
| -------------------------------------------------------- | --------- | ----------- |
| [Docker](/integrations/platforms/docker) | Platform | Available |
| [Docker-Compose](/integrations/platforms/docker-compose) | Platform | Available |
-| Kubernetes | Platform | Coming soon |
+| [Kubernetes](/integrations/platforms/kubernetes) | Platform | Available |
| [Heroku](/integrations/cloud/heroku) | Cloud | Available |
-| [Vercel](/integrations/cloud/vercel) | Cloud | Coming soon |
-| AWS | Cloud | Coming soon |
-| GCP | Cloud | Coming soon |
-| Azure | Cloud | Coming soon |
-| DigitalOcean | Cloud | Coming soon |
-| GitLab | CI/CD | Coming soon |
-| CircleCI | CI/CD | Coming soon |
-| TravisCI | CI/CD | Coming soon |
-| GitHub Actions | CI/CD | Coming soon |
-| Jenkins | CI/CD | Coming soon |
+| [Vercel](/integrations/cloud/vercel) | Cloud | Available |
+| [Netlify](/integrations/cloud/netlify) | Cloud | Available |
| [React](/integrations/frameworks/react) | Framework | Available |
| [Vue](/integrations/frameworks/vue) | Framework | Available |
| [Express](/integrations/frameworks/express) | Framework | Available |
@@ -36,3 +28,14 @@ Missing an integration? Throw in a [request](https://github.com/Infisical/infisi
| [Flask](/integrations/frameworks/flask) | Framework | Available |
| [Laravel](/integrations/frameworks/laravel) | Framework | Available |
| [Ruby on Rails](/integrations/frameworks/rails) | Framework | Available |
+| [Render](/integrations/cloud/render) | Cloud | Coming soon |
+| [Fly.io](/integrations/cloud/flyio) | Cloud | Coming soon |
+| AWS | Cloud | Coming soon |
+| GCP | Cloud | Coming soon |
+| Azure | Cloud | Coming soon |
+| DigitalOcean | Cloud | Coming soon |
+| GitLab | CI/CD | Coming soon |
+| [CircleCI](/integrations/cicd/circleci) | CI/CD | Coming soon |
+| TravisCI | CI/CD | Coming soon |
+| GitHub Actions | CI/CD | Coming soon |
+| Jenkins | CI/CD | Coming soon |
diff --git a/docs/integrations/platforms/kubernetes.mdx b/docs/integrations/platforms/kubernetes.mdx
new file mode 100644
index 0000000000..51a9d7ec4f
--- /dev/null
+++ b/docs/integrations/platforms/kubernetes.mdx
@@ -0,0 +1,161 @@
+---
+title: 'Kubernetes'
+---
+
+The Infisical Secrets Operator is a custom Kubernetes controller that helps keep secrets in a cluster up to date by synchronizing them.
+It is installed in its own namespace within the cluster and follows strict RBAC policies.
+The operator uses InfisicalSecret custom resources to identify which secrets to sync and where to store them.
+It is responsible for continuously updating managed secrets, and in the future may also automatically reload deployments that use them as needed.
+
+## Install Operator
+
+The operator can be install via [Helm](helm.sh) or [kubectl](https://github.com/kubernetes/kubectl)
+
+
+
+ Install Infisical Helm repository
+ ```bash
+ helm repo add infisical-helm-charts 'https://dl.cloudsmith.io/public/infisical/helm-charts/helm/charts/'
+
+ helm repo update
+ ```
+
+ Install the Helm chart
+ ```bash
+ helm install --generate-name infisical-helm-charts/secrets-operator
+ ```
+
+
+
+ The operator will be installed in `infisical-operator-system` namespace
+ ```
+ kubectl apply -f https://raw.githubusercontent.com/Infisical/infisical/main/k8-operator/kubectl-install/install-secrets-operator.yaml
+ ```
+
+
+
+## Sync Infisical Secrets to your cluster
+
+To retrieve secrets from an Infisical project and store them in your Kubernetes cluster, you can use the InfisicalSecret custom resource.
+This resource is available after installing the Infisical operator. In order to specify the Infisical Token location and the location where the retrieved secrets should be stored, you can use the `tokenSecretReference` and `managedSecretReference` fields within the InfisicalSecret resource.
+
+
+ The `tokenSecretReference` field in the InfisicalSecret resource is used to specify the location of the Infisical Token, which is required for authenticating and retrieving secrets from an Infisical project.
+
+ To create a Kubernetes secret containing an [Infisical Token](../../getting-started/dashboard/token), you can run the following command.
+ ``` bash
+ kubectl create secret generic service-token --from-literal=infisicalToken=
+ ```
+
+Once the secret is created, add the name and namespace of the secret under `tokenSecretReference` field in the InfisicalSecret custom resource.
+
+{' '}
+
+
+ No matter what the name of the secret is or its namespace, it must contain a
+ key named `infisicalToken` with a valid Infisical Token as the value
+
+
+
+
+
+The `managedSecretReference` field in the InfisicalSecret resource is used to specify the location where secrets retrieved from an Infisical project should be stored.
+You should specify the name and namespace of the Kubernetes secret that will hold these secrets. The operator will create the secret for you, you just need to provide its name and namespace.
+
+It is recommended that the managed secret be created in the same namespace as the deployment that will use it.
+
+
+
+```yaml
+apiVersion: secrets.infisical.com/v1alpha1
+kind: InfisicalSecret
+metadata:
+ # Name of of this InfisicalSecret resource
+ name: infisicalsecret-sample
+spec:
+ # The host that should be used to pull secrets from. The default value is https://infisical.com/api.
+ hostAPI: https://infisical.com/api
+
+ # The Infisical project from which to pull secrets from
+ projectId: 62faf98ae0b05e8529b5da46
+
+ # The environment (dev, prod, testing, etc.) of the above project from where secrets should be pulled from
+ environment: dev
+
+ # The Kubernetes secret the stores the Infisical token
+ tokenSecretReference:
+ # Kubernetes secret name
+ secretName: service-token
+ # The secret namespace
+ secretNamespace: default
+
+ # The Kubernetes secret that Infisical Operator will create and populate with secrets from the above project
+ managedSecretReference:
+ # The name of managed Kubernetes secret that should be created
+ secretName: managed-secret
+ # The namespace the managed secret should be installed in
+ secretNamespace: default
+```
+
+## Verify
+
+To use the InfisicalSecret custom resource in your deployment, you can simply reference the managed secret specified in the `managedSecretReference` field as you would any other Kubernetes secret.
+To verify that the operator has successfully created the managed secret, you can check the secrets in the namespace that was specified.
+
+```bash
+# Verify managed secret is created
+kubectl get secrets -n
+```
+
+
+ The Infisical secrets will be synced and stored into the managed secret every
+ 5 minutes.
+
+
+## Troubleshoot
+
+If the operator is unable to fetch secrets from the API, it will not affect the managed Kubernetes secret.
+It will continue attempting to reconnect to the API indefinitely.
+The InfisicalSecret resource uses the `status.conditions` field to report its current state and any errors encountered.
+
+```yaml
+$ kubectl get infisicalSecrets
+NAME AGE
+infisicalsecret-sample 12s
+
+$ kubectl describe infisicalSecret infisicalsecret-sample
+...
+Spec:
+...
+Status:
+ Conditions:
+ Last Transition Time: 2022-12-18T04:29:09Z
+ Message: Infisical controller has located the Infisical token in provided Kubernetes secret
+ Reason: OK
+ Status: True
+ Type: secrets.infisical.com/LoadedInfisicalToken
+ Last Transition Time: 2022-12-18T04:29:10Z
+ Message: Failed to update secret because: 400 Bad Request
+ Reason: Error
+ Status: False
+ Type: secrets.infisical.com/ReadyToSyncSecrets
+Events:
+```
+
+## Uninstall Operator
+
+The managed secret created by the operator will not be deleted when the operator is uninstalled.
+
+
+
+ Install Infisical Helm repository
+ ```bash
+ helm uninstall add
+ ```
+
+
+ ```
+ kubectl delete -f https://raw.githubusercontent.com/Infisical/infisical/main/k8-operator/kubectl-install/install-secrets-operator.yaml
+ ```
+
+
diff --git a/docs/mint.json b/docs/mint.json
index 547291fa5e..ff00ca83a1 100644
--- a/docs/mint.json
+++ b/docs/mint.json
@@ -21,7 +21,9 @@
"to": "#F8B7BD"
}
},
- "topbarLinks": [{ "name": "Log In", "url": "https://app.infisical.com/login" }],
+ "topbarLinks": [
+ { "name": "Log In", "url": "https://app.infisical.com/login" }
+ ],
"topbarCtaButton": {
"name": "Start for Free",
"url": "https://app.infisical.com/signup"
@@ -113,27 +115,33 @@
"pages": ["self-hosting/configuration/envars"]
}
]
- },
+ },
{
"group": "Integrations",
- "pages": [
- "integrations/overview"
- ]
+ "pages": ["integrations/overview"]
},
{
"group": "Platforms",
"pages": [
"integrations/platforms/docker",
- "integrations/platforms/docker-compose"
+ "integrations/platforms/docker-compose",
+ "integrations/platforms/kubernetes"
]
},
{
"group": "Cloud",
"pages": [
"integrations/cloud/heroku",
- "integrations/cloud/vercel"
+ "integrations/cloud/vercel",
+ "integrations/cloud/netlify",
+ "integrations/cloud/render",
+ "integrations/cloud/flyio"
]
},
+ {
+ "group": "CI/CD",
+ "pages": ["integrations/cicd/circleci"]
+ },
{
"group": "Frameworks",
"pages": [
@@ -171,5 +179,8 @@
]
}
],
- "backgroundImage": "/images/background.png"
+ "backgroundImage": "/images/background.png",
+ "integrations": {
+ "intercom": "hsg644ru"
+ }
}
diff --git a/frontend/Dockerfile b/frontend/Dockerfile
index 5deb468f74..520f0fb7fc 100644
--- a/frontend/Dockerfile
+++ b/frontend/Dockerfile
@@ -10,7 +10,7 @@ WORKDIR /app
COPY package.json package-lock.json next.config.js ./
# Install dependencies
-RUN npm ci --only-production
+RUN npm ci --only-production --ignore-scripts
# Rebuild the source code only when needed
diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev
index 2bae23823f..cb462bbc4d 100644
--- a/frontend/Dockerfile.dev
+++ b/frontend/Dockerfile.dev
@@ -9,7 +9,7 @@ COPY package.json ./
COPY package-lock.json ./
# Install
-RUN npm install
+RUN npm install --ignore-scripts
# Copy over next.js config
COPY next.config.js ./next.config.js
@@ -17,4 +17,4 @@ COPY next.config.js ./next.config.js
# Copy all files
COPY . .
-CMD ["npm", "run", "dev"]
\ No newline at end of file
+CMD ["npm", "run", "dev"]
diff --git a/frontend/Dockerfile.prod b/frontend/Dockerfile.prod
deleted file mode 100644
index d95c00883f..0000000000
--- a/frontend/Dockerfile.prod
+++ /dev/null
@@ -1,20 +0,0 @@
-# Base layer
-FROM node:16-alpine
-
-# Set the working directory
-WORKDIR /app
-
-# Copy over dependency files
-COPY package.json ./
-COPY package-lock.json ./
-
-# Install
-RUN npm install
-
-# Copy over next.js config
-COPY next.config.js ./next.config.js
-
-# Copy all files
-COPY . .
-
-CMD ["npm", "run", "start:docker"]
diff --git a/frontend/components/basic/Listbox.tsx b/frontend/components/basic/Listbox.tsx
index aa8c41f85d..e726b7f56c 100644
--- a/frontend/components/basic/Listbox.tsx
+++ b/frontend/components/basic/Listbox.tsx
@@ -10,10 +10,10 @@ import { Listbox, Transition } from "@headlessui/react";
interface ListBoxProps {
selected: string;
- onChange: () => void;
- data: string[];
- text: string;
- buttonAction: () => void;
+ onChange: (arg: string) => void;
+ data: string[] | null;
+ text?: string;
+ buttonAction?: () => void;
isFull?: boolean;
}
diff --git a/frontend/components/basic/buttons/Button.tsx b/frontend/components/basic/buttons/Button.tsx
index 9f8d481b4f..562a82a36f 100644
--- a/frontend/components/basic/buttons/Button.tsx
+++ b/frontend/components/basic/buttons/Button.tsx
@@ -9,7 +9,7 @@ import {
const classNames = require("classnames");
type ButtonProps = {
- text: string;
+ text?: string;
onButtonPressed: () => void;
loading?: boolean;
color?: string;
diff --git a/frontend/components/integrations/CloudIntegration.tsx b/frontend/components/integrations/CloudIntegration.tsx
index f64268dbac..75a8019a5e 100644
--- a/frontend/components/integrations/CloudIntegration.tsx
+++ b/frontend/components/integrations/CloudIntegration.tsx
@@ -1,30 +1,32 @@
import React from "react";
import Image from "next/image";
import { useRouter } from "next/router";
-import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import {
faCheck,
faX,
} from "@fortawesome/free-solid-svg-icons";
+import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
import deleteIntegrationAuth from "../../pages/api/integrations/DeleteIntegrationAuth";
interface CloudIntegrationOption {
- isAvailable: Boolean;
+ isAvailable: boolean;
name: string;
type: string;
clientId: string;
docsLink: string;
+ slug: string;
}
interface IntegrationAuth {
- id: string;
+ _id: string;
integration: string;
}
interface Props {
cloudIntegrationOption: CloudIntegrationOption;
- setSelectedIntegrationOption: () => void;
- integrationOptionPress: () => void;
+ setSelectedIntegrationOption: (cloudIntegration: CloudIntegrationOption) => void;
+ integrationOptionPress: (cloudIntegrationOption: CloudIntegrationOption) => void;
integrationAuths: IntegrationAuth[];
}
@@ -45,9 +47,7 @@ const CloudIntegration = ({
onClick={() => {
if (!cloudIntegrationOption.isAvailable) return;
setSelectedIntegrationOption(cloudIntegrationOption);
- integrationOptionPress({
- integrationOption: cloudIntegrationOption
- });
+ integrationOptionPress(cloudIntegrationOption);
}}
key={cloudIntegrationOption.name}
>
diff --git a/frontend/components/integrations/CloudIntegrationSection.tsx b/frontend/components/integrations/CloudIntegrationSection.tsx
index 87ff74df14..58fb92cb2a 100644
--- a/frontend/components/integrations/CloudIntegrationSection.tsx
+++ b/frontend/components/integrations/CloudIntegrationSection.tsx
@@ -1,11 +1,14 @@
import React from "react";
+
import CloudIntegration from "./CloudIntegration";
interface CloudIntegrationOption {
+ isAvailable: boolean;
name: string;
type: string;
clientId: string;
docsLink: string;
+ slug: string;
}
interface Props {
diff --git a/frontend/components/integrations/FrameworkIntegrationSection.tsx b/frontend/components/integrations/FrameworkIntegrationSection.tsx
index c83599dc1d..8535c595b0 100644
--- a/frontend/components/integrations/FrameworkIntegrationSection.tsx
+++ b/frontend/components/integrations/FrameworkIntegrationSection.tsx
@@ -1,14 +1,17 @@
import React from "react";
+
import FrameworkIntegration from "./FrameworkIntegration";
interface Framework {
name: string;
image: string;
link: string;
+ slug: string;
+ docsLink: string;
}
interface Props {
- framework: Framework
+ frameworks: [Framework]
}
const FrameworkIntegrationSection = ({ frameworks }: Props) => {
diff --git a/frontend/components/integrations/Integration.tsx b/frontend/components/integrations/Integration.tsx
index a42fa5bcf8..053269be40 100644
--- a/frontend/components/integrations/Integration.tsx
+++ b/frontend/components/integrations/Integration.tsx
@@ -6,23 +6,33 @@ import {
faX,
} from "@fortawesome/free-solid-svg-icons";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
+
+import Button from "~/components/basic/buttons/Button";
+import ListBox from "~/components/basic/Listbox";
+
+import deleteIntegration from "../../pages/api/integrations/DeleteIntegration"
+import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
+import updateIntegration from "../../pages/api/integrations/updateIntegration"
import {
+ contextNetlifyMapping,
envMapping,
+ reverseContextNetlifyMapping,
reverseEnvMapping,
- reverseContextNetlifyMapping
} from "../../public/data/frequentConstants";
-import updateIntegration from "../../pages/api/integrations/updateIntegration"
-import deleteIntegration from "../../pages/api/integrations/DeleteIntegration"
-import getIntegrationApps from "../../pages/api/integrations/GetIntegrationApps";
-import Button from "~/components/basic/buttons/Button";
-import ListBox from "~/components/basic/Listbox";
interface Integration {
+ _id: string;
app?: string;
environment: string;
integration: string;
integrationAuth: string;
- isActive: Boolean;
+ isActive: boolean;
+ context: string;
+}
+
+interface IntegrationApp {
+ name: string;
+ siteId: string;
}
const Integration = ({
@@ -35,39 +45,44 @@ const Integration = ({
);
const [fileState, setFileState] = useState([]);
const router = useRouter();
- const [apps, setApps] = useState([]); // integration app objects
- const [integrationApp, setIntegrationApp] = useState(null); // integration app name
- const [integrationTarget, setIntegrationTarget] = useState(null); // vercel-specific integration param
- const [integrationContext, setIntegrationContext] = useState(null); // netlify-specific integration param
+ const [apps, setApps] = useState([]); // integration app objects
+ const [integrationApp, setIntegrationApp] = useState(""); // integration app name
+ const [integrationTarget, setIntegrationTarget] = useState(""); // vercel-specific integration param
+ const [integrationContext, setIntegrationContext] = useState(""); // netlify-specific integration param
- useEffect(async () => {
- interface App {
- name: string;
- siteId?: string;
- }
+ useEffect(() => {
- const tempApps = await getIntegrationApps({
- integrationAuthId: integration.integrationAuth,
- });
-
- setApps(tempApps);
- setIntegrationApp(
- integration.app ? integration.app : tempApps[0].name
- );
-
- switch (integration.integration) {
- case "vercel":
- setIntegrationTarget("Development");
- break;
- case "netlify":
- setIntegrationContext("All");
- break;
- default:
- break;
+ const loadIntegration = async () => {
+ interface App {
+ name: string;
+ siteId?: string;
+ }
+
+ const tempApps: [IntegrationApp] = await getIntegrationApps({
+ integrationAuthId: integration.integrationAuth,
+ });
+
+ setApps(tempApps);
+ setIntegrationApp(
+ integration.app ? integration.app : tempApps[0].name
+ );
+
+ switch (integration.integration) {
+ case "vercel":
+ setIntegrationTarget("Development");
+ break;
+ case "netlify":
+ setIntegrationContext(integration?.context ? contextNetlifyMapping[integration.context] : "Local development");
+ break;
+ default:
+ break;
+ }
}
+
+ loadIntegration();
}, []);
- const renderIntegrationSpecificParams = (integration) => {
+ const renderIntegrationSpecificParams = (integration: Integration) => {
try {
switch (integration.integration) {
case "vercel":
@@ -77,11 +92,11 @@ const Integration = ({
ENVIRONMENT
@@ -94,13 +109,12 @@ const Integration = ({
CONTEXT
@@ -128,7 +142,9 @@ const Integration = ({
"Production",
] : null}
selected={integrationEnvironment}
- onChange={setIntegrationEnvironment}
+ onChange={(environment) => {
+ setIntegrationEnvironment(environment);
+ }}
isFull={true}
/>
@@ -152,9 +168,11 @@ const Integration = ({
APP
app.name)}
+ data={!integration.isActive ? apps.map((app) => app.name) : null}
selected={integrationApp}
- onChange={setIntegrationApp}
+ onChange={(app) => {
+ setIntegrationApp(app);
+ }}
/>
{renderIntegrationSpecificParams(integration)}
@@ -172,7 +190,10 @@ const Integration = ({
{
- const siteId = apps.find((app) => app.name === integrationApp).siteId ? apps.find((app) => app.name === integrationApp).siteId : null;
+
+ const siteApp = apps.find((app) => app.name === integrationApp); // obj or undefined
+ const siteId = siteApp?.siteId ? siteApp.siteId : null;
+
const result = await updateIntegration({
integrationId: integration._id,
environment: envMapping[integrationEnvironment],
@@ -182,6 +203,7 @@ const Integration = ({
context: integrationContext ? reverseContextNetlifyMapping[integrationContext] : null,
siteId
});
+
router.reload();
}}
color="mineshaft"
diff --git a/frontend/components/integrations/IntegrationSection.tsx b/frontend/components/integrations/IntegrationSection.tsx
index cfa43b4ef3..52d5565ff5 100644
--- a/frontend/components/integrations/IntegrationSection.tsx
+++ b/frontend/components/integrations/IntegrationSection.tsx
@@ -1,15 +1,23 @@
import React from "react";
-import Integration from "./Integration";
-import guidGenerator from "~/utilities/randomId";
-interface Integration {
+import guidGenerator from "~/utilities/randomId";
-}
+import Integration from "./Integration";
interface Props {
integrations: any
}
+interface IntegrationType {
+ _id: string;
+ app?: string;
+ environment: string;
+ integration: string;
+ integrationAuth: string;
+ isActive: boolean;
+ context: string;
+}
+
const ProjectIntegrationSection = ({
integrations
}: Props) => {
@@ -21,12 +29,12 @@ const ProjectIntegrationSection = ({
Manage your integrations of Infisical with third-party services.
- {integrations.map((integration => (
+ {integrations.map((integration: IntegrationType) => (
- )))}
+ ))}
) :
}
diff --git a/frontend/components/utilities/attemptLogin.js b/frontend/components/utilities/attemptLogin.js
index b0d70146e7..6bf575bd10 100644
--- a/frontend/components/utilities/attemptLogin.js
+++ b/frontend/components/utilities/attemptLogin.js
@@ -42,9 +42,9 @@ const attemptLogin = async (
async () => {
const clientPublicKey = client.getPublicKey();
- const { serverPublicKey, salt } = await login1(email, clientPublicKey);
-
try {
+ const { serverPublicKey, salt } = await login1(email, clientPublicKey);
+
client.setSalt(salt);
client.setServerPublicKey(serverPublicKey);
const clientProof = client.getProof(); // called M1
diff --git a/frontend/components/utilities/config/index.ts b/frontend/components/utilities/config/index.ts
index 42dee56188..7570785ba6 100644
--- a/frontend/components/utilities/config/index.ts
+++ b/frontend/components/utilities/config/index.ts
@@ -4,15 +4,11 @@ const POSTHOG_HOST =
process.env.NEXT_PUBLIC_POSTHOG_HOST! || "https://app.posthog.com";
const STRIPE_PRODUCT_PRO = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_PRO!;
const STRIPE_PRODUCT_STARTER = process.env.NEXT_PUBLIC_STRIPE_PRODUCT_STARTER!;
-const CLIENT_ID_HEROKU = process.env.NEXT_PUBLIC_CLIENT_ID_HEROKU!;
-const CLIENT_ID_NETLIFY = process.env.NEXT_PUBLIC_CLIENT_ID_NETLIFY!;
export {
ENV,
POSTHOG_API_KEY,
POSTHOG_HOST,
STRIPE_PRODUCT_PRO,
- STRIPE_PRODUCT_STARTER,
- CLIENT_ID_HEROKU,
- CLIENT_ID_NETLIFY
+ STRIPE_PRODUCT_STARTER
};
\ No newline at end of file
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 5e3bb010b9..b65657f854 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -60,7 +60,6 @@
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-simple-import-sort": "^8.0.0",
"postcss": "^8.4.14",
- "prettier": "2.7.1",
"tailwindcss": "^3.1.4",
"typescript": "^4.9.3"
}
@@ -5999,21 +5998,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
- "dev": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
"node_modules/process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
@@ -11950,12 +11934,6 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
- "prettier": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.7.1.tgz",
- "integrity": "sha512-ujppO+MkdPqoVINuDFDRLClm7D78qbDt0/NR+wp5FqEZOoTNAjPHWj17QRhu7geIHJfcNhRk1XVQmF8Bp3ye+g==",
- "dev": true
- },
"process-nextick-args": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 7f7926c880..12b2e9ec27 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,7 @@
{
"private": true,
"scripts": {
+ "prepare": "cd .. && npm install",
"dev": "next dev",
"build": "next build",
"start": "next start",
@@ -63,7 +64,6 @@
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-simple-import-sort": "^8.0.0",
"postcss": "^8.4.14",
- "prettier": "2.7.1",
"tailwindcss": "^3.1.4",
"typescript": "^4.9.3"
}
diff --git a/frontend/pages/api/bot/setBotActiveStatus.ts b/frontend/pages/api/bot/setBotActiveStatus.ts
index e85ed3c771..0a974a5883 100644
--- a/frontend/pages/api/bot/setBotActiveStatus.ts
+++ b/frontend/pages/api/bot/setBotActiveStatus.ts
@@ -7,7 +7,7 @@ interface BotKey {
interface Props {
botId: string;
- isActive: Boolean;
+ isActive: boolean;
botKey: BotKey;
}
diff --git a/frontend/pages/api/integrations/updateIntegration.js b/frontend/pages/api/integrations/updateIntegration.ts
similarity index 87%
rename from frontend/pages/api/integrations/updateIntegration.js
rename to frontend/pages/api/integrations/updateIntegration.ts
index c77297e3a2..8afcfc0a95 100644
--- a/frontend/pages/api/integrations/updateIntegration.js
+++ b/frontend/pages/api/integrations/updateIntegration.ts
@@ -22,6 +22,15 @@ const updateIntegration = ({
target,
context,
siteId
+}: {
+ integrationId: string,
+ app: string,
+ environment: string,
+ isActive: boolean,
+ target: string | null,
+ context: string | null,
+ siteId: string | null
+
}) => {
return SecurityClient.fetchCall(
"/api/v1/integration/" + integrationId,
@@ -40,7 +49,7 @@ const updateIntegration = ({
}),
}
).then(async (res) => {
- if (res.status == 200) {
+ if (res && res.status == 200) {
return res;
} else {
console.log("Failed to start an integration");
diff --git a/frontend/pages/integrations/[id].js b/frontend/pages/integrations/[id].js
index 624874f422..c47822c801 100644
--- a/frontend/pages/integrations/[id].js
+++ b/frontend/pages/integrations/[id].js
@@ -2,20 +2,22 @@ import React, { useEffect, useState } from "react";
import Head from "next/head";
import Image from "next/image";
import { useRouter } from "next/router";
-import NavHeader from "~/components/navigation/NavHeader";
-import Integration from "~/components/integrations/Integration";
-import FrameworkIntegrationSection from "~/components/integrations/FrameworkIntegrationSection";
+
+import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog";
+import IntegrationAccessTokenDialog from "~/components/basic/dialog/IntegrationAccessTokenDialog";
import CloudIntegrationSection from "~/components/integrations/CloudIntegrationSection";
+import FrameworkIntegrationSection from "~/components/integrations/FrameworkIntegrationSection";
+import Integration from "~/components/integrations/Integration";
import IntegrationSection from "~/components/integrations/IntegrationSection";
+import NavHeader from "~/components/navigation/NavHeader";
+
import frameworkIntegrationOptions from "../../public/json/frameworkIntegrations.json";
-import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
-import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
-import getIntegrationOptions from "../api/integrations/GetIntegrationOptions";
import getBot from "../api/bot/getBot";
import setBotActiveStatus from "../api/bot/setBotActiveStatus";
+import getIntegrationOptions from "../api/integrations/GetIntegrationOptions";
+import getWorkspaceAuthorizations from "../api/integrations/getWorkspaceAuthorizations";
+import getWorkspaceIntegrations from "../api/integrations/getWorkspaceIntegrations";
import getLatestFileKey from "../api/workspace/getLatestFileKey";
-import ActivateBotDialog from "~/components/basic/dialog/ActivateBotDialog";
-import IntegrationAccessTokenDialog from "~/components/basic/dialog/IntegrationAccessTokenDialog";
const {
decryptAssymmetric,
encryptAssymmetric
@@ -39,7 +41,7 @@ export default function Integrations() {
setCloudIntegrationOptions(
await getIntegrationOptions()
);
-
+
// get project integration authorizations
setIntegrationAuths(
await getWorkspaceAuthorizations({
@@ -132,7 +134,7 @@ export default function Integrations() {
window.location = `https://id.heroku.com/oauth/authorize?client_id=${integrationOption.clientId}&response_type=code&scope=write-protected&state=${state}`;
break;
case 'Vercel':
- window.location = `https://vercel.com/integrations/infisical-dev/new?state=${state}`;
+ window.location = `https://vercel.com/integrations/${integrationOption.clientSlug}/new?state=${state}`;
break;
case 'Netlify':
window.location = `https://app.netlify.com/authorize?client_id=${integrationOption.clientId}&response_type=code&redirect_uri=${integrationOption.redirectURL}&state=${state}`;
@@ -158,14 +160,13 @@ export default function Integrations() {
/**
* Open dialog to activate bot if bot is not active.
* Otherwise, start integration [integrationOption]
- * @param {Object} obj
- * @param {Object} obj.integrationOption - an integration option
- * @param {String} obj.name
- * @param {String} obj.type
- * @param {String} obj.docsLink
+ * @param {Object} integrationOption - an integration option
+ * @param {String} integrationOption.name
+ * @param {String} integrationOption.type
+ * @param {String} integrationOption.docsLink
* @returns
*/
- const integrationOptionPress = ({ integrationOption }) => {
+ const integrationOptionPress = (integrationOption) => {
try {
if (bot.isActive) {
// case: bot is active -> proceed with integration
diff --git a/frontend/pages/netlify.js b/frontend/pages/netlify.js
index f25c6af83a..6907d6db47 100644
--- a/frontend/pages/netlify.js
+++ b/frontend/pages/netlify.js
@@ -16,20 +16,18 @@ export default function Netlify() {
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => {
- if (state === localStorage.getItem('latestCSRFToken')) {
- localStorage.removeItem('latestCSRFToken');
-
- await AuthorizeIntegration({
- workspaceId: localStorage.getItem('projectData.id'),
- code,
- integration: "netlify"
- });
-
- router.push("/integrations/" + localStorage.getItem("projectData.id"));
- }
-
try {
-
+ if (state === localStorage.getItem('latestCSRFToken')) {
+ localStorage.removeItem('latestCSRFToken');
+
+ await AuthorizeIntegration({
+ workspaceId: localStorage.getItem('projectData.id'),
+ code,
+ integration: "netlify"
+ });
+
+ router.push("/integrations/" + localStorage.getItem("projectData.id"));
+ }
} catch (err) {
console.error('Netlify integration error: ', err);
}
diff --git a/frontend/pages/vercel.js b/frontend/pages/vercel.js
index a674c27d4f..adfffe77ef 100644
--- a/frontend/pages/vercel.js
+++ b/frontend/pages/vercel.js
@@ -15,20 +15,18 @@ export default function Vercel() {
*/
// eslint-disable-next-line react-hooks/exhaustive-deps
useEffect(async () => {
- if (state === localStorage.getItem('latestCSRFToken')) {
- localStorage.removeItem('latestCSRFToken');
-
- await AuthorizeIntegration({
- workspaceId: localStorage.getItem('projectData.id'),
- code,
- integration: "vercel"
- });
-
- router.push("/integrations/" + localStorage.getItem("projectData.id"));
- }
-
try {
-
+ if (state === localStorage.getItem('latestCSRFToken')) {
+ localStorage.removeItem('latestCSRFToken');
+
+ await AuthorizeIntegration({
+ workspaceId: localStorage.getItem('projectData.id'),
+ code,
+ integration: "vercel"
+ });
+
+ router.push("/integrations/" + localStorage.getItem("projectData.id"));
+ }
} catch (err) {
console.error('Vercel integration error: ', err);
}
diff --git a/frontend/public/data/frequentConstants.ts b/frontend/public/data/frequentConstants.ts
index bbaa42adef..d2beee6d26 100644
--- a/frontend/public/data/frequentConstants.ts
+++ b/frontend/public/data/frequentConstants.ts
@@ -1,23 +1,29 @@
-const envMapping = {
+interface Mapping {
+ [key: string]: string;
+}
+
+const envMapping: Mapping = {
Development: "dev",
Staging: "staging",
Production: "prod",
Testing: "test",
};
-
-const reverseEnvMapping = {
+
+const reverseEnvMapping: Mapping = {
dev: "Development",
staging: "Staging",
prod: "Production",
test: "Testing",
};
-const vercelMapping = {
-
+const contextNetlifyMapping: Mapping = {
+ "dev": "Local development",
+ "branch-deploy": "Branch deploys",
+ "deploy-review": "Deploy Previews",
+ "production": "Production"
}
-const reverseContextNetlifyMapping = {
- "All": "all",
+const reverseContextNetlifyMapping: Mapping = {
"Local development": "dev",
"Branch deploys": "branch-deploy",
"Deploy Previews": "deploy-preview",
@@ -25,7 +31,7 @@ const reverseContextNetlifyMapping = {
}
export {
+ contextNetlifyMapping,
envMapping,
- reverseEnvMapping,
- reverseContextNetlifyMapping
-};
+ reverseContextNetlifyMapping,
+ reverseEnvMapping}
diff --git a/helm-charts/README.md b/helm-charts/README.md
index 0974640287..468275e3fb 100644
--- a/helm-charts/README.md
+++ b/helm-charts/README.md
@@ -14,3 +14,4 @@ helm install infisical-helm-charts/
#### Available chart names
- infisical
+- secrets-operator
diff --git a/helm-charts/infisical/Chart.yaml b/helm-charts/infisical/Chart.yaml
index a9297b385e..1c0639f22b 100644
--- a/helm-charts/infisical/Chart.yaml
+++ b/helm-charts/infisical/Chart.yaml
@@ -7,7 +7,7 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 0.1.3
+version: 0.1.4
# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
diff --git a/helm-charts/infisical/templates/backend-deployment.yaml b/helm-charts/infisical/templates/backend-deployment.yaml
index 6a2faa6885..120518d47a 100644
--- a/helm-charts/infisical/templates/backend-deployment.yaml
+++ b/helm-charts/infisical/templates/backend-deployment.yaml
@@ -20,6 +20,9 @@ spec:
imagePullPolicy: {{ .Values.backend.image.pullPolicy }}
ports:
- containerPort: 4000
+ envFrom:
+ - secretRef:
+ name: {{ .Values.backend.kubeSecretRef }}
env:
{{- range $key, $value := .Values.backendEnvironmentVariables }}
{{- if $value | quote | eq "MUST_REPLACE" }}
diff --git a/helm-charts/infisical/templates/frontend-deployment.yaml b/helm-charts/infisical/templates/frontend-deployment.yaml
index 3cd29f326c..d803d03a88 100644
--- a/helm-charts/infisical/templates/frontend-deployment.yaml
+++ b/helm-charts/infisical/templates/frontend-deployment.yaml
@@ -18,6 +18,9 @@ spec:
- name: frontend
image: infisical/frontend
imagePullPolicy: {{ .Values.frontend.image.pullPolicy }}
+ envFrom:
+ - secretRef:
+ name: {{ .Values.frontend.kubeSecretRef }}
env:
{{- range $key, $value := .Values.frontendEnvironmentVariables }}
{{- if $value | quote | eq "MUST_REPLACE" }}
diff --git a/helm-charts/secrets-operator/.helmignore b/helm-charts/secrets-operator/.helmignore
new file mode 100644
index 0000000000..0e8a0eb36f
--- /dev/null
+++ b/helm-charts/secrets-operator/.helmignore
@@ -0,0 +1,23 @@
+# Patterns to ignore when building packages.
+# This supports shell glob matching, relative path matching, and
+# negation (prefixed with !). Only one pattern per line.
+.DS_Store
+# Common VCS dirs
+.git/
+.gitignore
+.bzr/
+.bzrignore
+.hg/
+.hgignore
+.svn/
+# Common backup files
+*.swp
+*.bak
+*.tmp
+*.orig
+*~
+# Various IDEs
+.project
+.idea/
+*.tmproj
+.vscode/
diff --git a/helm-charts/secrets-operator/Chart.yaml b/helm-charts/secrets-operator/Chart.yaml
new file mode 100644
index 0000000000..26a402da3d
--- /dev/null
+++ b/helm-charts/secrets-operator/Chart.yaml
@@ -0,0 +1,21 @@
+apiVersion: v2
+name: secrets-operator
+description: A Helm chart for Infisical secrets
+# A chart can be either an 'application' or a 'library' chart.
+#
+# Application charts are a collection of templates that can be packaged into versioned archives
+# to be deployed.
+#
+# Library charts provide useful utilities or functions for the chart developer. They're included as
+# a dependency of application charts to inject those utilities and functions into the rendering
+# pipeline. Library charts do not define any templates and therefore cannot be deployed.
+type: application
+# This is the chart version. This version number should be incremented each time you make changes
+# to the chart and its templates, including the app version.
+# Versions are expected to follow Semantic Versioning (https://semver.org/)
+version: 0.1.0
+# This is the version number of the application being deployed. This version number should be
+# incremented each time you make changes to the application. Versions are not expected to
+# follow Semantic Versioning. They should reflect the version the application is using.
+# It is recommended to use it with quotes.
+appVersion: "0.1.0"
diff --git a/helm-charts/secrets-operator/templates/_helpers.tpl b/helm-charts/secrets-operator/templates/_helpers.tpl
new file mode 100644
index 0000000000..44e464d939
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/_helpers.tpl
@@ -0,0 +1,62 @@
+{{/*
+Expand the name of the chart.
+*/}}
+{{- define "secrets-operator.name" -}}
+{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Create a default fully qualified app name.
+We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
+If release name contains chart name it will be used as a full name.
+*/}}
+{{- define "secrets-operator.fullname" -}}
+{{- if .Values.fullnameOverride }}
+{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- $name := default .Chart.Name .Values.nameOverride }}
+{{- if contains $name .Release.Name }}
+{{- .Release.Name | trunc 63 | trimSuffix "-" }}
+{{- else }}
+{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
+{{- end }}
+{{- end }}
+{{- end }}
+
+{{/*
+Create chart name and version as used by the chart label.
+*/}}
+{{- define "secrets-operator.chart" -}}
+{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
+{{- end }}
+
+{{/*
+Common labels
+*/}}
+{{- define "secrets-operator.labels" -}}
+helm.sh/chart: {{ include "secrets-operator.chart" . }}
+{{ include "secrets-operator.selectorLabels" . }}
+{{- if .Chart.AppVersion }}
+app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
+{{- end }}
+app.kubernetes.io/managed-by: {{ .Release.Service }}
+{{- end }}
+
+{{/*
+Selector labels
+*/}}
+{{- define "secrets-operator.selectorLabels" -}}
+app.kubernetes.io/name: {{ include "secrets-operator.name" . }}
+app.kubernetes.io/instance: {{ .Release.Name }}
+{{- end }}
+
+{{/*
+Create the name of the service account to use
+*/}}
+{{- define "secrets-operator.serviceAccountName" -}}
+{{- if .Values.serviceAccount.create }}
+{{- default (include "secrets-operator.fullname" .) .Values.serviceAccount.name }}
+{{- else }}
+{{- default "default" .Values.serviceAccount.name }}
+{{- end }}
+{{- end }}
diff --git a/helm-charts/secrets-operator/templates/deployment.yaml b/helm-charts/secrets-operator/templates/deployment.yaml
new file mode 100644
index 0000000000..026728bfa8
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/deployment.yaml
@@ -0,0 +1,108 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-controller-manager
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-controller-manager
+ labels:
+ app.kubernetes.io/component: manager
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ control-plane: controller-manager
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+spec:
+ replicas: {{ .Values.controllerManager.replicas }}
+ selector:
+ matchLabels:
+ control-plane: controller-manager
+ {{- include "secrets-operator.selectorLabels" . | nindent 6 }}
+ template:
+ metadata:
+ labels:
+ control-plane: controller-manager
+ {{- include "secrets-operator.selectorLabels" . | nindent 8 }}
+ annotations:
+ kubectl.kubernetes.io/default-container: manager
+ spec:
+ affinity:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: kubernetes.io/arch
+ operator: In
+ values:
+ - amd64
+ - arm64
+ - ppc64le
+ - s390x
+ - key: kubernetes.io/os
+ operator: In
+ values:
+ - linux
+ containers:
+ - args:
+ - --secure-listen-address=0.0.0.0:8443
+ - --upstream=http://127.0.0.1:8080/
+ - --logtostderr=true
+ - --v=0
+ env:
+ - name: KUBERNETES_CLUSTER_DOMAIN
+ value: {{ .Values.kubernetesClusterDomain }}
+ image: {{ .Values.controllerManager.kubeRbacProxy.image.repository }}:{{ .Values.controllerManager.kubeRbacProxy.image.tag
+ | default .Chart.AppVersion }}
+ name: kube-rbac-proxy
+ ports:
+ - containerPort: 8443
+ name: https
+ protocol: TCP
+ resources: {{- toYaml .Values.controllerManager.kubeRbacProxy.resources | nindent
+ 10 }}
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ - args:
+ - --health-probe-bind-address=:8081
+ - --metrics-bind-address=127.0.0.1:8080
+ - --leader-elect
+ command:
+ - /manager
+ env:
+ - name: KUBERNETES_CLUSTER_DOMAIN
+ value: {{ .Values.kubernetesClusterDomain }}
+ image: {{ .Values.controllerManager.manager.image.repository }}:{{ .Values.controllerManager.manager.image.tag
+ | default .Chart.AppVersion }}
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources: {{- toYaml .Values.controllerManager.manager.resources | nindent 10
+ }}
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ securityContext:
+ runAsNonRoot: true
+ serviceAccountName: {{ include "secrets-operator.fullname" . }}-controller-manager
+ terminationGracePeriodSeconds: 10
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml b/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml
new file mode 100644
index 0000000000..c5b82fa14d
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/infisicalsecret-crd.yaml
@@ -0,0 +1,160 @@
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ name: infisicalsecrets.secrets.infisical.com
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.10.0
+ labels:
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+spec:
+ group: secrets.infisical.com
+ names:
+ kind: InfisicalSecret
+ listKind: InfisicalSecretList
+ plural: infisicalsecrets
+ singular: infisicalsecret
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: InfisicalSecret is the Schema for the infisicalsecrets API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation
+ of an object. Servers should convert recognized schemas to the latest
+ internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this
+ object represents. Servers may infer this from the endpoint the client
+ submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: InfisicalSecretSpec defines the desired state of InfisicalSecret
+ properties:
+ environment:
+ description: The Infisical environment such as dev, prod, testing
+ type: string
+ hostAPI:
+ default: https://app.infisical.com/api
+ description: Infisical host to pull secrets from
+ type: string
+ managedSecretReference:
+ properties:
+ secretName:
+ description: The name of the Kubernetes Secret
+ type: string
+ secretNamespace:
+ description: The name space where the Kubernetes Secret is located
+ type: string
+ required:
+ - secretName
+ - secretNamespace
+ type: object
+ projectId:
+ description: The Infisical project id
+ type: string
+ tokenSecretReference:
+ properties:
+ secretName:
+ description: The name of the Kubernetes Secret
+ type: string
+ secretNamespace:
+ description: The name space where the Kubernetes Secret is located
+ type: string
+ required:
+ - secretName
+ - secretNamespace
+ type: object
+ required:
+ - environment
+ - projectId
+ type: object
+ status:
+ description: InfisicalSecretStatus defines the observed state of InfisicalSecret
+ properties:
+ conditions:
+ items:
+ description: "Condition contains details for one aspect of the current
+ state of this API Resource. --- This struct is intended for direct
+ use as an array at the field path .status.conditions. For example,
+ \n type FooStatus struct{ // Represents the observations of a foo's
+ current state. // Known .status.conditions.type are: \"Available\",
+ \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge
+ // +listType=map // +listMapKey=type Conditions []metav1.Condition
+ `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\"
+ protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
+ properties:
+ lastTransitionTime:
+ description: lastTransitionTime is the last time the condition
+ transitioned from one status to another. This should be when
+ the underlying condition changed. If that is not known, then
+ using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: message is a human readable message indicating details
+ about the transition. This may be an empty string.
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ description: observedGeneration represents the .metadata.generation
+ that the condition was set based upon. For instance, if .metadata.generation
+ is currently 12, but the .status.conditions[x].observedGeneration
+ is 9, the condition is out of date with respect to the current
+ state of the instance.
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ description: reason contains a programmatic identifier indicating
+ the reason for the condition's last transition. Producers of
+ specific condition types may define expected values and meanings
+ for this field, and whether the values are considered a guaranteed
+ API. The value should be a CamelCase string. This field may
+ not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: status of the condition, one of True, False, Unknown.
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ description: type of condition in CamelCase or in foo.example.com/CamelCase.
+ --- Many .condition.type values are consistent across resources
+ like Available, but because arbitrary conditions can be useful
+ (see .node.status.conditions), the ability to deconflict is
+ important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ required:
+ - conditions
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+status:
+ acceptedNames:
+ kind: ""
+ plural: ""
+ conditions: []
+ storedVersions: []
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/templates/leader-election-rbac.yaml b/helm-charts/secrets-operator/templates/leader-election-rbac.yaml
new file mode 100644
index 0000000000..dc41acf148
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/leader-election-rbac.yaml
@@ -0,0 +1,59 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-leader-election-role
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-leader-election-rolebinding
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: '{{ include "secrets-operator.fullname" . }}-leader-election-role'
+subjects:
+- kind: ServiceAccount
+ name: '{{ include "secrets-operator.fullname" . }}-controller-manager'
+ namespace: '{{ .Release.Namespace }}'
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/templates/manager-rbac.yaml b/helm-charts/secrets-operator/templates/manager-rbac.yaml
new file mode 100644
index 0000000000..a560790f6f
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/manager-rbac.yaml
@@ -0,0 +1,71 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-manager-role
+ labels:
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - update
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - get
+ - list
+ - update
+ - watch
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - infisicalsecrets
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - infisicalsecrets/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - infisicalsecrets/status
+ verbs:
+ - get
+ - patch
+ - update
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-manager-rolebinding
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: '{{ include "secrets-operator.fullname" . }}-manager-role'
+subjects:
+- kind: ServiceAccount
+ name: '{{ include "secrets-operator.fullname" . }}-controller-manager'
+ namespace: '{{ .Release.Namespace }}'
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml b/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml
new file mode 100644
index 0000000000..7d7ceba46e
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/metrics-reader-rbac.yaml
@@ -0,0 +1,14 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-metrics-reader
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+rules:
+- nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/templates/metrics-service.yaml b/helm-charts/secrets-operator/templates/metrics-service.yaml
new file mode 100644
index 0000000000..ebf7ce5493
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/metrics-service.yaml
@@ -0,0 +1,17 @@
+apiVersion: v1
+kind: Service
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-controller-manager-metrics-service
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ control-plane: controller-manager
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+spec:
+ type: {{ .Values.metricsService.type }}
+ selector:
+ control-plane: controller-manager
+ {{- include "secrets-operator.selectorLabels" . | nindent 4 }}
+ ports:
+ {{- .Values.metricsService.ports | toYaml | nindent 2 -}}
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/templates/proxy-rbac.yaml b/helm-charts/secrets-operator/templates/proxy-rbac.yaml
new file mode 100644
index 0000000000..5f07e29081
--- /dev/null
+++ b/helm-charts/secrets-operator/templates/proxy-rbac.yaml
@@ -0,0 +1,40 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-proxy-role
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ name: {{ include "secrets-operator.fullname" . }}-proxy-rolebinding
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/part-of: k8-operator
+ {{- include "secrets-operator.labels" . | nindent 4 }}
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: '{{ include "secrets-operator.fullname" . }}-proxy-role'
+subjects:
+- kind: ServiceAccount
+ name: '{{ include "secrets-operator.fullname" . }}-controller-manager'
+ namespace: '{{ .Release.Namespace }}'
\ No newline at end of file
diff --git a/helm-charts/secrets-operator/values.yaml b/helm-charts/secrets-operator/values.yaml
new file mode 100644
index 0000000000..32ae2f789c
--- /dev/null
+++ b/helm-charts/secrets-operator/values.yaml
@@ -0,0 +1,32 @@
+controllerManager:
+ kubeRbacProxy:
+ image:
+ repository: gcr.io/kubebuilder/kube-rbac-proxy
+ tag: v0.13.1
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 5m
+ memory: 64Mi
+ manager:
+ image:
+ repository: infisical/kubernetes-operator
+ tag: latest
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+ replicas: 1
+kubernetesClusterDomain: cluster.local
+metricsService:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: https
+ type: ClusterIP
diff --git a/k8-operator/Makefile b/k8-operator/Makefile
index 9116f85220..a8b0563769 100644
--- a/k8-operator/Makefile
+++ b/k8-operator/Makefile
@@ -1,6 +1,6 @@
# Image URL to use all building/pushing image targets
-IMG ?= controller:latest
+IMG ?= infisical/kubernetes-operator:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.25.0
@@ -36,6 +36,17 @@ all: build
help: ## Display this help.
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf " \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)
+
+## Chart
+helm-chart:
+ $(KUSTOMIZE) build config/default | helmify ../helm-charts/secrets-operator
+
+## Yaml for Kubectl
+kubectl-install: manifests kustomize
+ mkdir -p kubectl-install
+ cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
+ $(KUSTOMIZE) build config/default > kubectl-install/install-secrets-operator.yaml
+
##@ Development
.PHONY: manifests
diff --git a/k8-operator/README.md b/k8-operator/README.md
index 6d953997eb..807476c0c0 100644
--- a/k8-operator/README.md
+++ b/k8-operator/README.md
@@ -1,8 +1,8 @@
# k8-operator
-// TODO(user): Add simple overview of use/purpose
+// TODO
## Description
-// TODO(user): An in-depth paragraph about your project and overview of use
+// TODO
## Getting Started
You’ll need a Kubernetes cluster to run against. You can use [KIND](https://sigs.k8s.io/kind) to get a local cluster for testing, or run against a remote cluster.
@@ -42,7 +42,7 @@ make undeploy
```
## Contributing
-// TODO(user): Add detailed information on how you would like others to contribute to this project
+// TODO
### How it works
This project aims to follow the Kubernetes [Operator pattern](https://kubernetes.io/docs/concepts/extend-kubernetes/operator/)
@@ -76,19 +76,3 @@ make manifests
More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html)
-## License
-
-Copyright 2022.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-
diff --git a/k8-operator/api/v1alpha1/infisicalsecret_types.go b/k8-operator/api/v1alpha1/infisicalsecret_types.go
index 74e3f97882..7940474f9c 100644
--- a/k8-operator/api/v1alpha1/infisicalsecret_types.go
+++ b/k8-operator/api/v1alpha1/infisicalsecret_types.go
@@ -16,8 +16,8 @@ type KubeSecretReference struct {
// InfisicalSecretSpec defines the desired state of InfisicalSecret
type InfisicalSecretSpec struct {
- InfisicalToken KubeSecretReference `json:"infisicalToken,omitempty"`
- ManagedSecret KubeSecretReference `json:"managedSecret,omitempty"`
+ TokenSecretReference KubeSecretReference `json:"tokenSecretReference,omitempty"`
+ ManagedSecretReference KubeSecretReference `json:"managedSecretReference,omitempty"`
// The Infisical project id
// +kubebuilder:validation:Required
@@ -26,6 +26,10 @@ type InfisicalSecretSpec struct {
// The Infisical environment such as dev, prod, testing
// +kubebuilder:validation:Required
Environment string `json:"environment"`
+
+ // Infisical host to pull secrets from
+ // +kubebuilder:default="https://app.infisical.com/api"
+ HostAPI string `json:"hostAPI,omitempty"`
}
// InfisicalSecretStatus defines the observed state of InfisicalSecret
diff --git a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
index e6053ba748..af9eff318b 100644
--- a/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
+++ b/k8-operator/api/v1alpha1/zz_generated.deepcopy.go
@@ -88,8 +88,8 @@ func (in *InfisicalSecretList) DeepCopyObject() runtime.Object {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *InfisicalSecretSpec) DeepCopyInto(out *InfisicalSecretSpec) {
*out = *in
- out.InfisicalToken = in.InfisicalToken
- out.ManagedSecret = in.ManagedSecret
+ out.TokenSecretReference = in.TokenSecretReference
+ out.ManagedSecretReference = in.ManagedSecretReference
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InfisicalSecretSpec.
diff --git a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml
index ca00676b41..c885fbdde3 100644
--- a/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml
+++ b/k8-operator/config/crd/bases/secrets.infisical.com_infisicalsecrets.yaml
@@ -38,7 +38,11 @@ spec:
environment:
description: The Infisical environment such as dev, prod, testing
type: string
- infisicalToken:
+ hostAPI:
+ default: https://app.infisical.com/api
+ description: Infisical host to pull secrets from
+ type: string
+ managedSecretReference:
properties:
secretName:
description: The name of the Kubernetes Secret
@@ -50,7 +54,10 @@ spec:
- secretName
- secretNamespace
type: object
- managedSecret:
+ projectId:
+ description: The Infisical project id
+ type: string
+ tokenSecretReference:
properties:
secretName:
description: The name of the Kubernetes Secret
@@ -62,9 +69,6 @@ spec:
- secretName
- secretNamespace
type: object
- projectId:
- description: The Infisical project id
- type: string
required:
- environment
- projectId
diff --git a/k8-operator/config/default/kustomization.yaml b/k8-operator/config/default/kustomization.yaml
index 4cab9014cc..1237b893d2 100644
--- a/k8-operator/config/default/kustomization.yaml
+++ b/k8-operator/config/default/kustomization.yaml
@@ -1,12 +1,12 @@
# Adds namespace to all resources.
-namespace: k8-operator-system
+namespace: infisical-operator-system
# Value of this field is prepended to the
# names of all resources, e.g. a deployment named
# "wordpress" becomes "alices-wordpress".
# Note that it should also match with the prefix (text before '-') of the namespace
# field above.
-namePrefix: k8-operator-
+namePrefix: infisical-operator-
# Labels to add to all resources and selectors.
#commonLabels:
diff --git a/k8-operator/config/manager/kustomization.yaml b/k8-operator/config/manager/kustomization.yaml
index 5c5f0b84cb..96ea36924a 100644
--- a/k8-operator/config/manager/kustomization.yaml
+++ b/k8-operator/config/manager/kustomization.yaml
@@ -1,2 +1,8 @@
resources:
- manager.yaml
+apiVersion: kustomize.config.k8s.io/v1beta1
+kind: Kustomization
+images:
+- name: controller
+ newName: infisical/kubernetes-operator
+ newTag: latest
diff --git a/k8-operator/config/samples/sample.yaml b/k8-operator/config/samples/sample.yaml
new file mode 100644
index 0000000000..ad352d3e73
--- /dev/null
+++ b/k8-operator/config/samples/sample.yaml
@@ -0,0 +1,13 @@
+apiVersion: secrets.infisical.com/v1alpha1
+kind: InfisicalSecret
+metadata:
+ name: infisicalsecret-sample
+spec:
+ projectId: 62faf98ae0b05e8529b5da46
+ environment: dev
+ tokenSecretReference:
+ secretName: service-token
+ secretNamespace: first-project
+ managedSecretReference:
+ secretName: managed-secret
+ secretNamespace: first-project
diff --git a/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml b/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml
deleted file mode 100644
index 7ab08e9cd5..0000000000
--- a/k8-operator/config/samples/secrets_v1alpha1_infisicalsecret.yaml
+++ /dev/null
@@ -1,19 +0,0 @@
-apiVersion: secrets.infisical.com/v1alpha1
-kind: InfisicalSecret
-metadata:
- labels:
- app.kubernetes.io/name: infisicalsecret
- app.kubernetes.io/instance: infisicalsecret-sample
- app.kubernetes.io/part-of: k8-operator
- app.kubernetes.io/managed-by: kustomize
- app.kubernetes.io/created-by: k8-operator
- name: infisicalsecret-sample
-spec:
- projectId: 62faf98ae0b05e8529b5da46
- environment: dev
- infisicalToken:
- secretName: service-token
- secretNamespace: default
- managedSecret:
- secretName: managed-secret
- secretNamespace: default
diff --git a/k8-operator/controllers/infisicalsecret_controller.go b/k8-operator/controllers/infisicalsecret_controller.go
index 297e29bfbb..f6422e1779 100644
--- a/k8-operator/controllers/infisicalsecret_controller.go
+++ b/k8-operator/controllers/infisicalsecret_controller.go
@@ -36,6 +36,8 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
var infisicalSecretCR v1alpha1.InfisicalSecret
err := r.Get(ctx, req.NamespacedName, &infisicalSecretCR)
+ requeueTime := time.Minute * 5
+
if err != nil {
if errors.IsNotFound(err) {
log.Info("Infisical Secret not found")
@@ -43,7 +45,7 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
} else {
log.Error(err, "Unable to fetch Infisical Secret from cluster. Will retry")
return ctrl.Result{
- RequeueAfter: time.Minute,
+ RequeueAfter: requeueTime,
}, nil
}
}
@@ -58,13 +60,13 @@ func (r *InfisicalSecretReconciler) Reconcile(ctx context.Context, req ctrl.Requ
if err != nil {
log.Error(err, "Unable to reconcile Infisical Secret and will try again")
return ctrl.Result{
- RequeueAfter: time.Minute,
+ RequeueAfter: requeueTime,
}, nil
}
// Sync again after the specified time
return ctrl.Result{
- RequeueAfter: time.Minute,
+ RequeueAfter: requeueTime,
}, nil
}
diff --git a/k8-operator/controllers/infisicalsecret_helper.go b/k8-operator/controllers/infisicalsecret_helper.go
index fbcacadea3..be4e35dd5c 100644
--- a/k8-operator/controllers/infisicalsecret_helper.go
+++ b/k8-operator/controllers/infisicalsecret_helper.go
@@ -29,12 +29,12 @@ func (r *InfisicalSecretReconciler) GetKubeSecretByNamespacedName(ctx context.Co
func (r *InfisicalSecretReconciler) GetInfisicalToken(ctx context.Context, infisicalSecret v1alpha1.InfisicalSecret) (string, error) {
tokenSecret, err := r.GetKubeSecretByNamespacedName(ctx, types.NamespacedName{
- Namespace: infisicalSecret.Spec.InfisicalToken.SecretNamespace,
- Name: infisicalSecret.Spec.InfisicalToken.SecretName,
+ Namespace: infisicalSecret.Spec.TokenSecretReference.SecretNamespace,
+ Name: infisicalSecret.Spec.TokenSecretReference.SecretName,
})
if err != nil {
- return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.ManagedSecret.SecretName, infisicalSecret.Spec.ManagedSecret.SecretNamespace, err)
+ return "", fmt.Errorf("failed to read Infisical token secret from secret named [%s] in namespace [%s]: with error [%w]", infisicalSecret.Spec.ManagedSecretReference.SecretName, infisicalSecret.Spec.ManagedSecretReference.SecretNamespace, err)
}
infisicalServiceToken := tokenSecret.Data[INFISICAL_TOKEN_SECRET_KEY_NAME]
@@ -54,8 +54,8 @@ func (r *InfisicalSecretReconciler) CreateInfisicalManagedKubeSecret(ctx context
// create a new secret as specified by the managed secret spec of CRD
newKubeSecretInstance := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
- Name: infisicalSecret.Spec.ManagedSecret.SecretName,
- Namespace: infisicalSecret.Spec.ManagedSecret.SecretNamespace,
+ Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
+ Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
},
Type: "Opaque",
Data: plainProcessedSecrets,
@@ -94,15 +94,15 @@ func (r *InfisicalSecretReconciler) ReconcileInfisicalSecret(ctx context.Context
}
managedKubeSecret, err := r.GetKubeSecretByNamespacedName(ctx, types.NamespacedName{
- Name: infisicalSecret.Spec.ManagedSecret.SecretName,
- Namespace: infisicalSecret.Spec.ManagedSecret.SecretNamespace,
+ Name: infisicalSecret.Spec.ManagedSecretReference.SecretName,
+ Namespace: infisicalSecret.Spec.ManagedSecretReference.SecretNamespace,
})
if err != nil && !errors.IsNotFound(err) {
return fmt.Errorf("something went wrong when fetching the managed Kubernetes secret [%w]", err)
}
- secretsFromApi, err := api.GetAllEnvironmentVariables(infisicalSecret.Spec.ProjectId, infisicalSecret.Spec.Environment, infisicalToken)
+ secretsFromApi, err := api.GetAllEnvironmentVariables(infisicalSecret.Spec.ProjectId, infisicalSecret.Spec.Environment, infisicalToken, infisicalSecret.Spec.HostAPI)
if err != nil {
return err
diff --git a/k8-operator/kubectl-install/install-secrets-operator.yaml b/k8-operator/kubectl-install/install-secrets-operator.yaml
new file mode 100644
index 0000000000..6a3cb8e6d9
--- /dev/null
+++ b/k8-operator/kubectl-install/install-secrets-operator.yaml
@@ -0,0 +1,475 @@
+apiVersion: v1
+kind: Namespace
+metadata:
+ labels:
+ app.kubernetes.io/component: manager
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: system
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: namespace
+ app.kubernetes.io/part-of: k8-operator
+ control-plane: controller-manager
+ name: infisical-operator-system
+---
+apiVersion: apiextensions.k8s.io/v1
+kind: CustomResourceDefinition
+metadata:
+ annotations:
+ controller-gen.kubebuilder.io/version: v0.10.0
+ creationTimestamp: null
+ name: infisicalsecrets.secrets.infisical.com
+spec:
+ group: secrets.infisical.com
+ names:
+ kind: InfisicalSecret
+ listKind: InfisicalSecretList
+ plural: infisicalsecrets
+ singular: infisicalsecret
+ scope: Namespaced
+ versions:
+ - name: v1alpha1
+ schema:
+ openAPIV3Schema:
+ description: InfisicalSecret is the Schema for the infisicalsecrets API
+ properties:
+ apiVersion:
+ description: 'APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources'
+ type: string
+ kind:
+ description: 'Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds'
+ type: string
+ metadata:
+ type: object
+ spec:
+ description: InfisicalSecretSpec defines the desired state of InfisicalSecret
+ properties:
+ environment:
+ description: The Infisical environment such as dev, prod, testing
+ type: string
+ hostAPI:
+ default: https://app.infisical.com/api
+ description: Infisical host to pull secrets from
+ type: string
+ managedSecretReference:
+ properties:
+ secretName:
+ description: The name of the Kubernetes Secret
+ type: string
+ secretNamespace:
+ description: The name space where the Kubernetes Secret is located
+ type: string
+ required:
+ - secretName
+ - secretNamespace
+ type: object
+ projectId:
+ description: The Infisical project id
+ type: string
+ tokenSecretReference:
+ properties:
+ secretName:
+ description: The name of the Kubernetes Secret
+ type: string
+ secretNamespace:
+ description: The name space where the Kubernetes Secret is located
+ type: string
+ required:
+ - secretName
+ - secretNamespace
+ type: object
+ required:
+ - environment
+ - projectId
+ type: object
+ status:
+ description: InfisicalSecretStatus defines the observed state of InfisicalSecret
+ properties:
+ conditions:
+ items:
+ description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, \n type FooStatus struct{ // Represents the observations of a foo's current state. // Known .status.conditions.type are: \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type // +patchStrategy=merge // +listType=map // +listMapKey=type Conditions []metav1.Condition `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` \n // other fields }"
+ properties:
+ lastTransitionTime:
+ description: lastTransitionTime is the last time the condition transitioned from one status to another. This should be when the underlying condition changed. If that is not known, then using the time when the API field changed is acceptable.
+ format: date-time
+ type: string
+ message:
+ description: message is a human readable message indicating details about the transition. This may be an empty string.
+ maxLength: 32768
+ type: string
+ observedGeneration:
+ description: observedGeneration represents the .metadata.generation that the condition was set based upon. For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date with respect to the current state of the instance.
+ format: int64
+ minimum: 0
+ type: integer
+ reason:
+ description: reason contains a programmatic identifier indicating the reason for the condition's last transition. Producers of specific condition types may define expected values and meanings for this field, and whether the values are considered a guaranteed API. The value should be a CamelCase string. This field may not be empty.
+ maxLength: 1024
+ minLength: 1
+ pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$
+ type: string
+ status:
+ description: status of the condition, one of True, False, Unknown.
+ enum:
+ - "True"
+ - "False"
+ - Unknown
+ type: string
+ type:
+ description: type of condition in CamelCase or in foo.example.com/CamelCase. --- Many .condition.type values are consistent across resources like Available, but because arbitrary conditions can be useful (see .node.status.conditions), the ability to deconflict is important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt)
+ maxLength: 316
+ pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$
+ type: string
+ required:
+ - lastTransitionTime
+ - message
+ - reason
+ - status
+ - type
+ type: object
+ type: array
+ required:
+ - conditions
+ type: object
+ type: object
+ served: true
+ storage: true
+ subresources:
+ status: {}
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: controller-manager
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: serviceaccount
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-controller-manager
+ namespace: infisical-operator-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: leader-election-role
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: role
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-leader-election-role
+ namespace: infisical-operator-system
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - configmaps
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - coordination.k8s.io
+ resources:
+ - leases
+ verbs:
+ - get
+ - list
+ - watch
+ - create
+ - update
+ - patch
+ - delete
+- apiGroups:
+ - ""
+ resources:
+ - events
+ verbs:
+ - create
+ - patch
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ creationTimestamp: null
+ name: infisical-operator-manager-role
+rules:
+- apiGroups:
+ - ""
+ resources:
+ - secrets
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - update
+ - watch
+- apiGroups:
+ - apps
+ resources:
+ - deployments
+ verbs:
+ - get
+ - list
+ - update
+ - watch
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - infisicalsecrets
+ verbs:
+ - create
+ - delete
+ - get
+ - list
+ - patch
+ - update
+ - watch
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - infisicalsecrets/finalizers
+ verbs:
+ - update
+- apiGroups:
+ - secrets.infisical.com
+ resources:
+ - infisicalsecrets/status
+ verbs:
+ - get
+ - patch
+ - update
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: metrics-reader
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: clusterrole
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-metrics-reader
+rules:
+- nonResourceURLs:
+ - /metrics
+ verbs:
+ - get
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRole
+metadata:
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: proxy-role
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: clusterrole
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-proxy-role
+rules:
+- apiGroups:
+ - authentication.k8s.io
+ resources:
+ - tokenreviews
+ verbs:
+ - create
+- apiGroups:
+ - authorization.k8s.io
+ resources:
+ - subjectaccessreviews
+ verbs:
+ - create
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: leader-election-rolebinding
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: rolebinding
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-leader-election-rolebinding
+ namespace: infisical-operator-system
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: infisical-operator-leader-election-role
+subjects:
+- kind: ServiceAccount
+ name: infisical-operator-controller-manager
+ namespace: infisical-operator-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/component: rbac
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: manager-rolebinding
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: clusterrolebinding
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-manager-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: infisical-operator-manager-role
+subjects:
+- kind: ServiceAccount
+ name: infisical-operator-controller-manager
+ namespace: infisical-operator-system
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: ClusterRoleBinding
+metadata:
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: proxy-rolebinding
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: clusterrolebinding
+ app.kubernetes.io/part-of: k8-operator
+ name: infisical-operator-proxy-rolebinding
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: ClusterRole
+ name: infisical-operator-proxy-role
+subjects:
+- kind: ServiceAccount
+ name: infisical-operator-controller-manager
+ namespace: infisical-operator-system
+---
+apiVersion: v1
+kind: Service
+metadata:
+ labels:
+ app.kubernetes.io/component: kube-rbac-proxy
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: controller-manager-metrics-service
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: service
+ app.kubernetes.io/part-of: k8-operator
+ control-plane: controller-manager
+ name: infisical-operator-controller-manager-metrics-service
+ namespace: infisical-operator-system
+spec:
+ ports:
+ - name: https
+ port: 8443
+ protocol: TCP
+ targetPort: https
+ selector:
+ control-plane: controller-manager
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+ labels:
+ app.kubernetes.io/component: manager
+ app.kubernetes.io/created-by: k8-operator
+ app.kubernetes.io/instance: controller-manager
+ app.kubernetes.io/managed-by: kustomize
+ app.kubernetes.io/name: deployment
+ app.kubernetes.io/part-of: k8-operator
+ control-plane: controller-manager
+ name: infisical-operator-controller-manager
+ namespace: infisical-operator-system
+spec:
+ replicas: 1
+ selector:
+ matchLabels:
+ control-plane: controller-manager
+ template:
+ metadata:
+ annotations:
+ kubectl.kubernetes.io/default-container: manager
+ labels:
+ control-plane: controller-manager
+ spec:
+ affinity:
+ nodeAffinity:
+ requiredDuringSchedulingIgnoredDuringExecution:
+ nodeSelectorTerms:
+ - matchExpressions:
+ - key: kubernetes.io/arch
+ operator: In
+ values:
+ - amd64
+ - arm64
+ - ppc64le
+ - s390x
+ - key: kubernetes.io/os
+ operator: In
+ values:
+ - linux
+ containers:
+ - args:
+ - --secure-listen-address=0.0.0.0:8443
+ - --upstream=http://127.0.0.1:8080/
+ - --logtostderr=true
+ - --v=0
+ image: gcr.io/kubebuilder/kube-rbac-proxy:v0.13.1
+ name: kube-rbac-proxy
+ ports:
+ - containerPort: 8443
+ name: https
+ protocol: TCP
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 5m
+ memory: 64Mi
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ - args:
+ - --health-probe-bind-address=:8081
+ - --metrics-bind-address=127.0.0.1:8080
+ - --leader-elect
+ command:
+ - /manager
+ image: infisical/kubernetes-operator:latest
+ livenessProbe:
+ httpGet:
+ path: /healthz
+ port: 8081
+ initialDelaySeconds: 15
+ periodSeconds: 20
+ name: manager
+ readinessProbe:
+ httpGet:
+ path: /readyz
+ port: 8081
+ initialDelaySeconds: 5
+ periodSeconds: 10
+ resources:
+ limits:
+ cpu: 500m
+ memory: 128Mi
+ requests:
+ cpu: 10m
+ memory: 64Mi
+ securityContext:
+ allowPrivilegeEscalation: false
+ capabilities:
+ drop:
+ - ALL
+ securityContext:
+ runAsNonRoot: true
+ serviceAccountName: infisical-operator-controller-manager
+ terminationGracePeriodSeconds: 10
diff --git a/k8-operator/packages/api/api.go b/k8-operator/packages/api/api.go
index ac2cc5b250..64f8add462 100644
--- a/k8-operator/packages/api/api.go
+++ b/k8-operator/packages/api/api.go
@@ -13,10 +13,8 @@ import (
"golang.org/x/crypto/nacl/box"
)
-const INFISICAL_URL = "https://app.infisical.com/api"
-
-func GetAllEnvironmentVariables(projectId string, envName string, infisicalToken string) ([]models.SingleEnvironmentVariable, error) {
- envsFromApi, err := GetSecretsFromAPIUsingInfisicalToken(infisicalToken, envName, projectId)
+func GetAllEnvironmentVariables(projectId string, envName string, infisicalToken string, hostAPI string) ([]models.SingleEnvironmentVariable, error) {
+ envsFromApi, err := GetSecretsFromAPIUsingInfisicalToken(infisicalToken, envName, projectId, hostAPI)
if err != nil {
return nil, err
}
@@ -24,7 +22,7 @@ func GetAllEnvironmentVariables(projectId string, envName string, infisicalToken
return SubstituteSecrets(envsFromApi), nil
}
-func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string) ([]models.SingleEnvironmentVariable, error) {
+func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string, projectId string, hostAPI string) ([]models.SingleEnvironmentVariable, error) {
if infisicalToken == "" || projectId == "" || envName == "" {
return nil, errors.New("infisical token, project id and or environment name cannot be empty")
}
@@ -44,7 +42,7 @@ func GetSecretsFromAPIUsingInfisicalToken(infisicalToken string, envName string,
SetQueryParam("environment", envName).
SetQueryParam("channel", "cli").
SetResult(&pullSecretsByInfisicalTokenResponse).
- Get(fmt.Sprintf("%v/v1/secret/%v/service-token", INFISICAL_URL, projectId))
+ Get(fmt.Sprintf("%v/v1/secret/%v/service-token", hostAPI, projectId))
if err != nil {
return nil, err
diff --git a/package-lock.json b/package-lock.json
index 3a41579024..cf02a1760d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8,8 +8,7 @@
"license": "ISC",
"devDependencies": {
"eslint": "^8.29.0",
- "husky": "^8.0.2",
- "prettier": "^2.8.1"
+ "husky": "^8.0.2"
}
},
"node_modules/@eslint/eslintrc": {
@@ -899,21 +898,6 @@
"node": ">= 0.8.0"
}
},
- "node_modules/prettier": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz",
- "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==",
- "dev": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
"node_modules/punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
@@ -1809,12 +1793,6 @@
"integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
"dev": true
},
- "prettier": {
- "version": "2.8.1",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.1.tgz",
- "integrity": "sha512-lqGoSJBQNJidqCHE80vqZJHWHRFoNYsSpP9AjFhlhi9ODCJA541svILes/+/1GM3VaL/abZi7cpFzOpdR9UPKg==",
- "dev": true
- },
"punycode": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
diff --git a/package.json b/package.json
index b0d02f3a04..4d042a13dd 100644
--- a/package.json
+++ b/package.json
@@ -14,15 +14,12 @@
"prepare": "husky install"
},
"lint-staged": {
- "**/*": "prettier --write --ignore-unknown",
"*.{js,jsx,ts,tsx}": [
- "eslint --fix",
- "prettier --write"
+ "eslint --fix"
]
},
"devDependencies": {
"eslint": "^8.29.0",
- "husky": "^8.0.2",
- "prettier": "^2.8.1"
+ "husky": "^8.0.2"
}
}