CrowdStrike 블루스크린 (2024) — 보안 업데이트가 전 세계 윈도우를 죽이다

CrowdStrike 블루스크린 (2024) — 보안 업데이트가 전 세계 윈도우를 죽이다

2024년 7월 19일, 커널 레벨 보안 드라이버의 잘못된 업데이트가 850만 대의 윈도우를 블루스크린으로 만든 사건


2024년 7월 19일 금요일 새벽. 전 세계에서 동시에 윈도우 컴퓨터들이 블루스크린(BSOD)을 띄우며 죽기 시작했음. 재부팅해도 다시 블루스크린. 무한 블루스크린 루프.

항공사 체크인 시스템이 죽었고, 병원 시스템이 멈췄고, 은행 ATM이 안 됐고, 뉴스 방송국 화면이 블루스크린으로 나갔음. 전 세계적으로 약 850만 대의 윈도우 디바이스가 영향을 받았다.

원인? CrowdStrike Falcon이라는 보안 소프트웨어의 자동 업데이트.

사건 요약

보안 기업 CrowdStrike의 엔드포인트 보호 소프트웨어 Falcon Sensor에 배포된 채널 파일(Channel File 291) 업데이트에 결함이 있었음. 이 파일이 커널 레벨 드라이버에서 처리될 때 잘못된 메모리 접근이 발생하여 윈도우가 BSOD(Blue Screen of Death)를 띄우고 재부팅됨. 재부팅 시 같은 파일을 다시 로드하므로 무한 블루스크린 루프에 빠짐. 전 세계적으로 약 850만 대의 윈도우 디바이스에 영향.


CrowdStrike Falcon이 뭔데

CrowdStrike는 사이버보안 기업으로, 주력 제품인 Falcon은 EDR(Endpoint Detection and Response) 솔루션임. 간단히 말해 "기업용 고급 백신" 같은 거.

근데 일반 백신과 다른 핵심 차이점이 있음: 커널 레벨에서 동작한다는 것.

일반 앱:
┌─────────────────┐
│  사용자 공간     │  ← 일반 앱이 사는 곳 (크롬, 워드 등)
│  (User Space)    │     앱이 죽어도 OS는 살아남
├─────────────────┤
│  커널 공간       │  ← OS의 핵심 (메모리 관리, 파일시스템 등)
│  (Kernel Space)  │     여기서 문제가 생기면 OS 전체가 죽음
└─────────────────┘

CrowdStrike Falcon:
┌─────────────────┐
│  사용자 공간     │  ← Falcon 서비스 (UI, 통신 등)
├─────────────────┤
│  커널 공간       │  ← Falcon 커널 드라이버 (csagent.sys)
│                  │     여기서 시스템 호출을 가로채서 위협 감지
│                  │     여기서 죽으면 = BSOD
└─────────────────┘
왜 커널에서 돌아야 하는가

보안 소프트웨어가 커널 레벨에서 동작하는 이유: 악성 코드도 커널 레벨에서 동작할 수 있기 때문. 사용자 공간에서만 동작하는 보안 소프트웨어는 커널 레벨 악성 코드(루트킷)를 감지하거나 차단할 수 없음. 그래서 보안 소프트웨어도 커널에 들어가야 하는데, 그 대가는 "실수하면 OS를 죽인다"는 것.


Channel File 291: 무엇이 문제였나

CrowdStrike의 업데이트 구조

Falcon은 두 가지 유형의 업데이트를 사용함:

typescript
// CrowdStrike 업데이트 구조 (개념적)

interface FalconUpdateTypes {
  // 1. 센서 업데이트 (드라이버 코드 자체)
  sensorUpdate: {
    type: 'binary';
    frequency: '주기적 (수주 간격)';
    process: '테스트 → 카나리 → 단계적 배포';
    requiresReboot: true;
    // 이건 충분한 테스트를 거침
  };

