Tailscale과 Google Cloud로 공인 IP 받기

2026년 6월 26일

새로 이사한 곳에 인터넷이 별로 품질도 좋지 않고, 포트 포워딩은 당연히 허용하지 않는 곳에 와버렸네요. 사전 조사를 제대로 하지 않은 탓이죠. 어쩔 수 없으니 예전에 이 문제를 어떻게 해결했는지 기억해봅시다!

예전에는 리버스 SSH 터널링을 사용하여 공인 IP를 받는 방법을 블로그에서 소개했었는데, 문제는 모든 트래픽이 SSH 프로토콜에 담겨 보내지게 된다는 점입니다. 공인 IP가 필요한 이유가 웹사이트와 서비스들을 호스팅하기 위해서인데, TCP-속-TCP로는 성능이 그렇게 좋을 리가 없거든요.

다른 한편으론 Ersei가 작성한, Wireguard를 활용한 좋은 블로그 글이 있는데, 이미 Tailscale을 쓰면서 또 Wireguard를 추가로 설치하긴 싫었습니다. 어쩔 수 없지만 이 친구가 쓴 글을 훔쳐서 Tailscale과 호환되는 글을 적어야겠군요. (Ersei 미안!)

1번: VPS 구입

근데 알아보니까 Google Cloud가 무료 티어에서 그냥 무료로 e2-micro 인스턴스를 하나 준다고요? 그럼 당연히 받아야죠!

한 가지 조심할 부분은 무료 티어에서 사용할 수 있는 지역이 제한되어 있는데, 이 글을 작성하는 시점에는 오리건주 (Oregon), 아이오와주 (Iowa), 그리고 사우스캐롤라이나주 (South Carolina) 지역에서만 혜택이 적용된다고 합니다. 자세한 정보는 여기에서 확인하세요.

콘솔에서 다시는 수동으로 VM 생성을 하기 싫기 때문에, gcloud CLI 도구를 사용하여 똑같이 만드는 명령문을 복사해왔습니다:

gcloud compute instances create public-ip-vm \
    --project=public-ip-vm \
    --zone=us-east1-c \
    --machine-type=e2-micro \
    --network-interface=network-tier=PREMIUM,stack-type=IPV4_ONLY,subnet=default \
    --metadata=enable-osconfig=TRUE \
    --can-ip-forward \
    --maintenance-policy=MIGRATE \
    --provisioning-model=STANDARD \
    --host-error-timeout-seconds=330 \
    --service-account=redacted-compute@developer.gserviceaccount.com \
    --scopes=https://www.googleapis.com/auth/devstorage.read_only,https://www.googleapis.com/auth/logging.write,https://www.googleapis.com/auth/monitoring.write,https://www.googleapis.com/auth/service.management.readonly,https://www.googleapis.com/auth/servicecontrol,https://www.googleapis.com/auth/trace.append \
    --enable-display-device \
    --tags=http-server,https-server \
    --create-disk=auto-delete=yes,boot=yes,device-name=public-ip-vm,image=projects/debian-cloud/global/images/debian-13-trixie-v20260609,mode=rw,size=30,type=pd-standard \
    --shielded-secure-boot \
    --shielded-vtpm \
    --shielded-integrity-monitoring \
    --labels=goog-ops-agent-policy=v2-template-1-7-0,goog-ec-src=vm_add-gcloud \
    --reservation-affinity=any \
&& \
printf 'agentsRule:\n  packageState: installed\n  version: latest\ninstanceFilter:\n  inclusionLabels:\n  - labels:\n      goog-ops-agent-policy: v2-template-1-7-0\n' > config.yaml \
&& \
gcloud compute instances ops-agents policies create goog-ops-agent-v2-template-1-7-0-us-east1-c \
    --project=public-ip-vm \
    --zone=us-east1-c \
    --file=config.yaml

…다시 생각해보니 그냥 웹콘솔로 만드는 것이 더 쉽겠네요.

2번: DNS 설정하기

원하시면 새 VPS의 공인 IP 주소에 대한 DNS 레코드를 생성하시는 것을 추천드립니다.

3번: nftables와 Tailscale 설치

