포스트

서버 포트에 cloudflare 터널로 간편하게 서브도메인을 연결하기

보통 웹서비스를 개발하다 보면, localhost가 아닌 실제 https로 인증된 도메인을 가지고 테스트를 해야 할 필요가 있습니다. https에서만 작동하는 기능이라던지, 웹훅이 그렇습니다.

실제 구매한 도메인을 인증까지 받고 연결해서 사용해도 좋지만, 테스트용으로는 매번 다른 컴퓨터마다 새로 도메인을 연결하기는 번거롭습니다.

그래서 ngrok 이라는 서비스가 있습니다.

무료 플랜은 아무 랜덤 https 주소를 줘서 그걸로 내 웹사이트에 연결해서 사용할 수 있습니다. 하지만 이 주소는 매번 바뀌기 때문에 유료 플랜을 결재해서 고정 https 주소를 받아서 쓰게 됩니다.

한편, 그 대안으로 cloudflare의 tunnel을 이용하는 방법이 있습니다.

웹훅 요청을 보내는 내 서비스의 백엔드가 있는 port를 tunnel에 연결해주면, 해당 port에서 보내는 요청은 localhost가 아닌 cloudflare domain으로 바뀌어서 나가게 됩니다.

이를 위해 먼저 cloudflared를 컴퓨터에 설치합니다.

윈도우라면 관리자 권한으로 powershell에서,

1
2
choco install cloudflared

단, Chololatey가 설치되어 있어야 합니다. 안되어있다면 https://chocolatey.org/install 참고.

잘 설치됬는지 확인합니다.

1
cloudflared --version

이제 두 가지를 할 수 있습니다.

  1. 도메인 없이 https 주소로 서버 노출
1
cloudflared tunnel --url http://localhost:8003

8003번 로컬호스트 포트가 https://supported-montgomery-yeah-ide.trycloudflare.com/로 연결되었습니다.

다른 기기에서 LTE연결로 접속해도 잘 됩니다. 클라우드 플레어 터널이 잘 작동합니다.

이제 작동중이던 cli를 닫아주면 발급된 https 주소도 연결이 끊깁니다. 매번 새 주소가 발급됩니다.

  1. cloudflare에 연결해둔 도메인이 있을 시, 고정 터널을 생성해서 항상 같은 https 주소를 갖도록 할 수 있습니다.
1
2
cloudflared login         # 새 cert.pem 발급
cloudflared tunnel list   # 기존 터널 목록 확인 가능

웹사이트가 열리고, 로그인한 후 연결하려는 도메인을 선택하면

C:\Users\유저이름.cloudflared\cert.pem

이 생성됩니다.

주의할 점은, 해당 인증서는 절대 사라지지 않고 만료기간도 없으니, 잃어버리면 안됩니다.

잃버리고 유출될 경우, 도메인 탈취 수준의 피해가 발생합니다.

다른 사람이 악성 사이트를 도메인에 연결할 수 있고, 기존 도메인의 api를 해커의 서버에 연결할 수 있고, 유료 서비스 연계를 할 시 요금도 발생합니다.

따라서 Github에 노출하면 안되고, 만약 노출된 경우 해당 깃을 비공개하거나 삭제하고, exposed secrets검색, 그리고 cloudflare 지원팀에 문의해야 합니다.

다행인 점은 cert.pem은 터널을 만들고 dns를 연결할 때에만 필요하고, 일단 만들어진 터널을 사용할 때에는 필요 없습니다. 따라서 필요한 터널 생성 명령을 하고 난 후에는 cert.pem 을 깨끗이 지워줍니다.

1
2
3
4
5
# 터널 신규 생성 시
cloudflared login                 # 인증
cloudflared tunnel create my-api-tunnel
cloudflared tunnel route dns my-api-tunnel api.내도메인.com

-> 작업 끝났으면 cert.pem은 안전하게 잘 지웁니다.

1
2
3
del C:\Users\USERNAME\.cloudflared\cert.pem  # Windows
rm ~/.cloudflared/cert.pem                  # Linux/Mac

(직접 경로 들어가서 지워졌는지 확인하기)

-> 이후 터널 실행만 할 때는

1
2
cloudflared tunnel run my-api-tunnel

-> 그리고 이후 터널 지울 때는 다음 두 가지 중 하나를 해주시면 됩니다.

  1. 로컬에 생성된 터널ID.json 파일 삭제(터널ID.json이 없이는 해당 터널에 접속 못하므로 사실상 터널 지운 셈)

  1. 확실하게 터널 삭제.(이 명령어 사용 시 터널ID.json도 자동 삭제됨)
1
2
cloudflared tunnel delete my-api-tunnel
cloudflared tunnel list

터널을 좀 더 세련되게 만들고 관리하고 싶다면 직접 config.yml 파일을 작성해서 터널을 생성할 수 있습니다. 프로젝트에 다음과 같은 cloudflared_config.yml 을 만듭니다.

