← 전체 글
네트워킹/약 11분 읽기/— 조회

레거시 OPC DA 서버를 DCOM 삽질 없이 SCADA에 붙이는 법

OPC Classic(DA/HDA)이 DCOM에서 깨지는 이유. 포트 135 endpoint mapper, 동적 포트, 서버 identity, 방화벽, 가장 빠른 연결법.

네트워킹OPC UA문제 해결SCADA

멀쩡히 돌아가던 OPC 서버가 있는 공장을 인수한다. PLC 옆에 놓인 KEPServerEX나 Matrikon 박스이고, 예전 SCADA는 수년간 여기서 잘 읽어 왔다. 이제 새 SCADA 노드는 다른 장비에 있고, 그 노드의 OPC DA 클라이언트를 서버로 향하게 해 태그를 브라우징하면 0x800706BA — The RPC server is unavailable가 돌아온다. 더 나쁘게는 브라우징도 되고 연결도 되는데, 아이템을 추가하는 순간 0x80070005 — Access is denied를 던진다. OPC 서버 자체 로그에는 아무 설명도 없다. 실패가 애초에 OPC 계층까지 도달하지 못했기 때문이다. DCOM에서 죽었다.

이게 OPC Classic이 물리는 세금이다. DA, HDA, A&E는 전부 COM 인터페이스이고, 클라이언트와 서버가 다른 장비에 놓이는 순간 COM은 DCOM이 된다. 1990년대 후반 마이크로소프트의 원격 객체 배관 기술이고, 그에 딸린 인증 짐도 전부 따라온다. OPC Foundation이 UA로 넘어간 건 바로 이걸 벗어나기 위해서였다. 하지만 현장을 다시 짤 수는 없으니, DCOM을 길에서 치우는 법을 익히게 된다.

원격 OPC DA 클라이언트가 연결될 때 실제로 일어나는 일

네트워크 대화는 두 번 오간다. 그런데 사람들은 한쪽만 생각한다.

먼저 클라이언트는 서버 장비의 TCP 135번 포트에 있는 RPC endpoint mapper에 접속한다. 이게 전화번호부다. 클라이언트가 "OPCEnum 서비스 / OPC 서버 객체가 어디 있냐"고 물으면, mapper는 동적으로 할당된 포트로 답한다. 최신 Windows에서는 기본적으로 49152–65535 어딘가, 구형 빌드에서는 1024–65535다. 그다음 클라이언트는 그 동적 포트로 두 번째 연결을 열어 실제로 객체와 통신한다.

그래서 포트 135만 열고 나머지를 막은 방화벽 규칙은, 모두를 헷갈리게 하는 바로 그 증상을 만든다. 브라우징은 된다(endpoint-mapper 트래픽이니까). 그런데 실제 세션은 동적 포트가 막혀 연결되지 않는다. 브라우징 성공 뒤에 연결 시점에서 나오는 0x800706BA는, 반증되기 전까지는 이거다.

세 번째로 사람들이 놓치는 것: DCOM은 양방향으로 인증한다. 클라이언트가 서버에 인증하고, 그다음 서버가 콜백을 전달하려고 클라이언트에게 되돌아 인증한다. OPC DA 데이터 변경은 폴링이 아니라 COM 콜백으로 도착한다. 두 장비가 그 역방향 호출에 쓸 identity에 합의하지 못하면(서로 다른 로컬 계정, 일치하지 않는 비밀번호, 도메인 신뢰 없음) 온디맨드 읽기는 되는데 구독 업데이트는 절대 안 오는 연결이 되거나, 부하가 걸릴 때만 Access is denied가 튀어나온다.

대부분을 해결하는 체크리스트