  // 2. 채널 파일 업데이트 (탐지 규칙/설정)
  channelFileUpdate: {
    type: 'configuration';
    frequency: '수시 (하루에도 여러 번)';
    process: '자동 배포'; // ← 여기가 문제
    requiresReboot: false;
    // 새로운 위협에 빠르게 대응하기 위해 빈번하게 배포
    // 충분한 테스트 없이 배포될 수 있는 구조
  };
}
채널 파일의 딜레마

채널 파일은 "새로운 악성 코드 시그니처"를 빠르게 배포하기 위한 메커니즘. 새로운 제로데이가 발견되면 분 단위로 대응해야 하므로, 무거운 테스트/배포 프로세스를 거칠 수 없었음. 속도와 안전성 사이의 트레이드오프에서 속도를 선택한 결과가 이 사건.

Channel File 291의 구체적 문제

2024년 7월 19일 04:09 UTC에 배포된 Channel File 291에 결함이 있었음.

typescript
// 문제를 단순화하여 설명

// Channel File은 탐지 규칙을 담고 있음
// 커널 드라이버(csagent.sys)가 이 파일을 파싱하여 규칙을 적용

// 문제: 파일의 데이터가 드라이버의 예상과 맞지 않았음
interface ChannelFileData {
  templateType: number;      // 템플릿 유형
  templateInstances: Array<{
    // 탐지 규칙의 파라미터들
    fields: any[];
  }>;
}

// Channel File 291의 데이터
const problematicFile: ChannelFileData = {
  templateType: 291,
  templateInstances: [
    {
      // 드라이버가 기대하는 필드 수보다 적은(또는 잘못된) 데이터
      // 커널 드라이버가 이 데이터를 읽으려고 할 때
      // 범위 밖 메모리 접근(out-of-bounds read) 발생
      fields: [/* 잘못된 데이터 */],
    },
  ],
};

// 커널에서의 메모리 접근 위반 = BSOD
// 사용자 공간이었으면: "프로세스가 비정상 종료되었습니다"로 끝남
// 커널 공간이면: 전체 OS 크래시 (BSOD)
실제 블루스크린 메시지:

STOP: 0x00000050 (PAGE_FAULT_IN_NONPAGED_AREA)

What failed: csagent.sys

이 에러는 커널 드라이버(csagent.sys)가
존재하지 않는 메모리 주소에 접근하려고 했다는 의미

그리고 윈도우가 재부팅되면?
→ csagent.sys가 다시 로드됨
→ Channel File 291을 다시 읽음
→ 같은 메모리 접근 위반
→ 다시 BSOD
→ 무한 루프
무한 블루스크린 루프의 공포

가장 치명적이었던 점은 자동 복구가 불가능했다는 것. 윈도우가 부팅될 때 CrowdStrike 드라이버가 자동으로 로드되고, 결함 있는 채널 파일을 읽어서 다시 크래시. 안전 모드(Safe Mode)로 부팅해서 해당 파일을 수동 삭제하는 것만이 복구 방법이었음. 서버 1대면 모르겠는데, 수천 대는?


피해 규모: 세상이 멈추다

타임라인

2024-07-19 (UTC)

04:09  Channel File 291 배포 시작
       전 세계 CrowdStrike Falcon 설치 기기로 자동 배포

04:09~ 블루스크린 보고 시작
       영향 받는 기기: Windows 10/11 (Windows Server 포함)
       영향 안 받는 기기: Mac, Linux

05:27  CrowdStrike가 문제를 인지하고 수정된 채널 파일 배포
       하지만 이미 블루스크린에 빠진 기기는 네트워크에 접속 불가
       → 수정 파일을 받을 수 없음
       → 수동 복구만 가능

       문제 파일 배포 시간: 약 78분
       이 78분 동안 온라인이었던 모든 Falcon 윈도우 기기가 영향

영향 받은 산업

