본문 바로가기

네트워크

EMS 없이 150명에게 FortiClient 프로파일 배포하기 (Part 4)

개요

  • FortiClient 7.4.3이 무료 VPN-only 배포판의 마지막 버전이다. 여기에 고정했다.
  • XML 프로파일은 EMS 없이도 수동 작성·배포가 가능하다. 다만 스키마가 문서화되어 있지 않다.
  • 암호화된 PSK 블롭은 수동 편집 후 무효가 된다. 평문으로 교체해야 한다.
  • 태그의 위치(순서)가 동작에 영향을 준다. 유효한 XML인데 인증이 실패하는 이유가 여기 있었다.

이 글은 누구를 위한 글인가

EMS 라이선스 없이 FortiClient VPN 설정을 다수 사용자에게 배포해야 하는 사람. 그리고 "XML은 맞는데 왜 안 되지"에 갇힌 사람.

읽고 나면 프로파일을 안전하게 편집하는 절차와, 편집 후 깨지기 쉬운 지점을 알게 된다.

왜 7.4.3에 고정했나

FortiClient의 무료 VPN 전용 배포판은 7.4.3이 사실상 마지막 세대다. 이후 버전은 관리형 제품 쪽으로 흘렀다.

우리 판단은 이랬다. 버전을 하나로 고정하고 그 버전에서 확실히 동작하게 만든다. 사용자마다 버전이 다르면 장애 문의가 들어올 때 변수가 하나 더 늘고, 1인 담당 체제에서는 이 변수 하나가 치명적이다.

7.4.3 계열은 우리 환경에서 TCP transport로 한 달 이상 안정적으로 동작한 이력이 있다.
(Part 8에서 이 표현의 조건을 다시 정확히 한다.)

프로파일을 손으로 만들지 말 것 — 내보내서 편집할 것

첫 번째 원칙이다.

FortiClient XML 스키마는 공개 문서가 부실하다. 태그 이름을 추측해서 처음부터 쓰면 유효한 XML인데 클라이언트가 조용히 무시하는 필드가 생긴다. 오류가 안 나고, 그냥 기본값으로 동작한다. 이게 최악이다.

절차:

  1. FortiClient GUI에서 연결을 수동으로 하나 만든다
  2. 정상 동작을 확인한다
  3. 설정을 XML로 내보낸다 (백업/내보내기 기능)
  4. 그 파일을 기준으로 편집한다
  5. 다시 가져와서 동작을 재확인한다

이렇게 하면 그 버전에서 실제로 유효한 태그 이름과 구조를 확보할 수 있다.

프로파일 골격

내보낸 파일에서 IPsec 연결 부분의 구조는 대략 이렇다. 태그 이름은 버전별로 다를 수 있으므로 반드시 본인이 내보낸 파일을 기준으로 삼을 것.

 
xml
<?xml version="1.0" encoding="UTF-8"?>
<forticlient_configuration>
  <vpn>
    <ipsecvpn>
      <options>
        <enabled>1</enabled>
      </options>
      <connections>
        <connection>
          <name>Corp-IPsec-UDP</name>
          <description>기본 프로파일 (UDP)</description>
          <server>vpn.example.com</server>

          <!-- 왜: 0=UDP, 1=TCP, 2=Auto. 숫자여야 한다. 문자열은 무시된다. (Part 6) -->
          <transport_mode>0</transport_mode>

          <!-- 왜: 큰 IKE 페이로드가 중간 노드에서 버려지는 것을 방지 -->
          <enable_ike_fragmentation>1</enable_ike_fragmentation>

          <ike_version>2</ike_version>
          <dhgroup>14</dhgroup>

          <auth_data>
            <!-- 왜: 암호화 블롭을 수동 편집하면 무효가 된다. 평문으로 둔다. -->
            <presharedkey>REDACTED</presharedkey>
          </auth_data>

          <!-- 왜: 이 태그의 위치가 동작에 영향을 준다. auth_data 닫는 태그 직후. -->
          <eap_method>ttls</eap_method>

          <save_username>1</save_username>
        </connection>
      </connections>
    </ipsecvpn>
  </vpn>
</forticlient_configuration>

함정 1: 암호화된 PSK 블롭은 편집 후 죽는다

내보낸 XML에서 PSK는 평문이 아니다. 암호화된 블롭으로 들어 있다(ENC 접두사 형태).

