얼마 전에 Black Hat USA 2021 학회에서 가상 머신 탈출 탐지 및 방어 관련 연구(Alcatraz)를 발표하게 되었다고 말씀을 드렸는데요, 해당 연구가 QEMU와 아마존(Amazon)에서 만든 MicroVM 기술(Firecracker)을 대상으로 하고 있습니다. MicroVM 기술은 전체 시스템을 가상화하여 가상 머신(Virtual Machine, VM)을 생성하는 QEMU와 달리 컨테이너 구동을 위한 최소한의 기능만 구현한 경량(micro) 가상 머신입니다. 그래서 이름도 MicroVM이죠. QEMU보다는 최근에 나온 기술이라 많이 생소하실 텐데요, QEMU보다 공격면이 작고 좀 더 안전하기 때문에 앞으로 주목받을 것 같습니다. ^^;;;

Firecracker는 현재 https://github.com/firecracker-microvm/firecracker에서 프로젝트가 진행 중입니다. 문서가 비교적 잘 정리되어 있어서 따라 하는 것이 크게 어렵지 않은데요, 좀 더 쉽게 사용할 수 있는 방법이 있어서 정리하는 셈 치고 남겨봅니다. ^^;;

1. 우분투 환경에서 도커(Docker) 설치하기

Firecracker는 MicroVM 내부에 도커(Docker)를 구동하기 때문에 먼저 설치해 줍니다. 우분투 환경에서 도커는 아래와 같이 설치할 수 있습니다.

# 도커 패키지 설치
$> sudo apt install docker.io

# 현재 유저 계정을 도커 그룹에 추가, 그룹 추가가 완료되면 로그아웃 및 로그인 수행 필요
$> sudo useradd -aG docker ${USER}

2. 아마존(Amazon) Firecracker 다운로드 및 빌드

Firecracker의 소스코드는 이미 공개되어 있으므로, 아래와 같이 입력하여 소스코드를 받아 빌드합니다.

# 소스코드 다운로드
$> git clone https://github.com/firecracker-microvm/firecracker

# 빌드 및 링크 생성
$> cd firecracker
$> tools/devtool build
$> ln -s build/cargo_target/x86_64-unknown-linux-musl/debug/firecracker firecracker

3. Firecracker 구동용 스크립트 생성하기

Firecracker는 웹 API를 제공합니다. 그래서 curl을 이용해서 직접 커맨드를 실행해도 되지만, 너무 불필요한 작업이 많아서 아래처럼 스크립트를 만들었습니다. 이 글을 쓰게 된 이유이기도 하죠. ^-^)/

#!/bin/bash
# 00_get_kernel_and_rootfs.sh로 저장 후 chmod +x로 실행권한 부여

arch=`uname -m`
dest_kernel="hello-vmlinux.bin"
dest_rootfs="hello-rootfs.ext4"
image_bucket_url="https://s3.amazonaws.com/spec.ccfc.min/img"

if [ ${arch} = "x86_64" ]; then
    kernel="${image_bucket_url}/quickstart_guide/x86_64/kernels/vmlinux.bin"
    rootfs="${image_bucket_url}/hello/fsfiles/hello-rootfs.ext4"
elif [ ${arch} = "aarch64" ]; then
    kernel="${image_bucket_url}/quickstart_guide/aarch64/kernels/vmlinux.bin"
    rootfs="${image_bucket_url}/aarch64/ubuntu_with_ssh/fsfiles/xenial.rootfs.ext4"
else
    echo "Cannot run firecracker on $arch architecture!"
    exit 1
fi

echo "Downloading $kernel..."
curl -fsSL -o $dest_kernel $kernel

echo "Downloading $rootfs..."
curl -fsSL -o $dest_rootfs $rootfs

echo "Saved kernel file to $dest_kernel and root block device to $dest_rootfs."
#!/bin/bash
# 01_start_firecracker.sh로 저장 후 chmod +x로 실행권한 부여

echo "Remove existing socker."
rm -f /tmp/firecracker.socket

echo ""
echo "Start firecracker."
./firecracker --api-sock /tmp/firecracker.socket
#!/bin/bash
# 02_set_guest_kernel.sh로 저장 후 chmod +x로 실행권한 부여

arch=`uname -m`
kernel_path=$(pwd)"/hello-vmlinux.bin"

if [ ${arch} = "x86_64" ]; then
    curl --unix-socket /tmp/firecracker.socket -i \
      -X PUT 'http://localhost/boot-source'   \
      -H 'Accept: application/json'           \
      -H 'Content-Type: application/json'     \
      -d "{
            \"kernel_image_path\": \"${kernel_path}\",
            \"boot_args\": \"console=ttyS0 reboot=k panic=1 pci=off\"
       }"