항공업
├── Delta Air Lines: 수천 편 결항, 손실 $5.5억 추정
├── United Airlines: 운항 지연/결항
├── American Airlines: 체크인 시스템 마비
├── 전 세계 공항 체크인 카운터에 블루스크린
└── 수동 탑승권 발급 (종이에 손으로 쓰기)

의료
├── 병원 전자 의료 기록(EMR) 시스템 다운
├── 예약/접수 시스템 마비
├── 일부 수술 연기
└── 911 (119) 시스템 영향 받은 지역 있음

금융
├── 은행 ATM 일부 사용 불가
├── 거래 시스템 지연
├── 주식 거래소 영향 (제한적)
└── 결제 시스템 장애

방송
├── Sky News: 생방송 중 블루스크린 송출
├── 여러 방송국 송출 장애
└── 방송 사고 화면이 밈으로 퍼짐

소매/물류
├── POS(판매시점관리) 시스템 다운
├── 물류 추적 시스템 장애
└── 배달 서비스 지연

정부
├── 미국 911 서비스 일부 영향
├── 각국 정부 시스템 장애
└── 법원 전자 시스템 다운
숫자로 보는 피해
  • 영향 받은 윈도우 디바이스: 약 850만 대 (전체 윈도우의 약 1% 미만이지만)
  • 하지만 이 1%가 기업/정부의 핵심 시스템이었음
  • 경제적 손실 추정: $54억 (Fortune 500 기업 기준)
  • Delta Air Lines 단독 손실: $5.5억 (CrowdStrike를 상대로 소송)
  • CrowdStrike 주가: 사건 후 한때 35% 이상 하락

복구: 왜 이렇게 오래 걸렸나

이 사건의 가장 고통스러운 부분. 자동 복구가 불가능했기 때문에 모든 기기를 수동으로 복구해야 했음.

복구 절차

공식 복구 절차:

1. 안전 모드로 부팅
   - 부팅 시 F8 또는 Shift+재시작
   - BitLocker 암호화된 경우 → 복구 키 필요! (이것도 큰 문제)

2. C:\Windows\System32\drivers\CrowdStrike 폴더로 이동

3. "C-00000291*.sys" 파일을 찾아서 삭제

4. 정상 모드로 재부팅

5. CrowdStrike가 수정된 채널 파일을 자동으로 다운로드
typescript
// 이 절차가 왜 대규모에서 악몽이 되는지

interface RecoveryChallenge {
  problem: string;
  scale: string;
  painLevel: number; // 1-10
}

const challenges: RecoveryChallenge[] = [
  {
    problem: '물리적 접근 필요',
    scale: '안전 모드 부팅은 원격으로 못 함. 서버실에 직접 가야 함.',
    painLevel: 9,
  },
  {
    problem: 'BitLocker 복구 키',
    scale:
      '디스크 암호화된 기기는 안전 모드 진입 시 복구 키 필요. ' +
      '복구 키가 Active Directory에 있는데 AD 서버도 블루스크린이면? ' +
      '닭이 먼저냐 달걀이 먼저냐.',
    painLevel: 10,
  },
  {
    problem: '기기 수',
    scale:
      '대기업은 수만 대의 기기를 보유. ' +
      '각각 수동으로 안전 모드 부팅 → 파일 삭제 → 재부팅. ' +
      'IT 팀 전원이 며칠간 야근.',
    painLevel: 10,
  },
  {
    problem: '리모트 사이트',
    scale:
      '지방 지점, 해외 사무소 등 IT 인력이 없는 곳의 기기. ' +
      '비기술 직원에게 전화로 안전 모드 부팅 방법을 안내해야 함.',
    painLevel: 8,
  },
  {
    problem: '클라우드 VM',
    scale:
      'Azure/AWS의 윈도우 VM도 영향. ' +
      '콘솔 접속으로 복구 가능하지만, VM이 수천 개면...',
    painLevel: 7,
  },
];
BitLocker 지옥

