(뒤늦게 작성하는) TURN 서버 구현하기

pds·2023년 4월 2일
2

TIL

목록 보기
46/60

항해 최종프로젝트에서 오디오/비디오 채팅 기능이 있었고

이 때 일부 네트워크 간 P2P 연결이 안되는 문제를 해결하기 위해 TURN 서버를 구현했었다.


급하게 구현하느라 기록하지 못했던 점, 복습차원 겸,

계속 켜둬서 AWS 과금이 계속 발생했기에 평생무료인 Oracle Cloud Instance로 서버를 교체하는 겸사겸사 뒤늦게 구현 과정을 기록해보았다.

안그래도 돈 없는데 잊고 있다가 결제 기록을 보고 마음이 아팠다..  

TURN 서버가 왜 필요할까?


무료 STUN 서버가 있는데

const peerConnection = new RTCPeerConnection({
          iceServers: [
            {
              urls: 'stun:stun.l.google.com:19302',
            },
          ],
});

구글에서 제공하는 무료 STUN서버를 사용하면 된다.

하지만 개발하며 직접 테스트할 때 다음과 같은 환경에서 문제가 있었다.

모바일 LTE환경에서 접속한 사용자와 랜선 사용자 간 P2P 연결이 되지 않았다. 마찬가지로 모바일 와이파이를 사용해도 그랬다.

특정 카페 와이파이로 접속했을 때 다른 사용자와 P2P 연결이 되지 않았다.

위와 같은 문제를 겪어보며 왜 그런지 조사하였고 이유를 알게되었다.


STUN 서버는

NAT을 통과하는 클라이언트 장치의 공인 IP주소와 포트를 확인하고 이를 활용해 클라이언트 간 연결을 수행할 수 있게 도와준다.

(1) 어떤 종단이 NAT/Firewall 뒤에 있는지를 판단하게 해준다.

(2) 어떤 종단에 대한 Public IP Address를 결정하고 NAT/FIrewall의 유형에 대해서 알려준다.

NAT(Network Address Translation)

인터넷 같은 공용 네트워크에서 사설 네트워크를 사용하기 위한 기술입니다.

NAT은 사설 네트워크에서 사용되는 IP 주소 범위인 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16과 같은 사설 IP 주소를 인터넷에서 사용되는 공인 IP 주소로 변환하고 이 변환된 주소를 사용해 인터넷에 연결합니다.

그리고 이 NAT은 다양한 변환 방식이 존재한다.

Full Cone NAT

클라이언트와 한번 매핑하면 다른 곳에서도 해당 포트로 접속이 가능하다.

Restricted NAT

외부에서 내부로 들어오는 트래픽에 대해서만 포트 매핑을 생성하고 유지한다.

Symmetric NAT

같은 내부 IP와 포트에서 나가는 패킷이 항상 같은 공인 IP주소와 포트로 매핑되지 않고 랜덤하게 매핑된다.


개념 이해는 부족했으나 STUN 서버만으로 안되는 이유는

NAT의 변환 방식이 다양하고 동적이여서 통신에 사용되는 public ip가 늘 같지 않다는 것이다.

따라서 앞서 공인 IP와 포트를 확인해 연결시킨다는 역할을 하는 STUN서버만으로는 연결이 확실하지 않을 수 있다는 결론을 얻었다.

더 찾아보니 기업 네트워크, 공용 와이파이 등에서 Symmetric NAT을 주로 사용하고

모바일 네트워크 같은 경우도 동적으로 할당되는 공인 IP주소를 활용한다고 한다.

공인 IP주소가 동적으로 바뀌는 경우 STUN서버에 제대로 전달되지 않거나 이전에 수집한 NAT정보와 매칭되지 않게되어 잘못된 정보를 전달할 수 있다고 하며

그 외에도 네트워크의 방화벽 문제로 STUN서버를 통해 연결할 수 없는 상황이 있다고 한다.


TURN 서버는

클라이언트 서버 간 데이터 전송을 중계한다.

STUNNAT 뒤의 공인 IP주소를 알아내 클라이언트끼리 이를 활용해 연결할 수 있도록 중계하는 역할을 했다면

TURN은 데이터를 중계해준다.

따라서 공인IP가 변경되어도 중계서버인 TURN을 통해 데이터를 주고받을 수 있게 해준다.

TURN서버는 하나의 공인 주소를 가지고 있고 이 주소를 통해 클라이언트들이 직접적으로 미디어를 릴레이하기 때문에 네트워크와 컴퓨팅 자원이 소모된다.


TURN 서버 구현하기

Twillo 등의 서비스를 찾아보았지만 결국 14일 무료 체험판 뿐이였고 무료버전은 없었다.

