티스토리 뷰

MQTT 통신 구간에 대해 암호화가 적용되어 있지 않으면 중요 정보가 평문으로 노출될 위험이 있다.

안전한 MQTT 통신을 하기 위해서 TLS를 이용한 통신 구간 암호화가 필요하다.

RabbitMQ는 TLS를 지원하고 있으며, 보안 설정을 통해 TLS 적용이 가능하다.

 

※ 최신 버전의 Erlang에서 TLS가 지원된다고 되어있으므로 최신 버전의 Erlang 사용을 권장한다.

 

RabbitMQ에서 TLS 지원을 사용하도록 설정하려면 인증 기관 번들의 위치, 서버의 인증서 파일 및 서버키를 알 수 있도록 노드를 구성해야 한다. TLS와 관련된 필수 구성 설정은 다음과 같다.

구성 키 설명
리스너.ssl TLS 연결을 위한 listen 포트 목록
(RabbitMQ는 단일 인터페이스 또는 여러 인터페이스에서 listen 할 수 있음)
ssl_options.cacertfile 인증서 기관(CA) 번들 파일 경로
ssl_options.certfile 서버 인증서 파일 경로
ssl_options.keyfile 서버 개인 키 파일 경로
ssl_options.verify peer 인증 사용 여부 설정
ssl_options.fail_if_no_피어_인증서 클라이언트가 인증서를 제공하지 않을 경우 true로 설정하면 TLS 연결 거부

 

1. 평문 전송 테스트

TLS가 적용되지 않은 환경에서 평문 전송되는 것을 확인한다.

 

1) paho를 이용한 데이터 전송

- paho를 이용하여 서버에 데이터를 전송한 후 wireshark를 이용해 확인해보았다.

-

/topic/01 토큰으로 데이터를 publish 한다.

import paho.mqtt.client as paho

broker = "192.168.64.136"
#broker = "localhost"
port = 1883

def on_publish(client,userdata,result):
    print("data published \n")
    pass

client1 = paho.Client("control1")
client1.username_pw_set('tester','tester')
client1.on_publish = on_publish
client1.connect(broker,port)
ret = client1.publish("/topic/01", "username: test, pw: p@ssword")
$ python3 paho_publisher.py    
data published

 

2) wireshark를 이용한 데이터 확인

- 메시지가 평문으로 노출되는 것을 확인할 수 있다.

 

2. TLS 사용을 위한 config 파일 구성

ubuntu에 설치된 RabbitMQ의 경우 /etc/rabbitmq 경로에 config 파일이 위치하며, 용도에 따라 다양한 환경 구성 파일이 존재한다. TLS 설정에는 rabbitmq-env.conf와 rabbitmq.conf 파일이 필요하다.

 

각 conf 파일의 사용 용도는 다음과 같다.

rabbitmq-env.conf : 노드 이름, 파일 및 디렉터리 위치, 환경 구성 파일 등을 정의

rabbitmq.conf : 서버 및 플로그인 설정 관련 정의

 

TLS 설정을 위해 rabbitmq-env.conf에 rabbitmq.conf의 경로를 지정해주고, rabbitmq.conf에 TLS 구성을 진행했다.

 

1) rabbitmq-env.conf 설정

- rabbitmq-env.conf에 conf 파일의 경로를 오버라이드 해준다.

$ ls
enabled_plugins  rabbitmq-env.conf

$ cat rabbitmq-env.conf
# Defaults to rabbit. This can be useful if you want to run more than one node
# per machine - RABBITMQ_NODENAME should be unique per erlang-node-and-machine
# combination. See the clustering on a single machine guide for details:
# http://www.rabbitmq.com/clustering.html#single-machine
#NODENAME=rabbit

# By default RabbitMQ will bind to all interfaces, on IPv4 and IPv6 if
# available. Set this if you only want to bind to one network interface or#
# address family.
#NODE_IP_ADDRESS=127.0.0.1

