Post

ovpn 파일은 무엇을 담고, OpenVPN은 어떻게 터널을 여는가

AWS Client VPN과 OpenVPN을 기준으로, .ovpn 파일이 실제로 담는 것과 클라이언트/서버가 인증서 검증, 세션 키 합의, 터널 암호화를 어떤 순서로 수행하는지 PKI와 TLS 관점에서 정리한 기술 노트입니다.

AWS Client VPN을 만지다 보면 .ovpn 파일이 자꾸 하나의 “암호화된 VPN 파일”처럼 느껴진다.
그런데 이 표현은 정확하지 않다.
.ovpn은 보통 암호화 그 자체를 담은 파일이 아니라, OpenVPN 클라이언트가 터널을 열기 위해 필요한 설정과 자격 증명 참조를 담은 프로파일에 가깝다.

내가 궁금했던 것도 결국 이 지점이었다.

  • .ovpn 파일 안에는 정확히 무엇이 들어가는가
  • OpenVPN client는 이 파일을 읽고 실제로 무엇을 시작하는가
  • server는 어떤 방식으로 클라이언트를 검증하고 세션 키를 만드는가
  • 런타임에 실제 패킷은 어떤 키로 보호되는가

이 글은 그 흐름을 파일 포맷 -> PKI -> TLS 핸드셰이크 -> 데이터 채널 순서로 정리한 노트다.

먼저 결론부터: .ovpn은 대체로 평문 프로파일이다

가장 먼저 바로잡아야 할 점은 이것이다.
.ovpn 파일 자체가 어떤 특별한 암호화 포맷으로 “만들어지는” 것은 아니다.

일반적으로 .ovpn은 다음 둘 중 하나다.

  1. OpenVPN 설정 지시어를 담은 평문 텍스트 파일
  2. 설정 지시어와 인증서/키를 inline으로 함께 포함한 평문 텍스트 파일

.ovpn은 ZIP이나 PKCS#12 같은 독립된 암호화 컨테이너가 아니라,
OpenVPN이 읽을 설정 문서에 더 가깝다.

중요한 것은 파일 형식이 아니라 그 안에 무엇이 들어 있느냐다.

  • ca: 어떤 CA를 신뢰할지
  • cert: 클라이언트 인증서
  • key: 클라이언트 개인 키
  • tls-auth 또는 tls-crypt: 제어 채널 보호용 사전 공유 키
  • remote, proto, dev: 어느 서버에 어떤 전송 방식으로 붙을지
  • data-ciphers, auth: 데이터 채널 암호화 및 무결성 관련 옵션

따라서 .ovpn을 봐야 할 때 핵심 질문은
“이 파일이 암호화되어 있나?”보다
“이 파일이 어떤 비밀 값을 직접 담고 있나?”에 더 가깝다.

.ovpn 안에는 보통 이런 정보가 들어간다

가장 단순화하면 .ovpn은 아래와 비슷하다.

client
dev tun
proto udp
remote cvpn-endpoint.example.com 443
remote-cert-tls server

<ca>
... CA certificate ...
</ca>

<cert>
... client certificate ...
</cert>

<key>
... client private key ...
</key>

환경에 따라 여기에 아래 같은 항목이 더 붙는다.

  • tls-auth keyfile 1
  • tls-crypt keyfile
  • auth-user-pass
  • verify-x509-name
  • cipher 또는 data-ciphers
  • reneg-sec

여기서 성격이 완전히 다른 값들이 함께 등장한다.

1. 공개 가능한 값

  • 서버 주소
  • 포트
  • 프로토콜(udp, tcp)
  • CA 인증서
  • 클라이언트 인증서

인증서와 CA는 기본적으로 공개 가능한 재료다.
인증서의 핵심 역할은 신원과 공개키를 담고, 그 위에 CA 서명이 붙어 있다는 점이다.

2. 절대 비밀로 다뤄야 하는 값

  • 클라이언트 개인 키
  • tls-auth/tls-crypt 프리셰어드 키
  • 경우에 따라 사용자 인증 비밀번호

진짜 민감한 것은 대개 이쪽이다.
특히 <key>...</key>가 inline으로 들어간 .ovpn은 사실상 “설정 파일”이 아니라 자격 증명 번들에 더 가깝다.

예시로 보면 .ovpn은 이렇게 읽는다

실제로 파일을 열었을 때는 각 줄을 “무슨 기능인가”보다
“이 줄이 연결 정책인가, 공개 정보인가, 비밀 정보인가”로 나눠 읽는 편이 좋다.

client                         # 이 프로파일은 클라이언트 역할이다
dev tun                        # L3 터널 인터페이스를 사용한다
proto udp                      # 전송 프로토콜은 UDP
remote cvpn-endpoint.example.com 443  # 접속할 서버 주소와 포트