이 순서로 하라. 계정이 맞춰지기 전에 방화벽 규칙부터 건드리지 마라.

  1. 클라이언트 장비에 OPC Core Components를 설치한다. OPCEnum.exe 서비스와 proxy/stub DLL(opcproxy.dll, opccomn_ps.dll)이 양쪽 끝에 다 있어야 한다. OPC Foundation 재배포판이 이걸 설치한다. OPC 서버가 한 번도 깔린 적 없는 클라이언트 박스에는 대개 이게 없고, 그러면 브라우징이 조용히 빈 결과만 돌려준다.

  2. identity를 일치시킨다. 도메인이 아닌 장비에서 확실한 방식은, 양쪽에 같은 사용자명과 같은 비밀번호를 가진 로컬 계정이다 — 예를 들어 opcuser / 동일 비밀번호로, 나중에 DCOM 권한을 줄 그룹의 멤버로. 도메인이라면 도메인 서비스 계정을 쓴다. 이게 "읽기는 되는데 업데이트가 안 온다" 증상의 가장 흔한 근본 원인이고, DCOMCNFG가 대신 해 줄 수 없는 수정이다.

  3. 서버의 DCOM identity를 명시적으로 지정한다. dcomcnfg → 구성 요소 서비스 → DCOM 구성에서 OPC 서버(그리고 OPCEnum)를 찾아 속성 → ID로 간다. "시작 사용자(The launching user)"로 두면 서버는 접속한 사람 누구로든 실행되고, 콜백 인증이 애매해진다. "다음 사용자(This user)"로 전용 계정을 지정해, 프로세스가 매번 하나의 알려진 identity로 돌게 한다.

  4. Launch/Activation과 Access 권한을 부여한다. 서버의 DCOM 구성 항목 보안 탭에서, 계정/그룹을 Launch and Activation Permissions와 Access Permissions 양쪽에 원격 권한을 체크해 추가한다. 장비 전체 "내 컴퓨터" 속성(COM 보안)에서도 똑같이 한다 — 서버별 권한만 주고 장비 전체 기본값이 여전히 원격 접근을 거부하면 아무것도 안 된다.

  5. 동적 포트 범위를 고정한 뒤, 그 범위로 방화벽을 연다. 구성 요소 서비스 → 내 컴퓨터 → 속성 → 기본 프로토콜에서 "연결 지향 TCP/IP"를 편집해 명시적 범위를 지정한다. 나는 지루하게 외우기 쉬운 작은 범위, 예컨대 5000–5020을 쓴다. 서버 몇 개면 충분하다. 그러면 방화벽 규칙이 유한해진다. TCP 135에 TCP 5000–5020, 양방향, 두 호스트 주소로 한정. 범위를 바꾼 뒤에는 재부팅하라. DCOM은 시작 시점에 이 값을 읽는다.

이렇게 하고도 DA 데이터가 안 흐르면 유선에서 캡처해서(tcpdump/Wireshark), 클라이언트가 동적 포트로 보낸 SYN에 응답이 없는지 본다. 그건 여전히 방화벽이나 범위 문제지, OPC 문제가 아니다.

대부분의 통합 담당자가 결국 택하는 게으른 답

DCOM을 단단히 조이는 데 쓴 한 시간은, 당신이 버리려는 프로토콜에 쓴 한 시간이다. 그리고 보안 관점에서 DCOM은 진짜로 잠그기 어렵다 — IEC 62443의 zone-and-conduit 사고방식은, 제어 존과 상위 감시 존 사이에 동적 RPC 포트 무리가 뚫려 있는 걸 결코 원하지 않는다. 감사관은 이걸 싫어하고, 그게 맞다.

그래서 하루를 살려 주는 수는 COM 터널러다. OPC 서버 박스에 작은 에이전트 하나, 클라이언트 박스에 하나를 두고, 둘 사이는 단일 포트의 평범한 TCP로 통신하며, 각 끝의 소프트웨어에게는 로컬 OPC 서버/클라이언트를 내놓는다. Cogent DataHub, Matrikon OPC Tunneller, KEPServerEX 자체 터널링이 다 이렇게 한다. DCOM은 네트워크를 절대 건너지 않는다 — 각 장비에서 로컬로만 돈다. 로컬에서는 사소한 일이다. 포트 하나만 열면 되고, TLS로 감쌀 수 있으며, 역방향 콜백 인증 문제도 콜백이 이제 로컬이라 사라진다. 서브넷이나 보안 경계를 건너는 것이라면 이제 나는 dcomcnfg를 열기도 전에 이걸 먼저 잡는다.

로드맵에 조금이라도 영향력이 있다면, 진짜 해법은 DA 서버 앞에 OPC UA wrapper를 두고(대부분 벤더가 제공한다 — KEPServerEX는 같은 런타임에서 UA와 DA를 동시에 내놓는다) 새 SCADA를 UA로 연결하는 것이다. TCP 포트 하나, 인증서 기반 신뢰, DCOM 없음. 레거시 DA 서버는 계속 PLC와 통신하고, 북쪽 링크만 바뀐다. 5년 뒤에 돌리고 있고 싶은 버전이 이거고, 대개 마이그레이션이 아니라 설정 변경이다.

마지막 실무 팁: 막혔을 때는 뭔가를 건드리기 전에 어느 계층이 실패했는지부터 증명하라. telnet server 135(또는 Test-NetConnection)로 endpoint mapper 도달 여부를 확인한다. 135는 응답하는데 세션이 죽으면, 그건 동적 포트나 콜백 identity 문제지 — OPC 서버도, 태그도, 누군가 반드시 다시 꽂아 보라고 할 그 케이블도 아니다.