# Defaults to 5672.
#NODE_PORT=5672

// 경로 지정
# overrides primary config file location
RABBITMQ_CONFIG_FILE=/etc/rabbitmq/rabbitmq.conf

 

2) SSL Plugin 설정

- SSL Plugin을 활성화 시킨다.

$ rabbitmq-plugins enable rabbitmq_auth_mechanism_ssl

$ cat /etc/rabbitmq/enabled_plugins
[rabbitmq_auth_mechanism_ssl,rabbitmq_management,rabbitmq_mqtt].

 

- 재실행 후 로그파일을 확인해보면 플러그인이 적용되는 것을 확인할 수 있다.

  (로그파일은 /var/log/rabbitmq에서 확인할 수 있다.)

 

3) TLS CA 설치

- TLS 사용을 위한 CA 설치한다.

※ CA가 맞지 않으면 추후 TLS 연결 시 문제가 생길 수 있으므로, Broker 서버의 ip로 맞춰준다.

   (Broker 서버의 도메인네임을 입력해야 하나 도메인네임이 없어 ip로 테스트 진행)

$ git clone https://github.com/michaelklishin/tls-gen tls-gen
$ cd tls-gen/basic
# private key password
$ make PASSWORD={패스워드 지정} CA={broker server ip}
$ make verify
$ make info
$ ls -l ./result

 

- 관리하기 편하기 위해 다른 디렉토리로 파일을 옮긴다

  (/etc/rabbit 경로에 cert 디렉토리 생성하여 옮겨주었음)

$ mv * /etc/rabbitmq/cert
$ cd /etc/rabbitmq/cert
$ ls
ca_certificate.pem      client_key.p12          server_key.p12
ca_key.pem              client_key.pem          server_key.pem
client_certificate.pem  server_certificate.pem

 

4) TLS 설정

- rabbitmq.conf 파일에 TLS 설정을 진행한다.

- 기본적으로 rabbitmq.conf 파일이 존재하지 않을 경우, 새로 생성하여 진행하면 된다.

listeners.tcp = none						# non-TLS 비활성화
mqtt.listeners.ssl.default = 5671                # TLS 포트 지정

ssl_options.cacertfile = /etc/rabbitmq/cert/ca_certificate.pem
ssl_options.certfile   = /etc/rabbitmq/cert/server_certificate.pem
ssl_options.keyfile    = /etc/rabbitmq/cert/server_key.pem
ssl_options.password = testtest             # 인증키 암호화 패스워드
ssl_options.verify     = verify_peer        # peer 인증 사용
ssl_options.fail_if_no_peer_cert = true     # 인증서 없는 클라이언트 거부
ssl_options.versions.1 = tlsv1.3            # tls 1.3 버전 사용

 

5) TLS 적용 확인

- TLS가 적용된 상태로 실행되는지 서비스 재시작 후 로그 파일을 확인해본다.

$ service rabbitmq-server restart
$ cat /var/log/rabbitmq/rabbit@ubuntu.log
...
...
2021-08-06 08:56:05.628519-07:00 [info] <0.643.0> started MQTT TCP listener on [::]:1883
2021-08-06 08:56:05.629370-07:00 [info] <0.662.0> started MQTT TLS listener on [::]:8883
2021-08-06 08:56:05.631881-07:00 [info] <0.511.0> Ready to start client connection listeners
2021-08-06 08:56:05.632682-07:00 [info] <0.684.0> started TCP listener on [::]:5672
2021-08-06 08:56:05.674992-07:00 [info] <0.511.0> Server startup complete; 5 plugins started.
2021-08-06 08:56:05.674992-07:00 [info] <0.511.0>  * rabbitmq_auth_mechanism_ssl
2021-08-06 08:56:05.674992-07:00 [info] <0.511.0>  * rabbitmq_mqtt
2021-08-06 08:56:05.674992-07:00 [info] <0.511.0>  * rabbitmq_management
2021-08-06 08:56:05.674992-07:00 [info] <0.511.0>  * rabbitmq_web_dispatch
2021-08-06 08:56:05.674992-07:00 [info] <0.511.0>  * rabbitmq_management_agent
...
...

 

