MQTT로 카톡서버 만들어보자 - 7/10. ACL

빙고리우스·2024년 4월 3일
1
post-thumbnail

내가 딱 정해줄께

문 앞에서 민증 깠다고 다 된 건 아니다.
20대는 홀로, 30대는 룸으로~
선글라스 아저씨가 안내하는 곳으로 가야 한다.
노는 물이 다르다.

pub/sub 도 마찬가지다.
server는 server 토픽에 붙고, client는 client 토픽에 붙어야지.
client가 server 토픽에 붙으면 안된다.
그랬다가는 다른 사람 대화 내용을 다 들여다 볼 수 있다.
개폭망이다.

저 아저씨 귀에 꽂고 있는 거 뺏긴 것과 같은 거다.
몰래 다 듣고 있다는 뜻이다.

자 그럼 어떡해야 하나?

앞 글 1. Pub/Sub으로 로그인? 에 나와 있는 아래 그림을 보자.

  1. sub server/login : server/login으로 들어오는 건 내가 처리할께
  2. sub clients/client1 : 결과는 clients/client1 로 주세요
  3. pub server/login : server님, 저 login할래요
  4. 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

ACL(Access Control List)은 프로토콜 영역이 아니다.
MQTT 3.1 Spec에서 검색해봐라. 안 나온다.

누가 어디에 붙을 수 있는지 정하는 건 broker 영역이다.
이건 mosquitto conf 에서 찾아야 한다.

위 문서에서 acl로 검색해보면 다음과 같이 나온다.
acl_file

우리 규칙에 맞게 시키는대로 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/#
profile
다할줄아는 사람보다 뭔가 한가지 똑부러지게하는 사람이되자.

0개의 댓글