elif [ ${arch} = "aarch64" ]; then
    curl --unix-socket /tmp/firecracker.socket -i \
      -X PUT 'http://localhost/boot-source'   \
      -H 'Accept: application/json'           \
      -H 'Content-Type: application/json'     \
      -d "{
            \"kernel_image_path\": \"${kernel_path}\",
            \"boot_args\": \"keep_bootcon console=ttyS0 reboot=k panic=1 pci=off\"
       }"
else
    echo "Cannot run firecracker on $arch architecture!"
    exit 1
fi
#!/bin/bash
# 03_set_rootfs.sh로 저장 후 chmod +x로 실행권한 부여

rootfs_path=$(pwd)"/hello-rootfs.ext4"
curl --unix-socket /tmp/firecracker.socket -i \
  -X PUT 'http://localhost/drives/rootfs' \
  -H 'Accept: application/json'           \
  -H 'Content-Type: application/json'     \
  -d "{
        \"drive_id\": \"rootfs\",
        \"path_on_host\": \"${rootfs_path}\",
        \"is_root_device\": true,
        \"is_read_only\": false
   }"
#!/bin/bash
# 04_start_microvm.sh로 저장 후 chmod +x로 실행권한 부여

curl --unix-socket /tmp/firecracker.socket -i \
  -X PUT 'http://localhost/actions'       \
  -H  'Accept: application/json'          \
  -H  'Content-Type: application/json'    \
  -d '{
      "action_type": "InstanceStart"
   }'

4. Firecracker 실행

최초 실행 시에는 커널(Kernel) 파일과 루트파일시스템(rootfs)가 없으므로, 00_get_kernel_and_rootfs.sh를 실행해서 이미지를 다운로드할 필요가 있습니다. 한 번 다운로드한 뒤에는 이미지를 계속 활용하면 되니 01_start_firecracker.sh부터 실행하면 됩니다. 여기서 주의할 점이 있는데요, 01_start_firecracker.sh 스크립트를 실행하면 터미널이 계속 대기상태로 있다는 겁니다. MicroVM이 시작되기를 기다리는 건데요, 이후에 다른 터미널을 열어서 스크립트를 실행하면 대기상태가 풀리고 로그인 쉘이 표시되기 때문에 걱정 안 하셔도 됩니다. 스크립트를 통해 Firecracker를 실행하는 과정은 아래와 같습니다.

# 커널 및 루트파일시스템 이미지를 다운로드하는 단계이므로, 최초 1회만 실행
$> ./00_get_kernel_and_rootfs.sh

# Firecracker 실행, 이때 터미널이 대기상태로 빠지므로 다른 터미널을 열어서 다음 스크립트를 실행
$> ./01_start_firecracker.sh

# MicroVM용 커널 설정
$> ./02_set_guest_kernel.sh

# MicroVM용 루트파일시스템 설정
$> ./03_set_rootfs.sh

# MicroVM 실행
$> ./04_start_microvm.sh

위의 스크립트를 차례로 실행하면, 01_start_firecracker.sh를 실행하여 대기중이던 터미널에서 아래와 같이 부팅이 진행되고 로그인 쉘이 표시됩니다. 기본 로그인 정보는 ID/PWD가 root/root로 설정되어 있습니다. 로그를 보니 Alpine 리눅스를 기반으로 MicroVM 이미지를 생성한 것 같네요.

[    0.000000] Linux version 4.14.225 (ubuntu@ip-172-31-11-16) (gcc version 7.5.0 (Ubuntu 7.5.0-3ubuntu1~18.04)) #1 SMP Fri Mar 12 16:47:14 UTC 2021
[    0.000000] Command line: console=ttyS0 reboot=k panic=1 pci=off root=/dev/vda rw virtio_mmio.device=4K@0xd0000000:5
[    0.000000] x86/fpu: Supporting XSAVE feature 0x001: 'x87 floating point registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x002: 'SSE registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x004: 'AVX registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x008: 'MPX bounds registers'
[    0.000000] x86/fpu: Supporting XSAVE feature 0x010: 'MPX CSR'
[    0.000000] x86/fpu: xstate_offset[2]:  576, xstate_sizes[2]:  256
[    0.000000] x86/fpu: xstate_offset[3]:  832, xstate_sizes[3]:   64
[    0.000000] x86/fpu: xstate_offset[4]:  896, xstate_sizes[4]:   64
[    0.000000] x86/fpu: Enabled xstate features 0x1f, context size is 960 bytes, using 'compacted' format.
[    0.000000] e820: BIOS-provided physical RAM map:

... omitted ...

Starting default runlevel

Welcome to Alpine Linux 3.8
Kernel 4.14.225 on an x86_64 (ttyS0)

localhost login:

그럼 즐거운 저녁 되세요 >ㅁ<)-b

+ Recent posts