1. 프로젝트 개요
이번 포스팅에서는 Python으로 WebSocket 클라이언트와 서버를 구현하고, SSL 인증을 사용하여 보안을 강화하는 과정을 다룹니다. 특히 양방향 SSL 인증(서버와 클라이언트 모두 인증서를 사용하는 방식)을 설정하고 디버깅을 통해 문제를 해결하는 과정을 공유합니다.
이 예제에서는 네이버 클라우드의 원격 서버를 활용하여 WebSocket 서버를 설정합니다. 인증서는 /home1/ncloud/certificates/ 경로에 저장되어 있습니다.
2. 준비물
- Python 3.8 이상
WebSocket 서버와 클라이언트를 구현합니다. - Websockets 라이브러리
WebSocket 통신에 사용됩니다. pip install websockets 명령어로 설치 가능합니다. - OpenSSL
SSL 인증서를 생성하고 관리합니다. - 네이버 클라우드 원격 서버
서버의 Public IP와 경로를 활용해 WebSocket 서버를 설정합니다. - WSL(Ubuntu 환경) 또는 Linux 환경
클라이언트 측의 테스트 환경을 구축합니다.
3. 구현 과정
3.1. WebSocket 서버 코드 작성
네이버 클라우드 서버에서 다음과 같은 서버 코드를 작성합니다. 인증서는 /home1/ncloud/certificates/ 경로에 저장되어야 합니다.
import asyncio
import ssl
import websockets
# TLS 인증 설정
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ssl_context.load_cert_chain(certfile="/home1/ncloud/certificates/server.crt",
keyfile="/home1/ncloud/certificates/server.key")
ssl_context.load_verify_locations(cafile="/home1/ncloud/certificates/ca.crt")
async def handle_client(websocket, path):
print("New connection established.")
try:
while True:
await websocket.send("Hello, WebSocket client!")
message = await websocket.recv()
print(f"Received: {message}")
except websockets.exceptions.ConnectionClosed:
print("Client disconnected.")
# WebSocket 서버 실행
start_server = websockets.serve(handle_client, "0.0.0.0", 12345, ssl=ssl_context)
asyncio.get_event_loop().run_until_complete(start_server)
print("WebSocket server started on wss://<네이버클라우드_Public_IP>:12345")
asyncio.get_event_loop().run_forever()
3.2. WebSocket 클라이언트 코드 작성
로컬 테스트 환경(WSL 또는 Linux)에서 클라이언트 코드를 작성합니다. 클라이언트는 서버와 동일한 인증서를 활용하여 SSL 검증을 수행합니다.
import asyncio
import ssl
import websockets
import logging
import traceback
# 디버그 로깅 설정
logging.basicConfig(level=logging.DEBUG)
async def connect_to_server():
ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
ssl_context.load_cert_chain(certfile="client.crt", keyfile="client.key") # 클라이언트 인증서 및 키
ssl_context.load_verify_locations("ca.crt") # CA 인증서 경로
ssl_context.check_hostname = True # 호스트 이름 검증 활성화
ssl_context.verify_mode = ssl.CERT_REQUIRED # 인증서 검증 활성화
try:
logging.debug("Attempting to connect to server...")
async with websockets.connect(
"wss://myserver.example.com:12345", ssl=ssl_context
) as websocket:
logging.info("Connected to server.")
await websocket.send("Hello, server!")
response = await websocket.recv()
logging.debug(f"Received message from server: {response}")
print(f"Server message: {response}")
except ssl.SSLCertVerificationError as e:
logging.error("SSL certificate verification failed.")
logging.error(traceback.format_exc())
except Exception as e:
logging.error(f"Unexpected error: {e}")
logging.error(traceback.format_exc())
if __name__ == "__main__":
asyncio.run(connect_to_server())
4. SSL 인증서 생성
OpenSSL을 사용하여 CA 인증서, 서버 인증서, 클라이언트 인증서를 생성합니다.
4.1. OpenSSL 구성 파일 작성
openssl.cnf 파일의 내용을 아래와 같이 작성합니다. CN(Common Name)은 호스트 이름이나 IP를 기반으로 설정합니다.
[req]
distinguished_name = req_distinguished_name
x509_extensions = v3_req
prompt = no
[req_distinguished_name]
C = KR
ST = Seoul
L = Seoul
O = MyCompany
OU = IT
CN = myserver.example.com
[v3_req]
keyUsage = critical, digitalSignature, keyEncipherment
extendedKeyUsage = serverAuth, clientAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = myserver.example.com
IP.1 = 203.0.113.1
4.2. 인증서 생성 명령
- CA 인증서 생성
- openssl req -x509 -newkey rsa:2048 -keyout ca.key -out ca.crt -days 365 -nodes -subj "/CN=MyCA"
- 서버 인증서 생성
openssl req -new -keyout server.key -out server.csr -config openssl.cnf -nodes
openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 365 -extensions v3_req -extfile openssl.cnf
4.클라이언트 인증서 생성
openssl req -new -keyout client.key -out client.csr -config openssl.cnf -nodes
openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days 365 -extensions v3_req -extfile openssl.cnf -nodes
5. 생성된 파일 권한 변경 인증서 및 키 파일의 소유권을 변경하여 권한 문제를 해결합니다:
sudo chown ubuntu:ubuntu client.crt client.key
6. server.key, server.crt, server.crt, ca.crt, ca.key를 네이버 클라우드 서버로 옮긴다.
sudo scp server.key, server.crt, server.crt, ca.crt, ca.key ncloud@203.0.113.1:/home1/ncloud/certificates/
5. 호스트 네임 설정
클라이언트가 서버로 접속할 수 있도록 /etc/hosts 파일에 다음 내용을 추가합니다:
203.0.113.1 myserver.example.com
6. 디버깅 및 문제 해결
- SSL 인증서 검증 실패
- 문제: SSL: CERTIFICATE_VERIFY_FAILED 오류 발생
- 해결: 클라이언트 인증서를 코드에서 명시적으로 로드하도록 수정.
- 파일 권한 문제
- 문제: PermissionError: [Errno 13] Permission denied
- 해결: 파일 권한을 적절히 설정:
- chmod 600 server.key client.key
- 디버깅 활성화
- Python 코드에 디버그 로그 추가:
- logging.basicConfig(level=logging.DEBUG)
7. 결과
클라이언트와 서버 간 성공적인 통신 로그:
INFO:root:Connected to server.
DEBUG:websockets.client:< TEXT 'Hello, WebSocket client!' [24 bytes]
Server message: Hello, WebSocket client!
8. 결론
이 프로젝트를 통해 WebSocket 통신의 기본 설정뿐만 아니라 SSL/TLS를 활용하여 보안성을 강화하는 방법도 학습할 수 있었습니다. 특히, 네이버 클라우드 서버를 활용해 실제 환경에서 구현하며 실습의 효과를 높였습니다. 앞으로 양방향 인증이 필요한 복잡한 시스템을 구축할 때 유용한 레퍼런스가 될 것입니다.
9. 추가 참고 자료
- Python websockets 라이브러리 공식 문서
- OpenSSL Documentation
포스팅이 도움이 되셨다면 댓글과 공유 부탁드립니다! 😊
'임베디드 관련 카테고리 > 보안' 카테고리의 다른 글
CA 키와 인증서 생성하기: 신뢰 기반 인증서 발급의 첫걸음 (0) | 2024.12.05 |
---|
댓글