- openssl을 이용하여 TLS 접속 테스트를 할 수 있다.

- 앞서 생성한 CA 인증서, Client 인증서, Client 키 값을 이용하여 접속 테스트가 가능하다.

- 에러가 발생할 경우 아래의 링크를 참고하여 차근차근히 트러블 슈팅을 해보는 것이 좋다.

※ 참고 : Troubleshooting TLS-enabled Connections — RabbitMQ

$ openssl s_client -connect localhost:5671 -cert client_certificate.pem -key client_key.pem -CAfile ca_certificate.pem
Enter pass phrase for client_key.pem:
CONNECTED(00000005)
depth=1 CN = TLSGenSelfSignedtRootCA, L = $$$$
verify return:1
depth=0 CN = 192.168.64.136, O = server
verify return:1
---
Certificate chain
 0 s:CN = 192.168.64.136, O = server
   i:CN = TLSGenSelfSignedtRootCA, L = $$$$
 1 s:CN = TLSGenSelfSignedtRootCA, L = $$$$
   i:CN = TLSGenSelfSignedtRootCA, L = $$$$
---
Server certificate
-----BEGIN CERTIFICATE-----
MIIDdTCCAl2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAxMSAwHgYDVQQDDBdUTFNH
ZW5TZWxmU2lnbmVkdFJvb3RDQTENMAsGA1UEBwwEJCQkJDAeFw0yMTA4MDQxNDU3
NTlaFw0zMTA4MDIxNDU3NTlaMCoxFzAVBgNVBAMMDjE5Mi4xNjguNjQuMTM2MQ8w
DQYDVQQKDAZzZXJ2ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCZ
LcwYl7EGHOm+sZi2MXshcgHAynd2XO+X/NOq2sMskpbc7c/8/jRtkTxiaTWhsKjN
fhM42s9cbJw2Xykxfni2PN5Wg7v9YgvAmGz/7S0klQ/4QEVt/4MHpyK5vxXkMkgz
o5pX1dNgxm1H7Hl1xuu+MyzlGWcQBiC+1/tSh5O5Xx21zOBqODJiic/nMNvDgnU9
EqosjuM3sFtLhw3+vu0YJWHMqgDXU7hVY1IOtstklMduOx/X5gHC4j9T+FEnHhfi
paZHAGbwwOgoHSEEv6uI4IpMjdu0j8agOfw90biVYid97p5wF6a1G2KK6DXott4a
XniW8PnPmDtU2yeHnw4fAgMBAAGjgZ4wgZswCQYDVR0TBAIwADALBgNVHQ8EBAMC
BaAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwLAYDVR0RBCUwI4IOMTkyLjE2OC42NC4x
MzaCBnVidW50dYIJbG9jYWxob3N0MB0GA1UdDgQWBBTZISY+5qul759Gpwndlrkq
MRNiTzAfBgNVHSMEGDAWgBSL+8kgU+4vmsmGxOCSxxNTSacNKTANBgkqhkiG9w0B
AQsFAAOCAQEAOYUccy7Y3selVFJFOzZsEPTseyVkGiP4X+oeekJxrAmTyuNCI+o8
p2hE51ieRpV5zklfXlbr2mUplOoMBOSCJvrTRJ8wttMl/cF5pTfcW1j7cv/lz/RF
rGy9qeyH+eQLHkNCNzOjNbHOu5twUZAUql5luVE3USCnOv4RcRPux2RUgWkSCt2/
qbKf/OibfqLRPudr+E3jidddPj2UsyG7wi8ABgqsL6BM7mG8kVew93kfzgqE1wGu
uBMFBXMixSYLUnHTgjms458zyk7pbHjUXq9aXizBsynpOTbO/X3MIppbXInd1tt1
7b4Zu97MGxVxp5uMxu+bb34EzADUEs/5HA==
-----END CERTIFICATE-----
subject=CN = 192.168.64.136, O = server

