스케치는 WireGuard 개인 키를 문자 배열에 넣고 터널은 동작한다. 그래서 이 질문이 계속 나온다. 정말로 내려야 하는 결정은 «이것이 안전한가»가 아니라 «세 조건 중 어느 것이 이 배포에 해당하는가»이다. 조건은 이렇다. 누가 기기를 물리적으로 쥘 수 있는가, 당신의 부품에서 플래시를 읽으면 무엇이 나오는가, 플릿이 키 하나를 공유하는가 아니면 각자 자기 키를 들고 다니는가. 이에 답하면 설계는 따라온다. 각 조건은 키가 유출될 수 있다는 사실이 아니라 유출됐을 때의 비용을 바꾸기 때문이다.

키가 허용하는 것

WireGuard는 공개 키로 피어를 인증한다. 서버는 각 피어의 공개 키를 하나의 주소와 AllowedIPs 목록에 매핑한다. 그래서 개인 키가 있어야 클라이언트가 그 주소에서 트래픽을 보낼 수 있다.

같은 항목이 기기가 도달할 수 있는 목적지를 정한다. 따라서 키가 유출되면 터널 주소뿐 아니라 터널 뒤의 네트워크도 드러난다.

조건 하나: 누가 기기를 쥘 수 있는가

장치가 잠긴 캐비닛에 있고 팀 밖의 누구도 만지지 않는다면 노출은 주로 펌웨어 배포의 문제다. 고객에게 출하되거나 차량에 있거나 보증으로 돌아온다면 물리적 소유가 자격 증명이고 바이너리의 비밀성은 부차적이다. 기기를 쥔 사람은 키를 읽을 필요가 없다. 실행 중인 펌웨어가 그를 위해 터널을 열어 주기 때문이다. 그래서 서버 측 답에는 보호된 이미지뿐 아니라 폐기와 제한된 수명이 들어가야 한다.

이 조건을 나머지 둘보다 먼저 정하라. 둘 모두의 예산을 이 조건이 정하기 때문이다.

조건 둘: 플래시를 읽으면 무엇이 나오는가

플래시 이미지를 읽을 수 있는 사람은 리터럴로 저장된 키를 복구한다. 디버그 연결, 업데이트 파일, 백업, 반환된 장치 어느 경로든 마찬가지다. 플래시 암호화는 직접 덤프의 비용을 높인다. ESP-IDF 문서는 특정 부품과 구성에서 이 기능이 무엇을 덮는지 설명한다. 모든 공격 모델에서 키를 추출 불가능하게 만들지는 않으며, 기기가 손에 있는 경우에는 아무것도 해 주지 않는다. 비용을 바꾸는 한 겹으로 다뤄야지, 답으로 다루면 안 된다.

설계 검토에서 어떤 속성을 주장하기 전에 정확한 칩과 구성에 대한 플랫폼 문서를 읽어라.

조건 셋: 전체 플릿에서의 재사용

양산 로트에 키 하나라는 것은 모든 장치가 하나의 신원을 공유한다는 뜻이다. 서버는 로그에서 그것들을 구분할 수 없고, 전부 폐기하지 않고 하나만 폐기할 수 없으며, 복제품을 정품과 구분할 수 없다. 기기별 키는 이 셋을 모두 해결한다. 대가는 등록 단계와 시리얼 번호에서 공개 키로 가는 매핑이다. 그 매핑이 폐기를 플릿 전체 재기록 대신 한 줄 변경으로 바꾸는 자산이다.

플릿이 작업대 위의 다섯 대라면 키 공유는 합리적인 거래일 수 있다. 현장의 오백 대라면 거래조차 아니다.

기기별 키를 실용적으로 만드는 등록 패턴

첫 부팅 때 기기에서 키 쌍을 생성하고, 개인 절반은 보호된 비휘발성 저장소에 두고, 공개 키만 주소를 할당하고 피어 항목을 쓰는 등록 서비스로 보낸다. 등록 응답을 인증하라. 그렇지 않으면 로컬 네트워크의 공격자가 자기 키를 당신의 계정에 등록할 수 있다. 프로비저닝 중에 개인 키를 시리얼 콘솔로 절대 출력하지 마라. 기기의 시리얼 번호를 공개 키 옆에 기록해, 분실한 장치가 정확히 하나의 피어 항목에 대응하게 하라.

작은 서비스지만, 감사할 수 있는 플릿과 다시 기록하는 수밖에 없는 플릿의 차이가 여기에 있다.

시간, 엔드포인트, 그리고 결정

예제는 터널을 시작하기 전에 시간을 동기화한다. 이는 TLS 밖에서도 중요하다. WireGuard 핸드셰이크는 재전송 방지를 위한 타임스탬프를 싣기 때문에, 시계가 틀린 채 부팅한 기기는 핸드셰이크가 거부될 수 있다. 엔드포인트는 호스트 이름이라, 서버의 공개 키가 여전히 피어를 인증하더라도 DNS를 신뢰 경로에 넣는다. 잘못된 주소는 시도를 낭비하고 기기가 언제 온라인인지 드러낸다. 키를 논하기 전에 둘 다 처리하라.

그런 다음 결정하라. 소유가 통제되고 플릿이 작은 동안만 공유 키를, 둘 중 하나라도 사실이 아니게 되는 순간 기기별 키를.

관련 글