같은 증상으로 검색해 오신 분들께
증상: IKEv2 dialup + EAP-TTLS 구성에서 인증이 실패하거나, 인증은 성공하는데 트래픽이 통하지 않음. 실패 양상이 시도마다 다름. 환경: FortiGate 201F / FortiOS 7.4.11 / FortiClient 7.4.3 / FreeRADIUS 3.0.20 결론: 독립적인 원인 5개가 동시에 존재했다. 하나를 고칠 때마다 증상이 바뀌어서 계속 헛다리를 짚었다. 핵심 교훈: 증상이 시도마다 다르면 원인이 하나가 아니라고 가정하고, 원인을 세는 방식으로 접근한다. 검색 키워드: IKE SA created EAP-TTLS authentication failed sites-enabled FortiClient eap_method assign-ip-from usrgrp
1. 증상 — 무엇이 어떻게 실패했는가
Part 3, 4의 구성을 마치고 첫 접속을 시도했다. 실패했다. 여기까지는 예상 범위다.
문제는 그다음이었다. 같은 조건으로 다시 시도할 때마다 다른 곳에서 실패했다.
| 1회차 | IKE 성립, 인증 실패 |
| 2회차 | 인증 성공, IP 할당 안 됨 |
| 3회차 | 인증 성공, IP 할당됨, 내부망 통신 안 됨 |
| 4회차 | 인증 실패 (1회차와 다른 로그) |
| 5회차 | 특정 노트북에서만 성공 |
이 표를 만든 순간이 전환점이었다. 설정 하나를 못 찾는 상황이 아니라, 서로 무관한 문제가 여러 개 겹쳐 있는 상황이라는 게 여기서 보였다.
2. 처음 세운 가설과, 그것이 왜 틀렸는가
첫 가설: RADIUS 설정 어딘가가 잘못됐다. 인증 계층이 가장 새로 만든 부분이니까.
합리적이었지만 접근 방식이 틀렸다. 나는 하나의 원인을 찾으려 했다.
그래서 이런 루프를 돌았다. FreeRADIUS 설정을 바꾼다 → 접속해본다 → 다른 데서 실패한다 → "아직 못 찾았구나" → 다시 FreeRADIUS를 바꾼다. 실제로는 첫 번째 변경으로 원인 하나를 이미 고쳤는데, 남은 네 개 때문에 여전히 실패하니까 그 변경을 되돌려버린 적도 있었다.
이게 이 편에서 가장 값비싼 교훈이다.
증상이 매번 다르면, "고쳤는데도 실패한다"가 아니라 "하나 고쳤고 아직 남았다"일 수 있다. 이때 변경을 되돌리면 이미 해결한 것까지 잃는다.
그 뒤로 방식을 바꿨다. 변경사항을 기록하고, 효과가 없어 보여도 되돌리지 않았다. 대신 각 계층을 독립적으로 검증할 수 있는 방법을 찾았다.
3. 계층별 독립 검증 — 이걸 먼저 했어야 했다
전체 경로를 한 번에 테스트하지 않고 구간별로 쪼갰다.
[클라이언트] --(A)--> [FortiGate] --(B)--> [FreeRADIUS]
|
(C) 정책/라우팅
| A | diagnose vpn ike log filter rem-addr4 <IP> + IKE 디버그 | IKE 협상 어디까지 갔는지 |
| B | diagnose test authserver radius <서버> pap <계정> <PW+OTP> | RADIUS 왕복이 되는지 (VPN과 무관하게) |
| B | 서버에서 radiusd -X | RADIUS 내부에서 어떤 모듈까지 갔는지 |
| C | diagnose debug flow | 인증 후 트래픽이 어느 정책에 걸리는지 |
B 구간을 VPN과 분리해서 테스트할 수 있다는 것이 가장 중요했다. diagnose test authserver는 IKE를 거치지 않고 FortiGate에서 RADIUS로 직접 요청을 보낸다. 이게 성공하면 RADIUS는 무죄고, 문제는 A나 C에 있다.
이 명령 하나가 탐색 범위를 절반으로 줄였다.
4. 찾아낸 원인 다섯 개
발견 순서대로 쓴다. 논리적 순서가 아니라 실제 순서다.
원인 1 — 방화벽 정책이 없었다
증상: 인증 성공, IP 할당 성공, 그런데 내부망으로 아무것도 안 감. 사용자 화면에는 "연결됨"으로 표시된다.
가장 찾기 어려운 종류의 실패다. 모든 게 성공했다는 신호만 보이기 때문이다.
diagnose debug flow filter addr 10.10.60.20
diagnose debug flow show function-name enable
diagnose debug flow trace start 20
diagnose debug enable
출력에서 확인한 것:
... find a route: ...
... Denied by forward policy check (policy 0)
policy 0은 "매칭되는 정책 없음"을 뜻한다. VPN 인터페이스에서 내부망으로 가는 정책을 만들지 않았다.
Part 3에 정책 설정을 넣어뒀지만, 실제 작업 순서에서는 Phase1/Phase2를 만들고 곧바로 테스트에 들어가서 이걸 건너뛰었다.
교훈: Denied by forward policy check (policy 0)을 보면 정책 부재다. 정책 번호가 실제 번호로 나오면 그 정책의 조건을 보면 된다. 0이면 매칭 자체가 없다는 뜻이고, 이 차이가 진단 시간을 크게 가른다.
원인 2 — Phase2가 없었다
증상: IKE 협상은 완료되는데 터널이 올라오지 않음.
diagnose vpn tunnel list | grep -A5 ra-ipsec
Phase1은 있고 Phase2 SA가 없었다. GUI 마법사를 쓰면 자동으로 만들어지는데, CLI로 Phase1만 손으로 쓰다가 잊었다.
config vpn ipsec phase2-interface
edit "ra-ipsec-p2"
set phase1name "ra-ipsec"
set proposal aes256gcm aes256-sha256 aes128-sha256
set pfs enable
set dhgrp 20 14
set keepalive enable
next
end
부끄러운 실수지만 이 편에 넣는 이유가 있다. 로그가 "Phase2가 없다"고 말해주지 않는다. IKE SA는 정상적으로 생성되고, 그 뒤에 아무 일도 일어나지 않는다. 이 침묵의 패턴은 Part 8에서 훨씬 어려운 형태로 다시 나온다.
원인 3 — sites-enabled/의 백업 파일
이게 가장 오래 걸렸다. 상세는 Part 2에 썼으니 요약만.
sites-enabled/default를 수정하기 전에 습관적으로 같은 디렉터리에 default.bak을 만들었다. FreeRADIUS는 이 디렉터리의 모든 파일을 읽고, 확장자를 보지 않는다. 가상 서버가 둘이 되어 요청이 예측 불가능하게 갈렸다.
이게 "매번 다른 곳에서 실패하는" 증상의 주범이었다. 어느 서버가 처리하느냐에 따라 결과가 달랐던 것이다.
# 확인: 심볼릭 링크가 아닌 파일이 있는지
ls -la /etc/raddb/sites-enabled/
ls -la /etc/raddb/mods-enabled/
교훈: 재현성이 없는 실패를 만나면 설정이 유일하게 로드되고 있는지부터 확인한다. 중복 로드는 무작위성을 만들고, 무작위성은 모든 가설 검증을 무력화한다.
원인 4 — FortiClient XML의 태그 세 개
상세는 Part 4에 있다. 요약:
| 추가 | <enable_ike_fragmentation>1</...> | 큰 IKE 페이로드가 중간 노드에서 버려짐 |
| 삭제 | <mode> 태그 | transport 설정과 충돌 |
| 이동 | <eap_method>를 </auth_data> 직후로 | EAP 협상 자체를 시도하지 않음 |
교훈: 클라이언트 측 설정이 무시되고 있는지 확인하는 방법은 가져온 뒤 GUI에서 값을 육안으로 확인하는 것이다. 무시된 필드는 오류를 내지 않고 기본값으로 표시된다.
원인 5 — 클라이언트에 CA 인증서가 없었다
증상: 특정 노트북에서만 실패. 성공하는 노트북과 설정이 동일.
RADIUS 서버 인증서를 새 CA로 갱신했는데, 새 CA를 신뢰 저장소에 넣은 장비에서만 성공했던 것이다. EAP-TTLS는 클라이언트가 서버 인증서 체인을 검증하므로, 체인을 세울 수 없으면 터널 수립 단계에서 실패한다.
# 확인
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -like "*example*" }
# 설치
Import-Certificate -FilePath .\radius-ca.crt -CertStoreLocation Cert:\LocalMachine\Root
교훈: "특정 장비에서만"이라는 조건은 거의 항상 클라이언트 상태 차이를 가리킨다. 서버 설정을 아무리 봐도 나오지 않는다. Part 9에서 이 교훈의 더 극단적인 사례가 나온다.
5. 원인 다섯 개의 상호작용
이 다섯 개가 왜 그렇게 진단을 어렵게 만들었는지 정리하면.
| 3. sites-enabled 중복 | 인증 (무작위) | 매번 다름 ← 여기가 핵심 |
| 4. XML 태그 | EAP 협상 시작 전 | 인증 실패 |
| 5. CA 미설치 | TTLS 터널 수립 | 특정 장비만 실패 |
| 2. Phase2 부재 | 인증 후 터널 수립 | 연결 안 됨 |
| 1. 정책 부재 | 터널 후 트래픽 | 연결됨인데 통신 불가 |
원인 3이 무작위성을 주입하고 있었기 때문에, 나머지 네 개에 대한 모든 가설 검증이 신뢰할 수 없었다.
A를 바꾸고 성공하면 "A가 원인"이라 결론 내리는데, 실제로는 우연히 정상 가상 서버로 갔을 뿐이었다. 다음 시도에서 실패하면 A를 되돌린다. 이 루프가 며칠 갔다.
재현성 없는 실패를 만나면 다른 모든 디버깅을 멈추고 무작위성의 원인부터 제거한다. 무작위성이 남아 있는 상태에서의 실험 결과는 전부 노이즈다.
6. 조치 후 확인 절차
전부 고친 뒤, 다음 사고를 위해 검증 순서를 문서로 만들었다.
# 1. 설정 유일 로드 확인 (서버)
ls -la /etc/raddb/sites-enabled/ /etc/raddb/mods-enabled/
radiusd -XC
# 2. RADIUS 왕복 (FortiGate에서, VPN 무관)
diagnose test authserver radius radius-primary pap <계정> '<비밀번호+OTP>'
# 3. IKE 협상 (특정 클라이언트만)
diagnose vpn ike log filter clear
diagnose vpn ike log filter rem-addr4 <클라이언트공인IP>
diagnose debug application ike -1
diagnose debug enable
# 4. 터널 SA 확인
diagnose vpn tunnel list | grep -A5 ra-ipsec
# 5. 트래픽 경로 확인
diagnose debug flow filter addr <할당된VPN IP>
diagnose debug flow trace start 20
# 6. 클라이언트 로그 (Windows)
# C:\Users\<사용자>\AppData\Roaming\Fortinet\FortiClient\logs\trace\FortiIKE_1.log
이 6단계는 아래에서 위로 올라가지 않고 위에서 아래로 진행한다. 1번이 깨져 있으면 2~6번 결과는 의미가 없다.
7. 아직 해결되지 않은 것
이 시점에서 UDP transport로는 안정적으로 동작하게 됐다. 하지만 TCP transport에서는 여전히 이상했다.
절반은 붙고 절반은 안 붙었다. 그런데 위 다섯 개를 다 고친 뒤였으므로, 나는 이것도 같은 종류의 설정 문제라고 생각했다.
그게 아니었다. Part 8에서 다룬다.
8. 여기서 배운 것
- 증상이 시도마다 달라지면 원인을 세라. 하나를 찾으려는 접근이 실패의 원인이었다.
- 효과가 없어 보이는 변경을 되돌리지 말고 기록만 해둔다. 남은 원인 때문에 효과가 가려질 수 있다.
- 재현성 없음은 다른 모든 디버깅의 전제를 무너뜨린다. 무작위성 제거가 항상 최우선이다.
- 전체 경로 테스트 대신 구간별 독립 검증 수단을 먼저 확보한다. diagnose test authserver처럼 한 계층만 떼어내 테스트하는 도구가 있는지 찾는 것이 초기 투자로 가장 남는다.
- policy 0 같은 특수값의 의미를 아는 것이 진단 시간을 결정한다. 0은 "정책이 거부함"이 아니라 "매칭되는 정책 없음"이다.
다음 편에서는
Part 6에서 이해할 수 없었던 현상을 다룬다. 어떤 프로파일은 TCP로 붙는다고 표시되는데 실제로는 UDP로 붙고 있었다. 원인은 XML 값의 자료형이었다.
'네트워크' 카테고리의 다른 글
| authusrgrp를 쓰면 방화벽 정책의 그룹 매칭이 죽는다 — 문서에 없던 상호작용 (Part 7) (0) | 2026.07.25 |
|---|---|
| transport_mode="tcp"는 조용히 무시된다 — 우연히 동작했던 프로파일의 정체 (Part 6) (0) | 2026.07.25 |
| EMS 없이 150명에게 FortiClient 프로파일 배포하기 (Part 4) (0) | 2026.07.25 |
| FortiGate Phase1/Phase2와 IP 풀 설계 — 나중에 살아남을 대역 나누기 (Part 3) (0) | 2026.07.25 |
| FreeRADIUS 3.0.20에 TOTP를 붙이기 — 파일 기반으로 어디까지 갈 수 있는가 (Part 2) (1) | 2026.07.25 |