BitLocker로 디스크 암호화된 기업용 PC가 특히 문제였음. 안전 모드로 부팅하려면 BitLocker 복구 키가 필요한데, 이 키가 보통 Active Directory나 Azure AD에 저장되어 있음. 근데 그 서버도 CrowdStrike가 깔려 있어서 블루스크린이면? 복구 키를 조회할 수가 없는 교착 상태. 일부 기업은 복구에 수일이 걸렸다.


기술적 분석: 왜 이런 일이 가능했나

1. 커널 드라이버의 입력 검증 부재

typescript
// 핵심 문제: 커널 드라이버가 채널 파일 데이터를 충분히 검증하지 않았음

// 안 좋은 접근 (2024년 7월 19일 당시)
function processChannelFile(fileData: Buffer): void {
  // 파일에서 템플릿 인스턴스 읽기
  const instanceCount = fileData.readUInt32LE(0);

  for (let i = 0; i < instanceCount; i++) {
    const offset = calculateOffset(i);
    // 위험: offset이 유효한 범위인지 확인하지 않음
    // fileData의 범위를 벗어나는 접근 가능
    const fieldCount = fileData.readUInt32LE(offset);

    for (let j = 0; j < fieldCount; j++) {
      const fieldOffset = calculateFieldOffset(offset, j);
      // 위험: fieldOffset도 범위 확인 없음
      const value = fileData.readUInt32LE(fieldOffset);
      // → 여기서 out-of-bounds read 발생 → BSOD
    }
  }
}

// 안전한 접근
function processChannelFileSafe(fileData: Buffer): boolean {
  // 1. 파일 최소 크기 확인
  if (fileData.length < MINIMUM_FILE_SIZE) {
    logError('채널 파일 크기가 최소 요구사항 미달');
    return false;
  }

  // 2. 체크섬/해시 검증
  const expectedHash = fileData.slice(-32);
  const actualHash = computeHash(fileData.slice(0, -32));
  if (!actualHash.equals(expectedHash)) {
    logError('채널 파일 해시 불일치');
    return false;
  }

  // 3. 모든 읽기 연산에 경계 검사
  const instanceCount = safeReadUInt32(fileData, 0);
  if (instanceCount === null || instanceCount > MAX_INSTANCES) {
    logError('인스턴스 수가 비정상');
    return false;
  }

  for (let i = 0; i < instanceCount; i++) {
    const offset = calculateOffset(i);
    if (offset + 4 > fileData.length) {
      logError(`인스턴스 ${i}: 오프셋 범위 초과`);
      return false; // 이 파일을 무시하고 이전 규칙 유지
    }
    // ... 모든 접근에 대해 경계 검사
  }

  return true;
}

2. 커널 vs. 사용자 공간

typescript
// 사용자 공간에서의 메모리 접근 위반
function userSpaceCrash(): void {
  const arr = new Array(10);
  // 범위 밖 접근
  const value = arr[99999]; // JavaScript: undefined 반환
  // C/C++: segmentation fault → 프로세스만 죽음
  // OS는 멀쩡함
}

// 커널 공간에서의 메모리 접근 위반
function kernelSpaceCrash(): void {
  // 커널 드라이버에서 같은 실수를 하면:
  // 잘못된 메모리 주소 접근
  // → 커널 패닉 (Linux) 또는 BSOD (Windows)
  // → 전체 운영체제 크래시
  // → 모든 프로세스, 모든 데이터에 영향
}

// 이것이 커널 드라이버 개발이 위험한 이유:
// - 사용자 공간: 실수하면 앱 하나가 죽음
// - 커널 공간: 실수하면 OS 전체가 죽음
// - CrowdStrike의 csagent.sys는 커널 공간에서 동작
// - 채널 파일 데이터를 파싱하다가 메모리 접근 위반
// - → 전체 윈도우 BSOD

3. 배포 프로세스의 문제

typescript
// CrowdStrike의 채널 파일 배포 프로세스 (사건 당시 추정)

