# 내 최소 메모리 안전 Go rsync가 취약점을 피하는 방법

> Clean Markdown view of GeekNews topic #29863. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=29863](https://news.hada.io/topic?id=29863)
- GeekNews Markdown: [https://news.hada.io/topic/29863.md](https://news.hada.io/topic/29863.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-05-26T00:23:19+09:00
- Updated: 2026-05-26T00:23:19+09:00
- Original source: [michael.stapelberg.ch](https://michael.stapelberg.ch/posts/2026-05-24-minimal-memory-safe-go-rsync-vulns/)
- Points: 1
- Comments: 1

## Topic Body

- **gokrazy/rsync**는 Go로 작성한 최소 rsync 구현으로, 2025년 1월·2026년 5월 공개된 rsync 취약점 12개를 기준으로 검토됨
- Go의 **경계 검사**와 0 초기화는 힙 오버플로·스택 정보 유출을 panic 또는 무해한 값으로 바꾸지만, panic은 여전히 서비스 거부가 될 수 있음
- 경로 순회와 TOCTOU 계열은 Go 1.24의 **os.Root** 같은 순회 저항 파일 API가 핵심 방어가 되며, gokrazy/rsync는 전면 전환됨
- 최소 구현 전략은 `--inc-recursive`, 압축, `--safe-links`, 프록시, 호스트명 ACL 등 미구현 기능 관련 취약점을 피하게 해 공격 표면을 줄임
- 취약점 대부분은 **검증 누락**과 과도한 복잡성에서 나왔고, 단순한 사용 사례에는 단순한 구현이 더 적절할 수 있음

---

### 배경과 범위
- 2025년 1월 여러 보안 연구자가 upstream Samba [rsync](https://github.com/RsyncProject/rsync)에서 총 [6개 보안 취약점](https://www.openwall.com/lists/oss-security/2025/01/14/3)을 공개했고, 일부는 임의 코드 실행과 파일 유출을 허용함
- Go로 작성한 호환·최소 구현인 [gokrazy/rsync](https://github.com/gokrazy/rsync)가 이런 취약점 계열을 실제로 피할 수 있는지가 핵심 검토 대상임
- 분석 대상은 **2025년 1월 배치와 2026년 5월 배치**를 합친 12개 취약점임
- upstream [rsync](https://github.com/RsyncProject/rsync)를 프로덕션에서 실행 중이면 **3.4.3 이상**으로 업그레이드해야 하며, [gokrazy/rsync](https://github.com/gokrazy/rsync)는 **v0.3.3 이상**으로 올려야 함
- [gokrazy/rsync](https://github.com/gokrazy/rsync)는 Linux 배포판 연구 프로젝트 [distri](https://distr1.org/)의 소프트웨어 패키지를 [router7](https://router7.org/)에서 제공하려고 작성됐고, router7은 Go 어플라이언스 플랫폼 [gokrazy](https://gokrazy.org/) 기반임
- 초기에는 rsync 서버만 있었지만 현재는 모든 방향을 지원하며, Go 프로그램에 링크할 수 있는 rsync 기본 기능으로 여러 gokrazy/rsync 서버에서 사용 중임

### 2025년 1월 취약점
- ## CVE-2024-12084: 힙 버퍼 오버플로, 심각도 9.8
  - rsync는 네트워크에서 받은 체크섬 길이를 `MAX_DIGEST_LEN`과 비교했지만, 내부 자료구조는 항상 16바이트 버퍼 `char sum2[SUM_LENGTH]`를 사용함
  - `MAX_DIGEST_LEN`은 SHA256 또는 SHA512 체크섬 지원으로 컴파일되면 더 커질 수 있어, 공격자가 `sum2` 버퍼 한계를 최대 **48바이트** 넘겨 쓸 수 있었음
  - 문제는 SHA256/SHA512 체크섬 지원을 추가한 2022년 9월 [commit `ae16850`](https://github.com/RsyncProject/rsync/commit/ae16850dc58e884eb9f5cb7f772342b2db28f471)에서 도입됨
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/0902b52f6687b1f7952422080d50b93108742e53)은 `sum2`를 동적 할당되는 `sum2_array`로 바꾸고, 전송 알고리듬의 체크섬 길이인 `xfer_sum_len`으로 할당·검사하도록 고침
  - Go에서는 잘못된 경계 검사가 힙 버퍼 오버플로로 이어지지 않고 런타임 경계 검사로 panic이 발생함
  - gokrazy/rsync에도 sum header 검증 누락이 있었지만 크기 혼동은 아니었고, `ChecksumLength`를 `512`로 바꾸면 Go 런타임이 `panic: runtime error: slice bounds out of range [:512] with length 16`을 발생시킴
  - 서버 전체 크래시는 바람직한 실패 모드가 아니므로, [누락된 경계 검사를 추가해 panic을 error로 바꿈](https://github.com/gokrazy/rsync/commit/178216f10f2e05fd74bf865f2d0725fce4f907cd)
- ## CVE-2024-12085: 스택 정보 유출로 ASLR 우회, 심각도 7.5
  - CVE-2024-12084와 같은 검증 누락 때문에 공격자가 짧은 체크섬 알고리듬을 선택한 뒤 더 긴 체크섬을 보냈다고 주장할 수 있었음
  - [Google Security report](https://github.com/google/security-research/security/advisories/GHSA-p5pg-x43v-mvqj)에 따르면 힙 버퍼 오버플로와 정보 유출이 결합되면 익명 읽기 접근만 가진 클라이언트가 rsync 서버 머신에서 임의 코드를 실행할 수 있음
  - `hash_search()`는 스택의 `char sum2[MAX_DIGEST_LEN]`에 청크 digest를 만들고 `memcmp()`로 비교했지만, 로컬 `sum2` 스택 버퍼가 초기화되지 않아 남은 바이트가 스택 내용을 담을 수 있었음
  - 악성 클라이언트는 파일 다운로드당 1바이트를 유출할 수 있고, 유출 데이터에는 heap 객체 포인터, stack cookie, 지역 변수, 전역 변수 포인터, return pointer가 포함될 수 있음
  - [“Some checksum buffer fixes”](https://github.com/RsyncProject/rsync/commit/0902b52f6687b1f7952422080d50b93108742e53)는 공격자 제어 `s->s2length`가 전송 체크섬 길이보다 커질 수 없게 했고, [“prevent information leak off the stack”](https://github.com/RsyncProject/rsync/commit/589b0691e59f761ccb05ddb8e1124991440db2c7)는 `sum2` 메모리를 0으로 초기화함
  - Go는 모든 변수를 **zero value**로 초기화하므로 이 취약점에 영향받지 않으며, gokrazy/rsync는 MD4 외 체크섬 선택이 도입된 프로토콜 버전 30이 아니라 프로토콜 버전 27을 구현함
- ## CVE-2024-12087: 심볼릭 링크를 이용한 경로 순회, 심각도 7.5
  - [Google Security report](https://github.com/google/security-research/security/advisories/GHSA-p5pg-x43v-mvqj)에 따르면 `-l` 또는 `-a`(`--archive`)로 심볼릭 링크 동기화가 활성화됐을 때, 악성 서버가 클라이언트에게 대상 디렉터리 밖 임의 파일을 쓰게 만들 수 있음
  - 공격은 `--inc-recursive` 모드에서 여러 파일 목록을 보내고, 첫 목록에서는 `symlink`를 디렉터리로, 다음 목록에서는 같은 이름을 대상 디렉터리 밖을 가리키는 심볼릭 링크로 바꾸는 방식임
  - Go 자체는 이 취약점을 막지 못하며, 원인은 여러 파일 목록을 병합한 뒤 다시 검증하지 않은 논리 오류임
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/688f5c379a433038bde36897a156d589be373a98)은 누락된 검증을 추가함
  - gokrazy/rsync는 incremental recursion 모드(`--inc-recursive`)를 구현하지 않아 영향받지 않음
  - incremental recursion은 전체 파일 집합을 전송 시작 전에 모두 스캔하지 않고 “windowed” 방식으로 처리하게 해주지만, 구현 복잡성과 자원 사용량 사이의 트레이드오프가 있음
- ## CVE-2024-12088: `--safe-links` 우회, 심각도 7.5
  - [Google Security report](https://github.com/google/security-research/security/advisories/GHSA-p5pg-x43v-mvqj)에 따르면 `--safe-links`는 서버에서 받은 심볼릭 링크가 대상 디렉터리 안을 가리키도록 검증하는 기능임
  - 우회는 심볼릭 링크 대상 경로 중간에 다른 심볼릭 링크가 있는지를 고려하지 않아 발생함
  - 예를 들어 `{DESTINATION}/a -> .`와 `{DESTINATION}/foo -> a/a/a/a/a/a/../../`가 있으면, `foo`는 실제로 대상 디렉터리 밖을 가리키지만 `unsafe_symlink()`는 `a/`를 디렉터리로 가정해 안전하다고 판단함
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/407c71c7ce562137230e8ba19149c81ccc47c387)은 경로 시작 부분을 제외한 위치의 `../`를 허용하지 않도록 `unsafe_symlink()`를 더 엄격하게 만듦
  - Go 자체는 잘못된 검증 함수를 막지 못하며, gokrazy/rsync는 아직 `--safe-links` 기능을 구현하지 않아 취약하지 않음
- ## CVE-2024-12086: 임의 파일 유출, 심각도 6.8
  - rsync receiver가 클라이언트 모드에서 rsync sender가 제공한 파일 이름을 정리하지 않았고, 대상 트리 밖 파일 열기를 막지 못함
  - 악성 sender는 receiver에게 대상 트리 밖 임의 파일의 체크섬을 비교하도록 지시하고, 1바이트 체크섬에 대한 receiver 반응을 관찰해 임의 파일을 유출할 수 있었음
  - [Google Security report](https://github.com/google/security-research/security/advisories/GHSA-p5pg-x43v-mvqj)에 따르면 클라이언트가 악성 서버에 연결하면 서버는 클라이언트 머신의 임의 파일 내용을 유출할 수 있음
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/c35e28331f10ba6eba370611abd78bde32d54da7)은 sender 제공 경로를 검증해 대상 트리 밖 파일 열기를 방지함
  - Go에는 이를 막을 수 있는 API가 있으며, 관련 방어로 Go의 `os.Root`가 제시됨
  - gokrazy/rsync는 fuzzy matching 기능이 도입된 프로토콜 버전 29가 아니라 프로토콜 버전 27을 구현하므로 취약하지 않음
- ## CVE-2024-12747: 심볼릭 링크 경쟁 조건, 심각도 5.6
  - [Red Hat Security Advisory](https://access.redhat.com/security/cve/CVE-2024-12747)에 따르면 rsync의 심볼릭 링크 처리 중 경쟁 조건에서 발생한 결함임
  - rsync의 기본 동작은 심볼릭 링크를 만나면 건너뛰는 것이지만, 공격자가 적절한 시점에 일반 파일을 심볼릭 링크로 바꾸면 기본 동작을 우회해 심볼릭 링크를 순회할 수 있었음
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/0590b09d9a34ae72741b91ec0708a820650198b0)은 rsync sender의 `open()` 호출에 `O_NOFOLLOW` 옵션을 사용하도록 바꿈
  - gokrazy/rsync는 [commit `1b1fbf6`](https://github.com/gokrazy/rsync/commit/1b1fbf65b8ed696db464bf53445e8f9b97a37210) 전까지 취약했으며, 이 커밋은 upstream rsync와 같은 `O_NOFOLLOW` 완화를 도입함
  - Damien Neil은 [gokrazy의 CVE-2024-12747 수정](https://github.com/gokrazy/rsync/commit/1b1fbf65b8ed696db464bf53445e8f9b97a37210)이 불충분하다고 봤고, `O_NOFOLLOW`는 마지막 경로 구성 요소의 심볼릭 링크 순회만 막는다고 판단함
  - 예를 들어 `os.Open("dir/passwd")`에서 이전 경로 구성 요소인 `dir`을 `/etc`로 향하는 심볼릭 링크로 바꾸면 여전히 우회 가능함
  - 이 문제는 2025년 4월 rsync 보안 연락처로 보고됐고, 2026-05-20에 공개된 CVE-2026-29518로 이어짐

### 2026년 5월 취약점
- ## CVE-2026-29518: 심볼릭 링크 경쟁 조건, 심각도 7.0
  - [rsync 3.4.3 NEWS entry](https://github.com/RsyncProject/rsync/pull/895/changes/8471fdd1561049ef5f58df44a1811a50bd9a531d)에 따르면 chroot 없이 daemon mode에서 로컬 권한 상승을 허용하는 TOCTOU 심볼릭 링크 경쟁 조건임
  - `use chroot = no`로 설정된 rsync daemon은 parent path component에 대한 time-of-check/time-of-use 경쟁에 노출됨
  - 모듈에 쓰기 접근 권한이 있는 로컬 공격자는 receiver의 검사와 `open()` 사이에 parent directory component를 심볼릭 링크로 바꿔, 읽기에서는 basis-file disclosure를, 쓰기에서는 module 밖 file overwrite를 유발할 수 있음
  - 기본값인 `use chroot = yes`는 노출되지 않음
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/859d44fa4f1420775e4ba050337ef32092f2894c)은 Go의 `os.Root` API와 유사한 `secure_relative_open()`을 사용함
  - gokrazy/rsync는 [sender](https://github.com/gokrazy/rsync/commit/b6d50e5e9fd1fdf196f09947a6bc796b1ac48f46)와 [receiver](https://github.com/gokrazy/rsync/commit/cc42e03cad132ba19e0905f149c7c34dab45cd3a)를 순회 저항 `os.Root` API로 전환하기 전까지 취약했음
- ## CVE-2026-43618: 정수 오버플로로 원격 메모리 유출, 심각도 8.1
  - rsync 수신자의 **압축 토큰 디코더**가 32비트 부호 있는 카운터를 오버플로 검사 없이 누적해, 악의적 송신자가 프로세스 메모리 내용을 유출할 수 있음
  - 유출 대상에는 환경 변수, 비밀번호, 힙과 라이브러리 포인터가 포함될 수 있으며, ASLR을 약화하고 추가 익스플로잇을 쉽게 만들 수 있음
  - 영향 범위는 압축이 활성화된 인증된 데몬 연결이며, 프로토콜 30 이상에서 양쪽 피어가 압축을 광고하면 기본값으로 활성화됨
  - 우회책은 rsyncd.conf에서 `refuse options = compress`로 데몬 압축을 비활성화하는 것임
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/c44c90e9460c666c965446a8c0957f0b9fa4c66a)은 누락된 검사를 추가함
  - gokrazy/rsync는 **압축을 구현하지 않아** 취약하지 않으며, 압축 지원이 단순해 보이지만 비자명한 이유는 [gokrazy/rsync issue #35](https://github.com/gokrazy/rsync/issues/35#issuecomment-2988582190)에 정리돼 있음
- ## CVE-2026-43620: 범위 밖 읽기 이후 서비스 거부, 심각도 6.5
  - 2025년에 `send_files()`에 추가된 `parent_ndx<0` 가드가 시각적으로 동일한 `recv_files()` 블록에는 적용되지 않아 발생함
  - 악의적 rsync 서버가 `CF_INC_RECURSE` 호환성 플래그와 잘못 구성된 flist를 보내면 수신자가 `dir_flist->files[-1]`을 읽고 역참조해 결정적 `SIGSEGV`로 이어질 수 있음
  - 영향 범위는 공격자가 제어하는 URL에서 일반 pull을 수행하는 모든 rsync 클라이언트이며, 프로토콜 30 이상의 기본값인 `inc_recurse` 때문에 피해자 쪽 특수 옵션이 필요 없음
  - 클라이언트에서 `--no-inc-recursive`를 사용하는 것이 우회책이며, [upstream 수정](https://github.com/RsyncProject/rsync/commit/0cf200ecbb8baaf58070d825c4fbd892b4a63b69)은 `recv_files()`에도 `parent_ndx<0` 가드를 추가함
  - gokrazy/rsync는 `--inc-recursive` **증분 재귀 모드**를 구현하지 않아 [CVE-2024-12087](https://michael.stapelberg.ch/posts/2026-05-24-minimal-memory-safe-go-rsync-vulns/#cve-2024-12087)과 마찬가지로 영향을 받지 않음
- ## CVE-2026-43619: 추가 심볼릭 링크 경쟁 조건, 심각도 6.3
  - 수신자의 `open()` 호출에 대한 심볼릭 링크 경쟁 조건 수정(CVE-2026-29518)이 `chmod`, `lchown`, `utimes`, `rename`, `unlink`, `mkdir`, `symlink`, `mknod`, `link`, `rmdir`, `lstat` 등 다른 경로 기반 시스템 호출에는 적용되지 않았음
  - `use chroot = no`로 구성된 rsync 데몬에서 로컬 공격자가 수신자의 검사와 시스템 호출 사이에 부모 디렉터리 구성요소를 심볼릭 링크로 바꿔 내보낸 모듈 밖으로 리디렉션할 수 있음
  - 기본값인 `use chroot = yes`는 노출되지 않음
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/30656c5e358b1c6033f4caf24f3e11b29d25c9f4)은 Linux 5.6+의 `openat2`, FreeBSD 13+와 macOS 15+의 `O_RESOLVE_BENEATH`, 그 외 환경의 구성요소별 `O_NOFOLLOW` 순회처럼 커널이 강제하는 제한 아래 열린 부모 `dirfd`를 통해 영향을 받는 경로 기반 시스템 호출을 처리함
  - gokrazy/rsync는 Go의 **`os.Root` API**를 전반적으로 사용하기 때문에 영향을 받지 않음
- ## CVE-2026-43617: 호스트명 기반 ACL 우회, 심각도 4.8
  - 전역 `daemon chroot = /X` rsyncd.conf 설정이 있는 rsync 데몬에서 접속 클라이언트의 역방향 DNS 조회가 데몬이 `/X`로 chroot한 뒤 수행됐음
  - `/X`에 glibc 해석에 필요한 `/etc/resolv.conf`, `/etc/nsswitch.conf`, `/etc/hosts`, NSS 서비스 모듈이 없으면 조회가 실패하고 접속 호스트명이 `"UNKNOWN"`으로 설정됨
  - `hosts deny = *.evil.example` 같은 **호스트명 기반 deny 규칙**이 매칭되지 않아, PTR 레코드를 제어하는 공격자가 관리자가 차단하려던 호스트명에서 접속할 수 있음
  - IP 기반 ACL은 영향을 받지 않고, 모듈별 `use chroot` 설정은 이 취약점과 무관함
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/c38f20c5ffabacd0c0c483786ab224a36e14bf43)은 DNS 조회를 프로토콜의 더 이른 시점으로 이동함
  - gokrazy/rsync는 호스트명 기반 allow/deny 목록을 구현하지 않고 **IP 기반 allow/deny 목록만** 구현해 취약하지 않음
- ## CVE-2026-45232: 스택 범위 밖 쓰기, 심각도 3.1
  - rsync 클라이언트의 HTTP `CONNECT` 프록시 지원에는 `establish_proxy_connection()`의 off-by-one 스택 범위 밖 쓰기가 있음
  - 프록시 또는 중간자 공격자가 첫 응답 줄에 `'\n'` 없이 1023바이트 이상을 반환하면, 후속 코드가 1024바이트 버퍼 바로 뒤에 `'\0'`을 써 인접 스택 슬롯을 손상시킬 수 있음
  - AddressSanitizer는 `establish_proxy_connection` 프레임의 `socket.c:95`에서 `stack-buffer-overflow`를 보고함
  - [upstream 수정](https://github.com/RsyncProject/rsync/commit/a5fc5ebe7a8ef1aa72f6e344599f97fd4427ecba)은 공격자가 제공한 데이터를 검증함
  - gokrazy/rsync는 이런 **프록시 지원**을 구현하지 않아 취약하지 않음

### Go와 gokrazy/rsync가 실제로 막은 것
- Go 런타임의 **경계 검사**는 더 심각한 보안 문제를 panic으로 바꾸며, panic은 여전히 서비스 거부 위험이지만 더 나은 실패 모드로 평가됨
- Go는 메모리를 0으로 초기화해 CVE-2024-12085 같은 정보 유출을 불가능하게 만듦
- Go의 `os.Root` API는 남은 취약점 대부분을 방지함
- 12개 취약점 중 **CVE-2026-43617 하나만** Go 사용으로 막을 수 없는 애플리케이션 로직 버그로 분류됨
- gokrazy/rsync와 공식 upstream rsync의 핵심 차이는 Go로 작성됐다는 점 외에도 **구현이 최소화**돼 있다는 점임
- gokrazy/rsync는 `--inc-recursive`, `--safe-links`, 압축, 프록시, 호스트명 ACL처럼 문제가 된 여러 기능을 구현하지 않아 여러 취약점을 피함
- 모든 wire protocol 호환 rsync 구현처럼 gokrazy/rsync는 프로토콜 버전 27을 대상으로 하며, 이후 프로토콜 버전은 상당한 복잡성을 도입함
- 게시 시점 기준 CVE별 gokrazy/rsync 상태:
  - `2024-12084`: 구현은 있었고 panic 발생
  - `2024-12085`: 프로토콜 30 기능이라 구현하지 않아 취약하지 않음
  - `2024-12086`: 프로토콜 29 기능이라 구현하지 않아 취약하지 않음
  - `2024-12087`: `inc-rec`를 구현하지 않아 취약하지 않음
  - `2024-12088`: `safe-links`를 구현하지 않아 취약하지 않음
  - `2024-12747`: 구현이 있었고 취약했음
  - `2026-29518`: 구현이 있었고 패치됨
  - `2026-43617`: 호스트 deny 목록을 구현하지 않아 취약하지 않음
  - `2026-43618`: 압축을 구현하지 않아 취약하지 않음
  - `2026-43619`: 구현이 있었고 패치됨
  - `2026-43620`: `inc-rec`를 구현하지 않아 취약하지 않음
  - `2026-45232`: 프록시를 구현하지 않아 취약하지 않음
- gokrazy/rsync의 알려진 모든 취약점은 수정됐으며, 위 상태는 각 CVE가 공개됐던 시점의 상태를 나타냄
- 2025년 1월 취약점 공개 당시 gokrazy/rsync는 CVE-2024-12084에서 panic이 발생했고 CVE-2024-12747 TOCTOU 경쟁 조건에는 취약했음
- TOCTOU 문제를 수정하는 과정에서 CVE-2026-29518이 발견됐고, 해당 CVE 공개 전에 gokrazy/rsync에서 수정됨
- CVE-2026-43619는 더 늦게 발견됐지만, Go의 **`os.Root` 전면 사용**이라는 같은 수정으로 이미 gokrazy/rsync에서 해결돼 있었음

### 역할 구분과 취약점 명명
- 여러 취약점 보고서는 “server”와 “client”라는 표현을 사용했지만, rsync 전송에서는 rsync 클라이언트와 서버 양쪽 모두가 송신자(sender, 파일 업로드) 또는 수신자(receiver, 파일 다운로드) 역할을 맡을 수 있음
- 데몬 모드에서는 파일시스템 접근을 미리 구성된 모듈 경로로 제한할 수 있지만, command mode에서는 그렇지 않음
- 4가지 구성과 역할/프로토콜 계층은 [rsync combinations](https://michael.stapelberg.ch/posts/2026-05-24-minimal-memory-safe-go-rsync-vulns/rsync-combinations.png) 다이어그램에서 확인 가능함
- Arbitrary File Leak 취약점(CVE-2024-12086)의 원래 제목인 “Server leaks arbitrary client files”는 오해를 부르기 쉬움
- 더 정확한 표현은 **rsync 수신자**가 **악의적 송신자**에게 임의 파일을 유출한다는 것임
- 악의적 클라이언트 송신자는 SSH를 통한 command mode 같은 환경에서 패치되지 않은 원격 rsync가 대상 트리 밖의 파일, 예를 들어 `/etc/shadow` 시스템 비밀번호 데이터베이스를 열게 만들 수 있음
- 데몬 모드에서는 서버가 추가 경로 정규화(path sanitization)를 활성화해 이 공격을 막음
- Symlink Path Traversal 취약점(CVE-2024-12087)도 “malicious server”라고 표현하지만, 정확히는 클라이언트 또는 서버 어느 쪽이든 될 수 있는 **악의적 송신자**임

### OpenBSD openrsync와 비교
- OpenBSD 프로젝트는 보안에 집중하는 것으로 알려져 있으며, openrsync는 체크섬 길이를 검증하고 체크섬 크기/알고리듬으로 MD4 하나만 지원해 Heap Buffer Overflow(CVE-2024-12084)와 Stack Info Leak(CVE-2024-12085)의 영향을 받지 않음
- openrsync는 gokrazy/rsync처럼 관련 기능을 구현하지 않아 CVE-2024-12086, CVE-2024-12087, CVE-2024-12088의 영향을 받지 않음
- 설령 취약했더라도 OpenBSD에서 실행될 때는 [OpenBSD `unveil(2)`](https://man.openbsd.org/unveil.2)와 [`pledge(2)`](https://man.openbsd.org/pledge.2)로 파일시스템 접근을 제한하는 **심층 방어**가 성공적 악용을 막았을 수 있음
- openrsync는 심볼릭 링크 지원을 구현한 순간부터 [`O_NOFOLLOW`를 사용](https://github.com/kristapsdz/openrsync/commit/8f1927c35021cd1fbf31cb8c5a6393cc37ba4de8)했기 때문에 CVE-2024-12747의 영향을 받지 않음
- 하지만 `O_NOFOLLOW`는 이 문제에 충분한 수정이 아니므로 openrsync는 **CVE-2026-29518에는 영향**을 받음
- 2026년 5월 묶음도 대부분 관련 기능을 구현하지 않았다는 점에서 유사함
- openrsync는 검증을 성실히 구현하고 공격 표면을 제한하며 심층 방어를 적용해 보고된 취약점 대부분의 영향을 받지 않음

### 심층 방어와 os.Root
- ## Linux mount namespace
  - gokrazy/rsync 프로젝트를 시작한 지 몇 주 안에 파일시스템 객체 접근을 제한하기 위해 Linux에서 권한 강하와 mount/pid namespace 사용을 지원하는 기능이 [추가됨](https://github.com/gokrazy/rsync/commit/d63aaed3be4f69ac98581c4b0ac2646c37f1f8c0)
  - 이 접근은 **경로 순회 공격** 완화에 잘 작동하지만 권한이 필요해 `root`로 실행하거나 배포판/시스템에서 활성화된 경우 [Linux user namespace](https://github.com/gokrazy/rsync/commit/da9802e33c89af947d4958ec31e51b14f43ebfb2) 안에서 실행해야 함
  - mount namespace는 서버 구성에는 적합하지만, 보통 사람 사용자의 계정으로 실행되는 대화형 일회성 전송에는 사용할 수 없는 경우가 많음
- ## systemd 하드닝
  - Linux mount/pid namespace 지원을 도입한 같은 커밋에는 홈 디렉터리로 파일시스템 접근을 제한하는 systemd 서비스 파일도 포함됐고, README에서는 사용 사례에 따라 파일시스템 접근을 더 제한하도록 권장함
  - 이런 파일시스템 제한이 올바르게 구성되면 File Leak(CVE-2024-12086)과 Path Traversal(CVE-2024-12087) 취약점을 완화함
  - Symlink Race Condition(CVE-2024-12747)은 rsync 프로세스를 통한 권한 상승에 의존하지만, [DynamicUser](https://0pointer.net/blog/dynamic-users-with-systemd.html) 기능 덕분에 프로세스가 다른 사용자보다 적은 권한을 가짐
  - mount namespace와 마찬가지로 서버 구성에는 좋지만 대화형 일회성 사용에 설정하기에는 번거로움
- ## Linux Landlock
  - [Porting OpenBSD pledge() to Linux (2022)](https://justine.lol/pledge/#paths)를 통해 Linux에도 OpenBSD의 [`unveil(2)`](https://man.openbsd.org/unveil)와 유사한 비권한·프로세스별 접근 제어인 [Landlock API](https://docs.kernel.org/userspace-api/landlock.html)가 있음을 상기함
  - 기본 아이디어는 프로그램이 작업할 디렉터리를 알고 나면 `unveil("/home/michael/backups", "rw");` 같은 호출로 해당 경로 외의 파일시스템 위치에 더 이상 접근하지 못하게 하는 것임
  - 2025년 3월 파일시스템 접근 제한을 위해 [Landlock 지원이 구현됨](https://github.com/gokrazy/rsync/commit/f84d1e39ec6613599cd9610974e89be5ca183a6d)
  - 설정이 맞춰진 뒤에는 비권한 `gokrazy/rsync` 실행에서도 rsync 전송을 소스에는 읽기 전용, 대상 디렉터리에는 읽기·쓰기 권한으로 제한할 수 있음
  - 단점은 Landlock이 프로세스 수준에서 동작한다는 점임
  - Landlock 정책에는 프로그램이 필요한 파일도 포함돼야 하며, 예를 들어 `gokrazy/rsync`는 사용자 ID 조회를 위해 `/etc/passwd`를 읽을 수 있어야 하므로 공격자가 `/etc/passwd`를 노린다면 Landlock은 도움이 되지 않음
- ## Go의 os.Root
  - 2025년 2월 Go 1.24는 경로 순회에 강한 [`os.Root`](https://pkg.go.dev/os#Root) API를 도입했으며, 관련 배경은 Damien Neil의 [The Go Blog: Traversal-resistant file APIs](https://go.dev/blog/osroot)에 정리돼 있음
  - `os.Root`는 Landlock과 비교해 파일시스템 작업별로 더 세밀한 제어를 제공함
  - 2025년 8월 출시된 Go 1.25는 `os.Root`에 더 많은 메서드를 추가해 대부분의 파일시스템 사용에 편리한 선택지가 됨
  - `gokrazy/rsync`의 모든 파일시스템 사용은 `os.Root`로 전환됐고, 사용자가 입출력 디렉터리를 구성하지만 네트워크로 받은 파일명은 신뢰할 수 없다는 모델에 잘 맞음
  - `mknod(2)`처럼 API로 직접 만들 수 없을 것 같은 시스템 호출도 안전하게 사용할 수 있음
  - Damien Neil은 `os.Root.OpenFile`로 대상의 부모 디렉터리를 열고, `File.Fd`로 해당 디렉터리 파일 디스크립터를 얻은 뒤, [`golang.org/x/sys/unix#Mknodat`](https://pkg.go.dev/golang.org/x/sys/unix#Mknodat)로 파일을 만들 수 있다고 설명함
  - 실제 사용 예시는 [`internal/receiver/generatormknod_linux.go`, line 15-29](https://github.com/gokrazy/rsync/blob/45444634288e753178af56c61e064ffb08636695/internal/receiver/generatormknod_linux.go#L15-L29)에 있음
  - Linux에는 [`mknodat(2)`](https://manpages.debian.org/mknodat.2)는 있지만, Linux 7.0 기준 [`bind(2)`](https://manpages.debian.org/bind.2)에 대응하는 `bindat`는 없음
  - [Lennart Poettering은](https://mastodon.social/@pid_eins/115869484548013992) `bindat` 없이 경로 해석을 건너뛰는 트릭으로 `/proc/self/&lt;fd&gt;/foobar`에 bind할 수 있다고 제안함
  - 알려진 안전한 `/proc/self/&lt;fd&gt;` 뒤에 경로가 아니라 basename, 즉 마지막 경로 구성요소만 지정하면 경로 해석이 건너뛰어지며, 관련 코드는 [line 49-56](https://github.com/gokrazy/rsync/blob/45444634288e753178af56c61e064ffb08636695/internal/receiver/generatormknod_linux.go#L49-L56)에 있음
  - 이 두 가지 팁을 통해 `gokrazy/rsync` v0.3.1 이상은 `os.Root`를 완전히 사용하며, 모든 파일시스템 접근이 **경로 순회 안전성**을 갖게 됨

### 핵심 교훈
- TOCTOU 취약점(CVE-2024-12747, CVE-2026-29518, CVE-2026-43619)을 제외한 모든 취약점은 입력 검증 누락 또는 잘못된 입력 검증에서 비롯됨
- 세 경우에는 애초에 검증이 없었고, CVE-2024-12088은 파일시스템 경로 해석이라는 주제가 까다로워 기존 검증이 모든 경계 사례를 다루지 못한 경우임
- 가장 가치 있는 구조적 수정은 경계 검사처럼 항상 켜진 검증과 Go의 `os.Root` 같은 **기본 안전 API**를 제공하는 것임
- 일부 취약점은 rsync 프로토콜 진화에서 발생했으며, 원래는 충분한 검증을 하던 코드에 새 기능이 추가되면서 검증이 올바르게 갱신되지 않았음
- 체크섬 알고리듬 협상과 증분 재귀는 프로토콜 버전 30에 추가됐고, 기존 검증은 새 기능의 처리 방식에 맞게 업데이트되지 않았음
- gokrazy/rsync와 openrsync는 취약한 기능을 구현하지 않았다는 이유만으로 12개 보안 취약점 중 **8개**에 취약하지 않았음
- 해당 기능들은 어느 시점에 누군가에게 가치가 있었기 때문에 rsync에 추가됐으며, 소프트웨어 개발을 멈춰야 한다는 의미는 아님
- 이상적인 선택은 사용 사례의 복잡성에 적절하고 비례하는 복잡성의 구현을 사용하는 것이며, 단순한 사용 사례에는 단순한 구현을 선택하고 필요할 때만 모든 기능을 갖춘 구현을 선택하는 것임

## Comments



### Comment 58211

- Author: neo
- Created: 2026-05-26T00:23:20+09:00
- Points: 1

###### [Lobste.rs 의견들](https://lobste.rs/s/5y9u93/how_my_minimal_memory_safe_go_rsync_steers) 
- 훌륭한 글임. 모든 언어가 표준 라이브러리에 **`os.Root` 같은 API**를 넣어야 할 만큼 좋음  
  SecureDrop에서 몇 번의 **경로 순회 취약점**을 겪은 뒤 아주 단순화한 버전을 썼고, 이제 올바른 API를 쓰면 취약점 한 부류가 통째로 사라짐
  - 맞음. 이 블로그 글의 주인공은 Go라기보다 **`os.Root`** 인 것 같음