아무래도 직접적인 미디어 트래픽이 발생하기 때문에 공짜가 있을리 없지 않나 싶긴 했다.

따라서 coturn이라는 오픈소스로 iaas 인프라에 직접 구축하기로 했다.

여기를 참고하여 구현했다.


다행히 예전에 구현했던 서버에 history가 남아있어 이를 기반으로 다시 구현했다.

ubuntu 20.04 기준이다.

Coturn 설치

sudo apt-get update
sudo apt-get install coturn

인스턴스 종료해도 자동 재시작 설정

sudo vi /etc/default/coturn

TURNSERVER_ENABLED=1

Coturn 시작

sudo systemctl start coturn
sudo service coturn status

Coturn 설정하기

원본을 복제해두자

sudo mv /etc/turnserver.conf /etc/turnserver.conf.original

설정 추가하기

sudo vi /etc/turnserver.conf
realm=[realm]
server-name=[서버이름]

listening-ip=0.0.0.0
external-ip=[public-ip]


listening-port=3478
min-port=49152
max-port=65535

# Use fingerprint in TURN messages
fingerprint

# log the file
log-file=/var/log/turnserver.log

#enable verbose logging
verbose

# Specify the user for the turn authentication
user=[이름]:[비번]

# Enable long term credential mechanism
It-cred-mech

사용되는 포트 수신 규칙을 잘 설정하자.(생략)


재시작

sudo service coturn restart

연결 테스트

여기서 테스트해보면 된다.

Done이 뜨면 relay가 되는 것이다

TLS 적용하기

Let's Encrypt 서비스를 통해 매우 쉽게 도메인과 함께 보안 인증서를 적용할 수 있다.

Let's Encrypt 설치

 sudo apt-get install certbot

등록된 도메인(A 레코드로 public ip가 매핑되어있는)을 등록해 인증서를 발급한다.

sudo certbot certonly --standalone --preferred-challenges http -d [your-domain]

인증서를 적용해준다.

turn server를 실행하는 coturn이 인증서를 읽을 수 있게 하기 위함이다.

sudo mkdir /etc/letsencrypt/renewal-hooks/deploy
sudo vi /etc/letsencrypt/renewal-hooks/deploy/coturn
#!/bin/bash -e
for certfile in fullchain.pem privkey.pem ; do
        cp -L /etc/letsencrypt/live/<turn.example.com>/"${certfile}" /etc/turnserver/"${certfile}".new
        chown turnserver:turnserver /etc/turnserver/"${certfile}".new
        mv /etc/turnserver/"${certfile}".new /etc/turnserver/"${certfile}"
done
systemctl kill -sUSR2 coturn.service

turnsever.conf에 다음 설정을 추가해준다.

# for TLS (secure)
tls-listening-port=5349

재시작!

sudo service coturn restart

마찬가지로 테스트 해보고 연결이 되면 성공이다!

log 설정하기

위의 turnserver.conf 파일에 로깅 설정을 했지만 사실 해당 로그파일은 없다.

로그파일을 만들어주고 coturn이 이를 로깅할 수 있게 해주고 로그가 많이 찍히기 때문에 logrotate를 설정해 관리하도록 해주었다.

sudo vi /etc/logrotate.d/coturn # logrotate
/var/log/turnserver/*.log
{
	rotate 7
	daily
	missingok
	notifempty
	compress
	postrotate
		/bin/systemctl kill -s HUP coturn.service
	endscript
}

turnservervar/log/turnserver에 로그를 찍을 수 있도록 해준다.

sudo mkdir -p /var/log/turnserver
sudo chown turnserver:turnserver /var/log/turnserver

coturn을 재시작해준다.

sudo systemctl daemon-reload
sudo systemctl restart coturn

서비스하면서 발생한 로그들이다. 열어보면 어마어마하게 많이 찍힌다.

근데 솔직히 의미를 알기 쉽지는 않았다..


TURN 서버 적용하기

const peerConnection = new RTCPeerConnection({
          iceServers: [
            {
              urls: 'stun:stun.l.google.com:19302',
            },
            {
              urls: "turn:yourdomain",
              username: "username",
              credential: "password",
            },
          ],
        });

적용하는 것은 매우 간단하다.

peer connectionTURN서버도 지정해주기만 하면 된다.

RTCPeerConnection 객체가 iceServers 배열에 등록된 서버들 중 STUN서버를 통해 먼저 연결을 시도하고 P2P연결이 성공할 수 없는 경우에 TURN서버를 사용한다.


References

profile
강해지고 싶은 주니어 프론트엔드 개발자

0개의 댓글