interface ChannelFileDeployment {
  // 센서 업데이트: 테스트 → 카나리 → 단계적 배포
  sensorUpdate: {
    testing: 'extensive';
    canary: true;
    stagedRollout: true;
    rollbackCapability: true;
  };

  // 채널 파일 업데이트: ???
  channelFileUpdate: {
    testing: 'automated-only';  // 자동 테스트만
    canary: false;               // 카나리 없음!
    stagedRollout: false;        // 단계적 배포 없음!
    rollbackCapability: false;   // 이미 BSOD니까 롤백 불가
  };
}

// 만약 카나리 배포가 있었다면:
// 1% 기기에 먼저 배포 → 블루스크린 발생 감지 → 즉시 배포 중단
// 영향: 전체의 1%만 복구하면 됨
// 실제: 100% 동시 배포 → 850만 대 전부 사망

더 깊은 이슈: 커널 접근 권한 논쟁

이 사건은 "보안 소프트웨어가 커널 레벨에 접근해야 하는가?"라는 근본적인 논쟁을 불러일으킴.

Microsoft의 입장

typescript
// Windows의 커널 접근 정책 변화

interface KernelAccessPolicy {
  era: string;
  policy: string;
  thirdPartyKernelAccess: boolean;
  context: string;
}

const policies: KernelAccessPolicy[] = [
  {
    era: 'Windows XP 이전',
    policy: '커널 드라이버 자유 설치',
    thirdPartyKernelAccess: true,
    context: '보안 위협이 상대적으로 적었던 시대',
  },
  {
    era: 'Windows Vista',
    policy: '드라이버 서명 필수 (64비트)',
    thirdPartyKernelAccess: true,
    context: '악성 드라이버 방지 시작',
  },
  {
    era: '2006년 EU 합의',
    policy: 'Microsoft가 서드파티에 커널 접근 보장',
    thirdPartyKernelAccess: true,
    context:
      'EU 반독점 규제로 인해 Microsoft가 보안 벤더에게 ' +
      '커널 접근을 보장하기로 합의. ' +
      '이로 인해 CrowdStrike 같은 보안 소프트웨어가 커널에 진입 가능.',
  },
  {
    era: 'CrowdStrike 사건 이후 논의',
    policy: '커널 접근 제한 검토',
    thirdPartyKernelAccess: true, // 아직 변경 안 됨
    context:
      'Microsoft가 보안 벤더의 커널 접근을 제한하고 ' +
      '대신 사용자 공간 API를 제공하는 방안 검토 중',
  },
];
Apple의 접근: 커널 확장 제거

Apple은 macOS Catalina(2019)부터 서드파티 커널 확장(kext)을 단계적으로 제거하고, System Extension Framework(사용자 공간)으로 대체함. 보안 소프트웨어도 커널이 아닌 사용자 공간에서 동작하도록 강제. 이 덕분에 CrowdStrike 사건이 Mac에서는 발생하지 않았음 (Mac 버전은 커널 드라이버가 아닌 사용자 공간 확장 사용).

typescript
// 대안: 사용자 공간에서의 보안 모니터링

interface SecurityArchitectureOptions {
  option: string;
  pros: string[];
  cons: string[];
}

const options: SecurityArchitectureOptions[] = [
  {
    option: '커널 드라이버 (현재 CrowdStrike)',
    pros: [
      '모든 시스템 이벤트 가로채기 가능',
      '커널 레벨 악성코드(루트킷) 탐지 가능',
      '가장 강력한 보호',
    ],
    cons: [
      '버그 시 OS 전체 크래시',
      '업데이트 리스크가 매우 높음',
      '한 벤더의 실수가 전체 시스템에 영향',
    ],
  },
  {
    option: '사용자 공간 에이전트 + OS 제공 API',
    pros: [
      '버그 시 에이전트만 죽음 (OS 무사)',
      '업데이트가 안전',
      '자동 복구 가능',
    ],
    cons: [
      'OS API가 충분하지 않으면 탐지 능력 제한',
      '커널 레벨 악성코드 탐지 어려움',
      'OS 벤더에 의존',
    ],
  },
  {
    option: '하이퍼바이저 기반 보안',
    pros: [
      '커널보다 더 낮은 레벨에서 보호',
      '커널 자체를 모니터링 가능',
    ],
    cons: [
      '성능 오버헤드',
      '구현 복잡도',
      '모든 환경에서 사용 불가',
    ],
  },
];