remote-cert-tls server         # 상대 인증서가 서버 용도인지 검사
verify-x509-name server name   # 기대하는 서버 이름을 추가 검증할 수 있음

resolv-retry infinite          # DNS 실패 시 계속 재시도
nobind                         # 로컬 포트를 고정 바인딩하지 않음
persist-key                    # 재연결 시 키 상태를 유지
persist-tun                    # 재연결 시 TUN 인터페이스를 유지

auth SHA256                    # 데이터 채널 무결성 알고리즘 옵션
data-ciphers AES-256-GCM:AES-128-GCM  # 데이터 채널 대칭 암호 후보

<ca>
-----BEGIN CERTIFICATE-----
... CA certificate ...
-----END CERTIFICATE-----
</ca>                           # 공개 정보: 어떤 CA를 신뢰할지 정의

<cert>
-----BEGIN CERTIFICATE-----
... client certificate ...
-----END CERTIFICATE-----
</cert>                         # 공개 정보: 내 공개키와 신원 정보

<key>
-----BEGIN PRIVATE KEY-----
... client private key ...
-----END PRIVATE KEY-----
</key>                          # 비밀 정보: 절대 보호해야 하는 값

key-direction 1                # tls-auth 사용 시 방향 지정
<tls-auth>
-----BEGIN OpenVPN Static key V1-----
... shared key ...
-----END OpenVPN Static key V1-----
</tls-auth>                     # 비밀 정보: 제어 채널 초기 필터용 키

여기서 줄별로 핵심만 다시 요약하면 이렇다.

  • client, dev, proto, remote: 어디에 어떤 방식으로 붙을지 정하는 연결 정책
  • remote-cert-tls, verify-x509-name: 서버 인증서가 기대한 상대인지 더 엄격하게 확인하는 정책
  • auth, data-ciphers: 데이터 채널에서 어떤 무결성/암호 옵션을 쓸지 정하는 협상 힌트
  • <ca>: 무엇을 신뢰 루트로 볼지 결정하는 재료
  • <cert>: 내 공개 신원 문서
  • <key>: 내 비밀키
  • <tls-auth>/<tls-crypt>: 제어 채널 앞단을 더 보호하는 추가 비밀키

즉 파일을 해석할 때는
“인증서가 있으니 안전하다”가 아니라
“어떤 줄이 신뢰 기준을 정의하고, 어떤 줄이 실제 비밀을 담고 있는가”를 먼저 봐야 한다.

.ovpn은 무엇을 정의하고, 무엇은 런타임에 결정되는가

이 구분을 분명히 해두면 헷갈림이 크게 줄어든다.

.ovpn이 하는 일:

  • 어떤 서버에 붙을지 알려준다
  • 어떤 인증서와 키를 사용할지 알려준다
  • 어떤 보안 옵션을 허용할지 알려준다
  • 제어 채널 보호용 추가 키가 있는지 알려준다

.ovpn이 하지 않는 일:

  • 세션 키를 미리 담아두지 않는다
  • 실제 데이터 채널 키를 고정된 값으로 정의하지 않는다
  • 연결 시점의 난수와 키 교환 결과를 담아두지 않는다

.ovpn연결을 여는 재료와 정책을 전달할 뿐이고,
실제 세션 키와 데이터 채널 키는 핸드셰이크 과정에서 런타임에 만들어진다.

OpenVPN은 제어 채널과 데이터 채널을 분리해서 본다

OpenVPN을 이해할 때 가장 중요한 구조는 채널 분리다.

  • Control channel: 인증, 키 교환, 설정 협상
  • Data channel: 실제 터널 패킷 전송

이 구조 때문에 OpenVPN은 “TLS를 쓴다”는 말 하나로 끝나지 않는다.
TLS는 우선 제어 채널을 보호하고, 그 제어 채널 위에서 실제 VPN 트래픽에 쓸 키를 교환한다.

즉 개념적으로는 이렇게 보면 된다.

  1. 제어 채널이 TLS로 안전해진다.
  2. 제어 채널을 통해 데이터 채널 키가 협상된다.
  3. 실제 IP 패킷은 데이터 채널에서 대칭키로 보호된다.

이 점 때문에 .ovpn 안에 인증서가 들어 있다고 해서
“이 파일이 데이터 트래픽을 암호화한다”라고 표현하면 어색하다.
정확히는 이 파일이 데이터 채널 키를 안전하게 협상할 수 있게 만드는 출발점을 제공한다고 보는 편이 맞다.

OpenVPN client는 .ovpn을 읽고 실제로 무엇을 하나

