// offsite-backup/index.ts // // Offsite backup target (CONTRACT_004 §4.1; ADR-004 "second self-hosted location"): // the `olsitec-foundation` bucket + a SCOPED service account on the home Synology // DS923+ MinIO. The Helsinki foundation VM pushes backup bundles here with the // scoped creds (never the MinIO root). Modeled on olsicloud4 modules/minio. // // Admin creds via ENV (export MINIO_BACKUP_USER/MINIO_BACKUP_PASSWORD from pass), // never committed. Endpoint is the PUBLIC one (reachable from Hetzner). import * as pulumi from "@pulumi/pulumi"; import * as minio from "@pulumi/minio"; const adminUser = process.env.MINIO_BACKUP_USER; const adminPass = process.env.MINIO_BACKUP_PASSWORD; if (!adminUser || !adminPass) { throw new Error( 'MINIO_BACKUP_USER/MINIO_BACKUP_PASSWORD env required: export from pass olsicloud4/MINIO_BACKUP_*', ); } const endpoint = "minio.wob.olsitec.de:19000"; // public S3 API (-> 62.176.248.112) const bucketName = "olsitec-foundation"; const provider = new minio.Provider("home-minio", { minioServer: endpoint, minioUser: adminUser, minioPassword: adminPass, minioSsl: true, }); const popts = { provider }; const bucket = new minio.S3Bucket( "foundation-backup-bucket", { bucket: bucketName, acl: "private", forceDestroy: false, // holds backups — never silently wipe on destroy }, popts, ); // Scoped policy: full object ops on this bucket only. const policy = new minio.IamPolicy( "foundation-backup-policy", { name: "olsitec-foundation-backup", policy: bucket.bucket.apply((b) => JSON.stringify({ Version: "2012-10-17", Statement: [ { Effect: "Allow", Action: ["s3:ListBucket", "s3:GetBucketLocation", "s3:ListBucketMultipartUploads"], Resource: [`arn:aws:s3:::${b}`], }, { Effect: "Allow", Action: ["s3:PutObject", "s3:GetObject", "s3:DeleteObject", "s3:ListMultipartUploadParts", "s3:AbortMultipartUpload"], Resource: [`arn:aws:s3:::${b}/*`], }, ], }), ), }, popts, ); const user = new minio.IamUser("foundation-backup-user", { name: "olsitec-foundation-backup" }, popts); new minio.IamUserPolicyAttachment( "foundation-backup-user-policy", { userName: user.name, policyName: policy.name }, { ...popts, dependsOn: [bucket, policy, user] }, ); // Service account = the scoped access/secret key pair the VM actually uses. const sa = new minio.IamServiceAccount( "foundation-backup-sa", { targetUser: user.name }, { ...popts, dependsOn: [user] }, ); export const offsiteEndpoint = `https://${endpoint}`; export const offsiteBucket = bucket.bucket; export const offsiteAccessKey = pulumi.secret(sa.accessKey); export const offsiteSecretKey = pulumi.secret(sa.secretKey);