tailnet policy 오류 메시지가 가리키는 단계
tailscale: tailnet policy does not permit you to SSH to this node라는 배너를 실제로 받았다면 Tailscale SSH 인증 경로에 도달한 뒤 정책 평가가 거부됐거나 정책을 얻지 못한 경우입니다. v1.104.1 서버 소스의 clientAuth는 이를 rejected 또는 noPolicy로 나누며, 메시지에 as user가 나오면 별도의 rejectedUser 분기입니다. 우선 source·destination에 맞는 SSH 규칙과 요청한 OS 사용자를 확인합니다. node 정책 배너와 TCP 연결 시간 초과를 같은 증거로 다루지 않습니다.
이 구분은 소스 대조 결과이며 실제 tailnet 장애를 재현한 결과가 아닙니다. 전체 연결에는 TCP port 22의 네트워크 권한도 필요합니다. 아래에서 연결 경로, SSH 규칙, check 재인증을 차례로 분리하고, 메시지만으로 특정 selector가 잘못됐다고 단정하지 않습니다.
같은 ssh 명령이 두 서버 경로를 가질 수 있다
Tailscale SSH를 켠 Linux node에서는 tailnet에서 들어오는 port 22 연결을 Tailscale이 가로채 인증과 권한을 처리합니다. LAN 주소나 public 주소로 들어오는 기존 OpenSSH 연결은 계속 sshd가 처리할 수 있습니다. 따라서 한쪽 경로의 성공이나 실패를 다른 경로의 증거로 사용하면 안 됩니다.
Tailscale은 /etc/ssh/sshd_config와 ~/.ssh/authorized_keys를 수정하지 않는다고 문서화합니다. Tailscale SSH 거부를 해결하려고 두 파일을 먼저 바꾸면 정상적인 기존 SSH만 약화시키고, tailnet policy 오류는 그대로 남을 수 있습니다.
인증 전에 통과해야 하는 권한을 구분한다
| 단계 | 확인 대상 | 막히면 보이는 범위 |
|---|---|---|
| node 연결 | source와 destination의 online 상태 | SSH 이전의 tailnet 연결 문제입니다. |
| 네트워크 권한 | destination의 TCP port 22 허용 | SSH 규칙까지 요청이 도달하지 않습니다. |
| SSH 규칙 | src, dst, users, action | 허용되지 않은 OS 사용자 또는 source입니다. |
| check 동작 | 재인증 prompt와 checkPeriod | 거부가 아니라 추가 인증 대기일 수 있습니다. |
| 로컬 계정 | destination의 실제 사용자 존재 | policy가 허용해도 로그인할 계정이 없습니다. |
정책을 바꾸기 전 연결 증거
tailscale status
tailscale ip -4
tailscale ping <DESTINATION>
ssh -vvv <USER>@<DESTINATION>
tailscale ping 실패도 관찰했다면 사용한 ping 모드와 node 연결을 먼저 구분합니다. destination node가 offline인지, source가 다른 tailnet에 로그인했는지, device approval 또는 key 상태에 문제가 있는지 먼저 확인합니다. ssh -vvv 출력은 host name, 실제 destination 주소, 인증 단계가 어디까지 갔는지 남기는 데 사용합니다.
기본 discovery ping 또는 --tsmp 성공은 TCP 22의 grant나 SSH 사용자 허용을 증명하지 않습니다. --icmp는 ICMP 조건을 검사하므로 TCP 22와 별개입니다. ping 결과와 실제 SSH 배너를 함께 보세요.
네트워크 grant와 SSH 규칙을 각각 읽는다
Tailscale 공식 policy 문서는 Tailscale SSH 연결에 네트워크 접근과 SSH 접근이 모두 필요하다고 설명합니다. grant 또는 기존 ACL이 source에서 destination의 TCP port 22를 허용해야 하며, 별도의 ssh 규칙이 source, destination, 허용 OS 사용자를 지정해야 합니다. 한쪽만 추가해도 연결은 완성되지 않습니다.
정책 편집기에서 test 기능을 사용할 수 있다면 실제 source와 destination selector를 넣어 예상 결과를 확인합니다. 넓은 wildcard를 임시로 허용해 보는 방식은 원인 범위를 줄이지 못하고 불필요한 접근을 열 수 있습니다.
한 사람·한 서버·한 OS 계정으로 좁힌 정책 예제
다음은 [email protected]의 사용자 소유 클라이언트에서 tag:home-server 서버의 기존 ubuntu 계정으로 접속하는 예제입니다. 이메일·태그·OS 계정을 실제 환경에 맞게 바꾸고, 서버의 태그 지정과 Tailscale SSH 활성화는 별도로 확인합니다. TCP grant 예제와 SSH 규칙의 역할을 결합한 설명용 구성으로, 특정 tailnet에 적용해 로그인한 결과는 아닙니다.
운영 정책 전체를 이 예제로 덮어쓰지 마세요. 기존 정책을 백업하고 필요한 항목만 병합한 뒤 편집기의 검사를 통과시킵니다. 다른 allow 규칙이 이미 있다면 아래의 거부 예상이 달라질 수 있습니다.
{
"tagOwners": {
"tag:home-server": [
"autogroup:admin"
]
},
"grants": [
{
"src": [
"alice@example.com"
],
"dst": [
"tag:home-server"
],
"ip": [
"tcp:22"
]
}
],
"ssh": [
{
"action": "check",
"src": [
"alice@example.com"
],
"dst": [
"tag:home-server"
],
"users": [
"ubuntu"
],
"checkPeriod": "12h"
}
]
}
| 확인할 연결 | 이 구성에서의 해석 | 다음 확인 |
|---|---|---|
| Alice → 서버 → ubuntu | TCP 22와 SSH 규칙이 모두 일치 | check 재인증과 실제 OS 계정까지 확인 |
| Alice → 서버 → root | 이 예제에는 root 허용이 없음 | root를 추가하기 전 정말 필요한 계정인지 판단 |
| 다른 사용자 → 서버 → ubuntu | 이 예제의 source와 불일치 | 전체 정책에 더 넓은 기존 허용이 있는지도 확인 |
| Alice → 태그 없는 서버 → ubuntu | destination 태그와 불일치 | Machines 화면에서 실제 태그와 소유 상태 확인 |
표는 위 구성만 있을 때의 규칙 해석입니다. 실제 로그인은 네트워크 상태·서버 기능·계정·재인증까지 통과해야 합니다. source 장치 자체가 태그 소유 장치라면 이 사용자 source 예제를 그대로 쓰지 않습니다. 태그 간 접속은 별도 규칙이 필요하며 tagged source에는 check 모드를 사용할 수 없습니다.
node 배너와 user 배너의 다음 행동
node 배너는 v1.104.1 소스에서 정책 부재와 거부가 같은 문구를 사용하므로, ‘포트가 막혔다’ 또는 ‘특정 태그가 틀렸다’로 곧바로 확정할 수 없습니다. 서버가 받은 정책과 source/destination 선택을 먼저 봅니다. user 배너는 요청한 OS 사용자와 규칙의 users를 대조합니다. ssh ubuntu@SERVER처럼 사용자를 명시하면 클라이언트의 로컬 사용자명이 자동 사용되는 혼동을 줄일 수 있습니다.
서버 지원 범위: Synology·QNAP에서는 Tailscale SSH 서버를 지원하지 않습니다. NAS에서 일반 OpenSSH를 tailnet 경유로 쓰는 경우에는 port 22 네트워크 권한과 NAS의 sshd 인증을 확인합니다. Linux용 Tailscale SSH 활성화 명령을 NAS에 그대로 적용하지 마세요.
action이 accept인지 check인지 확인한다
accept는 일치하는 연결을 허용하고, check는 identity provider 재인증을 요구할 수 있습니다. 공식 문서가 설명하는 기본 check 기간은 12 hours이며 정책의 checkPeriod로 다른 기간을 지정할 수 있습니다. 브라우저 재인증 창이 다른 화면 뒤에 있거나 source의 identity session이 만료됐다면 SSH client는 단순 지연처럼 보일 수 있습니다.
root 같은 고위험 계정은 check를 유지하는 편이 안전합니다. 문제 확인을 위해 action을 accept로 바꾸기보다, prompt가 열리는지와 재인증 완료 후 같은 명령이 통과하는지를 기록합니다.
서버 측 활성화와 OS 사용자
먼저 조회만 합니다. 아래 prefs는 설치 버전이 제공할 때 사용하는 읽기 전용 진단이며, 전체 출력에는 민감한 설정이 있을 수 있어 RunSSH만 필터링합니다. 명령이 없거나 필드가 달라 unknown이면 활성 여부를 추정하지 말고 해당 버전의 도움말·관리 화면으로 확인합니다. 상태 조회는 실제 SSH 허용 정책과 로그인 성공을 보장하지 않습니다.
tailscale version
tailscale debug --help
# Only when this installed version provides the read-only prefs command:
set -o pipefail
sudo tailscale debug prefs | python3 -c 'import json,sys; p=json.load(sys.stdin); v=p.get("RunSSH"); print("RunSSH="+str(v).lower() if isinstance(v,bool) else "RunSSH=unknown"); sys.exit(0 if isinstance(v,bool) else 1)'
getent passwd USER
활성화가 필요하다고 확인한 경우에만 아래 변경을 수행합니다. 기존 RunSSH 값을 기록하고, Tailscale SSH와 별개로 정상 동작하는 관리 세션이나 콘솔을 먼저 확보하세요. 이 명령은 조회가 아니라 tailnet의 port 22 처리 경로를 바꾸는 작업입니다.
sudo tailscale set --ssh=true
원복은 실제로 기록한 이전 값으로만 합니다. 원래 켜져 있던 기능을 일괄 false로 바꾸지 않습니다. 아래 마지막 절의 끄기 명령은 Tailscale SSH를 사용하지 않기로 결정한 경우에만 해당합니다. macOS 변종·컨테이너·userspace 환경은 해당 버전 문서의 서버 지원 조건을 확인하세요. 특정 tailnet에서 활성화·정책 변경 실험은 하지 않았습니다.
검증과 롤백
정책 수정 뒤에는 허용해야 하는 source와 거부돼야 하는 source를 각각 시험합니다. 성공 사례 하나만 보면 wildcard가 과도하게 열렸는지 알 수 없습니다. session을 새로 열어 사용자와 destination을 다시 확인하고, 기존 LAN SSH도 별도로 검사합니다.
Tailscale SSH를 사용하지 않기로 했다면 destination에서 아래 명령으로 interception을 끕니다. 이 동작은 기존 sshd_config나 authorized keys를 복원하는 작업이 아닙니다. 애초에 Tailscale이 두 파일을 수정하지 않기 때문입니다.
sudo tailscale set --ssh=false
tailscale status
ssh -vvv <USER>@<LAN_IP>
같이 확인할 글
공식 문서 및 확인 기준
- Tailscale SSH – port 22 interception, 기존 sshd와 key 파일의 범위, check mode
- Tailnet policy syntax – 네트워크 접근과 SSH 규칙의 독립 조건
- Tailscale grants – source, destination과 TCP port 권한
- Tailscale connectivity troubleshooting – SSH 이전 node 연결 분리
이 글에 남아 있던 이전 검증 기록
검증 수준: 공식 문서 대조
Tailscale SSH의 네트워크 권한, SSH 규칙, check mode 경계를 공식 문서로 확인했습니다. 특정 tailnet policy는 재현하지 않았으므로 실제 selector와 identity 매핑은 운영자가 검증해야 합니다.