프로그래밍/기타

Tailscale SSH 보안 이슈(Tailworm) 정리 및 패치 전 대응 방법

SeanYoon 2026. 10. 27. 11:35
반응형

2026년 10월 25일, Tailscale과 headscale에서 11개의 보안 이슈를 정리한 "Tailworm" 보고서가 공개되면서 꽤 시끄러웠습니다.

저도 예전에 소개드린 집 PC/서버를 이용한 Public Service 구성처럼 클라우드와 집 서버를 Tailscale로 묶어서 쓰고 있어서, 무엇이 문제이고 패치가 나오기 전까지 무엇을 해두면 되는지 정리해봤습니다.

 

3줄 요약
1. Tailscale SSH가 소켓 포워딩을 root 권한으로 처리해서, tailnet 안의 기기 1대가 털리면 SSH가 허용된 다른 서버의 root까지 갈 수 있다는 내용입니다.
2. 2026.10.27 기준 공식 보안 공지·CVE·패치 릴리스는 아직 없습니다. (최신 클라이언트 v1.104.2)
3. 패치 전까지는 불필요한 Tailscale SSH 끄기 / 포워딩 차단(TS_SSH_DISABLE_FORWARDING) / ACL 축소가 핵심입니다.

 

1. 무슨 일이 있었나

Cyberdione Labs의 Erica Windisch가 Tailworm이라는 이름으로 tailscale 1.104.2, headscale 0.29.5를 랩 환경에서 테스트한 결과를 공개했습니다. 사전 조율(coordinated disclosure) 없이 공개되어 패치가 없는 상태로 알려졌습니다.

가장 중요한 건 Tailscale SSH 관련 내용입니다.

  • Tailscale SSH(tailscale set --ssh)는 일반 sshd가 아니라 root로 동작하는 tailscaled 안에서 SSH를 처리합니다.
  • OpenSSH는 포워딩 전에 로그인한 사용자 권한으로 내려가는데, Tailscale SSH는 ssh -L / ssh -R 유닉스 소켓 포워딩을 root 권한으로 처리합니다.
  • 그 결과 일반 사용자 세션으로도 /run/docker.sock, /run/systemd/private 같은 root 전용 소켓에 접근해 root 권한을 얻을 수 있었다고 합니다. (심링크 TOCTOU 레이스, 기존 소켓 덮어쓰기 포함)
  • tailscaled 관리 소켓을 가로채 authkey를 빼내고, 공격자 기기를 tailnet에 등록하는 것까지 시연했다고 합니다. (Tailnet Lock이 꺼진 환경 기준)

그 외에 App Connector(실험 기능 conn25)의 목적지 제어 문제, tailscale serve의 권한 우회, headscale 비인증 등록 요청으로 인한 DoS 등이 함께 포함되어 있습니다.

 

오해하면 안 되는 점: 인터넷에서 바로 뚫리는 RCE가 아닙니다.

공격자는 먼저 tailnet 안의 기기 1대를 장악해야 하고, ACL에서 그 기기가 대상 서버로 Tailscale SSH 접속이 허용되어 있어야 합니다. 즉 내부 확산(lateral movement) + 권한 상승 문제입니다. 일반 OpenSSH를 Tailscale 위로 쓰는 경우는 이 소켓 포워딩 이슈와 관계없다고 Tailscale 측(VP of Engineering Mike Shaver)이 밝혔습니다.

Tailworm 공격 흐름과 패치 전 차단 지점

 

2. 현재 패치 상태 (2026.10.27 기준)

항목 상태
Tailscale 보안 공지 최신이 TS-2026-013 (eMudhra 인증서 건) → Tailworm 관련 공지 없음
Tailscale 릴리스 v1.104.2 (10/21)가 최신. 수정 버전 미출시
main 브랜치 10/26 커밋 eba00ab: 포워딩을 root가 아닌 로그인 사용자 권한의 자식 프로세스에서 처리하도록 수정 (릴리스 전)
headscale v0.29.6 (10/24) 출시됐지만 Tailworm 수정은 미포함
CVE 미발급

커밋이 머지된 것과 패치가 배포된 것은 다릅니다. 릴리스가 나오기 전까지는 아래 임시 조치를 적용해두는 것이 좋습니다.

 

3. 내가 영향을 받는지 확인하기

Tailscale이 설치된 서버마다 아래 명령으로 확인합니다. RunSSH가 true인 노드가 핵심 점검 대상입니다.

# 버전 확인
tailscale version

# Tailscale SSH 사용 여부 ("RunSSH": true 이면 점검 대상)
tailscale debug prefs | grep -i runssh

# App Connector 사용 여부 ("Advertise": true 이면 점검 대상)
tailscale debug prefs | grep -i -A2 appconnector

# serve / funnel 설정 확인 (unix: 소켓으로 포워딩하는 항목이 있는지)
tailscale serve status
tailscale funnel status