이 블롭은 파일 컨텍스트에 묶여 있다. 같은 파일 안의 다른 값을 수동으로 편집하고 나면, 블롭이 더 이상 유효하게 복호화되지 않는다. 결과는 PSK 불일치이고, 로그에는 인증 실패로만 나타난다. "PSK를 건드리지도 않았는데 왜?"가 된다.

해결은 단순하다. 블롭을 평문 PSK로 교체한다.

 
xml
<!-- 이걸 -->
<presharedkey>ENC UgcAAG5TL0h...(생략)</presharedkey>

<!-- 이렇게 -->
<presharedkey>실제PSK문자열</presharedkey>

여기서 보안 판단이 필요하다. 평문 PSK가 파일에 들어간다는 건 그 파일을 받는 사람 전원이 PSK를 알게 된다는 뜻이다.

Part 1에서 정리한 대로 이 구성에서 PSK는 게이트웨이 식별용이고 사용자 인증은 EAP+RADIUS가 담당한다. 그래서 PSK 자체로는 들어올 수 없다. 그럼에도 PSK는 사내 사용자에게만 배포되어야 하는 값이라는 원칙은 유지했다. 배포 경로에 접근 통제를 걸었다(아래).

함정 2: 태그의 위치가 동작을 바꾼다

이건 며칠을 잡아먹은 문제다.

XML은 원칙적으로 형제 요소의 순서에 의미가 없다. 그런데 이 파서는 그렇지 않았다.

<eap_method> 태그를 <auth_data> 블록의 닫는 태그 바로 다음에 두지 않으면 EAP 설정이 적용되지 않았다. 파일 뒤쪽에 두면 XML 검증은 통과하는데 클라이언트가 EAP를 협상하지 않았다.

같은 종류의 문제가 하나 더 있었다. 내보낸 파일에 남아 있던 <mode> 태그였다. 이 태그가 남아 있으면 transport 설정과 충돌해서 의도와 다른 방식으로 붙었다. 삭제해야 했다.

세 가지를 정리하면:

조치내용
추가 <enable_ike_fragmentation>1</enable_ike_fragmentation>
삭제 <mode> 태그 (transport 설정과 충돌)
이동 <eap_method>를 </auth_data> 직후로

교훈: 스키마가 문서화되지 않은 XML을 다룰 때는 순서도 설정의 일부라고 가정한다. 그리고 편집은 한 번에 하나씩 하고 매번 검증한다. 세 개를 동시에 바꾸면 어느 것이 효과가 있었는지 영원히 모른다. (나는 동시에 바꿨고, 나중에 하나씩 되돌려가며 다시 확인해야 했다.)

함정 3: 클라이언트 신뢰 저장소

RADIUS 서버 인증서를 새 CA로 갱신한 뒤, 갱신 전에 접속했던 클라이언트에서만 실패가 났다.

원인은 명확하다. EAP-TTLS는 클라이언트가 서버 인증서를 검증한다. 새 CA가 클라이언트 신뢰 저장소에 없으면 터널 수립 자체가 실패한다.

Windows에서 확인:

 
powershell
# 신뢰된 루트 인증 기관 목록에서 확인
Get-ChildItem Cert:\LocalMachine\Root | Where-Object { $_.Subject -like "*example*" }

설치:

 
powershell
Import-Certificate -FilePath .\radius-ca.crt -CertStoreLocation Cert:\LocalMachine\Root

주의: 중간 CA가 있는 체인이면 중간 CA도 Cert:\LocalMachine\CA에 넣어야 한다. 서버가 체인을 다 내려주면 필요 없지만, 서버가 체인을 내려주지 않는 펌웨어 버그 사례가 실제로 존재한다. 7.4.2/7.4.3 브랜치와 7.6.7에서 각각 보고됐다. Part 12에서 다룬다.

이원화 전략: UDP 기본 / TCP 백업

Part 3에서 설명한 이유로, 프로파일을 두 개 만들어 같은 파일에 넣었다.

 
xml
<connections>
  <connection>
    <name>Corp-VPN</name>
    <description>기본 (평소 이걸 사용)</description>
    <transport_mode>0</transport_mode>   <!-- UDP -->
    ...
  </connection>
  <connection>
    <name>Corp-VPN-Alt</name>
    <description>기본이 안 될 때 (호텔/공용망 등)</description>
    <transport_mode>1</transport_mode>   <!-- TCP -->
    ...
  </connection>