CrowdStrike의 대응과 개선

즉각적 대응

CrowdStrike CEO George Kurtz의 대응:

1. 사건 당일 공식 성명 발표
   - "이것은 사이버 공격이 아니라 업데이트 결함"
   - 복구 방법 안내

2. 24시간 이내 기술적 세부사항 공개
   - Channel File 291의 문제 설명
   - 자동 복구 도구 배포 (USB 부팅 가능)

3. 수일 내 상세 포스트모템 공개
   - 근본 원인 분석
   - 개선 계획 발표

발표된 개선 사항

typescript
// CrowdStrike가 발표한 재발 방지 조치

interface PreventionMeasures {
  // 1. 채널 파일 테스트 강화
  testing: {
    // 기존: 자동 테스트만
    // 개선: 단계별 테스트 추가
    unitTests: true;
    integrationTests: true;
    stressTests: true;
    // 다양한 윈도우 버전/환경에서 테스트
    matrixTesting: true;
  };

  // 2. 카나리 배포 도입
  deployment: {
    // 기존: 전체 동시 배포
    // 개선: 단계적 배포
    canaryPercentage: 1;   // 1%부터 시작
    observationPeriod: '1h'; // 1시간 관찰
    autoRollback: true;     // 이상 감지 시 자동 롤백
  };

  // 3. 커널 드라이버 입력 검증 강화
  inputValidation: {
    boundaryChecks: true;      // 모든 메모리 접근에 경계 검사
    nullPointerChecks: true;   // null 포인터 검사
    dataIntegrityChecks: true; // 채널 파일 무결성 검증
  };

  // 4. 복구 메커니즘 개선
  recovery: {
    // 기존: 수동 안전 모드 부팅만 가능
    // 개선: 자동 롤백 메커니즘
    autoRollbackOnCrash: true;
    maxRetries: 3; // 3번 크래시하면 이전 채널 파일로 롤백
  };

  // 5. 서드파티 감사
  audit: {
    externalCodeReview: true;
    independentSecurityAudit: true;
  };
}

Staged Rollout: 왜 필수인가

이 사건의 가장 큰 교훈. 채널 파일이든 코드든 설정이든, 전 세계 동시 배포는 금지.

typescript
// Staged Rollout 구현 예시

interface RolloutStage {
  name: string;
  percentage: number;
  duration: string;
  healthChecks: string[];
  rollbackConditions: Record<string, number>;
}

class StagedRollout {
  private stages: RolloutStage[] = [
    {
      name: 'internal-dogfood',
      percentage: 0.01,  // 내부 기기만
      duration: '30m',
      healthChecks: ['bsod_rate', 'cpu_usage', 'crash_reports'],
      rollbackConditions: {
        bsod_rate: 0,      // BSOD 1건이라도 발생하면 즉시 롤백
        crash_rate: 0.001,  // 0.1% 이상 크래시하면 롤백
      },
    },
    {
      name: 'canary',
      percentage: 1,
      duration: '1h',
      healthChecks: ['bsod_rate', 'cpu_usage', 'crash_reports', 'customer_tickets'],
      rollbackConditions: {
        bsod_rate: 0,
        crash_rate: 0.0001,
      },
    },
    {
      name: 'early-adopters',
      percentage: 5,
      duration: '2h',
      healthChecks: ['bsod_rate', 'cpu_usage', 'crash_reports', 'customer_tickets'],
      rollbackConditions: {
        bsod_rate: 0,
        crash_rate: 0.0001,
      },
    },
    {
      name: 'broad',
      percentage: 25,
      duration: '4h',
      healthChecks: ['bsod_rate', 'cpu_usage', 'crash_reports'],
      rollbackConditions: {
        bsod_rate: 0,
        crash_rate: 0.00001,
      },
    },
    {
      name: 'global',
      percentage: 100,
      duration: 'ongoing',
      healthChecks: ['bsod_rate', 'cpu_usage', 'crash_reports'],
      rollbackConditions: {
        bsod_rate: 0,
        crash_rate: 0.00001,
      },
    },
  ];

