Skip to content

Update how credential data is saved in db when using aws secrets manager #4271

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 22 additions & 7 deletions packages/components/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -543,6 +543,15 @@ const getEncryptionKey = async (): Promise<string> => {
return process.env.FLOWISE_SECRETKEY_OVERWRITE
}
try {
if (USE_AWS_SECRETS_MANAGER && secretsManagerClient) {
const secretId = process.env.SECRETKEY_AWS_NAME || 'FlowiseEncryptionKey'
const command = new GetSecretValueCommand({ SecretId: secretId })
const response = await secretsManagerClient.send(command)

if (response.SecretString) {
return response.SecretString
}
}
return await fs.promises.readFile(getEncryptionKeyPath(), 'utf8')
} catch (error) {
throw new Error(error)
Expand All @@ -561,18 +570,24 @@ const decryptCredentialData = async (encryptedData: string): Promise<ICommonObje

if (USE_AWS_SECRETS_MANAGER && secretsManagerClient) {
try {
const command = new GetSecretValueCommand({ SecretId: encryptedData })
const response = await secretsManagerClient.send(command)
if (encryptedData.startsWith('FlowiseCredential_')) {
const command = new GetSecretValueCommand({ SecretId: encryptedData })
const response = await secretsManagerClient.send(command)

if (response.SecretString) {
const secretObj = JSON.parse(response.SecretString)
decryptedDataStr = JSON.stringify(secretObj)
if (response.SecretString) {
const secretObj = JSON.parse(response.SecretString)
decryptedDataStr = JSON.stringify(secretObj)
} else {
throw new Error('Failed to retrieve secret value.')
}
} else {
throw new Error('Failed to retrieve secret value.')
const encryptKey = await getEncryptionKey()
const decryptedData = AES.decrypt(encryptedData, encryptKey)
decryptedDataStr = decryptedData.toString(enc.Utf8)
}
} catch (error) {
console.error(error)
throw new Error('Credentials could not be decrypted.')
throw new Error('Failed to decrypt credential data.')
}
} else {
// Fallback to existing code
Expand Down
1 change: 1 addition & 0 deletions packages/server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ PORT=3000
# SECRETKEY_AWS_ACCESS_KEY=<your-access-key>
# SECRETKEY_AWS_SECRET_KEY=<your-secret-key>
# SECRETKEY_AWS_REGION=us-west-2
# SECRETKEY_AWS_NAME=FlowiseEncryptionKey

# NUMBER_OF_PROXIES= 1
# CORS_ORIGINS=*
Expand Down
74 changes: 35 additions & 39 deletions packages/server/src/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ import { StatusCodes } from 'http-status-codes'
import {
CreateSecretCommand,
GetSecretValueCommand,
PutSecretValueCommand,
SecretsManagerClient,
SecretsManagerClientConfig
} from '@aws-sdk/client-secrets-manager'
Expand Down Expand Up @@ -1394,6 +1393,29 @@ export const getEncryptionKey = async (): Promise<string> => {
if (process.env.FLOWISE_SECRETKEY_OVERWRITE !== undefined && process.env.FLOWISE_SECRETKEY_OVERWRITE !== '') {
return process.env.FLOWISE_SECRETKEY_OVERWRITE
}
if (USE_AWS_SECRETS_MANAGER && secretsManagerClient) {
const secretId = process.env.SECRETKEY_AWS_NAME || 'FlowiseEncryptionKey'
try {
const command = new GetSecretValueCommand({ SecretId: secretId })
const response = await secretsManagerClient.send(command)

if (response.SecretString) {
return response.SecretString
}
} catch (error: any) {
if (error.name === 'ResourceNotFoundException') {
// Secret doesn't exist, create it
const newKey = generateEncryptKey()
const createCommand = new CreateSecretCommand({
Name: secretId,
SecretString: newKey
})
await secretsManagerClient.send(createCommand)
return newKey
}
throw error
}
}
try {
return await fs.promises.readFile(getEncryptionKeyPath(), 'utf8')
} catch (error) {
Expand All @@ -1412,39 +1434,7 @@ export const getEncryptionKey = async (): Promise<string> => {
* @returns {Promise<string>}
*/
export const encryptCredentialData = async (plainDataObj: ICredentialDataDecrypted): Promise<string> => {
if (USE_AWS_SECRETS_MANAGER && secretsManagerClient) {
const secretName = `FlowiseCredential_${randomBytes(12).toString('hex')}`

logger.info(`[server]: Upserting AWS Secret: ${secretName}`)

const secretString = JSON.stringify({ ...plainDataObj })

try {
// Try to update the secret if it exists
const putCommand = new PutSecretValueCommand({
SecretId: secretName,
SecretString: secretString
})
await secretsManagerClient.send(putCommand)
} catch (error: any) {
if (error.name === 'ResourceNotFoundException') {
// Secret doesn't exist, so create it
const createCommand = new CreateSecretCommand({
Name: secretName,
SecretString: secretString
})
await secretsManagerClient.send(createCommand)
} else {
// Rethrow any other errors
throw error
}
}
return secretName
}

const encryptKey = await getEncryptionKey()

// Fallback to existing code
return AES.encrypt(JSON.stringify(plainDataObj), encryptKey).toString()
}

Expand All @@ -1465,14 +1455,20 @@ export const decryptCredentialData = async (
if (USE_AWS_SECRETS_MANAGER && secretsManagerClient) {
try {
logger.info(`[server]: Reading AWS Secret: ${encryptedData}`)
const command = new GetSecretValueCommand({ SecretId: encryptedData })
const response = await secretsManagerClient.send(command)
if (encryptedData.startsWith('FlowiseCredential_')) {
const command = new GetSecretValueCommand({ SecretId: encryptedData })
const response = await secretsManagerClient.send(command)

if (response.SecretString) {
const secretObj = JSON.parse(response.SecretString)
decryptedDataStr = JSON.stringify(secretObj)
if (response.SecretString) {
const secretObj = JSON.parse(response.SecretString)
decryptedDataStr = JSON.stringify(secretObj)
} else {
throw new Error('Failed to retrieve secret value.')
}
} else {
throw new Error('Failed to retrieve secret value.')
const encryptKey = await getEncryptionKey()
const decryptedData = AES.decrypt(encryptedData, encryptKey)
decryptedDataStr = decryptedData.toString(enc.Utf8)
}
} catch (error) {
console.error(error)
Expand Down