mirror of
https://github.com/ershisan99/coolify.git
synced 2025-12-17 04:59:30 +00:00
389 lines
11 KiB
JavaScript
389 lines
11 KiB
JavaScript
const dotEnvExtended = require('dotenv-extended');
|
||
dotEnvExtended.load();
|
||
const crypto = require('crypto');
|
||
const { PrismaClient } = require('@prisma/client');
|
||
const prisma = new PrismaClient();
|
||
const algorithm = 'aes-256-ctr';
|
||
|
||
async function main() {
|
||
// Enable registration for the first user
|
||
const settingsFound = await prisma.setting.findFirst({});
|
||
if (!settingsFound) {
|
||
await prisma.setting.create({
|
||
data: {
|
||
id: '0',
|
||
arch: process.arch
|
||
}
|
||
});
|
||
} else {
|
||
await prisma.setting.update({
|
||
where: {
|
||
id: settingsFound.id
|
||
},
|
||
data: {
|
||
id: '0'
|
||
}
|
||
});
|
||
}
|
||
// Create local docker engine
|
||
const localDocker = await prisma.destinationDocker.findFirst({
|
||
where: { engine: '/var/run/docker.sock' }
|
||
});
|
||
if (!localDocker) {
|
||
await prisma.destinationDocker.create({
|
||
data: {
|
||
engine: '/var/run/docker.sock',
|
||
name: 'Local Docker',
|
||
isCoolifyProxyUsed: true,
|
||
network: 'coolify'
|
||
}
|
||
});
|
||
}
|
||
|
||
// Set auto-update based on env variable
|
||
const isAutoUpdateEnabled = process.env['COOLIFY_AUTO_UPDATE'] === 'true';
|
||
await prisma.setting.update({
|
||
where: {
|
||
id: '0'
|
||
},
|
||
data: {
|
||
isAutoUpdateEnabled
|
||
}
|
||
});
|
||
// Create public github source
|
||
const github = await prisma.gitSource.findFirst({
|
||
where: { htmlUrl: 'https://github.com', forPublic: true }
|
||
});
|
||
if (!github) {
|
||
await prisma.gitSource.create({
|
||
data: {
|
||
apiUrl: 'https://api.github.com',
|
||
htmlUrl: 'https://github.com',
|
||
forPublic: true,
|
||
name: 'Github Public',
|
||
type: 'github'
|
||
}
|
||
});
|
||
}
|
||
// Create public gitlab source
|
||
const gitlab = await prisma.gitSource.findFirst({
|
||
where: { htmlUrl: 'https://gitlab.com', forPublic: true }
|
||
});
|
||
if (!gitlab) {
|
||
await prisma.gitSource.create({
|
||
data: {
|
||
apiUrl: 'https://gitlab.com/api/v4',
|
||
htmlUrl: 'https://gitlab.com',
|
||
forPublic: true,
|
||
name: 'Gitlab Public',
|
||
type: 'gitlab'
|
||
}
|
||
});
|
||
}
|
||
// Set new preview secrets
|
||
const secrets = await prisma.secret.findMany({ where: { isPRMRSecret: false } });
|
||
if (secrets.length > 0) {
|
||
for (const secret of secrets) {
|
||
const previewSecrets = await prisma.secret.findMany({
|
||
where: { applicationId: secret.applicationId, name: secret.name, isPRMRSecret: true }
|
||
});
|
||
if (previewSecrets.length === 0) {
|
||
await prisma.secret.create({ data: { ...secret, id: undefined, isPRMRSecret: true } });
|
||
}
|
||
}
|
||
}
|
||
}
|
||
async function reEncryptSecrets() {
|
||
const { execaCommand } = await import('execa');
|
||
const image = await execaCommand("docker inspect coolify --format '{{ .Config.Image }}'", {
|
||
shell: true
|
||
});
|
||
const version = image.stdout.split(':')[1] ?? null;
|
||
const date = new Date().getTime();
|
||
|
||
let backupfile = `/app/db/prod.db_${date}`;
|
||
if (version) {
|
||
backupfile = `/app/db/prod.db_${version}_${date}`;
|
||
}
|
||
await execaCommand('env | grep "^COOLIFY" | sort > .env', {
|
||
shell: true
|
||
});
|
||
const secretOld = process.env['COOLIFY_SECRET_KEY'];
|
||
let secretNew = process.env['COOLIFY_SECRET_KEY_BETTER'];
|
||
if (!secretNew) {
|
||
console.log('No COOLIFY_SECRET_KEY_BETTER found... Generating new one...');
|
||
const { stdout: newKey } = await execaCommand(
|
||
'openssl rand -base64 1024 | sha256sum | base64 | head -c 32',
|
||
{ shell: true }
|
||
);
|
||
secretNew = newKey;
|
||
}
|
||
if (secretOld !== secretNew) {
|
||
console.log(`Backup database to ${backupfile}.`);
|
||
await execaCommand(`cp /app/db/prod.db ${backupfile}`, { shell: true });
|
||
console.log(
|
||
'Secrets (COOLIFY_SECRET_KEY & COOLIFY_SECRET_KEY_BETTER) are different, so re-encrypting everything...'
|
||
);
|
||
await execaCommand(`sed -i '/COOLIFY_SECRET_KEY=/d' .env`, { shell: true });
|
||
await execaCommand(`sed -i '/COOLIFY_SECRET_KEY_BETTER=/d' .env`, { shell: true });
|
||
await execaCommand(`echo "COOLIFY_SECRET_KEY=${secretNew}" >> .env`, { shell: true });
|
||
await execaCommand('echo "COOLIFY_SECRET_KEY_BETTER=' + secretNew + '" >> .env ', {
|
||
shell: true
|
||
});
|
||
await execaCommand(`echo "COOLIFY_SECRET_KEY_OLD_${date}=${secretOld}" >> .env`, {
|
||
shell: true
|
||
});
|
||
const transactions = [];
|
||
const secrets = await prisma.secret.findMany();
|
||
if (secrets.length > 0) {
|
||
for (const secret of secrets) {
|
||
try {
|
||
const value = decrypt(secret.value, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
transactions.push(
|
||
prisma.secret.update({
|
||
where: { id: secret.id },
|
||
data: { value: newValue }
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const serviceSecrets = await prisma.serviceSecret.findMany();
|
||
if (serviceSecrets.length > 0) {
|
||
for (const secret of serviceSecrets) {
|
||
try {
|
||
const value = decrypt(secret.value, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
transactions.push(
|
||
prisma.serviceSecret.update({
|
||
where: { id: secret.id },
|
||
data: { value: newValue }
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const gitlabApps = await prisma.gitlabApp.findMany();
|
||
if (gitlabApps.length > 0) {
|
||
for (const gitlabApp of gitlabApps) {
|
||
try {
|
||
const value = decrypt(gitlabApp.privateSshKey, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
const appSecret = decrypt(gitlabApp.appSecret, secretOld);
|
||
const newAppSecret = encrypt(appSecret, secretNew);
|
||
transactions.push(
|
||
prisma.gitlabApp.update({
|
||
where: { id: gitlabApp.id },
|
||
data: { privateSshKey: newValue, appSecret: newAppSecret }
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const githubApps = await prisma.githubApp.findMany();
|
||
if (githubApps.length > 0) {
|
||
for (const githubApp of githubApps) {
|
||
try {
|
||
const clientSecret = decrypt(githubApp.clientSecret, secretOld);
|
||
const newClientSecret = encrypt(clientSecret, secretNew);
|
||
const webhookSecret = decrypt(githubApp.webhookSecret, secretOld);
|
||
const newWebhookSecret = encrypt(webhookSecret, secretNew);
|
||
const privateKey = decrypt(githubApp.privateKey, secretOld);
|
||
const newPrivateKey = encrypt(privateKey, secretNew);
|
||
|
||
transactions.push(
|
||
prisma.githubApp.update({
|
||
where: { id: githubApp.id },
|
||
data: {
|
||
clientSecret: newClientSecret,
|
||
webhookSecret: newWebhookSecret,
|
||
privateKey: newPrivateKey
|
||
}
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const databases = await prisma.database.findMany();
|
||
if (databases.length > 0) {
|
||
for (const database of databases) {
|
||
try {
|
||
const dbUserPassword = decrypt(database.dbUserPassword, secretOld);
|
||
const newDbUserPassword = encrypt(dbUserPassword, secretNew);
|
||
const rootUserPassword = decrypt(database.rootUserPassword, secretOld);
|
||
const newRootUserPassword = encrypt(rootUserPassword, secretNew);
|
||
transactions.push(
|
||
prisma.database.update({
|
||
where: { id: database.id },
|
||
data: {
|
||
dbUserPassword: newDbUserPassword,
|
||
rootUserPassword: newRootUserPassword
|
||
}
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const databaseSecrets = await prisma.databaseSecret.findMany();
|
||
if (databaseSecrets.length > 0) {
|
||
for (const databaseSecret of databaseSecrets) {
|
||
try {
|
||
const value = decrypt(databaseSecret.value, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
transactions.push(
|
||
prisma.databaseSecret.update({
|
||
where: { id: databaseSecret.id },
|
||
data: { value: newValue }
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const wordpresses = await prisma.wordpress.findMany();
|
||
if (wordpresses.length > 0) {
|
||
for (const wordpress of wordpresses) {
|
||
try {
|
||
const value = decrypt(wordpress.ftpHostKey, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
const ftpHostKeyPrivate = decrypt(wordpress.ftpHostKeyPrivate, secretOld);
|
||
const newFtpHostKeyPrivate = encrypt(ftpHostKeyPrivate, secretNew);
|
||
let newFtpPassword = undefined;
|
||
if (wordpress.ftpPassword != null) {
|
||
const ftpPassword = decrypt(wordpress.ftpPassword, secretOld);
|
||
newFtpPassword = encrypt(ftpPassword, secretNew);
|
||
}
|
||
|
||
transactions.push(
|
||
prisma.wordpress.update({
|
||
where: { id: wordpress.id },
|
||
data: {
|
||
ftpHostKey: newValue,
|
||
ftpHostKeyPrivate: newFtpHostKeyPrivate,
|
||
ftpPassword: newFtpPassword
|
||
}
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const sshKeys = await prisma.sshKey.findMany();
|
||
if (sshKeys.length > 0) {
|
||
for (const key of sshKeys) {
|
||
try {
|
||
const value = decrypt(key.privateKey, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
transactions.push(
|
||
prisma.sshKey.update({
|
||
where: { id: key.id },
|
||
data: {
|
||
privateKey: newValue
|
||
}
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const dockerRegistries = await prisma.dockerRegistry.findMany();
|
||
if (dockerRegistries.length > 0) {
|
||
for (const registry of dockerRegistries) {
|
||
try {
|
||
const value = decrypt(registry.password, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
transactions.push(
|
||
prisma.dockerRegistry.update({
|
||
where: { id: registry.id },
|
||
data: {
|
||
password: newValue
|
||
}
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
const certificates = await prisma.certificate.findMany();
|
||
if (certificates.length > 0) {
|
||
for (const certificate of certificates) {
|
||
try {
|
||
const value = decrypt(certificate.key, secretOld);
|
||
const newValue = encrypt(value, secretNew);
|
||
transactions.push(
|
||
prisma.certificate.update({
|
||
where: { id: certificate.id },
|
||
data: {
|
||
key: newValue
|
||
}
|
||
})
|
||
);
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
}
|
||
}
|
||
await prisma.$transaction(transactions);
|
||
} else {
|
||
console.log('secrets are the same, so no need to re-encrypt');
|
||
}
|
||
}
|
||
|
||
const encrypt = (text, secret) => {
|
||
if (text && secret) {
|
||
const iv = crypto.randomBytes(16);
|
||
const cipher = crypto.createCipheriv(algorithm, secret, iv);
|
||
const encrypted = Buffer.concat([cipher.update(text.trim()), cipher.final()]);
|
||
return JSON.stringify({
|
||
iv: iv.toString('hex'),
|
||
content: encrypted.toString('hex')
|
||
});
|
||
}
|
||
};
|
||
const decrypt = (hashString, secret) => {
|
||
if (hashString && secret) {
|
||
const hash = JSON.parse(hashString);
|
||
const decipher = crypto.createDecipheriv(algorithm, secret, Buffer.from(hash.iv, 'hex'));
|
||
const decrpyted = Buffer.concat([
|
||
decipher.update(Buffer.from(hash.content, 'hex')),
|
||
decipher.final()
|
||
]);
|
||
if (/<2F>/.test(decrpyted.toString())) {
|
||
throw new Error('Invalid secret. Skipping...');
|
||
}
|
||
return decrpyted.toString();
|
||
}
|
||
};
|
||
|
||
main()
|
||
.catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await prisma.$disconnect();
|
||
});
|
||
reEncryptSecrets()
|
||
.catch((e) => {
|
||
console.error(e);
|
||
process.exit(1);
|
||
})
|
||
.finally(async () => {
|
||
await prisma.$disconnect();
|
||
});
|