TLS 브로커에 연결
PDComm 시뮬레이터는 평문 TCP로만 브로커에 연결합니다. 이 튜토리얼에서는 시뮬레이터 옆에 로컬 브로커(edge)를 두고, 로컬 브로커가 TLS와 클라이언트 인증서로 원격 브로커(site)에 데이터를 넘기게 합니다.
simulator ── plain TCP ──▶ edge broker ══ TLS bridge ══▶ site broker ══ TLS ══▶ operator (publisher) 127.0.0.1:1883 (edge.conf) cert CN=pdcomm-edge 127.0.0.1:8883 cert CN=operator- site 브로커는 8883번 포트에서 TLS만 받고, 모든 클라이언트에게 튜토리얼 CA가 발급한 인증서를 요구합니다.
- edge 브로커는 루프백 주소의 1883번 포트에서 평문을 받고,
opcua/#토픽을 site로 브리지합니다. - 운영자(operator)는 자신의 인증서로 site에 연결해 데이터를 받습니다.
실습에서는 두 브로커를 한 컴퓨터에서 실행합니다. 실제 구성에서는 site 주소와 인증서의 이름만 바뀝니다.
준비물
섹션 제목: “준비물”- Mosquitto 2.x와
mosquitto_sub - OpenSSL 명령행 도구. 인증서를 만드는 명령은 OpenSSL 3과 macOS 기본 LibreSSL에서 모두 동작합니다. 8단계의
openssl s_client옵션은 OpenSSL 3 기준입니다. - PDComm OPC UA 시뮬레이터 패키지와 OPC UA PubSub over MQTT의
publisher-scenario.json - 터미널 창 4개. 1883번과 8883번 포트를 쓰는 다른 브로커가 없어야 합니다.
-
작업 디렉터리를 만들고 파일 다섯 개를 저장합니다.
터미널 창 mkdir mqtt-tlscd mqtt-tlscp ../mqtt-lab/publisher-scenario.json .인증서 확장 정보입니다. 브로커 인증서의 subjectAltName에는 클라이언트가 연결하는 이름과 주소를 넣습니다.
server.ext # X.509 extensions for the broker certificate. The subjectAltName entries# must match the host name or address that clients and bridges connect to.basicConstraints = critical, CA:FALSEkeyUsage = critical, digitalSignature, keyEnciphermentextendedKeyUsage = serverAuthsubjectAltName = DNS:localhost, IP:127.0.0.1client.ext # X.509 extensions for client certificates (the edge bridge and operators).basicConstraints = critical, CA:FALSEkeyUsage = critical, digitalSignature, keyEnciphermentextendedKeyUsage = clientAuthsite 브로커 설정입니다.
site.conf # Site broker: MQTT over TLS on port 8883. Every client must present a# certificate issued by the tutorial CA; its common name becomes the user name.listener 8883 127.0.0.1cafile ca.crtcertfile site.crtkeyfile site.keyrequire_certificate trueuse_identity_as_username trueallow_anonymous falsepersistence falselog_dest stdout설정 뜻 cafile,certfile,keyfile클라이언트 인증서를 검증할 CA, 브로커 인증서, 브로커 개인 키 require_certificate true인증서를 내지 않는 클라이언트는 TLS 핸드셰이크에서 끊습니다. use_identity_as_username true클라이언트 인증서의 CN을 사용자 이름으로 씁니다. allow_anonymous false사용자 이름이 없는 연결을 거부합니다. edge 브로커 설정입니다.
edge.conf # Edge broker: plain TCP for local clients such as the PDComm simulator,# and a TLS bridge that forwards their OPC UA topics to the site broker.listener 1883 127.0.0.1allow_anonymous truepersistence falselog_dest stdoutconnection siteaddress 127.0.0.1:8883bridge_protocol_version mqttv50bridge_cafile ca.crtbridge_certfile edge.crtbridge_keyfile edge.keyremote_clientid pdcomm-edge-bridgetopic opcua/# out 1설정 뜻 connection sitesite라는 이름의 브리지를 시작합니다.address 127.0.0.1:8883브리지가 연결할 site 브로커 주소. 인증서의 subjectAltName에 있어야 합니다. bridge_protocol_version mqttv50MQTT 5.0으로 브리지해 Content Type과 사용자 속성을 그대로 전달합니다. bridge_cafile,bridge_certfile,bridge_keyfilesite 인증서를 검증할 CA와 브리지의 클라이언트 인증서, 개인 키 topic opcua/# out 1로컬의 opcua/#메시지를 site로 QoS 1로 보냅니다(out). -
튜토리얼 CA와 인증서 세 개를 만듭니다.
터미널 창 openssl req -x509 -new -newkey rsa:2048 -nodes -sha256 -days 365 \-subj "/CN=PDComm Tutorial CA" -keyout ca.key -out ca.crtopenssl req -new -newkey rsa:2048 -nodes -subj "/CN=localhost" -keyout site.key -out site.csropenssl x509 -req -in site.csr -CA ca.crt -CAkey ca.key -CAcreateserial -sha256 -days 365 \-extfile server.ext -out site.crtopenssl req -new -newkey rsa:2048 -nodes -subj "/CN=pdcomm-edge" -keyout edge.key -out edge.csropenssl x509 -req -in edge.csr -CA ca.crt -CAkey ca.key -CAcreateserial -sha256 -days 365 \-extfile client.ext -out edge.crtopenssl req -new -newkey rsa:2048 -nodes -subj "/CN=operator" -keyout operator.key -out operator.csropenssl x509 -req -in operator.csr -CA ca.crt -CAkey ca.key -CAcreateserial -sha256 -days 365 \-extfile client.ext -out operator.crtchmod 600 *.key인증서가 CA로 이어지는지 확인합니다.
터미널 창 openssl verify -CAfile ca.crt site.crt edge.crt operator.crtsite.crt: OKedge.crt: OKoperator.crt: OK -
네 터미널 모두
mqtt-tls디렉터리에서 작업합니다. 첫 번째 터미널에서 site 브로커를 시작합니다.터미널 창 mosquitto -c site.confConfig loaded from site.conf.Opening ipv4 listen socket on port 8883.mosquitto version 2.1.2 running -
두 번째 터미널에서 운영자 인증서로 site에 연결해 구독합니다.
터미널 창 mosquitto_sub -h 127.0.0.1 -p 8883 --cafile ca.crt --cert operator.crt --key operator.key \-V 5 -t 'opcua/#' -F '%t qos=%q retain=%r content-type=%C user-properties=%P %l bytes'site 로그에 TLS 1.3 연결과 인증서에서 가져온 사용자 이름이 보입니다.
New client connected from 127.0.0.1:53292 as auto-A75E2731-… (p5, c1, k60, u'operator').Client auto-A75E2731-… negotiated TLSv1.3 cipher TLS_AES_256_GCM_SHA384 -
세 번째 터미널에서 edge 브로커를 시작합니다.
터미널 창 mosquitto -c edge.confOpening ipv4 listen socket on port 1883.Connecting bridge site (127.0.0.1:8883)mosquitto version 2.1.2 runningsite 로그에 브리지 연결이 나타납니다. 사용자 이름은 edge 인증서의 CN인
pdcomm-edge입니다.New client connected from 127.0.0.1:53293 as pdcomm-edge-bridge (p5, c0, k60, u'pdcomm-edge').Client pdcomm-edge-bridge negotiated TLSv1.3 cipher TLS_AES_256_GCM_SHA384 -
네 번째 터미널에서 시뮬레이터 발행자를 실행합니다. 시뮬레이터는 edge의
127.0.0.1:1883에 평문으로 연결합니다.터미널 창 "$PDCOMM_APPS/bin/pdcomm-opcua-headless" --scenario publisher-scenario.jsonPDCOMM_APPS가 없는 새 터미널이라면 먼저PDCOMM_APPS=<시뮬레이터 패키지 디렉터리>를 실행합니다. 시나리오는 종료 코드 0으로 끝납니다. 두 번째 터미널의 운영자에게 두 메시지가 TLS로 도착합니다.opcua/uadp/data/100/line1 qos=0 retain=0 content-type=application/opcua+uadp user-properties=UAMessageType:ua-data 52 bytesopcua/uadp/data/100/line1 qos=0 retain=0 content-type=application/opcua+uadp user-properties=UAMessageType:ua-data 52 bytesContent Type과
UAMessageType이 그대로 남아 있으므로 site 쪽 OPC UA 구독자도 이 메시지를 받을 수 있습니다. -
인증서 없는 연결이 거부되는지 확인합니다. 네 번째 터미널에서 실행합니다.
터미널 창 mosquitto_sub -h 127.0.0.1 -p 8883 --cafile ca.crt -t 'opcua/#' -C 1 -W 3Error: The connection was lostsite 로그에는 이유가 남습니다.
OpenSSL Error while trying to get the error[0]: error:0A0000C7:SSL routines::peer did not return a certificateClient 127.0.0.1 [127.0.0.1:53299] disconnected: Protocol error. -
openssl s_client로 TLS 구성을 MQTT와 따로 확인할 수도 있습니다.터미널 창 openssl s_client -connect 127.0.0.1:8883 -CAfile ca.crt -cert operator.crt -key operator.key \-verify_ip 127.0.0.1 -verify_return_error -brief < /dev/null출력의 주요 줄은 다음과 같습니다.
CONNECTION ESTABLISHEDProtocol version: TLSv1.3Ciphersuite: TLS_AES_256_GCM_SHA384Peer certificate: CN=localhost-verify_ip는 인증서의 subjectAltName에 그 IP 주소가 있는지 검사합니다. 호스트 이름으로 연결한다면-verify_hostname <이름>을 씁니다.
- 두 번째 터미널의
mosquitto_sub를 Ctrl+C로 끝냅니다. - 세 번째와 첫 번째 터미널의 브로커를 Ctrl+C로 끝냅니다.
- 시험이 끝나면
mqtt-tls디렉터리를 지웁니다. 특히ca.key는 다른 곳에 남기지 않습니다.
자주 나는 오류
섹션 제목: “자주 나는 오류”| 출력 | 원인 | 해결 |
|---|---|---|
Error: The connection was lost와 site 로그의 peer did not return a certificate |
클라이언트가 인증서를 내지 않았습니다. | --cert와 --key를 줍니다. |
Error: Protocol error와 site 로그의 tlsv1 alert unknown ca |
클라이언트가 site 인증서를 신뢰하지 않습니다. | --cafile ca.crt를 줍니다. |
edge 로그의 certificate verify failed와 Bridge site next backoff will be … ms |
브리지가 site 인증서를 검증하지 못했습니다. | bridge_cafile과 address가 인증서와 맞는지 확인합니다. |
시뮬레이터의 MQTT broker connection failed: MQTT transport I/O: ConnectionReset |
시뮬레이터를 TLS 포트(8883)에 연결했습니다. | 시뮬레이터는 edge의 평문 포트(1883)에 연결합니다. |
메시지별 원인은 TLS 문제에 더 자세히 정리했습니다.