위에 적혀있듯이, 이번 글에서는 Debian 13을 기준으로 작성합니다:

sudo apt update
sudo apt upgrade -y
sudo apt install -y nftables
curl -fsSL https://tailscale.com/install.sh | sh

4번: Tailscale 설정하기

sudo tailscale up을 실행한 후 나타나는 절차를 따르면 됩니다.

네트워크에 노드 연결을 허가해주기 전에, 태그를 설정하고 ACL 규칙들을 추가하여 원하는 로컬 머신만 해당 VPS과 통신할 수 있도록 설정해주는 것이 좋습니다. 그래야 Tailnet에 있는 다른 머신들이 해당 VPS에 접근하거나 반대로도 접근하는 것을 막아주죠.

5번: nftables 설정

이제 여기서 Ersei의 설정 대부분을 훔칠 단계입니다.

/etc/nftables/proxy.nft를 만들어줍니다:

table ip nat {
    chain PREROUTING {
        type nat hook prerouting priority dstnat; policy accept;
        meta l4proto tcp tcp dport 80   dnat to 100.67.67.67:80;
        meta l4proto tcp tcp dport 443  dnat to 100.67.67.67:443;
    }

    chain INPUT {
        type nat hook input priority 100; policy accept;
    }

    chain POSTROUTING {
        type nat hook postrouting priority srcnat; policy accept;

        # Tailscale 트래픽 masquerade
        oifname "tailscale0" masquerade;
    }

    chain OUTPUT {
        type nat hook output priority -100; policy accept;
    }
}

상단의 100.67.67.67 주소를 인터넷에 공유하기 원하는 로컬 머신의 Tailscale IP 주소로 교체해줍니다.

한 가지 다른 점은 여기에서 Tailscale 트래픽을 masquerade하는 부분입니다. 이렇게 하지 않으면 돌아오는 경로가 제대로 형성되지 않아 연결이 항상 시간 초과되어 버리거든요!

또 주의할 점은 원래 설정값에선 모든 ICMP 트래픽이 무시되는데, 저는 ping을 가끔씩 사용하여 호스트가 괜찮은지 확인하기 때문에 설정에서 제외했습니다. 만약 Ersei처럼 ICMP 트래픽이 필요없다면 다시 넣으셔도 괜찮습니다.

다음은 /etc/nftables/main.nft입니다:

# 기존 nftables 룰셋들을 무시해버립니다
flush ruleset

# IPv4과 IPv6를 위한 공통 테이블
table inet nftables_svc {

    # 허가할 프로토콜 목록
    set allowed_protocols {
        type inet_proto
        elements = { icmp, icmpv6 }
    }

    # 트래픽을 허가할 인터페이스 목록
    set allowed_interfaces {
        type ifname
        elements = { "lo", "tailscale0" }
    }

    # 허용할 TCP 서비스들
    set allowed_tcp_dports {
        type inet_service
        elements = { ssh, http, https }
    }

    # 허용할 UDP 서비스들
    # 전 없지만 있으시면 코멘트 해제하시면 됩니다
    #set allowed_udp_dports {
    #    type inet_service
    #    elements = { 25565 }
    #}

    # 이 체인은 모든 허가 조건을 모아줍니다
    chain allow {
        ct state established,related accept

        meta l4proto @allowed_protocols accept
        iifname @allowed_interfaces accept
        tcp dport @allowed_tcp_dports accept
        #udp dport @allowed_udp_dports accept
    }

    # 이 호스트로 가는 트래픽을 위한 베이스-체인
    chain INPUT {
        type filter hook input priority filter + 20
        policy accept

        jump allow
    }
}

include "/etc/nftables/proxy.nft"

이제 이 체인을 /etc/nftables.conf에서 포함시켜줘야 합니다:

# ... /etc/nftables.conf의 다른 내용

include "/etc/nftables/main.nft";

이제 nftables를 활성화해줍니다:

sudo systemctl enable --now nftables
sudo systemctl restart nftables

6번: IP 포워딩 활성화

sudo sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -p

그렇게 하면 완성입니다! 이제 새 VPS IP나 방금 설정했던 DNS 설정을 통해 로컬 서비스로 접속하실 수 있을 겁니다.