</connections>

사용자 안내는 딱 한 줄로 줄였다.

"Corp-VPN"으로 접속하세요. 안 되면 "Corp-VPN-Alt"를 쓰세요.

기술적 설명은 하지 않았다. 150명에게 UDP와 TCP의 차이를 설명하려는 시도는 실패할 뿐 아니라, 사용자가 잘못된 것을 먼저 시도하게 만든다.

transport_mode에 2(Auto)를 넣고 싶은 유혹이 있었다. 그러면 프로파일 하나로 끝나니까. 7.4에서는 이게 동작하지 않는다. Auto 폴백은 7.6 계열의 기능이다. 이걸 몰라서 헤맨 과정이 Part 6이다.

배포: EMS 없이

웹 포털 하나를 세웠다. 특별한 것 없다.

 
nginx
location /vpn-install/ {
    alias /var/www/vpn-install/;
    autoindex on;

    # 왜: 프로파일에 평문 PSK가 들어 있다. 공개 노출 금지.
    auth_basic           "VPN Setup";
    auth_basic_user_file /etc/nginx/.htpasswd-vpn;

    # 왜: .conf 파일이 브라우저에서 렌더링되지 않고 다운로드되게
    types { }
    default_type application/octet-stream;
}

포털에 올린 것:

파일용도
FortiClientVPNSetup_7.4.3.exe 클라이언트 설치 파일 (버전 고정)
corp-vpn.conf 프로파일 (Basic Auth 필요)
radius-ca.crt 서버 인증서 검증용 CA
설치안내.pdf 스크린샷 포함 4페이지

안내문을 4페이지로 줄이는 데 든 시간이 XML 작업 시간보다 길었다. 처음 버전은 11페이지였고, 아무도 읽지 않았다. 최종본의 구조는 이렇다.

  1. 1페이지: 설치 파일 실행 → 다음 → 다음 → 완료 (스크린샷 4장)
  2. 2페이지: 프로파일 가져오기 (스크린샷 3장)
  3. 3페이지: 접속 — 비밀번호 뒤에 OTP 6자리를 붙여 입력 (이 부분만 빨간 박스)
  4. 4페이지: 안 될 때 → "Corp-VPN-Alt 시도" → 그래도 안 되면 연락처

3페이지의 "비밀번호 뒤에 붙여서"가 가장 많은 문의를 만들었다. 별도 입력창이 없다는 걸 사용자가 예상하지 못한다. 여기에 실제 입력 예시 이미지를 넣고 나서야 문의가 줄었다.

여기서 막힌 사람들을 위한 확인 순서

  1. 프로파일을 가져온 뒤 GUI에서 값이 실제로 반영됐는지 육안 확인 — 무시된 필드는 기본값으로 표시된다
  2. transport_mode가 숫자인지 확인 (문자열이면 조용히 UDP로 감)
  3. PSK가 암호화 블롭인지 평문인지 확인
  4. <eap_method> 위치 확인
  5. CA 인증서가 클라이언트 신뢰 저장소에 있는지 확인
  6. 클라이언트 로그 확인:
 
   C:\Users\<사용자>\AppData\Roaming\Fortinet\FortiClient\logs\trace\FortiIKE_1.log

6번 로그 위치는 외워둘 가치가 있다. 서버 로그만 보면 절반만 보인다.

이 편에서 배운 것

  • 문서화되지 않은 설정 파일은 손으로 쓰지 말고 내보내서 편집한다.
  • 스키마가 불명확한 파서에서는 순서도 설정의 일부라고 가정한다.
  • 한 번에 하나씩 바꾸고 매번 검증한다. 세 개를 동시에 바꿔서 성공하면 무엇이 효과였는지 모르고, 그 무지는 다음 장애에서 청구된다.
  • 사용자 안내문에서 기술적 설명을 지우는 것이 정확성을 높인다. 선택지를 주면 잘못된 것을 먼저 고른다.

다음 편에서는

여기까지가 "정상 구축" 이야기다. 이제 실제로 무슨 일이 있었는지 쓴다.

Part 5에서는 IKE는 성립하는데 인증이 되지 않았던 문제를 다룬다. 원인이 하나가 아니었고, 다섯 개였다.

반응형