mirror of
https://github.com/Infisical/infisical.git
synced 2026-01-08 23:18:05 -05:00
feat(e2e-tests): secret rotations
This commit is contained in:
3
.github/workflows/run-backend-tests.yml
vendored
3
.github/workflows/run-backend-tests.yml
vendored
@@ -34,6 +34,9 @@ jobs:
|
||||
working-directory: backend
|
||||
- name: Start postgres and redis
|
||||
run: touch .env && docker compose -f docker-compose.dev.yml up -d db redis
|
||||
- name: Login to Oracle Container Registry
|
||||
run: echo "${{ secrets.ORACLE_DOCKER_REGISTRY_PASSWORD }}" | docker login container-registry.oracle.com -u "${{ secrets.ORACLE_DOCKER_REGISTRY_USERNAME }}" --password-stdin
|
||||
|
||||
- name: Start Secret Rotation testing databases
|
||||
run: docker compose -f docker-compose.e2e-dbs.yml up -d
|
||||
- name: Run unit test
|
||||
|
||||
@@ -7,7 +7,8 @@ import { seedData1 } from "@app/db/seed-data";
|
||||
|
||||
enum SecretRotationType {
|
||||
OracleDb = "oracledb",
|
||||
MySQL = "mysql"
|
||||
MySQL = "mysql",
|
||||
Postgres = "postgres"
|
||||
}
|
||||
|
||||
type TGenericSqlCredentials = {
|
||||
@@ -147,6 +148,40 @@ const createMySQLAppConnection = async (credentials: TGenericSqlCredentials) =>
|
||||
return json.appConnection.id as string;
|
||||
};
|
||||
|
||||
const createPostgresAppConnection = async (credentials: TGenericSqlCredentials) => {
|
||||
const createPostgresAppConnectionReqBody = {
|
||||
credentials: {
|
||||
host: credentials.host,
|
||||
port: credentials.port,
|
||||
database: credentials.database,
|
||||
username: credentials.username,
|
||||
password: credentials.password,
|
||||
sslEnabled: false,
|
||||
sslRejectUnauthorized: true
|
||||
},
|
||||
name: `postgres-test-${uuidv4()}`,
|
||||
description: "test-postgres",
|
||||
gatewayId: null,
|
||||
method: "username-and-password"
|
||||
};
|
||||
|
||||
const res = await testServer.inject({
|
||||
method: "POST",
|
||||
url: `/api/v1/app-connections/postgres`,
|
||||
headers: {
|
||||
authorization: `Bearer ${jwtAuthToken}`
|
||||
},
|
||||
body: createPostgresAppConnectionReqBody
|
||||
});
|
||||
|
||||
const json = JSON.parse(res.payload);
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(json.appConnection).toBeDefined();
|
||||
|
||||
return json.appConnection.id as string;
|
||||
};
|
||||
|
||||
const createOracleInfisicalUsers = async (
|
||||
credentials: TGenericSqlCredentials,
|
||||
userCredentials: TDatabaseUserCredentials[]
|
||||
@@ -169,7 +204,7 @@ const createOracleInfisicalUsers = async (
|
||||
|
||||
for await (const { username } of userCredentials) {
|
||||
// check if user exists, and if it does, don't create it
|
||||
const existingUser = await client.raw(`SELECT * FROM all_users WHERE username = '${username.toUpperCase()}'`);
|
||||
const existingUser = await client.raw(`SELECT * FROM all_users WHERE username = '${username}'`);
|
||||
|
||||
if (!existingUser.length) {
|
||||
await client.raw(`CREATE USER ${username} IDENTIFIED BY "temporary_password"`);
|
||||
@@ -220,6 +255,36 @@ const createMySQLInfisicalUsers = async (
|
||||
await client.destroy();
|
||||
};
|
||||
|
||||
const createPostgresInfisicalUsers = async (
|
||||
credentials: TGenericSqlCredentials,
|
||||
userCredentials: TDatabaseUserCredentials[]
|
||||
) => {
|
||||
const client = knex({
|
||||
client: "pg",
|
||||
connection: {
|
||||
database: credentials.database,
|
||||
port: credentials.port,
|
||||
host: credentials.host,
|
||||
user: credentials.username,
|
||||
password: credentials.password,
|
||||
connectionTimeoutMillis: 10000
|
||||
}
|
||||
});
|
||||
|
||||
for await (const { username } of userCredentials) {
|
||||
// check if user exists, and if it does, don't create it
|
||||
const existingUser = await client.raw("SELECT * FROM pg_catalog.pg_user WHERE usename = ?", [username]);
|
||||
|
||||
if (!existingUser.rows.length) {
|
||||
await client.raw(`CREATE USER "${username}" WITH PASSWORD 'temporary_password'`);
|
||||
}
|
||||
|
||||
await client.raw("GRANT ALL PRIVILEGES ON DATABASE ?? TO ??", [credentials.database, username]);
|
||||
}
|
||||
|
||||
await client.destroy();
|
||||
};
|
||||
|
||||
const createOracleDBSecretRotation = async (
|
||||
appConnectionId: string,
|
||||
credentials: TGenericSqlCredentials,
|
||||
@@ -324,6 +389,58 @@ const createMySQLSecretRotation = async (
|
||||
return res;
|
||||
};
|
||||
|
||||
const createPostgresSecretRotation = async (
|
||||
appConnectionId: string,
|
||||
credentials: TGenericSqlCredentials,
|
||||
userCredentials: TDatabaseUserCredentials[],
|
||||
secretMapping: TSecretMapping
|
||||
) => {
|
||||
const now = new Date();
|
||||
const rotationTime = new Date(now.getTime() - 2 * 60 * 1000); // 2 minutes ago
|
||||
|
||||
await createPostgresInfisicalUsers(credentials, userCredentials);
|
||||
|
||||
const createPostgresSecretRotationReqBody = {
|
||||
parameters: userCredentials.reduce(
|
||||
(acc, user, index) => {
|
||||
acc[`username${index + 1}`] = user.username;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, string>
|
||||
),
|
||||
secretsMapping: {
|
||||
username: secretMapping.username,
|
||||
password: secretMapping.password
|
||||
},
|
||||
name: `test-postgres-rotation-${uuidv4()}`,
|
||||
description: "Test Postgres Secret Rotation",
|
||||
secretPath: "/",
|
||||
isAutoRotationEnabled: true,
|
||||
rotationInterval: 5,
|
||||
rotateAtUtc: {
|
||||
hours: rotationTime.getUTCHours(),
|
||||
minutes: rotationTime.getUTCMinutes()
|
||||
},
|
||||
connectionId: appConnectionId,
|
||||
environment: seedData1.environment.slug,
|
||||
projectId: seedData1.projectV3.id
|
||||
};
|
||||
|
||||
const res = await testServer.inject({
|
||||
method: "POST",
|
||||
url: `/api/v2/secret-rotations/postgres-credentials`,
|
||||
headers: {
|
||||
authorization: `Bearer ${jwtAuthToken}`
|
||||
},
|
||||
body: createPostgresSecretRotationReqBody
|
||||
});
|
||||
|
||||
expect(res.statusCode).toBe(200);
|
||||
expect(res.json().secretRotation).toBeDefined();
|
||||
|
||||
return res;
|
||||
};
|
||||
|
||||
describe("Secret Rotations", async () => {
|
||||
const testCases = [
|
||||
{
|
||||
@@ -349,6 +466,52 @@ describe("Secret Rotations", async () => {
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.MySQL,
|
||||
name: "MySQL (8.0.29) Secret Rotation",
|
||||
dbCredentials: {
|
||||
database: "mysql-test",
|
||||
host: "127.0.0.1",
|
||||
username: "root",
|
||||
password: "mysql-test",
|
||||
port: 3307
|
||||
},
|
||||
secretMapping: {
|
||||
username: formatSqlUsername("MYSQL_USERNAME"),
|
||||
password: formatSqlUsername("MYSQL_PASSWORD")
|
||||
},
|
||||
userCredentials: [
|
||||
{
|
||||
username: formatSqlUsername("MYSQL_USER_1")
|
||||
},
|
||||
{
|
||||
username: formatSqlUsername("MYSQL_USER_2")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.MySQL,
|
||||
name: "MySQL (5.7.31) Secret Rotation",
|
||||
dbCredentials: {
|
||||
database: "mysql-test",
|
||||
host: "127.0.0.1",
|
||||
username: "root",
|
||||
password: "mysql-test",
|
||||
port: 3308
|
||||
},
|
||||
secretMapping: {
|
||||
username: formatSqlUsername("MYSQL_USERNAME"),
|
||||
password: formatSqlUsername("MYSQL_PASSWORD")
|
||||
},
|
||||
userCredentials: [
|
||||
{
|
||||
username: formatSqlUsername("MYSQL_USER_1")
|
||||
},
|
||||
{
|
||||
username: formatSqlUsername("MYSQL_USER_2")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.OracleDb,
|
||||
name: "OracleDB (23.8) Secret Rotation",
|
||||
@@ -371,6 +534,98 @@ describe("Secret Rotations", async () => {
|
||||
username: formatSqlUsername("INFISICAL_USER_2")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.OracleDb,
|
||||
name: "OracleDB (19.3) Secret Rotation",
|
||||
dbCredentials: {
|
||||
database: "ORCLPDB1",
|
||||
host: "127.0.0.1",
|
||||
username: "system",
|
||||
password: "OrCAKF112aaSfAdfdA2Ac3@@!",
|
||||
port: 1522
|
||||
},
|
||||
secretMapping: {
|
||||
username: formatSqlUsername("ORACLEDB_USERNAME"),
|
||||
password: formatSqlUsername("ORACLEDB_PASSWORD")
|
||||
},
|
||||
userCredentials: [
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_1")
|
||||
},
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_2")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.Postgres,
|
||||
name: "Postgres (17) Secret Rotation",
|
||||
dbCredentials: {
|
||||
database: "postgres-test",
|
||||
host: "127.0.0.1",
|
||||
username: "postgres-test",
|
||||
password: "postgres-test",
|
||||
port: 5433
|
||||
},
|
||||
secretMapping: {
|
||||
username: formatSqlUsername("POSTGRES_USERNAME"),
|
||||
password: formatSqlUsername("POSTGRES_PASSWORD")
|
||||
},
|
||||
userCredentials: [
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_1")
|
||||
},
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_2")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.Postgres,
|
||||
name: "Postgres (16) Secret Rotation",
|
||||
dbCredentials: {
|
||||
database: "postgres-test",
|
||||
host: "127.0.0.1",
|
||||
username: "postgres-test",
|
||||
password: "postgres-test",
|
||||
port: 5434
|
||||
},
|
||||
secretMapping: {
|
||||
username: formatSqlUsername("POSTGRES_USERNAME"),
|
||||
password: formatSqlUsername("POSTGRES_PASSWORD")
|
||||
},
|
||||
userCredentials: [
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_1")
|
||||
},
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_2")
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
type: SecretRotationType.Postgres,
|
||||
name: "Postgres (10.12) Secret Rotation",
|
||||
dbCredentials: {
|
||||
database: "postgres-test",
|
||||
host: "127.0.0.1",
|
||||
username: "postgres-test",
|
||||
password: "postgres-test",
|
||||
port: 5435
|
||||
},
|
||||
secretMapping: {
|
||||
username: formatSqlUsername("POSTGRES_USERNAME"),
|
||||
password: formatSqlUsername("POSTGRES_PASSWORD")
|
||||
},
|
||||
userCredentials: [
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_1")
|
||||
},
|
||||
{
|
||||
username: formatSqlUsername("INFISICAL_USER_2")
|
||||
}
|
||||
]
|
||||
}
|
||||
] as {
|
||||
type: SecretRotationType;
|
||||
@@ -382,12 +637,14 @@ describe("Secret Rotations", async () => {
|
||||
|
||||
const createAppConnectionMap = {
|
||||
[SecretRotationType.OracleDb]: createOracleDBAppConnection,
|
||||
[SecretRotationType.MySQL]: createMySQLAppConnection
|
||||
[SecretRotationType.MySQL]: createMySQLAppConnection,
|
||||
[SecretRotationType.Postgres]: createPostgresAppConnection
|
||||
};
|
||||
|
||||
const createRotationMap = {
|
||||
[SecretRotationType.OracleDb]: createOracleDBSecretRotation,
|
||||
[SecretRotationType.MySQL]: createMySQLSecretRotation
|
||||
[SecretRotationType.MySQL]: createMySQLSecretRotation,
|
||||
[SecretRotationType.Postgres]: createPostgresSecretRotation
|
||||
};
|
||||
|
||||
const appConnectionIds: { id: string; type: SecretRotationType }[] = [];
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
# Oracle Databases
|
||||
oracle-db-23.8:
|
||||
image: container-registry.oracle.com/database/free:23.8.0.0
|
||||
container_name: oracle-db-23.8
|
||||
@@ -18,6 +19,26 @@ services:
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
oracle-db-19.19:
|
||||
# Official Oracle 19.19.0.0 - requires docker login container-registry.oracle.com
|
||||
image: container-registry.oracle.com/database/enterprise:19.19.0.0
|
||||
container_name: oracle-db-19.19
|
||||
ports:
|
||||
- "1522:1521"
|
||||
environment:
|
||||
- ORACLE_SID=ORCLCDB
|
||||
- ORACLE_PDB=ORCLPDB1
|
||||
- ORACLE_PWD=OrCAKF112aaSfAdfdA2Ac3@@!
|
||||
- ORACLE_EDITION=enterprise
|
||||
- ORACLE_CHARACTERSET=AL32UTF8
|
||||
volumes:
|
||||
- oracle-data-19.19:/opt/oracle/oradata
|
||||
shm_size: 2gb
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
disable: true
|
||||
|
||||
# MySQL Databases
|
||||
mysql-8.4.6:
|
||||
image: mysql:8.4.6
|
||||
container_name: mysql-8.4.6
|
||||
@@ -38,6 +59,113 @@ services:
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
mysql-8.0.29:
|
||||
image: mysql:8.0.29
|
||||
container_name: mysql-8.0.28
|
||||
ports:
|
||||
- "3307:3306"
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=mysql-test
|
||||
- MYSQL_DATABASE=mysql-test
|
||||
- MYSQL_ROOT_HOST=%
|
||||
- MYSQL_USER=mysql-test
|
||||
- MYSQL_PASSWORD=mysql-test
|
||||
volumes:
|
||||
- mysql-data-8.0.29:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "mysql-test", "-pmysql-test"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
mysql-5.7.31:
|
||||
image: mysql:5.7.31
|
||||
container_name: mysql-5.7.31
|
||||
platform: linux/amd64
|
||||
ports:
|
||||
- "3308:3306"
|
||||
environment:
|
||||
- MYSQL_ROOT_PASSWORD=mysql-test
|
||||
- MYSQL_DATABASE=mysql-test
|
||||
- MYSQL_ROOT_HOST=%
|
||||
- MYSQL_USER=mysql-test
|
||||
- MYSQL_PASSWORD=mysql-test
|
||||
volumes:
|
||||
- mysql-data-5.7.31:/var/lib/mysql
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "mysql-test", "-pmysql-test"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
|
||||
|
||||
# PostgreSQL Databases
|
||||
postgres-17:
|
||||
image: postgres:17
|
||||
platform: linux/amd64
|
||||
container_name: postgres-17
|
||||
ports:
|
||||
- "5433:5432"
|
||||
environment:
|
||||
- POSTGRES_DB=postgres-test
|
||||
- POSTGRES_USER=postgres-test
|
||||
- POSTGRES_PASSWORD=postgres-test
|
||||
volumes:
|
||||
- postgres-data-17:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres-test -d postgres-test"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
postgres-16:
|
||||
image: postgres:16
|
||||
platform: linux/amd64
|
||||
container_name: postgres-16
|
||||
ports:
|
||||
- "5434:5432"
|
||||
environment:
|
||||
- POSTGRES_DB=postgres-test
|
||||
- POSTGRES_USER=postgres-test
|
||||
- POSTGRES_PASSWORD=postgres-test
|
||||
volumes:
|
||||
- postgres-data-16:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres-test -d postgres-test"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
postgres-10.12:
|
||||
image: postgres:10.12
|
||||
platform: linux/amd64
|
||||
container_name: postgres-10.12
|
||||
ports:
|
||||
- "5435:5432"
|
||||
environment:
|
||||
- POSTGRES_DB=postgres-test
|
||||
- POSTGRES_USER=postgres-test
|
||||
- POSTGRES_PASSWORD=postgres-test
|
||||
volumes:
|
||||
- postgres-data-10.12:/var/lib/postgresql/data
|
||||
restart: unless-stopped
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U postgres-test -d postgres-test"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 5
|
||||
|
||||
volumes:
|
||||
oracle-data-23.8:
|
||||
mysql-data-8.4.6:
|
||||
oracle-data-19.19:
|
||||
mysql-data-8.4.6:
|
||||
mysql-data-8.0.29:
|
||||
mysql-data-5.7.31:
|
||||
postgres-data-17:
|
||||
postgres-data-16:
|
||||
postgres-data-10.12:
|
||||
Reference in New Issue
Block a user