mirror of
https://github.com/Infisical/infisical.git
synced 2025-07-22 13:29:55 +00:00
Compare commits
33 Commits
infisical/
...
daniel/age
Author | SHA1 | Date | |
---|---|---|---|
8420667a8e | |||
f64cb10282 | |||
a0ea2627ed | |||
5c40b538af | |||
8dd94a4e10 | |||
041c4a20a0 | |||
4a2a5f42a8 | |||
9fcdf17a04 | |||
97ac8cb45a | |||
e952659415 | |||
1f3f061a06 | |||
5096ce3bdc | |||
621683f787 | |||
f63850e9e9 | |||
4ee0a2ec6c | |||
9569d3971a | |||
443b8f747b | |||
803393c385 | |||
8e95189fd2 | |||
c5f38b6ade | |||
30a1c5ac86 | |||
bbad2ba047 | |||
1445df7015 | |||
ae4a2089d5 | |||
0b924b6e45 | |||
1fcac4cadf | |||
155e315347 | |||
3dce03180f | |||
fb8c4bd415 | |||
48bf41ac8c | |||
1ad916a784 | |||
c91456838e | |||
79efe64504 |
8
backend/package-lock.json
generated
8
backend/package-lock.json
generated
@ -56,7 +56,7 @@
|
||||
"pg": "^8.11.3",
|
||||
"picomatch": "^3.0.1",
|
||||
"pino": "^8.16.2",
|
||||
"posthog-node": "^3.6.0",
|
||||
"posthog-node": "^3.6.2",
|
||||
"probot": "^13.0.0",
|
||||
"smee-client": "^2.0.0",
|
||||
"tweetnacl": "^1.0.3",
|
||||
@ -10318,9 +10318,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/posthog-node": {
|
||||
"version": "3.6.0",
|
||||
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-3.6.0.tgz",
|
||||
"integrity": "sha512-N/4//SIQR4fhwbHnDdJ2rQCYdu9wo0EVPK4lVgZswp5R/E42RKlpuO6ZfPsBl+Bcg06OYiOd/WR/jLV90FCoSw==",
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/posthog-node/-/posthog-node-3.6.2.tgz",
|
||||
"integrity": "sha512-tVIaShR3SxBx17AlAUS86jQTweKuJIFRedBB504fCz7YPnXJTYSrVcUHn5IINE2wu4jUQimQK6ihQr90Djrdrg==",
|
||||
"dependencies": {
|
||||
"axios": "^1.6.2",
|
||||
"rusha": "^0.8.14"
|
||||
|
@ -117,7 +117,7 @@
|
||||
"pg": "^8.11.3",
|
||||
"picomatch": "^3.0.1",
|
||||
"pino": "^8.16.2",
|
||||
"posthog-node": "^3.6.0",
|
||||
"posthog-node": "^3.6.2",
|
||||
"probot": "^13.0.0",
|
||||
"smee-client": "^2.0.0",
|
||||
"tweetnacl": "^1.0.3",
|
||||
|
@ -1,3 +1,5 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
import { Knex } from "knex";
|
||||
|
||||
import { TableName } from "../schemas";
|
||||
@ -8,10 +10,12 @@ export async function up(knex: Knex): Promise<void> {
|
||||
await knex.schema.alterTable(TableName.SuperAdmin, (t) => {
|
||||
t.uuid("instanceId").notNullable().defaultTo(knex.fn.uuid());
|
||||
});
|
||||
// this is updated to avoid race condition on replication
|
||||
// eslint-disable-next-line
|
||||
// @ts-ignore
|
||||
await knex(TableName.SuperAdmin).update({ id: ADMIN_CONFIG_UUID }).whereNotNull("id").limit(1);
|
||||
|
||||
const superUserConfigExists = await knex(TableName.SuperAdmin).where("id", ADMIN_CONFIG_UUID).first();
|
||||
if (!superUserConfigExists) {
|
||||
// eslint-disable-next-line
|
||||
await knex(TableName.SuperAdmin).update({ id: ADMIN_CONFIG_UUID }).whereNotNull("id").limit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export async function down(knex: Knex): Promise<void> {
|
||||
|
@ -1,5 +1,5 @@
|
||||
infisical:
|
||||
address: "http://localhost:8080"
|
||||
address: "https://app.infisical.com/"
|
||||
auth:
|
||||
type: "universal-auth"
|
||||
config:
|
||||
@ -13,3 +13,12 @@ sinks:
|
||||
templates:
|
||||
- source-path: my-dot-ev-secret-template
|
||||
destination-path: my-dot-env.env
|
||||
config:
|
||||
polling-interval: 60s
|
||||
execute:
|
||||
command: docker-compose -f docker-compose.prod.yml down && docker-compose -f docker-compose.prod.yml up -d
|
||||
- source-path: my-dot-ev-secret-template1
|
||||
destination-path: my-dot-env-1.env
|
||||
config:
|
||||
exec:
|
||||
command: mkdir hello-world1
|
||||
|
@ -490,5 +490,7 @@ func CallGetRawSecretsV3(httpClient *resty.Client, request GetRawSecretsV3Reques
|
||||
return GetRawSecretsV3Response{}, fmt.Errorf("CallGetRawSecretsV3: Unsuccessful response [%v %v] [status-code=%v] [response=%v]", response.Request.Method, response.Request.URL, response.StatusCode(), response.String())
|
||||
}
|
||||
|
||||
getRawSecretsV3Response.ETag = response.Header().Get(("etag"))
|
||||
|
||||
return getRawSecretsV3Response, nil
|
||||
}
|
||||
|
@ -505,4 +505,5 @@ type GetRawSecretsV3Response struct {
|
||||
SecretComment string `json:"secretComment"`
|
||||
} `json:"secrets"`
|
||||
Imports []any `json:"imports"`
|
||||
ETag string
|
||||
}
|
||||
|
@ -5,12 +5,15 @@ package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
@ -71,12 +74,56 @@ type Template struct {
|
||||
SourcePath string `yaml:"source-path"`
|
||||
Base64TemplateContent string `yaml:"base64-template-content"`
|
||||
DestinationPath string `yaml:"destination-path"`
|
||||
|
||||
Config struct { // Configurations for the template
|
||||
PollingInterval string `yaml:"polling-interval"` // How often to poll for changes in the secret
|
||||
Execute struct {
|
||||
Command string `yaml:"command"` // Command to execute once the template has been rendered
|
||||
Timeout int64 `yaml:"timeout"` // Timeout for the command
|
||||
} `yaml:"execute"` // Command to execute once the template has been rendered
|
||||
} `yaml:"config"`
|
||||
}
|
||||
|
||||
func ReadFile(filePath string) ([]byte, error) {
|
||||
return ioutil.ReadFile(filePath)
|
||||
}
|
||||
|
||||
func ExecuteCommandWithTimeout(command string, timeout int64) error {
|
||||
|
||||
shell := [2]string{"sh", "-c"}
|
||||
if runtime.GOOS == "windows" {
|
||||
shell = [2]string{"cmd", "/C"}
|
||||
} else {
|
||||
currentShell := os.Getenv("SHELL")
|
||||
if currentShell != "" {
|
||||
shell[0] = currentShell
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
if timeout > 0 {
|
||||
var cancel context.CancelFunc
|
||||
ctx, cancel = context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
defer cancel()
|
||||
}
|
||||
|
||||
cmd := exec.CommandContext(ctx, shell[0], shell[1], command)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
if err := cmd.Run(); err != nil {
|
||||
if exitError, ok := err.(*exec.ExitError); ok { // type assertion
|
||||
if exitError.ProcessState.ExitCode() == -1 {
|
||||
return fmt.Errorf("command timed out")
|
||||
}
|
||||
}
|
||||
return err
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func FileExists(filepath string) bool {
|
||||
info, err := os.Stat(filepath)
|
||||
if os.IsNotExist(err) {
|
||||
@ -170,20 +217,24 @@ func ParseAgentConfig(configFile []byte) (*Config, error) {
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func secretTemplateFunction(accessToken string) func(string, string, string) ([]models.SingleEnvironmentVariable, error) {
|
||||
func secretTemplateFunction(accessToken string, existingEtag string, currentEtag *string) func(string, string, string) ([]models.SingleEnvironmentVariable, error) {
|
||||
return func(projectID, envSlug, secretPath string) ([]models.SingleEnvironmentVariable, error) {
|
||||
secrets, err := util.GetPlainTextSecretsViaMachineIdentity(accessToken, projectID, envSlug, secretPath, false)
|
||||
res, err := util.GetPlainTextSecretsViaMachineIdentity(accessToken, projectID, envSlug, secretPath, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return secrets, nil
|
||||
if existingEtag != res.Etag {
|
||||
*currentEtag = res.Etag
|
||||
}
|
||||
|
||||
return res.Secrets, nil
|
||||
}
|
||||
}
|
||||
|
||||
func ProcessTemplate(templatePath string, data interface{}, accessToken string) (*bytes.Buffer, error) {
|
||||
func ProcessTemplate(templatePath string, data interface{}, accessToken string, existingEtag string, currentEtag *string) (*bytes.Buffer, error) {
|
||||
// custom template function to fetch secrets from Infisical
|
||||
secretFunction := secretTemplateFunction(accessToken)
|
||||
secretFunction := secretTemplateFunction(accessToken, existingEtag, currentEtag)
|
||||
funcs := template.FuncMap{
|
||||
"secret": secretFunction,
|
||||
}
|
||||
@ -203,7 +254,7 @@ func ProcessTemplate(templatePath string, data interface{}, accessToken string)
|
||||
return &buf, nil
|
||||
}
|
||||
|
||||
func ProcessBase64Template(encodedTemplate string, data interface{}, accessToken string) (*bytes.Buffer, error) {
|
||||
func ProcessBase64Template(encodedTemplate string, data interface{}, accessToken string, existingEtag string, currentEtag *string) (*bytes.Buffer, error) {
|
||||
// custom template function to fetch secrets from Infisical
|
||||
decoded, err := base64.StdEncoding.DecodeString(encodedTemplate)
|
||||
if err != nil {
|
||||
@ -212,7 +263,7 @@ func ProcessBase64Template(encodedTemplate string, data interface{}, accessToken
|
||||
|
||||
templateString := string(decoded)
|
||||
|
||||
secretFunction := secretTemplateFunction(accessToken)
|
||||
secretFunction := secretTemplateFunction(accessToken, existingEtag, currentEtag) // TODO: Fix this
|
||||
funcs := template.FuncMap{
|
||||
"secret": secretFunction,
|
||||
}
|
||||
@ -250,7 +301,16 @@ type TokenManager struct {
|
||||
}
|
||||
|
||||
func NewTokenManager(fileDeposits []Sink, templates []Template, clientIdPath string, clientSecretPath string, newAccessTokenNotificationChan chan bool, removeClientSecretOnRead bool, exitAfterAuth bool) *TokenManager {
|
||||
return &TokenManager{filePaths: fileDeposits, templates: templates, clientIdPath: clientIdPath, clientSecretPath: clientSecretPath, newAccessTokenNotificationChan: newAccessTokenNotificationChan, removeClientSecretOnRead: removeClientSecretOnRead, exitAfterAuth: exitAfterAuth}
|
||||
return &TokenManager{
|
||||
filePaths: fileDeposits,
|
||||
templates: templates,
|
||||
clientIdPath: clientIdPath,
|
||||
clientSecretPath: clientSecretPath,
|
||||
newAccessTokenNotificationChan: newAccessTokenNotificationChan,
|
||||
removeClientSecretOnRead: removeClientSecretOnRead,
|
||||
exitAfterAuth: exitAfterAuth,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (tm *TokenManager) SetToken(token string, accessTokenTTL time.Duration, accessTokenMaxTTL time.Duration) {
|
||||
@ -428,38 +488,79 @@ func (tm *TokenManager) WriteTokenToFiles() {
|
||||
}
|
||||
}
|
||||
|
||||
func (tm *TokenManager) FetchSecrets() {
|
||||
log.Info().Msgf("template engine started...")
|
||||
func (tm *TokenManager) WriteTemplateToFile(bytes *bytes.Buffer, template *Template) {
|
||||
if err := WriteBytesToFile(bytes, template.DestinationPath); err != nil {
|
||||
log.Error().Msgf("template engine: unable to write secrets to path because %s. Will try again on next cycle", err)
|
||||
return
|
||||
}
|
||||
log.Info().Msgf("template engine: secret template at path %s has been rendered and saved to path %s", template.SourcePath, template.DestinationPath)
|
||||
}
|
||||
|
||||
func (tm *TokenManager) MonitorSecretChanges(secretTemplate Template, sigChan chan os.Signal) {
|
||||
|
||||
pollingInterval := time.Duration(5 * time.Minute)
|
||||
|
||||
if secretTemplate.Config.PollingInterval != "" {
|
||||
interval, err := util.ConvertPollingIntervalToTime(secretTemplate.Config.PollingInterval)
|
||||
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to convert polling interval to time because %v", err)
|
||||
sigChan <- syscall.SIGINT
|
||||
return
|
||||
|
||||
} else {
|
||||
pollingInterval = interval
|
||||
}
|
||||
}
|
||||
|
||||
var existingEtag string
|
||||
var currentEtag string
|
||||
var firstRun = true
|
||||
|
||||
execTimeout := secretTemplate.Config.Execute.Timeout
|
||||
execCommand := secretTemplate.Config.Execute.Command
|
||||
|
||||
for {
|
||||
token := tm.GetToken()
|
||||
|
||||
if token != "" {
|
||||
for _, secretTemplate := range tm.templates {
|
||||
var processedTemplate *bytes.Buffer
|
||||
var err error
|
||||
if secretTemplate.SourcePath != "" {
|
||||
processedTemplate, err = ProcessTemplate(secretTemplate.SourcePath, nil, token)
|
||||
} else {
|
||||
processedTemplate, err = ProcessBase64Template(secretTemplate.Base64TemplateContent, nil, token)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
log.Error().Msgf("template engine: unable to render secrets because %s. Will try again on next cycle", err)
|
||||
var processedTemplate *bytes.Buffer
|
||||
var err error
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
if err := WriteBytesToFile(processedTemplate, secretTemplate.DestinationPath); err != nil {
|
||||
log.Error().Msgf("template engine: unable to write secrets to path because %s. Will try again on next cycle", err)
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
log.Info().Msgf("template engine: secret template at path %s has been rendered and saved to path %s", secretTemplate.SourcePath, secretTemplate.DestinationPath)
|
||||
if secretTemplate.SourcePath != "" {
|
||||
processedTemplate, err = ProcessTemplate(secretTemplate.SourcePath, nil, token, existingEtag, ¤tEtag)
|
||||
} else {
|
||||
processedTemplate, err = ProcessBase64Template(secretTemplate.Base64TemplateContent, nil, token, existingEtag, ¤tEtag)
|
||||
}
|
||||
|
||||
// fetch new secrets every 5 minutes (TODO: add PubSub in the future )
|
||||
time.Sleep(5 * time.Minute)
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to process template because %v", err)
|
||||
} else {
|
||||
if (existingEtag != currentEtag) || firstRun {
|
||||
|
||||
tm.WriteTemplateToFile(processedTemplate, &secretTemplate)
|
||||
existingEtag = currentEtag
|
||||
|
||||
if !firstRun && execCommand != "" {
|
||||
err := ExecuteCommandWithTimeout(execCommand, execTimeout)
|
||||
|
||||
if err != nil {
|
||||
log.Error().Msgf("unable to execute command because %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
if firstRun {
|
||||
firstRun = false
|
||||
}
|
||||
}
|
||||
}
|
||||
time.Sleep(pollingInterval)
|
||||
} else {
|
||||
// It fails to get the access token. So we will re-try in 3 seconds. We do this because if we don't, the user will have to wait for the next polling interval to get the first secret render.
|
||||
time.Sleep(3 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -544,7 +645,11 @@ var agentCmd = &cobra.Command{
|
||||
tm := NewTokenManager(filePaths, agentConfig.Templates, configUniversalAuthType.ClientIDPath, configUniversalAuthType.ClientSecretPath, tokenRefreshNotifier, configUniversalAuthType.RemoveClientSecretOnRead, agentConfig.Infisical.ExitAfterAuth)
|
||||
|
||||
go tm.ManageTokenLifecycle()
|
||||
go tm.FetchSecrets()
|
||||
|
||||
for i, template := range agentConfig.Templates {
|
||||
log.Info().Msgf("template engine started for template %v...", i+1)
|
||||
go tm.MonitorSecretChanges(template, sigChan)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
|
@ -34,6 +34,11 @@ type SingleEnvironmentVariable struct {
|
||||
Comment string `json:"comment"`
|
||||
}
|
||||
|
||||
type PlaintextSecretResult struct {
|
||||
Secrets []SingleEnvironmentVariable
|
||||
Etag string
|
||||
}
|
||||
|
||||
type SingleFolder struct {
|
||||
ID string `json:"_id"`
|
||||
Name string `json:"name"`
|
||||
|
41
cli/packages/util/agent.go
Normal file
41
cli/packages/util/agent.go
Normal file
@ -0,0 +1,41 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ConvertPollingIntervalToTime converts a string representation of a polling interval to a time.Duration
|
||||
func ConvertPollingIntervalToTime(pollingInterval string) (time.Duration, error) {
|
||||
length := len(pollingInterval)
|
||||
if length < 2 {
|
||||
return 0, fmt.Errorf("invalid format")
|
||||
}
|
||||
|
||||
unit := pollingInterval[length-1:]
|
||||
numberPart := pollingInterval[:length-1]
|
||||
|
||||
number, err := strconv.Atoi(numberPart)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
switch unit {
|
||||
case "s":
|
||||
if number < 60 {
|
||||
return 0, fmt.Errorf("polling interval should be at least 60 seconds")
|
||||
}
|
||||
return time.Duration(number) * time.Second, nil
|
||||
case "m":
|
||||
return time.Duration(number) * time.Minute, nil
|
||||
case "h":
|
||||
return time.Duration(number) * time.Hour, nil
|
||||
case "d":
|
||||
return time.Duration(number) * 24 * time.Hour, nil
|
||||
case "w":
|
||||
return time.Duration(number) * 7 * 24 * time.Hour, nil
|
||||
default:
|
||||
return 0, fmt.Errorf("invalid time unit")
|
||||
}
|
||||
}
|
@ -152,7 +152,7 @@ func GetPlainTextSecretsViaJTW(JTWToken string, receiversPrivateKey string, work
|
||||
return plainTextSecrets, nil
|
||||
}
|
||||
|
||||
func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool) ([]models.SingleEnvironmentVariable, error) {
|
||||
func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId string, environmentName string, secretsPath string, includeImports bool) (models.PlaintextSecretResult, error) {
|
||||
httpClient := resty.New()
|
||||
httpClient.SetAuthToken(accessToken).
|
||||
SetHeader("Accept", "application/json")
|
||||
@ -170,12 +170,12 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin
|
||||
|
||||
rawSecrets, err := api.CallGetRawSecretsV3(httpClient, api.GetRawSecretsV3Request{WorkspaceId: workspaceId, SecretPath: secretsPath, Environment: environmentName})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return models.PlaintextSecretResult{}, err
|
||||
}
|
||||
|
||||
plainTextSecrets := []models.SingleEnvironmentVariable{}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
return models.PlaintextSecretResult{}, fmt.Errorf("unable to decrypt your secrets [err=%v]", err)
|
||||
}
|
||||
|
||||
for _, secret := range rawSecrets.Secrets {
|
||||
@ -189,7 +189,10 @@ func GetPlainTextSecretsViaMachineIdentity(accessToken string, workspaceId strin
|
||||
// }
|
||||
// }
|
||||
|
||||
return plainTextSecrets, nil
|
||||
return models.PlaintextSecretResult{
|
||||
Secrets: plainTextSecrets,
|
||||
Hash: rawSecrets.ETag,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func InjectImportedSecret(plainTextWorkspaceKey []byte, secrets []models.SingleEnvironmentVariable, importedSecrets []api.ImportedSecretV3) ([]models.SingleEnvironmentVariable, error) {
|
||||
|
@ -4,7 +4,22 @@ title: "Changelog"
|
||||
|
||||
The changelog below reflects new product developments and updates on a monthly basis.
|
||||
|
||||
## January 2024
|
||||
## Feb 2024
|
||||
- Added org-scoped authentication enforcement for SAML
|
||||
- Added support for [SCIM](https://infisical.com/docs/documentation/platform/scim/overview) along with instructions for setting it up with [Okta](https://infisical.com/docs/documentation/platform/scim/okta), [Azure](https://infisical.com/docs/documentation/platform/scim/azure), and [JumpCloud](https://infisical.com/docs/documentation/platform/scim/jumpcloud).
|
||||
- Pushed out project update for non-E2EE w/ new endpoints like for project creation and member invitation.
|
||||
- Added API Integration testing for new backend.
|
||||
- Added capability to create projects in Terraform.
|
||||
- Added slug-based capabilities to both organizations and projects to gradually make the API more developer-friendly moving forward.
|
||||
- Fixed + improved various analytics/telemetry-related items.
|
||||
- Fixed various issues associated with the Python SDK: build during installation on Mac OS, Rust dependency.
|
||||
- Updated self-hosting documentation to reflect [new backend](https://infisical.com/docs/self-hosting/overview).
|
||||
- Released [Postgres-based Infisical helm chart](https://cloudsmith.io/~infisical/repos/helm-charts/packages/detail/helm/infisical-standalone/).
|
||||
- Added checks to ensure that breaking API changes don't get released.
|
||||
- Automated API reference documentation to be inline with latest releases of Infisical.
|
||||
|
||||
## Jan 2024
|
||||
- Completed Postgres migration initiative with restructed Fastify-based backend.
|
||||
- Reduced size of Infisical Node.js SDK by ≈90%.
|
||||
- Added secret fallback support to all SDK's.
|
||||
- Added Machine Identity support to [Terraform Provider](https://github.com/Infisical/terraform-provider-infisical).
|
||||
@ -12,21 +27,21 @@ The changelog below reflects new product developments and updates on a monthly b
|
||||
- Added symmetric encryption support to all SDK's.
|
||||
- Fixed secret reminders bug, where reminders were not being updated correctly.
|
||||
|
||||
## December 2023
|
||||
## Dec 2023
|
||||
|
||||
- Released [(machine) identities](https://infisical.com/docs/documentation/platform/identities/overview) and [universal auth](https://infisical.com/docs/documentation/platform/identities/universal-auth) features.
|
||||
- Created new cross-language SDKs for [Python](https://infisical.com/docs/sdks/languages/python), [Node](https://infisical.com/docs/sdks/languages/node), and [Java](https://infisical.com/docs/sdks/languages/java).
|
||||
- Released first version of the [Infisical Agent](https://infisical.com/docs/infisical-agent/overview)
|
||||
- Added ability to [manage folders via CLI](https://infisical.com/docs/cli/commands/secrets).
|
||||
|
||||
## November 2023
|
||||
## Nov 2023
|
||||
|
||||
- Replaced internal [Winston](https://github.com/winstonjs/winston) with [Pino](https://github.com/pinojs/pino) logging library with external logging to AWS CloudWatch
|
||||
- Added admin panel to self-hosting experience.
|
||||
- Released [secret rotation](https://infisical.com/docs/documentation/platform/secret-rotation/overview) feature with preliminary support for rotating [SendGrid](https://infisical.com/docs/documentation/platform/secret-rotation/sendgrid), [PostgreSQL/CockroachDB](https://infisical.com/docs/documentation/platform/secret-rotation/postgres), and [MySQL/MariaDB](https://infisical.com/docs/documentation/platform/secret-rotation/mysql) credentials.
|
||||
- Released secret reminders feature.
|
||||
|
||||
## October 2023
|
||||
## Oct 2023
|
||||
|
||||
- Added support for [GitLab SSO](https://infisical.com/docs/documentation/platform/sso/gitlab).
|
||||
- Became SOC 2 (Type II) certified.
|
||||
@ -35,7 +50,7 @@ The changelog below reflects new product developments and updates on a monthly b
|
||||
- Added native [Hasura Cloud integration](https://infisical.com/docs/integrations/cloud/hasura-cloud).
|
||||
- Updated resource deletion logic for user, organization, and project deletion.
|
||||
|
||||
## September 2023
|
||||
## Sep 2023
|
||||
|
||||
- Released [secret approvals](https://infisical.com/docs/documentation/platform/pr-workflows) feature.
|
||||
- Released an update to access controls; every user role now clearly defines and enforces a certain set of conditions across Infisical.
|
||||
@ -43,7 +58,7 @@ The changelog below reflects new product developments and updates on a monthly b
|
||||
- Added a native integration with [Qovery](https://infisical.com/docs/integrations/cloud/qovery).
|
||||
- Added service token generation capability for the CLI.
|
||||
|
||||
## August 2023
|
||||
## Aug 2023
|
||||
|
||||
- Release Audit Logs V2.
|
||||
- Add support for [GitHub SSO](https://infisical.com/docs/documentation/platform/sso/github).
|
||||
@ -171,7 +186,7 @@ The changelog below reflects new product developments and updates on a monthly b
|
||||
- Added sorting capability to sort keys by name alphabetically in dashboard.
|
||||
- Added downloading secrets back as `.env` file capability.
|
||||
|
||||
## August 2022
|
||||
## Aug 2022
|
||||
|
||||
- Released first version of the Infisical platform with push/pull capability and end-to-end encryption.
|
||||
- Improved security handling of authentication tokens by storing refresh tokens in HttpOnly cookies.
|
||||
|
@ -46,27 +46,34 @@ description: "Use Helm chart to install Infisical on your Kubernetes cluster"
|
||||
|
||||
<Tabs>
|
||||
<Tab title="Proof of concept deployment">
|
||||
For test or proof-of-concept purposes, you may omit `DB_CONNECTION_URI` and `REDIS_URL` from `infisical-secrets`. This is because the Helm chart will automatically provision and connect to the in-cluster instances of Postgres and Redis by default.
|
||||
|
||||
For test or proof-of-concept purposes, you may omit `DB_CONNECTION_URI` and `REDIS_URL` from `infisical-secrets`. This is because the Helm chart will automatically provision and connect to the in-cluster instances of Postgres and Redis by default.
|
||||
```yaml simple-values-example.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: infisical-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
AUTH_SECRET: <>
|
||||
ENCRYPTION_KEY: <>
|
||||
```
|
||||
</Tab>
|
||||
<Tab title="Production deployment">
|
||||
For production environments, we recommend using Cloud-based Platform as a Service (PaaS) solutions for PostgreSQL and Redis to ensure high availability. In on-premise setups, it's recommended to configure Redis and Postgres for high availability, either by using Bitnami charts or a custom configuration.
|
||||
|
||||
```yaml simple-values-example.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: infisical-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
AUTH_SECRET: <>
|
||||
ENCRYPTION_KEY: <>
|
||||
REDIS_URL: <>
|
||||
DB_CONNECTION_URI: <>
|
||||
```
|
||||
</Tab>
|
||||
</Tabs>
|
||||
|
||||
```yaml simple-values-example.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: infisical-secrets
|
||||
type: Opaque
|
||||
stringData:
|
||||
AUTH_SECRET: <>
|
||||
ENCRYPTION_KEY: <>
|
||||
REDIS_URL: <>
|
||||
DB_CONNECTION_URI: <>
|
||||
```
|
||||
</Step>
|
||||
|
||||
<Step title="Database schema migration ">
|
||||
|
@ -104,7 +104,7 @@ Once started, the migration script will transform MongoDB data into an equivalen
|
||||
<Step title="Clone Infisical Repository">
|
||||
Clone the Infisical MongoDB repository.
|
||||
```
|
||||
git clone -b infisical/v0.46.7-postgres https://github.com/Infisical/infisical.git
|
||||
git clone -b infisical/v0.46.11-postgres https://github.com/Infisical/infisical.git
|
||||
```
|
||||
</Step>
|
||||
<Step title="Install dependencies for backend">
|
||||
@ -192,7 +192,7 @@ For guidance on deployment options, please visit the [self-hosting documentation
|
||||
Remember to transfer the necessary [environment variables](/self-hosting/configuration/envars) from the MongoDB version of Infisical to the new Postgres based Infisical; rest assured, they are fully compatible.
|
||||
|
||||
<Warning>
|
||||
The first deployment of Postgres based Infisical must be deployed with Docker image tag `v0.46.7-postgres`.
|
||||
The first deployment of Postgres based Infisical must be deployed with Docker image tag `v0.46.11-postgres`.
|
||||
After deploying this version, you can proceed to update to any subsequent versions.
|
||||
</Warning>
|
||||
|
||||
|
Reference in New Issue
Block a user