서버 한 대, 세 가지 신뢰 역할, 하나의 장애 도메인
VPN 진입점, 프라이빗 서비스, CI 작업은 서버 한 대에서도 서로 다른 권한이 필요하다. 역할을 나누고 네트워크를 격리하며 콘솔 복구 수단을 유지한다.
빌드 러너가 VPN 게스트가 쓰는 호스트 소켓을 필요로 하는 순간 모든 것이 무너지고, 그때는 왜 작업이 게이트웨이의 키를 읽을 수 있는지 아무도 설명하지 못한다. 모든 것이 자리가 맞아서 한 대의 기계에 설치되었고, 작동시키는 가장 빠른 방법이었기 때문에 모든 게스트에 넓은 접근이 주어졌다. 결과는 신뢰 경계가 전원 케이블인 호스트다. 역할을 나누는 일은 들리는 것보다 적은 노력이며, 이름을 붙이는 것에서 시작한다.
지금 모든 것을 공유하는 세 역할
VPN 진입점, 프라이빗 서비스 묶음, 빌드 러너는 서로 다른 것을 원한다. 첫째는 공인 포트와 프라이빗 측의 좁은 네트워크가 필요하다. 둘째는 영구 저장소와 애플리케이션 비밀이 필요하다. 셋째는 다른 사람이 작성한 코드를 실행한다.
셋 모두에 같은 접근을 주면 어느 하나가 다른 것에 영향을 줄 수 있어 증상이 혼란스럽다. 하이퍼바이저 소켓에 닿는 빌드 작업은 VPN을 멈출 수 있는 작업이다. 관리 네트워크로 향하는 경로가 있는 서비스 게스트는 스위치 구성을 바꿀 수 있다. 권한이 문제이며, 프로세스 목록은 그것이 드러나는 장소일 뿐이다. 역할을 먼저 이름 붙이면 각 권한에 살 곳이 생긴다.
아래 스케치는 그 분할을 위한 출발점이며 고가용성의 약속이 아니다. 검증할 가설로 다루고, 완성된 설계로 다루지 말 것.
세 프로세스가 아니라 세 역할을 나눈다
한 호스트의 세 게스트는 세 개의 권한 집합과 세 개의 네트워크 위치를 준다. 세 개의 장애 도메인을 주지는 않으며, 설계는 그 점을 분명히 말해야 한다.
한 게스트의 침해가 다른 게스트에 자동으로 권한을 주지 않을 때만 분리가 도움이 되며, 그것은 이름 규칙이 아니라 하이퍼바이저 설정이다. VM을 게이트웨이라고 부른다고 다른 게스트가 호스트에 닿지 못하는 것은 아니다. 호스트 이름 대신 가상 스위치 구성을 읽고, 각 게스트가 생각하는 위치에 있는지 확인한다.
host
├─ vpn-gw 10.60.10.2/24 public 51820/udp only
├─ services 10.60.0.10/24 no route to the management network
└─ ci-runner 10.60.20.2/24 outbound only, no host socket
토폴로지를 적고 나서 그것을 소유한 도구로 검증한다. 잘못된 브리지에 나타나는 게스트는 세부 사항이 아니라 발견 사항이다. 서비스 구성을 바꾸기 전에 네트워크를 바로잡는다.
VPN 게스트가 자기 문만 열게 한다
라우터에서 터널 포트만 공개한다. 게스트 관리 포트는 관리 경로에 속하며 인터넷에 속하지 않고, 하이퍼바이저 자체의 인터페이스도 같은 곳에 속한다.
프라이빗 측 네트워크는 원격 사용자가 필요로 하는 서비스에 닿아야 하고 거기서 멈춰야 한다. 터널은 전송이지 로그인이 아니므로 애플리케이션 인증은 켜 둔다. VPN 게스트의 라우팅 테이블은 한 문장으로 설명할 수 있을 만큼 작게 유지한다. 그렇게 간단히 설명할 수 없다면 경계는 이미 흐려진 것이다.
virsh net-list --all
virsh net-dumpxml lab-private
sudo nft list ruleset | grep -n -E '51820|10.60'
호스트에서 수신 대기 중이고 관리에 필요 없는 것은 아예 수신하지 않아야 한다. 여기에는 하이퍼바이저 API도 포함되며, 공용 인터페이스에 노출된 채로 두기 쉽다.
빌드 작업에는 다른 종류의 경계가 필요하다
CI 작업은 저장소가 제어하는 명령을 실행하므로 그 신뢰는 누가 코드를 푸시할 수 있는지에 달려 있다. 셸 실행기는 그 명령을 호스트에서 러너의 사용자로 실행하며, 그것이 사용 가능한 가장 넓은 경계다.
컨테이너 실행기는 더 좁고, 권한 있는 컨테이너는 좁은 쪽이 아니다. 러너는 자체 네트워크 범위를 가진 자체 게스트에 두고, 작업에 필요한 산출물과 배포 접근만 준다. 일회용 워커는 보존되는 상태를 줄이지만 도달 가능성이나 비밀 노출을 줄이지는 않는다. 둘 다 여전히 명시적 한계가 필요하다.
서비스 게스트가 데이터를 소유하므로 그 백업 계획이 반드시 작동해야 한다. 저장소, 업로드, 데이터베이스를 같은 일정에 둔다. 작업 상태를 읽는 대신 복원을 시험한다.
셋이 공유하는 장애를 계획한다
호스트 하나는 장애 도메인 하나다. 재부팅은 터널과 모든 프라이빗 서비스를 같은 순간에 없애며, 바로 그때 들어가는 길이 가장 중요하다.
VPN 게스트에 의존하지 않는 콘솔 접근을, 물리 콘솔이든 대역 외 관리 인터페이스든 공급자 콘솔이든 유지한다. 같은 디스크에 저장한 스냅샷은 독립 사본이 아니며, 보호하는 게스트 안에서 실행되는 백업도 그렇지 않다. 다음으로 콜드 스타트를 호스트, 가상 네트워크, 터널 경로, 그리고 일반 계정 아래의 이름 있는 애플리케이션 하나라는 순서로, 점검표가 아니라 시험한다.
virsh list --all
systemctl is-active libvirtd
curl -sS -m 5 -o /dev/null -w '%{http_code}\n' https://10.60.0.10/
ss -ltnp | grep -E ':51820|:443'
프로세스 시작 순서는 경로와 DNS에 대해 아무것도 증명하지 않으므로 유닛 상태가 아니라 경로를 시험한다. 경로가 원격 사용자에게 실제로 필요한 것이다.