  async deploy(channelFile: Buffer): Promise<void> {
    for (const stage of this.stages) {
      console.log(`Stage: ${stage.name} (${stage.percentage}%)`);

      await this.deployToPercentage(channelFile, stage.percentage);
      await this.waitAndMonitor(stage.duration, stage.healthChecks);

      const metrics = await this.getMetrics(stage.healthChecks);

      for (const [metric, threshold] of Object.entries(stage.rollbackConditions)) {
        if (metrics[metric] > threshold) {
          console.error(`${metric} 초과! 즉시 롤백.`);
          await this.rollback();
          return;
        }
      }

      console.log(`Stage ${stage.name} 통과. 다음 단계로.`);
    }
  }
}

교훈 정리

CrowdStrike 블루스크린 사건에서 배우는 교훈

1. 커널 레벨 소프트웨어는 극도의 주의가 필요하다

  • 사용자 공간에서의 버그는 프로세스 하나를 죽임
  • 커널 공간에서의 버그는 OS 전체를 죽임
  • 커널 드라이버의 모든 입력은 철저하게 검증해야 함

2. 설정/규칙 업데이트도 코드 업데이트와 동일하게 취급하라

  • 채널 파일은 "그냥 설정 파일"이 아니라 커널에서 실행되는 코드와 동급
  • Cloudflare 정규식 사건과 동일한 교훈의 반복

3. Staged Rollout은 선택이 아니라 필수다

  • 전 세계 동시 배포는 장애 시 전 세계 동시 사망을 의미
  • 1% -> 5% -> 25% -> 100% 순서로 점진적 배포
  • 각 단계에서 건강 지표 모니터링 및 자동 롤백

4. 자동 복구 메커니즘이 있어야 한다

  • 블루스크린 무한 루프에 빠지면 자동 복구 불가
  • N회 연속 크래시 시 이전 안정 버전으로 자동 롤백하는 메커니즘 필요

5. 복구 도구는 장애 대상과 독립적이어야 한다

  • BitLocker 복구 키가 AD에, AD가 CrowdStrike 때문에 죽어 있는 교착 상태
  • 복구에 필요한 도구가 장애의 영향권에 있으면 안 됨

마치며

CrowdStrike 사건은 2024년 최대의 IT 장애이자, 단일 소프트웨어 업데이트가 일으킨 가장 광범위한 장애로 기록됨. 850만 대의 윈도우 디바이스, 수십억 달러의 손실.

가장 뼈아픈 점은, 이 모든 것이 "예방 가능했다"는 것. 카나리 배포, 입력 검증, 자동 롤백 — 이미 업계에서 수년 전부터 알려진 베스트 프랙티스들. AWS S3 장애(2017)에서 배운 교훈, Cloudflare 정규식 장애(2019)에서 배운 교훈이 여기서도 그대로 적용됨.

결국 반복되는 패턴임. "전 세계에 동시에 배포하지 마라". 이 한 문장을 지키지 않아서 반복되는 대형 장애들. 우리가 이 시리즈를 읽는 이유도 같은 실수를 반복하지 않기 위해서다.

다음 편에서는 대기업의 화려한 장애가 아닌, 우리 주변에서 매일 일어나는 소규모 장애들을 다룬다. DB 연결 풀 고갈, 메모리 릭, 무한 루프 — 규모는 작지만 당사자에게는 CrowdStrike 못지않은 지옥인 사건들.