# tailscaled에 걸린 환경변수 확인
systemctl show tailscaled -p Environment

서버가 여러 대라면 이렇게 한 번에 훑어볼 수 있습니다.

for h in server1 server2 server3; do
  echo "===== $h"
  ssh "$h" 'tailscale version | head -1; tailscale debug prefs | grep -i runssh'
done

 

4. 해결 방법 (패치 전 임시 조치)

4-1. Tailscale SSH가 필요 없다면 끄기

가장 확실한 방법입니다. 다만 Tailscale SSH가 유일한 접속 경로라면 끄는 순간 접속이 끊길 수 있으니, OpenSSH 등 다른 접속 경로를 먼저 확인하고 진행하세요.

# 다른 접속 경로(OpenSSH)가 살아있는지 먼저 확인 (Ubuntu: ssh / RHEL 계열: sshd)
sudo systemctl status ssh

# Tailscale SSH 끄기
sudo tailscale set --ssh=false

# "RunSSH": false 확인
tailscale debug prefs | grep -i runssh

 

4-2. Tailscale SSH를 계속 써야 한다면 포워딩 차단

tailscaled에는 TS_SSH_DISABLE_FORWARDING 환경변수가 있고, true로 설정하면 컨트롤 서버 정책과 관계없이 TCP/유닉스 소켓 포워딩을 모두 거부합니다. 공식 문서화된 옵션은 아니지만 headscale 환경에서도 동작하고, 수정 커밋에서도 이 변수 테스트가 추가되어 유지되는 것으로 보입니다.

systemd 환경에서는 drop-in 파일로 넣으면 됩니다.

sudo mkdir -p /etc/systemd/system/tailscaled.service.d

cat <<'EOF' | sudo tee /etc/systemd/system/tailscaled.service.d/10-disable-ssh-forwarding.conf
[Service]
Environment=TS_SSH_DISABLE_FORWARDING=true
EOF

sudo systemctl daemon-reload
sudo systemctl restart tailscaled

# 적용 확인
systemctl show tailscaled -p Environment

주의) tailscaled를 재시작하면 Tailscale 연결이 잠깐 끊깁니다. Tailscale SSH로 접속한 상태라면 세션이 끊길 수 있으니 OpenSSH나 콘솔에서 작업하세요.

sudo systemctl edit tailscaled로 편집기를 열어 아래 내용을 직접 넣어도 같습니다.

[Service]
Environment=TS_SSH_DISABLE_FORWARDING=true

적용 후 다른 기기에서 포워딩이 거부되는지 확인해봅니다.

# 터미널 1: 로컬 포워딩 시도
ssh -v -N -L 18080:127.0.0.1:22 user@server-name

# 터미널 2: 포워딩 포트로 접속
nc -v 127.0.0.1 18080

# 터미널 1의 로그에 "open failed" 류의 메시지가 찍히고 연결이 끊기면 차단된 것

 

4-3. SSH ACL을 1:1 쌍으로 좁히기

보고서에서 확산을 실제로 막은 유일한 통제가 "출발지 1개 → 목적지 1개"로 좁힌 SSH 규칙이었다고 합니다. 관리 콘솔의 Access controls에서 ssh 섹션을 확인하세요. 대상 계정 범위가 넓은 규칙을 제거하는 게 중요합니다. (non-root 계정으로 제한해도 이번 이슈는 일반 사용자 세션에서 터지므로 그것만으로는 해결되지 않습니다.)

이런 넓은 규칙은 제거 대상입니다.

{
  "action": "accept",
  "src":    ["autogroup:member"],
  "dst":    ["tag:prod"],
  "users":  ["root", "autogroup:nonroot"]
}

필요한 사람/기기만, 필요한 서버/계정으로, check 모드와 함께 좁혀줍니다.

{
  "action":      "check",
  "checkPeriod": "1h",
  "src":         ["sean@example.com"],
  "dst":         ["tag:homeserver"],
  "users":       ["sean"]
}

check 모드는 세션 시작 전에 재인증을 한 번 더 요구하는 것이라 근본 해결은 아니지만, 장벽을 하나 더 두는 효과가 있습니다.

 

4-4. serve / funnel 점검

tailscale serve --tcp로 unix: 소켓을 내보내는 설정, 특히 tailscaled.sock이나 docker.sock을 가리키는 항목이 있다면 제거하세요.

tailscale serve status
tailscale funnel status

# 문제 있는 설정이 있으면 초기화 (전체 설정이 지워지므로 필요한 것은 다시 등록)
sudo tailscale serve reset
sudo tailscale funnel reset

# TS_SERVE_ALLOW_ALL_INTERFACES 가 설정돼 있지 않아야 정상 (아무것도 안 나오면 OK)
systemctl show tailscaled -p Environment | grep -i SERVE_ALLOW_ALL_INTERFACES

 

