mattermost incoming webhook 보안설정

개인적으로 mattermost를 slack대체 도구로 사용하고 있다.
외부에서 notification을 받는 용도로 incoming webhook을 설정하고 있는데,
이게 문제가 좀있다.

incoming webhook URL은 기본적으로 별도의 인증과정이 존재하지 않는다.

여러 방법을 고민해보고 있으나 여러 환경을 고려해야하다보니 과정에서 ip주소 차단을 걸기는 좀 아닌 것 같아서 요청 헤더값으로 처리하는 방법을 적용하였다.

사용중인 Mattermost 서비스 환경

내가 사용 중인 mattermost는 두개의 haproxy를 거쳐서 통신을 하고 있다.

oracleCloud VM이 1차 HAProxy

내 연구환경의 VM이 2차 HAProxy이다.

1차 HAProxy의 역할

상대적으로 저사양의 스펙으로 사용하고 있다.
외부 트래픽이 많지않아 vCpu: 1core | vMem: 1gb로도 아직 문제가 없다..

  • 1차 HAProxy는 SSL Offloading을 하고 있으며 내 연구환경(집에 구축해놓은…)으로 트래픽을 넘겨주고있다.
  • 해외 아이피들을 black_list로 차단하는 기능을 추가하였다.
  • 특정 sub-path를 제어하고있다.

2차 HAProxy의 역할

개인 연구환경으로 유입된 트래픽을 라우팅하고, basic-auth 인증을 하는 역할을 하고 있다.
KVM위에 VM으로 구성되어 있으며 vCPU: 2core | vMem: 4gb로 구성되어 있다.
서버 리소스가 많이 사용될 것 같은 인증이나 내부 라우팅을 처리한다.

  • 2차 HAProxy는 위에서 들어온 도메인을 확인하고 쿠버네티스, 도커, 서버 등 라우팅을 처리하고 있다.
  • 필요에 따라 basic-auth가 추가되어 인증을 하고 있다.

해결 방법

백엔드 다중화

HAProxy에도 FrontEnd와 BackEnd라는 개념이 있다.
FrontEnd는 유입되는 정보를 토대로 BackEnd(ReversProxy)로 넘겨주는 역할을 한다.

이를 이용하여 세가지 조건을 셋팅하였다.
HAProxy FrontEnd에서 다수의 도메인을 처리하고 있어서 ACL에 도메인으로 구분할수 있는 정책이 추가되었다.

# ACL: Authorization 헤더가 존재하고, "Bearer"로 시작하는지 확인 (Bot 요청 판별)
acl ACL_bot_request hdr_beg(authorization) Bearer
# 도메인 주소 확인
acl ACL_mm.example.com hdr(host) -i mm.example.com
# webhook sub-path정보
acl ACL_webhook path_beg /hooks/{hash_value}

FrontEnd에서 ACL을 설정하고 BackEnd로 라우팅 처리를 하였다.

조건

HAProxy는 정책을 위에서 부터 아래로 적용을 받으므로 설정할때 주의가 필요하다.

  • 도메인 일치 && incoming webhook 일치 & bot 헤더값 존재 => 일반 BackEnd (사실 이건 설정하지 않아도 될 것 같다 기본 incoming webhook과 api주소가 다름…)
  • 도메인 일치 && incoming webhook 일치 => basic-auth BackEnd
  • 도메인만 일치 => 일반 BackEnd

HAProxy구성 예시

아래 내용은 예시이며, 실제 내 환경에 적용된 것은 좀더 복잡하다.

frontend example.com
#        mode tcp
        mode http
        bind :80

        # ACL: Authorization 헤더가 존재하고, "Bearer"로 시작하는지 확인 (Bot 요청 판별)
        acl ACL_bot_request hdr_beg(authorization) Bearer

       ......
       ......
       
        # MatterMost 설정
        acl ACL_mm.example.com hdr(host) -i mm.example.com
        acl ACL_webhook path_beg /hooks/61mfuz5mg7dw7f

        use_backend mm.example.com if ACL_mm.example.com ACL_webhook ACL_bot_request
        use_backend auth-mm.example.com if ACL_mm.example.com ACL_webhook
        use_backend mm.example.com if ACL_mm.example.com
        
backend mm.example.com
        mode http

        server mattermost 192.168.0.204:8065
        
backend auth-mm.example.com
        mode http

        # 특정 URI에 대해서만 인증 요구
        http-request auth unless { http_auth(mycredentials) }

        server mattermost 192.168.0.204:8065

테스트

incomming webhook 테스트

간단한 파이썬 코드를 이용하여 테스트 해본다.

#!/usr/bin/python3 
import requests
import json

icurfer_url = "https://www.example.com/posts/"

state_code = requests.get(icurfer_url)

mm_url = "https://mm.example.com/hooks/{hash_value}"
headers = {
    'Content-Type': 'application/json'
}
parms = {
    "text": f"디버깅",
    #"text": f"icurfer/posts  접속체크 상태 {state_code}",
}

#username = "haproxy basic-auth 계정"
#password = "비밀번호"

rs = requests.post(mm_url, json=parms, headers=headers)
#rs = requests.post(mm_url, json=parms, headers=headers, auth=(username, password))

print(rs)

<Respose [401]> 에러를 확인 할수 있을 것이다. basic-auth정보를 넣으면 정상 동작한다.

bot계정 테스트

위의 코드와 아래 코드를 보면 url정보로 눈치 챘을 수 있다. 위에서 acl정책이 한줄 필요없을수 있다는 이유가 이 것이다.

#!/usr/bin/python3
import requests
import json

# Mattermost API 정보
MATTERMOST_URL = "https://mm.example.com/api/v4/posts"
BOT_ACCESS_TOKEN = "토큰값"

headers = {
    "Content-Type": "application/json",
    "Authorization": f"Bearer {BOT_ACCESS_TOKEN}"
}

# 요청 데이터
data = {
    "channel_id": "채널 아이디 값",
    "message": f"bot-test",
}

# Mattermost로 POST 요청 보내기
response = requests.post(MATTERMOST_URL, json=data, headers=headers)

# 응답 확인
if response.status_code == 201:
    print("Message sent successfully:", response.json())
else:
    print(f"Error {response.status_code}: {response.text}")

간단한(?) 설정으로 incoming webhook의 기본 url 접근에 인증기능을 추가하여 막아보았다.