PXE Network Boot
PXE(Preboot eXecution Environment)는 디스크 대신 network에서 bootloader를 받아 실행하는 표준 절차다. firmware는 DHCP로 bootloader 파일의 위치를 알아내고, TFTP로 그 파일을 받아 실행한다. bootloader가 실행된 뒤부터는 디스크로 부팅할 때와 같은 과정이 이어진다.
Intro
수백 대의 server에 OS를 설치해야 한다고 해보자. USB를 한 대씩 꽂는 방식은 규모가 커지면 쓸 수 없다.
firmware는 전원이 들어오면 memory와 장치를 초기화하고, 디스크에서 bootloader를 찾아 실행한다. 그런데 firmware에는 디스크를 읽는 driver만 있는 것이 아니라, NIC를 다루는 driver와 작은 network 기능도 들어 있다. 이 기능을 쓰면 firmware는 디스크가 비어 있어도 network에서 bootloader를 받아 실행할 수 있고, 그 bootloader가 installer를 받아 설치까지 진행한다. 이 절차를 표준으로 정한 것이 PXE다.
How Network Boot Works
Booting from Disk
먼저 평소의 디스크 부팅을 짧게 떠올려 보자. firmware는 자기 안에 든 disk driver로 디스크를 읽어 bootloader를 찾는다. BIOS는 디스크 맨 앞 sector(MBR)를 그대로 읽고, UEFI는 ESP라는 FAT32 partition에서 .efi 파일을 찾는다. 찾은 bootloader를 memory에 올리고 실행하면 firmware의 일은 끝나고, kernel과 initramfs를 읽는 일은 bootloader가 맡는다.
“firmware는 장치에서 bootloader를 읽어 실행하는 데까지만 책임진다” 정도로 이해하면 되겠다.
Booting from Network
PXE는 이 과정에서 장치만 디스크에서 NIC로 바꾼다. 디스크에서는 정해진 sector나 파일 경로에서 bootloader를 찾으면 되지만, NIC 너머에는 그런 정해진 위치가 없다. 그래서 firmware는 network로 두 가지 일을 해야 한다. 먼저 어느 server에서 어떤 파일을 받을지 알아내고, 그다음 그 파일을 받아 memory에 올린다. firmware는 첫 번째 일을 DHCP로, 두 번째 일을 TFTP로 처리한다.
DHCP
DHCP(Dynamic Host Configuration Protocol)는 network에 새로 연결된 machine에게 IP address와 설정 값을 알려주는 protocol이다. 노트북을 Wi-Fi에 연결하면 IP가 자동으로 잡히는데, 이때 노트북과 공유기 사이에서 동작하는 것이 DHCP다.
동작은 묻고 답하는 형태다. 막 전원이 들어온 machine은 자기 IP를 모르므로, 같은 network의 모든 machine에게 “IP를 줄 server가 있는가”라고 묻는다. 그러면 DHCP server가 “이 IP를 쓰고, gateway는 이것이다”라고 답한다.
PXE firmware는 이 질문에 “나는 PXE client다”라는 표시를 함께 넣는다. DHCP server는 이 표시를 보고, 응답에 “어느 server에서 어떤 boot 파일을 받으라”는 정보를 추가한다. 그래서 firmware는 DHCP 응답 하나로 자기 IP와 bootloader의 위치를 함께 알게 된다.
이 DHCP server는 저절로 생기지 않는다. 집 공유기의 DHCP server는 boot 파일 정보를 주지 않으므로, PXE를 쓰려면 관리자가 같은 network에 DHCP server를 직접 두고 boot 파일 정보를 설정해야 한다. 보통 provisioning 전용 server 한 대에서
dnsmasq같은 프로그램이 DHCP와 TFTP를 함께 맡는다.
TFTP
TFTP(Trivial File Transfer Protocol)는 이름 그대로 아주 단순한 파일 전송 protocol이다. client가 파일 이름 하나를 보내면 server가 그 파일을 작은 조각으로 나눠 보내고, client는 조각을 하나 받을 때마다 “받았다”는 응답을 보낸다.
TFTP에는 로그인도 없고 directory 목록을 보는 기능도 없다. 대신 구현이 매우 작아서 용량이 작은 firmware 안에도 넣을 수 있고, 그래서 firmware는 HTTP 대신 TFTP를 쓴다.
Where the Network Code Lives
이 network 기능이 어디에 들어 있는지는 firmware 방식에 따라 다르다. BIOS 방식에서는 NIC에 붙은 작은 ROM(Option ROM)에 들어 있고, UEFI 방식에서는 메인보드 firmware 자체에 들어 있다. 그래서 NIC가 boot 목록에 나타나지 않으면, BIOS에서는 NIC의 PXE Option ROM 설정을, UEFI에서는 Network Stack 설정이 켜져 있는지 먼저 확인한다.
| 디스크 부팅 | PXE 부팅 | |
|---|---|---|
| firmware가 쓰는 코드 | disk driver | NIC driver와 작은 network 기능 |
| bootloader 위치를 찾는 방법 | MBR, ESP의 파일 경로 | DHCP 응답 |
| bootloader를 읽는 방법 | 디스크에서 읽기 | TFTP로 받기 |
| 그다음 | memory에 올리고 실행 | memory에 올리고 실행 |
Boot File Is a Bootloader
PXE로 받는 boot 파일이 하는 일은 디스크 부팅의 bootloader와 같다. pxelinux.0, grubx64.efi 같은 bootloader가 대표적이고, 이 bootloader들은 실행된 뒤 kernel과 initramfs를 다시 network로 받아 실행한다.
flowchart LR
accTitle: PXE 부팅 흐름
accDescr: firmware가 DHCP로 파일 위치를 알아내고 TFTP로 bootloader를 받아 실행하며, bootloader가 kernel과 initramfs를 network로 받아 실행하는 순서를 보여준다.
F["Firmware"] -->|DHCP| L["Boot file location"]
L -->|TFTP| N["Bootloader"]
N -->|Network| K["Kernel + initramfs"]
“firmware는 network로 bootloader까지만 받고, 나머지는 bootloader가 받는다” 정도로 이해하면 되겠다.
DHCP for PXE
Broadcast
막 전원이 들어온 machine은 IP가 없으므로, DHCP server의 주소를 알아도 그 주소로 packet을 보낼 수 없다. 그래서 첫 질문은 같은 network의 모든 machine이 받는 broadcast로 보낸다.
broadcast는 router를 넘어가지 않는다. 그래서 DHCP server가 다른 network에 있으면 router에 DHCP relay를 설정해서, router가 이 질문을 DHCP server에 대신 전달하게 해야 한다. PXE가 같은 network에서만 동작한다고 알려진 이유는 대부분 relay를 설정하지 않았기 때문이다.
How DHCP Works
DHCP는 네 개의 message로 IP를 할당한다. 각 message의 앞 글자를 따서 DORA라고 부른다.
sequenceDiagram
accTitle: DHCP DORA 과정
accDescr: client가 broadcast로 server를 찾고, server가 IP와 boot 파일 정보를 제안하며, client가 수락하고 server가 확정하는 네 단계를 보여준다.
participant C as Client
participant S as DHCP Server
C->>S: Discover (broadcast)
S->>C: Offer (IP, boot server, boot file)
C->>S: Request (accept this offer)
S->>C: Ack
- Discover: client가 broadcast로 DHCP server를 찾는다.
- Offer: server가 사용할 IP와 설정을 제안한다. PXE client에게는 boot 파일 정보도 함께 준다.
- Request: client가 받은 제안 중 하나를 고르고, 그 제안을 받겠다고 알린다.
- Ack: server가 할당을 확정한다.
PXE client에게 주는 boot 파일 정보는 두 값이다. 하나는 boot 파일이 있는 TFTP server의 IP이고, 다른 하나는 받을 파일의 이름이다. DHCP 설정에서는 이 두 값을 보통 next-server와 filename이라고 부른다.
BIOS or UEFI
BIOS와 UEFI는 실행할 수 있는 파일 형식이 다르다. 그래서 같은 PXE라도 BIOS machine에는 BIOS용 bootloader를, UEFI machine에는 .efi bootloader를 줘야 한다. 형식이 맞지 않는 파일을 주면 client는 파일을 받고도 실행하지 못하고 멈춘다.
이 구분은 client가 DHCP 질문에 직접 적어 보낸다. PXE firmware는 “나는 PXE client다”라는 표시와 함께 BIOS인지 UEFI인지도 적어 보내고, DHCP server는 그 값을 보고 응답할 파일 이름을 고른다.
Proxy DHCP
이미 DHCP server가 있어서 설정을 바꿀 수 없는 network에서는 proxy DHCP를 쓴다. proxy DHCP server는 IP를 할당하지 않고, PXE client의 질문에만 boot 파일 정보를 응답한다. client는 기존 server에서 IP를, proxy server에서 boot 정보를 받아 합쳐서 쓴다.
From TFTP to HTTP
TFTP Limits
TFTP는 단순한 대신 느리다. server는 조각 하나를 보낼 때마다 client의 “받았다”는 응답을 기다린 뒤에야 다음 조각을 보낸다. 그래서 network가 빨라도 전송 속도는 응답을 기다리는 시간에 묶인다.
크기가 작은 bootloader라면 이 속도로도 충분하지만, 수백 MB짜리 kernel, initramfs, installer image를 TFTP로 받으면 수 분이 걸린다. 그래서 요즘은 TFTP로는 작은 프로그램 하나만 받고, 큰 파일은 HTTP로 받는다.
iPXE
iPXE는 HTTP를 지원하는 오픈소스 network boot 프로그램이다. firmware가 TFTP로 iPXE를 받아 실행하면, 그때부터는 iPXE가 HTTP로 kernel과 initramfs를 받는다. 이렇게 firmware가 받은 프로그램이 다시 다음 프로그램을 받아 실행하는 방식을 chainload라고 부른다.
flowchart LR
accTitle: iPXE chainload 흐름
accDescr: firmware가 TFTP로 iPXE를 받고, iPXE가 HTTP로 script와 kernel, initramfs를 받아 installer를 실행하는 순서를 보여준다.
A["Firmware"] -->|TFTP| B["iPXE"]
B -->|HTTP| C["Boot script"]
C -->|HTTP| D["Kernel + initramfs"]
D --> E["Installer"]
설정할 때 한 가지만 주의하면 된다. iPXE도 실행되면 DHCP 질문을 다시 보내는데, DHCP server가 이번에도 iPXE 파일을 주면 iPXE가 자기 자신을 다시 받는 과정이 반복된다. 그래서 DHCP server는 질문에 “나는 iPXE다”라는 표시가 있으면, iPXE 파일 대신 HTTP로 받을 script 주소를 응답해야 한다.
UEFI HTTP Boot
처음부터 firmware가 HTTP로 받으면 iPXE를 거칠 필요가 없다. UEFI 2.5부터는 이 기능이 HTTP Boot라는 이름으로 규격에 들어갔다. 흐름은 PXE와 같고, DHCP server가 파일 이름 대신 http://... 주소를 응답한다는 점만 다르다.
다만 오래된 장비는 HTTP Boot를 지원하지 않는다. 그래서 여러 세대의 장비가 섞인 환경에서는 여전히 PXE로 iPXE를 받는 경로를 기본으로 두는 경우가 많다.
Unattended Install
network로 installer를 실행해도, 사람이 화면 앞에서 언어와 디스크 partition을 골라야 한다면 설치는 자동화되지 않는다. 그래서 배포판마다 설치 질문에 대한 답을 미리 적어 두는 파일 형식이 있다. RHEL 계열은 Kickstart, Debian은 preseed, Ubuntu Server는 autoinstall이라는 형식을 쓴다.
동작 방식은 셋 다 같다. bootloader나 iPXE가 kernel을 실행할 때 답안 파일의 HTTP 주소를 함께 넘기고, installer는 그 파일을 받아 사람에게 묻는 대신 파일에 적힌 값으로 설치한다. server가 이 파일을 machine마다 다르게 만들어 주면, 같은 installer로 수백 대에 서로 다른 hostname과 IP를 넣어 설치할 수 있다.
Security
PXE에서 쓰는 DHCP, TFTP, HTTP에는 상대가 진짜 server인지 확인하는 절차가 없다. 그래서 다음 세 가지를 주의해야 한다.
- 가짜 DHCP server: 같은 network의 누구든 DHCP 응답을 보낼 수 있다. 공격자가 먼저 응답하면 client는 공격자가 지정한 파일을 받아 실행한다. switch의 DHCP snooping 기능을 켜면 지정한 port에서 온 DHCP 응답만 통과시킬 수 있다.
- 변조된 boot 파일: 전송 중에 파일이 바뀌어도 TFTP는 알 수 없다. UEFI의 Secure Boot를 켜면 firmware가 서명이 확인된 파일만 실행하므로, 변조된 bootloader는 실행되지 않는다.
- 의도하지 않은 재설치: boot 순서에서 network가 디스크보다 앞에 있으면, 재부팅할 때 installer가 다시 실행되어 디스크가 지워질 수 있다. 설치가 끝난 machine에는 provisioning server가 “디스크로 부팅하라”고 응답하도록 상태를 관리한다.
그래서 PXE를 쓰는 network는 보통 provisioning 전용 network로 분리한다.
Summary
PXE boot는 다음 순서로 진행된다.
- firmware가 broadcast로 DHCP 질문을 보내면서, PXE client라는 표시와 BIOS인지 UEFI인지를 함께 적는다.
- DHCP server가 IP와 함께 boot 파일이 있는 server와 파일 이름을 응답한다.
- firmware가 TFTP로 bootloader(보통 iPXE)를 받아 실행한다.
- iPXE가 HTTP로 kernel과 initramfs를 받아 installer를 실행한다.
- installer가 미리 준비된 답안 파일을 읽어, 사람 개입 없이 설치를 끝낸다.
UEFI HTTP Boot를 쓰면 3단계 없이 firmware가 바로 HTTP로 받는다. 어느 경로든 protocol 자체에는 인증이 없으므로, DHCP snooping, Secure Boot, 전용 network 분리로 보완해야 한다.