UNPKG

container-image-scanner

Version:

๐Ÿšจ EMERGENCY Bitnami Migration Scanner - Critical Timeline Aug 28/Sep 29, 2025. Enterprise scanner for 280+ Bitnami images, 118+ Helm charts with emergency migration automation to AWS alternatives.

482 lines (425 loc) โ€ข 19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.EmergencyMigrationCommand = void 0; const tslib_1 = require("tslib"); const chart_migration_assistant_1 = require("./chart-migration-assistant"); const scanner_1 = require("./scanner"); const chalk_1 = tslib_1.__importDefault(require("chalk")); const fs = tslib_1.__importStar(require("fs/promises")); const path = tslib_1.__importStar(require("path")); class EmergencyMigrationCommand { constructor() { Object.defineProperty(this, "scanner", { enumerable: true, configurable: true, writable: true, value: new scanner_1.ContainerImageScanner() }); Object.defineProperty(this, "migrationAssistant", { enumerable: true, configurable: true, writable: true, value: new chart_migration_assistant_1.ChartMigrationAssistant() }); } async executeEmergencyMigration(options) { console.log(chalk_1.default.red.bold('๐Ÿšจ EMERGENCY BITNAMI MIGRATION - CRITICAL TIMELINE')); console.log(chalk_1.default.yellow('August 28, 2025: Images move to bitnamilegacy (NO UPDATES)')); console.log(chalk_1.default.red('September 29, 2025: Complete catalog deletion')); console.log('='.repeat(80)); const scanResults = await this.loadScanResults(options.input); const timeline = this.scanner.getDaysUntilBreaking(); this.displayCriticalTimeline(timeline); const actionPlan = await this.generateEmergencyActionPlan(scanResults); this.displayEmergencySummary(actionPlan); if (options.dryRun) { console.log(chalk_1.default.cyan('๐Ÿงช DRY RUN MODE - No changes will be applied')); await this.generateEmergencyScripts(actionPlan, options.outputDir); return; } await this.generateEmergencyScripts(actionPlan, options.outputDir); await this.generateMonitoringSetup(options.outputDir); console.log(chalk_1.default.green.bold('โœ… Emergency migration plan generated!')); console.log(chalk_1.default.yellow(`๐Ÿ“‚ Scripts available in: ${options.outputDir}`)); console.log(chalk_1.default.red.bold('โš ๏ธ CRITICAL: Review and execute scripts IMMEDIATELY')); } displayCriticalTimeline(timeline) { console.log('\n' + chalk_1.default.bgRed.white.bold(' CRITICAL TIMELINE ')); if (timeline.legacy <= 0) { console.log(chalk_1.default.red.bold('๐Ÿšจ DEADLINE PASSED: Images already moved to bitnamilegacy!')); console.log(chalk_1.default.red('โš ๏ธ All Bitnami images are now LEGACY - no security updates!')); } else { console.log(chalk_1.default.yellow(`๐Ÿ“… ${timeline.legacy} days until legacy migration (Aug 28, 2025)`)); } if (timeline.deletion <= 0) { console.log(chalk_1.default.red.bold('๐Ÿ’ฅ CATALOG DELETED: All images are now unavailable!')); } else { console.log(chalk_1.default.red(`๐Ÿ—“๏ธ ${timeline.deletion} days until complete deletion (Sep 29, 2025)`)); } if (timeline.emergency) { console.log(chalk_1.default.bgRed.white.bold('๐Ÿšจ EMERGENCY MODE ACTIVATED ๐Ÿšจ')); } console.log(); } displayEmergencySummary(actionPlan) { console.log(chalk_1.default.bgYellow.black.bold(' EMERGENCY ACTION SUMMARY ')); console.log(`๐Ÿ“Š Total Services Affected: ${actionPlan.summary.totalServices}`); console.log(`๐Ÿšจ Critical Systems: ${actionPlan.summary.criticalSystemsAffected}`); console.log(`โฑ๏ธ Estimated Downtime: ${actionPlan.summary.estimatedDowntime}`); console.log(`๐Ÿ”ง Estimated Effort: ${actionPlan.summary.estimatedEffort}`); console.log(chalk_1.default.red.bold(`\n๐Ÿšจ IMMEDIATE ACTIONS (${actionPlan.immediate.length}):`)); actionPlan.immediate.forEach(action => { console.log(chalk_1.default.red(` โ€ข ${action.service} (${action.namespace}) - ${action.rSig}`)); }); console.log(chalk_1.default.yellow.bold(`\nโš ๏ธ URGENT ACTIONS (${actionPlan.urgent.length}):`)); actionPlan.urgent.forEach(action => { console.log(chalk_1.default.yellow(` โ€ข ${action.service} (${action.namespace}) - ${action.rSig}`)); }); console.log(); } async generateEmergencyActionPlan(scanResults) { const timeline = this.scanner.getDaysUntilBreaking(); const immediate = []; const urgent = []; for (const image of scanResults.images || []) { if (!this.scanner.isBitnamiImage(image.image)) continue; const service = this.extractServiceName(image.image); const migrationPlan = this.migrationAssistant.getMigrationPlan(service); const rSig = this.scanner.generateRSig(image); const priority = this.scanner.assessMigrationPriority(image); const action = { service, namespace: image.namespace, workload: image.workload, workloadType: image.workloadType, rSig, currentImage: image.image, temporaryMitigation: { legacyImage: image.image.replace('/bitnami/', '/bitnamilegacy/'), patchCommand: `kubectl patch ${image.workloadType} ${image.workload} -n ${image.namespace} --type='merge' -p='{"spec":{"template":{"spec":{"containers":[{"name":"${image.container}","image":"${image.image.replace('/bitnami/', '/bitnamilegacy/')}"}]}}}}'`, testCommand: `kubectl get ${image.workloadType} ${image.workload} -n ${image.namespace} -o jsonpath='{.spec.template.spec.containers[0].image}'` }, permanentMigration: { targetChart: migrationPlan?.recommendedChart || 'manual-migration-required', migrationScript: migrationPlan?.migrationScript || '# Manual migration required', rollbackScript: migrationPlan?.rollbackScript || '# Manual rollback required', estimatedTime: migrationPlan?.estimatedTime || '4-8 hours' }, businessImpact: this.assessBusinessImpact(image, service), priority: priority }; if (priority === 'IMMEDIATE') { immediate.push(action); } else if (priority === 'URGENT') { urgent.push(action); } } const totalServices = immediate.length + urgent.length; const criticalSystemsAffected = immediate.filter(a => a.businessImpact === 'CRITICAL_SYSTEM').length; const estimatedDowntime = this.calculateDowntime(immediate, urgent); const estimatedEffort = this.calculateEffort(immediate, urgent); return { immediate, urgent, timeline: { daysUntilLegacyMigration: timeline.legacy, daysUntilCatalogDeletion: timeline.deletion, emergencyMode: timeline.emergency }, summary: { totalServices, criticalSystemsAffected, estimatedDowntime, estimatedEffort } }; } async generateEmergencyScripts(actionPlan, outputDir) { await fs.mkdir(outputDir, { recursive: true }); const immediateScript = this.generateImmediateMitigationScript(actionPlan.immediate); await fs.writeFile(path.join(outputDir, '01-immediate-mitigation.sh'), immediateScript); const urgentScript = this.generateUrgentMitigationScript(actionPlan.urgent); await fs.writeFile(path.join(outputDir, '02-urgent-mitigation.sh'), urgentScript); const permanentScript = this.generatePermanentMigrationScript([...actionPlan.immediate, ...actionPlan.urgent]); await fs.writeFile(path.join(outputDir, '03-permanent-migration.sh'), permanentScript); const rollbackScript = this.generateRollbackScript([...actionPlan.immediate, ...actionPlan.urgent]); await fs.writeFile(path.join(outputDir, '04-rollback.sh'), rollbackScript); const testScript = this.generateTestingScript([...actionPlan.immediate, ...actionPlan.urgent]); await fs.writeFile(path.join(outputDir, '05-test-validation.sh'), testScript); const readme = this.generateEmergencyReadme(actionPlan); await fs.writeFile(path.join(outputDir, 'README-EMERGENCY.md'), readme); } generateImmediateMitigationScript(immediateActions) { return `#!/bin/bash # EMERGENCY MITIGATION SCRIPT - EXECUTE IMMEDIATELY # This provides temporary relief using bitnamilegacy images # August 28, 2025 deadline: ${this.scanner.getDaysUntilBreaking().legacy} days remaining set -euo pipefail echo "๐Ÿšจ EMERGENCY BITNAMI MITIGATION - TEMPORARY FIX" echo "โš ๏ธ WARNING: This is a temporary fix using legacy images" echo "โš ๏ธ Legacy images receive NO SECURITY UPDATES" echo "==================================================" # Backup current configurations kubectl get deployments,statefulsets,daemonsets -A -o yaml > emergency-backup-$(date +%Y%m%d-%H%M%S).yaml ${immediateActions.map(action => ` echo "๐Ÿ”ง Patching ${action.service} in ${action.namespace}..." ${action.temporaryMitigation.patchCommand} echo "โœ… Testing ${action.service}..." ${action.temporaryMitigation.testCommand} echo "โฑ๏ธ Waiting for rollout..." kubectl rollout status ${action.workloadType}/${action.workload} -n ${action.namespace} --timeout=300s `).join('')} echo "โœ… Emergency mitigation complete!" echo "โš ๏ธ CRITICAL: Plan permanent migration IMMEDIATELY" echo "โš ๏ธ Legacy images are security risks - no updates!" `; } generateUrgentMitigationScript(urgentActions) { return `#!/bin/bash # URGENT MITIGATION SCRIPT - EXECUTE WITHIN 24-48 HOURS # Temporary migration to bitnamilegacy for urgent services set -euo pipefail echo "โš ๏ธ URGENT BITNAMI MITIGATION" echo "Days until catalog deletion: ${this.scanner.getDaysUntilBreaking().deletion}" echo "==================================" ${urgentActions.map(action => ` echo "๐Ÿ”ง Patching ${action.service} in ${action.namespace}..." ${action.temporaryMitigation.patchCommand} echo "โœ… Validating ${action.service}..." ${action.temporaryMitigation.testCommand} `).join('')} echo "โœ… Urgent mitigation complete!" `; } generatePermanentMigrationScript(allActions) { return `#!/bin/bash # PERMANENT MIGRATION SCRIPT - REPLACE BITNAMI COMPLETELY # This performs the full migration to upstream/alternative charts set -euo pipefail echo "๐Ÿš€ PERMANENT BITNAMI MIGRATION" echo "Moving to upstream/official charts" echo "====================================" ${allActions.map(action => ` echo "๐Ÿ”„ Migrating ${action.service} to ${action.permanentMigration.targetChart}..." echo "Estimated time: ${action.permanentMigration.estimatedTime}" ${action.permanentMigration.migrationScript} echo "โœ… ${action.service} migration complete!" echo "" `).join('')} echo "๐ŸŽ‰ Permanent migration complete!" echo "โœ… All services migrated from Bitnami" `; } generateRollbackScript(allActions) { return `#!/bin/bash # ROLLBACK SCRIPT - USE ONLY IN EMERGENCY # Rolls back to pre-migration state set -euo pipefail echo "๐Ÿ”™ EMERGENCY ROLLBACK" echo "=====================" ${allActions.reverse().map(action => ` echo "๐Ÿ”„ Rolling back ${action.service}..." ${action.permanentMigration.rollbackScript} `).join('')} echo "โœ… Rollback complete!" `; } generateTestingScript(allActions) { return `#!/bin/bash # TESTING AND VALIDATION SCRIPT # Verify all migrations are working correctly set -euo pipefail echo "๐Ÿงช TESTING MIGRATED SERVICES" echo "==============================" FAILED_TESTS=0 ${allActions.map(action => ` echo "Testing ${action.service} in ${action.namespace}..." # Basic pod health check if kubectl get pods -n ${action.namespace} -l app=${action.service} --field-selector=status.phase=Running | grep -q ${action.service}; then echo "โœ… ${action.service} pods running" else echo "โŒ ${action.service} pods not running" ((FAILED_TESTS++)) fi # Service connectivity test if kubectl exec -n ${action.namespace} deployment/${action.workload} -- echo "Health check" >/dev/null 2>&1; then echo "โœ… ${action.service} connectivity OK" else echo "โŒ ${action.service} connectivity failed" ((FAILED_TESTS++)) fi echo "" `).join('')} if [ $FAILED_TESTS -eq 0 ]; then echo "๐ŸŽ‰ ALL TESTS PASSED!" exit 0 else echo "โŒ $FAILED_TESTS tests failed!" exit 1 fi `; } generateEmergencyReadme(actionPlan) { return `# ๐Ÿšจ EMERGENCY BITNAMI MIGRATION PLAN ## CRITICAL TIMELINE - **August 28, 2025**: Images move to bitnamilegacy (${actionPlan.timeline.daysUntilLegacyMigration} days) - **September 29, 2025**: Complete catalog deletion (${actionPlan.timeline.daysUntilCatalogDeletion} days) ## SUMMARY - **Total Services**: ${actionPlan.summary.totalServices} - **Critical Systems**: ${actionPlan.summary.criticalSystemsAffected} - **Estimated Downtime**: ${actionPlan.summary.estimatedDowntime} - **Estimated Effort**: ${actionPlan.summary.estimatedEffort} ## EXECUTION ORDER (CRITICAL!) ### 1. IMMEDIATE ACTIONS (Execute NOW!) \`\`\`bash chmod +x 01-immediate-mitigation.sh ./01-immediate-mitigation.sh \`\`\` **Immediate Services (${actionPlan.immediate.length}):** ${actionPlan.immediate.map(action => `- **${action.service}** (${action.namespace}) - ${action.rSig} - Impact: ${action.businessImpact}`).join('\n')} ### 2. URGENT ACTIONS (Within 24-48 hours) \`\`\`bash chmod +x 02-urgent-mitigation.sh ./02-urgent-mitigation.sh \`\`\` **Urgent Services (${actionPlan.urgent.length}):** ${actionPlan.urgent.map(action => `- **${action.service}** (${action.namespace}) - ${action.rSig}`).join('\n')} ### 3. PERMANENT MIGRATION (Within 1 week) \`\`\`bash chmod +x 03-permanent-migration.sh ./03-permanent-migration.sh \`\`\` ### 4. VALIDATION \`\`\`bash chmod +x 05-test-validation.sh ./05-test-validation.sh \`\`\` ## EMERGENCY PROCEDURES ### If Deployments Fail (ImagePullBackOff) 1. Check image availability: \`docker pull <image-name>\` 2. Apply temporary patch: Use bitnamilegacy repository 3. Monitor for security vulnerabilities (legacy images have no updates) ### Rollback if Needed \`\`\`bash chmod +x 04-rollback.sh ./04-rollback.sh \`\`\` ## MONITORING & ALERTS - Set up continuous monitoring for Bitnami images - Alert when new Bitnami images discovered - Track migration progress ## SUPPORT CONTACTS - **Enterprise Support**: Contact your AWS Account Team - **Emergency Escalation**: AWS Specialist SAs - **Technical Issues**: Use AWS Support channels --- **๐Ÿšจ WARNING: This is an emergency situation. Legacy images receive NO security updates. Permanent migration is CRITICAL!** `; } async generateMonitoringSetup(outputDir) { const monitoringScript = `#!/bin/bash # CONTINUOUS MONITORING SETUP - 12-HOUR RESCANS # Monitors for new Bitnami images and migration progress set -euo pipefail echo "๐Ÿ” SETTING UP CONTINUOUS BITNAMI MONITORING" echo "==============================================" # Create monitoring namespace kubectl create namespace bitnami-monitoring --dry-run=client -o yaml | kubectl apply -f - # Create CronJob for 12-hour rescans kubectl apply -f - <<EOF apiVersion: batch/v1 kind: CronJob metadata: name: bitnami-scanner namespace: bitnami-monitoring spec: schedule: "0 */12 * * *" # Every 12 hours jobTemplate: spec: template: spec: containers: - name: scanner image: container-image-scanner:latest command: ["/bin/sh"] args: - -c - | echo "๐Ÿ” Scanning for Bitnami images..." container-image-scanner analyze --org-scan --critical-only --output /tmp/scan-results.json # Check for new Bitnami detections NEW_COUNT=\$(jq '.summary.criticalRisk' /tmp/scan-results.json) if [ "\$NEW_COUNT" -gt 0 ]; then echo "๐Ÿšจ ALERT: \$NEW_COUNT new critical Bitnami images detected!" # Send alert (webhook, Slack, email, etc.) fi restartPolicy: OnFailure EOF echo "โœ… Monitoring setup complete!" echo "๐Ÿ“Š Scans will run every 12 hours" echo "๐Ÿ”” Configure alerts in your monitoring system" `; await fs.writeFile(path.join(outputDir, '06-setup-monitoring.sh'), monitoringScript); } async loadScanResults(inputFile) { const data = await fs.readFile(inputFile, 'utf-8'); return JSON.parse(data); } extractServiceName(imageName) { const parts = imageName.split('/'); const imageWithTag = parts[parts.length - 1]; if (!imageWithTag) return 'unknown'; const serviceName = imageWithTag.split(':')[0]; if (!serviceName) return 'unknown'; return serviceName.toLowerCase(); } assessBusinessImpact(image, service) { const criticalServices = ['mysql', 'postgresql', 'mongodb', 'redis', 'elasticsearch', 'kafka']; const isProduction = image.namespace === 'production' || image.namespace.includes('prod'); const hasHighReplicas = image.replicas > 1; if (criticalServices.includes(service) && isProduction) { return 'CRITICAL_SYSTEM'; } else if (hasHighReplicas && isProduction) { return 'HIGH_AVAILABILITY'; } else if (isProduction) { return 'BUSINESS_FUNCTION'; } else { return 'DEV_TEST'; } } calculateDowntime(immediate, urgent) { const criticalCount = immediate.filter(a => a.businessImpact === 'CRITICAL_SYSTEM').length; const estimatedMinutes = (criticalCount * 15) + (immediate.length * 5) + (urgent.length * 3); if (estimatedMinutes < 60) { return `${estimatedMinutes} minutes`; } else { const hours = Math.floor(estimatedMinutes / 60); const mins = estimatedMinutes % 60; return `${hours}h ${mins}m`; } } calculateEffort(immediate, urgent) { const immediateEffort = immediate.length * 2; const urgentEffort = urgent.length * 1; const totalHours = immediateEffort + urgentEffort; if (totalHours < 24) { return `${totalHours} hours`; } else { const days = Math.floor(totalHours / 8); const hours = totalHours % 8; return `${days} days, ${hours} hours`; } } } exports.EmergencyMigrationCommand = EmergencyMigrationCommand;