클라이언트 측 동작을 단순화하면 아래 순서에 가깝다.

  1. .ovpn 파싱
  2. CA 인증서, 클라이언트 인증서, 개인 키 로드
  3. tls-auth 또는 tls-crypt 키가 있으면 함께 로드
  4. UDP/TCP 소켓 생성
  5. TUN/TAP 가상 네트워크 인터페이스 준비
  6. 서버와 제어 채널 연결 시작
  7. TLS 핸드셰이크 수행
  8. 서버 인증서 검증
  9. 자신의 클라이언트 인증서와 개인 키로 응답
  10. 세션 키 및 데이터 채널 키 협상
  11. 터널 인터페이스를 통해 패킷 송수신

여기서 눈여겨볼 지점은 두 군데다.

1. 클라이언트는 인증서를 “보여주는” 것만 하지 않는다

클라이언트는 인증서를 전송할 뿐 아니라,
그 인증서에 대응하는 개인 키를 정말 가지고 있다는 점도 증명해야 한다.

이 증명은 보통 TLS 핸드셰이크 안에서 일어난다.
개인 키 자체를 보내는 것이 아니라, 핸드셰이크 메시지에 대한 서명 응답을 통해
“나는 이 공개키에 대응하는 비밀키 소유자다”를 증명한다.

즉 mTLS의 핵심은 인증서 파일이 아니라
개인 키 소유 증명이다.

2. 실제 패킷 암호화는 인증서로 직접 하지 않는다

인증서는 신원 검증과 키 교환의 재료다.
실제 터널 안의 패킷을 계속 공개키로 암호화하면 너무 무겁다.

그래서 OpenVPN은 TLS 핸드셰이크가 끝난 뒤 협상된 대칭 세션 키를 사용해
데이터 채널 패킷을 보호한다.

요약하면 이렇다.

  • 인증서/개인 키: 상대 신원 검증과 세션 키 합의
  • 대칭 세션 키: 실제 데이터 채널 보호

Server는 어떤 방식으로 클라이언트를 검증하나

서버도 본질적으로는 클라이언트와 비슷한 일을 반대 방향에서 수행한다.

  1. 자신의 서버 인증서를 제시한다.
  2. 클라이언트가 신뢰하는 CA 체인에 속하는지 검증되도록 한다.
  3. 클라이언트 인증서를 받는다.
  4. 서버가 신뢰하는 CA가 서명한 클라이언트 인증서인지 확인한다.
  5. 클라이언트가 개인 키를 실제로 소유하는지 TLS 응답으로 확인한다.
  6. 정책에 맞으면 데이터 채널 키를 협상하고 터널을 연다.

즉 서버가 확인하는 것은 최소한 아래 세 가지다.

  • 이 클라이언트 인증서가 신뢰된 CA 체인에 속하는가
  • 이 인증서가 지금 유효한가
  • 이 연결 상대가 그 인증서의 개인 키를 실제로 가지고 있는가

이후에야 라우트, DNS, 내부 네트워크 설정 같은 것이 의미를 갖는다.

tls-auth, tls-crypt는 인증서와 또 다른 층이다

OpenVPN을 보다 보면 tls-authtls-crypt가 같이 등장한다.
이건 클라이언트 인증서/개인 키와는 또 다른 역할이다.

이 옵션들은 제어 채널 바깥쪽에 한 겹 더 두르는 보호막처럼 보면 이해가 쉽다.

tls-auth

  • 제어 채널 패킷에 HMAC 서명을 붙인다
  • 올바른 프리셰어드 키를 모르면 패킷이 초반에 버려진다
  • 일종의 초기 필터 역할을 한다

tls-crypt

  • tls-auth의 역할에 더해 제어 채널 패킷 자체를 추가로 암호화한다
  • TLS 세션이 완전히 열리기 전의 메타데이터 노출을 줄인다

즉 OpenVPN에서 인증서 기반 mTLS만 있는 것이 아니라,
환경에 따라 제어 채널용 사전 공유 키 보호층이 같이 쓰일 수 있다.

이때 .ovpn 안에 tls-auth/tls-crypt 키가 들어 있다면,
그 파일은 더더욱 민감한 비밀을 품은 상태가 된다.

데이터 채널에서 실제로 보호되는 것은 무엇인가

핸드셰이크가 끝난 뒤에는 실제 터널 데이터가 오간다.
이때 OpenVPN이 보호하는 대상은 대체로 아래와 같다.

  • 내부 DNS 질의
  • 사설 IP 패킷
  • 내부 서비스로 향하는 트래픽
  • 터널 안을 지나는 애플리케이션 세션

중요한 점은 이 단계에서 보호되는 것은 “HTTP 요청”이 아니라 우선 “패킷”이라는 점이다.
OpenVPN 데이터 채널은 애플리케이션 의미를 해석하지 않는다.
그냥 TUN/TAP 인터페이스로 들어온 네트워크 데이터를 암호화해 전달한다.