4-5. 쓰지 않는 App Connector 해제

sudo tailscale set --advertise-connector=false

계속 써야 한다면 커넥터 호스트에서 클라우드 메타데이터(169.254.169.254)나 내부망 대역으로 나가지 못하도록 egress 제한을 걸어두는 것이 좋습니다.

 

4-6. 키 만료, Tailnet Lock, 침해 기기 처리

  • 태그가 붙은 서버는 기본적으로 키 만료가 꺼져 있습니다. 서버들도 노드 키 만료를 켜두세요. (관리 콘솔 Machines 메뉴)
  • 가능하면 Tailnet Lock을 켜서 탈취한 키로 새 기기를 등록하지 못하게 합니다. 비활성화 시크릿을 잃어버리면 복구가 안 되니 꼭 따로 보관하세요.
  • 침해가 의심되는 기기는 관리 콘솔에서 즉시 삭제하고 관련 auth key를 폐기/재발급합니다. 보고서에 따르면 OS 재설치만으로는 이미 복사된 키가 무효화되지 않습니다.
# Tailnet Lock 상태 확인 (모든 노드가 같은 서명 키를 보고해야 정상)
tailscale lock status

 

4-7. headscale을 쓰는 경우

headscale 0.29.5는 SSH accept 규칙에 포워딩이 자동으로 허용되고 끌 수 없다고 합니다. headscale의 SSH 허용 규칙은 사실상 root 권한을 주는 것으로 보고, 위의 4-2 (클라이언트 쪽 TS_SSH_DISABLE_FORWARDING)를 꼭 적용하세요. 그리고 0.29.6으로 업데이트(DB 백업 먼저)하고, 메모리 제한과 재시작 설정을 걸어둡니다.

# DB 백업 (기본 sqlite 경로 기준)
sudo systemctl stop headscale
sudo cp /var/lib/headscale/db.sqlite /var/lib/headscale/db.sqlite.bak.$(date +%F)

# 0.29.6 설치 (amd64 기준, arm64는 파일명만 변경)
wget https://github.com/juanfont/headscale/releases/download/v0.29.6/headscale_0.29.6_linux_amd64.deb
sudo apt install ./headscale_0.29.6_linux_amd64.deb
sudo systemctl start headscale
sudo mkdir -p /etc/systemd/system/headscale.service.d

cat <<'EOF' | sudo tee /etc/systemd/system/headscale.service.d/10-limits.conf
[Service]
MemoryMax=512M
Restart=always
RestartSec=5
EOF

sudo systemctl daemon-reload
sudo systemctl restart headscale

또한 headscale 등록 엔드포인트를 인터넷에 그대로 노출하지 말고, 앞단에 rate limit이 걸린 프록시를 두는 것을 권장합니다. (0.29.6에는 Tailworm 수정이 포함되어 있지 않습니다.)

 

5. 패치가 나오면

공식 보안 공지와 수정 릴리스가 나오면 바로 업데이트하고, 앞으로를 위해 자동 업데이트도 켜두세요.

# 최신 배포 버전 확인
tailscale version --upstream

# 업데이트 및 자동 업데이트 설정
sudo tailscale update
sudo tailscale set --auto-update

패치 적용 후 포워딩이 꼭 필요하다면 4-2에서 넣은 설정을 제거하면 됩니다. (필요 없다면 그대로 두는 것이 더 안전합니다.)

sudo rm /etc/systemd/system/tailscaled.service.d/10-disable-ssh-forwarding.conf
sudo systemctl daemon-reload
sudo systemctl restart tailscaled

 

6. 체크리스트

점검 항목 명령/위치 조치
Tailscale SSH 사용 여부 tailscale debug prefs | grep -i runssh 불필요하면 tailscale set --ssh=false
SSH 포워딩 tailscaled 환경변수 TS_SSH_DISABLE_FORWARDING=true
SSH ACL 관리 콘솔 Access controls 1:1 쌍 + check 모드, 넓은 규칙 제거
serve / funnel tailscale serve status unix 소켓 포워딩 제거
App Connector tailscale debug prefs 불필요하면 --advertise-connector=false
키 만료 / Tailnet Lock 관리 콘솔, tailscale lock status 서버도 키 만료 활성화
headscale 버전/systemd 설정 0.29.6 + MemoryMax, 외부 노출 제한
패치 tailscale version --upstream 릴리스 시 즉시 tailscale update

 

참고 자료

 

※ 이 글은 2026년 10월 27일 기준 공개된 정보를 정리한 것입니다. 보고서 내용은 연구자의 랩 테스트 결과이며 Tailscale이 세부 내용을 공식 확인한 것은 아닙니다. 공식 공지가 나오면 그 내용을 우선해주세요.

반응형