10.11. HTTPS – 서버를 위한 전송 암호화¶
지금까지는 모든 것이 포트 80의 평문 HTTP였습니다. 브라우저와 카메라 사이의 패킷을 캡처할 수 있는 사람이라면 누구나 로그인 폼의 비밀번호, 그로부터 돌아오는 세션 쿠키, Authorization 헤더의 JWT, 그리고 캡처된 모든 프레임의 JPEG 바이트를 읽을 수 있습니다. HTTPS는 통신을 종단 간 암호화합니다.
인증서 작업 흐름 자체, 즉 개발용 자체 서명 인증서 생성, 운영용 CA 서명 인증서 발급, 파일을 올바른 형식으로 카메라에 복사하는 과정은 TLS 인증서 다루기 에서 다룹니다. 이 페이지는 이미 로드된 인증서를 microdot에 연결하는 것 에 관한 내용입니다.
10.11.1. SSL 컨텍스트 구성하기¶
ssl.SSLContext 는 인증서, 키, 프로토콜 옵션을 담는 표준 라이브러리의 컨테이너입니다. 서버라면 PROTOCOL_TLS_SERVER 와 카메라의 파일 시스템에서 로드한 인증서 체인이 필요합니다:
import ssl
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
ctx.load_cert_chain('/flash/cert.der', '/flash/key.der')
DER 경로는 TLS 섹션에서 실행한 작업 흐름에서 나옵니다. 개발용은 자체 서명, 운영용은 CA 서명입니다. 파일이 반드시 /flash 에 있어야 하는 것은 아니며 /sdcard 도 똑같이 잘 동작합니다.
10.11.2. start_server에 컨텍스트 전달하기¶
앞서의 start_server() 호출과 유일한 차이점은 ssl=ctx 인자와 포트 번호입니다. 포트 443은 HTTPS 기본값이므로 브라우저가 :443 을 입력할 필요가 없으며, 그냥 https://yard-cam.local/ 로 동작합니다:
async def main():
await asyncio.gather(
capture_loop(),
motion_detector(),
app.start_server(host='0.0.0.0', port=443, ssl=ctx),
)
asyncio.run(main())
서버 측은 이것으로 끝입니다. 기존의 모든 라우트, 즉 /status, /snapshot.jpg, /stream.jpg, /config, /events, /control, 정적 대시보드가 이제 다른 코드 변경 없이 TLS 위에서 동작합니다.
10.11.4. 여기서 다루지 않는 것¶
HTTP Strict Transport Security (HSTS), 인증서 자동 갱신, 아웃바운드 요청을 위한 카메라 측의 CA 신뢰 체인, 암호 스위트 선택은 모두 TLS 섹션에 있습니다. 여기서의 연결 작업, 즉 하나의 SSLContext 와 하나의 ssl=ctx 만이 microdot에 특화된 유일한 부분입니다.
대시보드 URL 표시줄은 이제 http:// 에서 https:// 로 바뀌고 WebSocket은 ws:// 에서 wss:// 로 바뀝니다. 대시보드 JavaScript는 이미 location.protocol 을 기반으로 올바른 스킴을 선택했으므로 클라이언트 변경은 필요하지 않습니다.
카메라가 HTTPS로 서비스합니다. 로그인 폼, JWT, 캡처된 프레임이 전송 중에 암호화됩니다.