즉 OpenVPN은 아래 질문에 답한다.

  • 이 패킷을 안전하게 운반할 수 있는가

반면 애플리케이션은 아래 질문에 답한다.

  • 이 사용자가 이 요청을 수행할 수 있는가

AWS Client VPN에서는 이 모델이 어떻게 보이나

AWS Client VPN은 관리형 서비스라 서버 데몬 내부 구현을 우리가 직접 보지는 못한다.
하지만 노출되는 인터페이스는 OpenVPN 모델과 거의 같은 개념으로 이해할 수 있다.

AWS 공식 문서 기준으로 mutual authentication을 쓸 때는:

  • 서버 인증서를 ACM에 올린다
  • 클라이언트 인증서와 키를 준비한다
  • 다운로드한 클라이언트 구성 파일에 클라이언트 인증서와 개인 키를 추가한다
  • 클라이언트는 OpenVPN 기반 애플리케이션으로 접속한다

이 모델에서 보이는 핵심은 분명하다.

1. .ovpn은 여전히 프로파일이다

AWS에서 내려받는 파일도 결국 OpenVPN 클라이언트가 읽을 프로파일이다.
여기에 mutual authentication용 클라이언트 인증서와 개인 키를 붙이면
실질적으로는 자격 증명 번들이 된다.

2. 서버 비밀키는 클라이언트에 오지 않는다

클라이언트는 서버 인증서를 검증하지만,
서버의 개인 키는 당연히 서버 측에만 남는다.
AWS에서는 이 서버 인증서/키 쌍이 ACM과 Endpoint 설정 쪽에서 관리된다.

3. 클라이언트 개인 키는 정말 민감하다

AWS 문서 흐름상 mutual authentication을 쓰면
클라이언트 인증서와 키를 .ovpn에 넣거나 클라이언트가 참조하도록 배포해야 한다.
즉 이 파일 배포 절차가 곧 보안 경계가 된다.

이 때문에 운영 관점에서는 .ovpn을 “설정 파일”로만 다루면 안 된다.

그럼 .ovpn 파일은 암호화해야 하나

이 질문은 보통 두 가지 뜻으로 섞여서 나온다.

1. .ovpn 포맷 자체가 암호화 포맷인가

아니다. 대체로 평문 프로파일이다.

2. .ovpn 안의 비밀을 보호해야 하는가

그건 그렇다. 아주 중요하다.

보호해야 할 것은 파일 확장자가 아니라 그 안의 비밀 값이다.

  • 클라이언트 개인 키
  • tls-auth/tls-crypt
  • 경우에 따라 사용자 자격 증명

즉 실무적으로는 다음처럼 보는 편이 낫다.

  • 별도 파일 참조 방식이면 키 파일 권한을 엄격히 제한한다
  • inline 방식이면 .ovpn 파일 자체를 민감 정보처럼 배포/보관한다
  • 가능하면 재발급과 폐기 절차를 먼저 설계한다

오프보딩에서 진짜 중요한 것은 발급보다 폐기다

인증서 기반 모델에서 흔한 착각은 “잘 발급했으면 끝”이라는 생각이다.
실제로는 폐기 절차가 더 중요하다.

클라이언트 인증서가 외부로 유출되거나 사용자가 퇴사했을 때 필요한 것은 대개 아래다.

  1. 해당 인증서 폐기
  2. CRL 갱신
  3. 서버가 최신 폐기 목록을 참조하도록 반영
  4. 필요 시 새 클라이언트 키/인증서 재발급

.ovpn 보안의 본질은 단순 저장 형식이 아니라
개인 키 수명 주기와 인증서 신뢰 철회 절차에 있다.

내가 정리한 최종 모델

이번에 다시 정리하고 나서, OpenVPN과 .ovpn을 보는 기준은 아래처럼 바뀌었다.

  1. .ovpn은 대체로 평문 텍스트 프로파일이다.
  2. 민감한 것은 확장자가 아니라 그 안에 포함된 개인 키와 프리셰어드 키다.
  3. mTLS의 본질은 인증서 파일 교환이 아니라 개인 키 소유 증명이다.
  4. TLS 핸드셰이크는 신원 검증과 세션 키 합의를 담당한다.
  5. 실제 터널 패킷은 협상된 대칭키로 보호된다.
  6. AWS Client VPN도 관리형 형태일 뿐, 개념적으로는 이 OpenVPN 모델 위에서 이해하는 편이 정확하다.

이 기준이 생기고 나니, 이제는 .ovpn을 볼 때
“이 파일이 무슨 암호화 파일이지?”가 아니라
“이 프로파일이 어떤 신뢰 체인과 어떤 비밀 값을 들고, 런타임에 어떤 키를 만들게 하는가?”를 먼저 보게 됐다.

참고

This post is licensed under CC BY 4.0 by the author.