문 앞에서 민증 깠다고 다 된 건 아니다.
20대는 홀로, 30대는 룸으로~
선글라스 아저씨가 안내하는 곳으로 가야 한다.
노는 물이 다르다.
pub/sub 도 마찬가지다.
server는 server 토픽에 붙고, client는 client 토픽에 붙어야지.
client가 server 토픽에 붙으면 안된다.
그랬다가는 다른 사람 대화 내용을 다 들여다 볼 수 있다.
개폭망이다.
저 아저씨 귀에 꽂고 있는 거 뺏긴 것과 같은 거다.
몰래 다 듣고 있다는 뜻이다.
자 그럼 어떡해야 하나?
앞 글 1. Pub/Sub으로 로그인? 에 나와 있는 아래 그림을 보자.
- sub server/login : server/login으로 들어오는 건 내가 처리할께
- sub clients/client1 : 결과는 clients/client1 로 주세요
- pub server/login : server님, 저 login할래요
- pub clients/client1 : clients/client1 DB에 있는 패스워드랑 다른데...너 누구냐?
즉 server는 server/# 을 subscribe(read)하고, clients/# 에 publish(write)한다.
반대로 client는 clients/# 을 subscribe(read)하고, server/# 에 publish(write)한다.
이게 우리의 룰이다.
client가 server/# 을 subscribe(read) 하면 안된다.
어떻게 해야할까?
즉 누가 어떤 topic에 sub할지 pub할지 정해주는게 필요하다.
자율에 맡기면 X된다.
그래서 나온게 ACL이다.
ACL(Access Control List)은 프로토콜 영역이 아니다.
MQTT 3.1 Spec에서 검색해봐라. 안 나온다.
누가 어디에 붙을 수 있는지 정하는 건 broker 영역이다.
이건 mosquitto conf 에서 찾아야 한다.
위 문서에서 acl로 검색해보면 다음과 같이 나온다.
우리 규칙에 맞게 시키는대로 acl_file을 만들자.
acl
# deny anonymous access
topic deny $SYS/#
user server
topic read server/#
topic write clients/#
user client
topic write server/#
topic read clients/#
일단 $SYS/# topic은 시스템 정보이므로 아무나 접근 못하게 막고,
server는 server/# 을 subscribe(read)하고, clients/# 은 publish(write)한다.
client는 clients/# 을 subscribe(read)하고, server/# 에 publish(write)한다.
멋지지 않은가
설정 파일에 acl을 등록하자.
mosquitto.conf
acl_file C:\Program Files\mosquitto\conf.d\acl
아래 코드는 앞에서 봤던 server.js로, 제대로 된 자격증명(server/pwd)으로 로그인한 거다.
다만 TLS/SSL 들어가면 복잡하니까, 최대한 단순하게 하기 위해 뺐자.
제대로 동작하는지 확인하기 위해 message를 받으면 출력하도록 바꿨다.
개발자가 가장 많이하는 디버깅 아닌가.
1번 server.js
const mqtt = require("mqtt");
const options = {
port: 1883,
host: 'stark',
username: 'server',
password: 'pwd'
}
const server = mqtt.connect(options);
server.on("connect", () => {
server.subscribe("server/login");
});
server.on("message", (topic, message) => {
var data = JSON.parse(message);
console.log('server', data);
var client_topic = 'clients/' + data.id;
if (data.pwd === 'pwd') {
var res = { msg: 'login ok' };
server.publish(client_topic, JSON.stringify(res));
} else {
var res = { msg: 'login fail' };
server.publish(client_topic, JSON.stringify(res));
}
});
아래는 fake 자격증명(client/pwd)으로 클라이언트 중 한 놈이 마치 server인양 server.js 를 실행한거다.
로그인은 통과하겠지만, 진짜 server가 아니다.
제대로 동작하는지 확인하기 위해 message를 받으면 출력하도록 했다.
2번 fake_server.js
const mqtt = require("mqtt");
const options = {
port: 1883,
host: 'stark',
username: 'client',
password: 'pwd'
}
const server = mqtt.connect(options);
server.on("connect", () => {
server.subscribe("server/login");
});
server.on("message", (topic, message) => {
var data = JSON.parse(message);
console.log('fake_server', data);
var client_topic = 'clients/' + data.id;
if (data.pwd === 'pwd') {
var res = { msg: 'login ok' };
server.publish(client_topic, JSON.stringify(res));
} else {
var res = { msg: 'login fail' };
server.publish(client_topic, JSON.stringify(res));
}
});
자 이 상태에서 3번 client.js를 실행하면 어떻게 될까?
$> node client.js
당연히 1번 server.js는 반응하지만, 2번 fake_server.js는 반응하지 않는다.
우쒸! fake_server.js에서 오류라도 떨어져야되는 거 아니예요.
Web 로그인에서도 아이디가 틀렸다, 패스워드가 틀렸다 이렇게 세세하게 안 알려준다.
뭔가를 알려주면 해커는 그것으로 유추하기 때문에, 걍 무응답이 답이다.😁
acl 파일의 server 부분은 다음과 같이 하나 하나 써주는게 보안에 도움이 된다.
acl
# deny anonymous access
topic deny $SYS/#
user server
topic read server/login
topic read server/logout
topic read server/signin
topic read server/signout
topic read server/talk
topic write clients/#
user client
topic write server/login
topic write server/logout
topic write server/signin
topic write server/signout
topic write server/talk
topic read clients/#