issuer=CN = TLSGenSelfSignedtRootCA, L = $$$$

---
No client certificate CA names sent
Requested Signature Algorithms: ECDSA+SHA512:ECDSA+SHA384:ECDSA+SHA256:RSA-PSS+SHA512:RSA-PSS+SHA384:RSA-PSS+SHA256:RSA-PSS+SHA512:RSA-PSS+SHA384:RSA-PSS+SHA256:Ed25519:Ed448:RSA+SHA512:RSA+SHA384:RSA+SHA256:ECDSA+SHA1:RSA+SHA1
Shared Requested Signature Algorithms: ECDSA+SHA512:ECDSA+SHA384:ECDSA+SHA256:RSA-PSS+SHA512:RSA-PSS+SHA384:RSA-PSS+SHA256:RSA-PSS+SHA512:RSA-PSS+SHA384:RSA-PSS+SHA256:Ed25519:Ed448:RSA+SHA512:RSA+SHA384:RSA+SHA256
Peer signing digest: SHA256
Peer signature type: RSA-PSS
Server Temp Key: X25519, 253 bits
---
SSL handshake has read 2369 bytes and written 2392 bytes
Verification: OK
---
New, TLSv1.3, Cipher is TLS_AES_256_GCM_SHA384
Server public key is 2048 bit
Secure Renegotiation IS NOT supported
Compression: NONE
Expansion: NONE
No ALPN negotiated
Early data was not sent
Verify return code: 0 (ok)
---
closed

TLS 1.3 적용 확인

 

3. 전송구간 암호화 적용 확인

1)  paho를 이용한 데이터 전송 (TLS 접속)

- TLS 접속 후 데이터를 전송하도록 코드를 작성한 뒤 실행한다.

- 실행 시 인증서 암호 값을 입력해준다.

import paho.mqtt.client as mqtt
import ssl

broker = "192.168.64.136"
#broker = "localhost"
port = 5671

def on_publish(client,userdata,result):
    print("data published \n")
    pass

client1 = mqtt.Client("control1")
client1.username_pw_set('tester','tester')
context = ssl.create_default_context(cafile="/etc/rabbitmq/test-cert/ca_certificate.pem")
context.load_cert_chain("/etc/rabbitmq/test-cert/client_certificate.pem","/etc/rabbitmq/test-cert/client_key.pem")
client1.tls_set_context(context)
client1.on_publish = on_publish
client1.connect(broker,port)
ret = client1.publish("/topic/01", "tls topic test!")
$ python3 paho_tls_publisher.py
Enter PEM pass phrase:
data published

 

2) wireshark를 이용한 데이터 확인

- 서버에서 설정한 tls 1.3이 적용되어 평문 데이터 확인이 불가하다.

 

3) TLS 적용되지 않은 연결 거부

- TLS 적용하지 않은 연결을 거부하도록 설정해두었기에, TLS 설정이 없으면 연결에 실패하는 것을 확인할 수 있다.

$ python3 no_tls.py
Traceback (most recent call last):
  File "/home/kali/pika/test3.py", line 3, in <module>
    connection = pika.BlockingConnection(pika.ConnectionParameters('192.168.64.136', credentials = pika.PlainCredentials(username='test',password='test')))
  File "/usr/local/lib/python3.9/dist-packages/pika/adapters/blocking_connection.py", line 360, in __init__
    self._impl = self._create_connection(parameters, _impl_class)
  File "/usr/local/lib/python3.9/dist-packages/pika/adapters/blocking_connection.py", line 451, in _create_connection
    raise self._reap_last_connection_workflow_error(error)
pika.exceptions.AMQPConnectionError
댓글
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday