20.7 보안 및 접근 제어(ACL) 관련 장애
사물인터넷(IoT)이 방화벽을 넘어 클라우드라는 광야와 연결되는 순간부터, 트래픽은 상시 위협받는 먹잇감으로 전락한다. 이를 막기 위해 TLS(Transport Layer Security) 암호화와 디테일한 ACL(접근 제어 리스트, Access Control List)을 구축하지만, 역설적이게도 운영 환경에서 이 ’방패’는 가끔 창을 돌려 아군을 찌르기도 한다.
“분명 통신망은 연결되어 있고 스카우팅도 동작하는데, 암호키 교환의 찰나에서 무한 루프로 튕겨나가거나, 내 정당한 제어 토픽이 아무 알람도 없이 거절(Access Denied) 당하는” 황당한 상황이 벌어진다. 이 절에서는 인증서 시계 오차, ACL 와일드카드 매칭의 오해, 대규모 암호화 연산이 발생시키는 네트워크 오버헤드 딜레이 등 보안 체계가 시스템의 목을 조를 때 단칼에 이를 베어내는 보안 전용 트러블슈팅 기법을 전수한다.
1. TLS/SSL 인증서 만료, 검증(Verification) 실패 및 핸드셰이크 오류
클라이언트 로그에 bad certificate, handshake failed, 혹은 certificate has expired가 보인다면 보안 프로토콜 단골 장애인 TLS 이슈다. 보안 관점에서는 실패에 대해 친절한 에러를 주지 않고 세션을 조용히 끊어버리기 때문에 디버깅이 가혹하다.
1.0.1 인증서 시계(Time) 불일치로 인한 Not Before/Not After 거부
로봇이나 라즈베리 파이 단말은 배터리가 방전되거나 전원이 내려가면 내장 하드웨어 시계(RTC)가 초기화되어 1970년이나 2000년으로 돌아갈 수 있다.
1970년에 살고 있는 로봇이 2026년에 발급된 클라우드 라우터의 인증서를 검증하려 하면, 암호화 라이브러리 입장에서는 이 인증서는 “아직 유효하지 않은 미래의 인증서(Not Before 오류)“로 판정되어 즉각 세션을 끊어버린다.
해법: 에지 기기 부팅 시 무조건 chrony나 NTP(Network Time Protocol) 동기화가 이루어져 현재 표준시를 확보한 “이후“에만 zenoh-bridge-dds 등 통신 연결 스크립트가 실행되도록 systemd 서비스 의존성 슬롯을 조율하라.
1.0.2 주체 대체 이름(SAN, Subject Alternative Name) 누락 에러
클라우드 라우터의 TLS 인증서를 오픈소스 CA로 발급받을 때 인증서 내부 도메인을 localhost 로만 묶어 발급했다 치자.
공장의 클라이언트가 IP 192.168.1.5 나 도메인 cloud-router.com 으로 접속 시 브라우저와 통신 라이브러리는 “접속하려는 주소와 인증서에 적힌 허용 주소(SAN)가 불일치한다!“며 도메인 검증 파열음(Hostname Verification Failed)을 낸다.
해법: 통신이 안 될 때 일단 개발자 로컬 환경에서 --tls_server_name_verification=false (또는 코드 상 검증 바이패스 플래그)를 켜고 테스트해 본다. 만약 연결이 된다면 100% SAN 누락 문제다. CA 발급 과정에서 프로덕션 IP와 도메인, 다수의 서브도메인(*.cloud-router.com)을 Array 배열 형태로 완벽하게 우겨넣어 재발급하라.
1.0.3 인증서 체인(Chain) 단절
TLS는 단순한 서버 인증서 파일 하나가 아니라, 루트 CA -> 중간 CA -> 서버 인증서로 엮인 신뢰의 사슬(Chain of Trust)을 전부 검사한다.
클라이언트로 넘어오는 .pem 텍스트 안에 서버 인증서만 덩그러니 있고 중간 CA 인증기관 문구가 누락되어 있다면 검증 시스템은 실패한다. 텍스트 편집기로 cert.pem을 열어 -----BEGIN CERTIFICATE----- 블럭이 최소 2개 이상 이어져 있는지 수동으로 육안 검증(Visual inspection)하라.
2. 권한 제어(ACL) 규칙 설정 오류로 인한 퍼블리시/서브스크라이브 부당 차단
가장 개발자를 농락하는 장애 시나리오는 네트워크 핑도 완벽하고, 인증서 핸드셰이크까지 통과하여 “연결 완수(Session Open)“까지 떴는데, 그 직후 “아무런 동작도 되지 않고 데이터가 막히는” 상태일 때다. 에지 센서가 아무리 Put을 보내도 라우터의 반응이 없다. 이는 Zenoh 데몬의 ACL 제어 정책(Policy)이 당신을 암살한 경우다.
2.0.1 와일드카드 매칭(Wildcard) 문법 오해로 인한 접근 불가
zenohd.json5 규칙에 "/factory/zone_A/sensor/*": "rw" 로 설정했다.
그런데 클라이언트 코드가 "/factory/zone_A/sensor/temp/motor_1" 이라는 토픽 계층 구조로 퍼블리시를 보냈다.
결과는 조용한 ‘Drop (폐기)’ 처분이다. Zenoh에서 단일 애스터리스크 * 는 “정확히 해당 노드 깊이(Depth) 1계층“만 포괄하고, 더 깊은 하위 디렉터리 전체를 포함하려면 더블 애스터리스크 ** 를 사용해야 한다는 문법 규칙을 망각한 죄다.
수천 개의 토픽이 이 사소한 오타 하나 덕분에 접근 권한 허가를 받지 못한다. 경로 정규식을 수시로 꼼꼼히 점검하라.
2.0.2 익명(Anonymous) 강등 접속의 함정
인증서와 토큰을 던져 라우터로부터 Admin (관리자) 유니크한 Role 맵을 받아야 하는 C++ 클라이언트가, 모종의 로그인 버그로 파라미터를 누락한 채 평문(Plain)으로 open을 호출해 버렸다.
이때 라우터는 자비로운(?) 기본 설정 탓에 에러를 뿜으며 튕겨내지 않고 이 클라이언트를 guest(익명) 등급 권한으로 통과시켜(Accept) 받아준다.
클라이언트 입장에서는 콘솔에 초록색 글씨로 “Session Opened!“가 뜨지만, 실제 제어명령 등 치명적 Put을 날리는 순간 Guest 제한 구역에 걸려 모조리 소멸당한다.
트러블슈팅 런북:
접속은 되는데 전송에 반응이 없다면, 즉시 라우터 측에서 보안 감시망 로그 장막을 개방하라.
RUST_LOG=zenoh_auth=debug,zenoh_transport=trace ./zenohd
로그 텍스트 중 클라이언트 고유 IP가 [Assigned Role: anonymous] 로 할당된 라인이 포착되는 순간, 범인은 라우터망이 아니라 클라이언트 로그인 바인딩 로직임이 적발된다.
3. 암호화/복호화 오버헤드로 인한 통신 지연
보안에는 막대한 вычисли(Computing) 비용이 소모된다. 서버용 Intel Xeon 칩에서는 눈에 띄지 않던 암호화 페널티가, CPU 캐시가 빈약한 초소형 MCU 계열 내장제나 무거운 Lidar 비디오 이미지를 송출하는 AMR 로봇에 부과되면 퍼포먼스는 거북이걸음이 된다.
모든 페이로드를 AES-256 계열의 최고급 알고리즘으로 무식하게 암호화(TLS Tunnel)하면, 하드웨어 연산기 한계에 부딪혀 통신 대역폭 자체가 병목에 갈려버린다.
3.0.1 가속화 대책 1. QUIC 프로토콜의 대안적 보안 이점
TCP 위에 TLS 1.2를 이중 코딩하여 올리는 전통적 방식을 폐기하라. 대신 그 자체로 TLS 1.3 보안 계층이 하부에 합체(Integrated)되어 있는 UDP 기반의 QUIC 프로토콜(quic/)을 최우선으로 검토하라.
보안 요구를 충족하면서도 초기 핸드셰이크(Handshake) 협상 비용 왕복이 RTT(Round Trip Time) 한 번으로 단축된다. 특히 모바일 로봇이 접속과 단절을 빈번히 겪을 때 통구이(Overhead) 딜레이를 획기적으로 깎아낸다.
3.0.2 가속화 대책 2. 선택적 페이로드 암호화 (Payload-level Encryption) 아키텍처 결단
통신망 자체가 IPSec 기반의 폐쇄망 VPN 터널 내부를 흐르고 있거나, 외부와 단절된 스마트 팩토리 인트라넷 환경이라면?
수만 개의 온도/습도 센서 데이터 배열을 굳이 라우터 단속망에서 한 번 더 TLS 패킷 단위 암호화로 감싸는 결벽증은 인프라 예산의 낭비일 뿐만 아니라 통신 레이턴시 지연의 주범이다.
차등 처리: 라우터 레벨의 네트워크 데이터 터널 암호화(Transport Security)는 과감히 평문(TCP/UDP) 수준으로 걷어내어 대역폭 고속도로를 뚫어라.
그리고 보안이 치명적으로 요구되는 패스워드 로그인, 금전 데이터, 혹은 cmd_vel 조향 제어 권한 토픽만, 개발자가 애플리케이션 코딩단에서 자체적으로 직렬화 후 내부 암호화(Payload-level Encryption : 예. JWT, AES)하여 이 평문 고속도로 위로 실어 보내라.
트러블슈팅의 궁극적인 정점은 보안이라는 미명 하에 덧칠된 군더더기를 걷어내 성능(Performance)과의 완벽한 황금비율을 조각해 내는 통찰력에 있다.
4. 토큰(Token) 및 키 관리 시스템과의 연동 장애
현대 엔터프라이즈 환경에서는 zenohd.json5 파일 안에 평문으로 비밀번호를 적어두는 정적 ACL(Static ACL) 따위는 허용되지 않는다. 필연적으로 외부의 SSO 서버나 OIDC(OpenID Connect), OAuth2 키클록(Keycloak) 등 인증 전용 플랫폼(Identity Provider)과 연이어 맞물려 동작하는 플러그인을 활성화해야 한다. 이때 제3자 서버와의 연동 딜레이가 라우터를 파괴한다.
4.0.1 외부 토큰 검증 서버의 타임아웃 지연
아침 9시에 1만 대의 모바일 클라이언트가 토큰 헤더(Token Header)를 들고 일제히 라우터에 세션 개방(Open)을 요청했다(Thundering Herd 문제).
보안을 위해 라우터는 이 토큰들이 올바른지 검사해야 한다. 매 연결이 들어올 때마다 외부 인증(Auth) 서버로 HTTP 쿼리를 날려 검증 확인을 기다리면, 즉시 라우터 내부의 I/O 리퀘스트 병목 스레드가 꽉 막히며 뒤따르는 모든 트래픽이 세션 대기열에 갇힌다.
무상태(Stateless) 증명 도입:
클라이언트가 제출한 구조가 JWT(JSON Web Token) 형식이라면, 라우터가 매번 중앙 Auth 서버를 찌르게 놔두지 마라. 라우터 로컬 램 단에 인증 서버의 **퍼블릭 키(Public Key)**를 미리 적재(Cache)해 두고, 네트워크 통신 없이 로컬 프로세스 안에서 자체적으로 서명을 검증하는 수학적 무상태 아키텍처로 탈바꿈시켜라.
4.0.2 토큰 만료 갱신(Refresh) 실패로 인한 세션 강제 절단
보안 강도가 완벽한 1급 통신방은 JWT 토큰의 엑세스 생명주기(TTL)를 5분 내지 10분으로 짧게 한정한다.
로봇 애플리케이션 코드를 짤 때 단순히 이니셜(초기) 타임에만 토큰을 사용해 zenoh.open()을 호출하고 끝이라 생각하면 오산이다.
5분이 경과되는 순간 라우터는 자비 없이 이 세션을 Expired 처리 후 잘라낸다. 로봇 제어 코드 베이스 안에는 반드시, 만료 시간이 다가오기 전에 리프레시 토큰(Refresh Token)을 사용해 새로운 키를 발급받아 기존 세션 밑단(Underlay) 정보에 다시 밀어 넣고 수명을 연장하는 코루틴(Goroutine/Async Task) 백그라운드 맥박(Heartbeat) 시스템이 탑재되어야 한다. 이를 누락한 클라이언트는 영원한 5분짜리 시한부 삶을 면치 못할 것이다.