credentials-file 경로는 프로젝트 밖의 안전한 장소로 지정해두어야 합니다. 절대경로로 연결해 두겠습니다.

도메인과 포트 정보를 적어놓을 텐데, 유출되고 크게 위협은 아니므로 그냥 프로젝트에 만들어놓겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
tunnel: my-api-tunnel
credentials-file: C:\Users\유저이름\.cloudflared\my-api-tunnel.json


ingress:
  # API 서버
  - hostname: api.내도메인.com
    service: http://localhost:내서비스서버포트

  # 관리자 페이지 (아직 미배포)
  - hostname: admin.내도메인.com
    service: http_status:503

  # 미리보기 서버
  - hostname: preview.내도메인.com
    service: http://localhost:내미리보기서버포트

  # 모든 나머지 요청에 대해 404
  - service: http_status:404

좀 더 안전하게 하려면 이를 동적으로 생성하면 됩니다.


.env 파일

1
2
3
4
5
6
7
# .env
TUNNEL_NAME=my-api-tunnel
CREDENTIALS_PATH=/home/youruser/.cloudflared/my-api-tunnel.json
DOMAIN=yourdomain.com
API_PORT=8000
PREVIEW_PORT=3000

start_cloudflare_tunnel.sh 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# start_cloudflare_tunnel.sh
#!/bin/bash

# .env 파일에서 환경변수 불러오기
set -a
source .env
set +a

# config.yml 생성
cat  cloudflare_config.yml
tunnel: $TUNNEL_NAME
credentials-file: $CREDENTIALS_PATH

ingress:
  - hostname: api.$DOMAIN
    service: http://localhost:$API_PORT

  - hostname: admin.$DOMAIN
    service: http_status:503

  - hostname: preview.$DOMAIN
    service: http://localhost:$PREVIEW_PORT

  - service: http_status:404
EOF

# 필요한 도메인에 tunnel 연결 (존재하지 않으면 자동 생성됨)
cloudflared tunnel route dns "$TUNNEL_NAME" "api.$DOMAIN"
cloudflared tunnel route dns "$TUNNEL_NAME" "preview.$DOMAIN"
cloudflared tunnel route dns "$TUNNEL_NAME" "admin.$DOMAIN"

# cloudflared 실행
cloudflared tunnel --config cloudflare_config.yml run

.gitignore파일

1
2
3
4
5
#.gitignore

!start_cloudflare_tunnel.sh
cloudflare_config.yml
.env

.env 파일

1
2
3
4
5
6
7
# .env
TUNNEL_NAME=my-api-tunnel
CREDENTIALS_PATH=C:\Users\devra\.cloudflared\my-api-tunnel.json
DOMAIN=blessflow.com
API_PORT=8000
PREVIEW_PORT=3000

start_coudflare_tunnel.ps1 파일

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# start_cloudflare_tunnel.ps1

# .env 파일에서 환경변수 불러오기
$envContent = Get-Content -Path .\.env
foreach ($line in $envContent) {
    if ($line -match '^\s*([^#][^=]+)=(.*)$') {
        $key = $matches[1].Trim()
        $value = $matches[2].Trim()
        Set-Variable -Name $key -Value $value
    }
}

# config.yml 생성
$configContent = @"
tunnel: $TUNNEL_NAME
credentials-file: $CREDENTIALS_PATH

ingress:
  - hostname: api.$DOMAIN
    service: http://localhost:$API_PORT

  - hostname: admin.$DOMAIN
    service: http_status:503

  - hostname: preview.$DOMAIN
    service: http://localhost:$PREVIEW_PORT

  - service: http_status:404
"@

$configContent | Out-File -FilePath .\cloudflare_config.yml -Encoding utf8

# 필요한 도메인에 tunnel 연결 (존재하지 않으면 자동 생성됨)
cloudflared tunnel route dns "$TUNNEL_NAME" "api.$DOMAIN"
cloudflared tunnel route dns "$TUNNEL_NAME" "preview.$DOMAIN"
cloudflared tunnel route dns "$TUNNEL_NAME" "admin.$DOMAIN"

# cloudflared 실행
cloudflared tunnel --config .\cloudflare_config.yml run

.gitignore파일

1
2
3
4
5
#.gitignore

!start_cloudflare_tunnel.ps1
cloudflare_config.yml
.env

이제 cloudflare_config.yml 로 터널을 실행하는 방법

1
cloudflared tunnel --config cloudflare_config.yml run

(주의할 점은, ingress의 서브도메인을 넣었다가 뺄 경우 이미 넣었던 것이 자동으로 지워지지는 않으니, cloudflare dns에 가서 직접 레코드를 지워줘야 함.)

결과!!

마지막으로 주의할 점!

  1. cert.pem 지웠는지 확인하기
  2. 터널 json 파일이 git에 노출되지 않았는지 잘 확인하기
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.