App Engine ์„œ๋น„์Šค์˜ ์•„์›ƒ๋ฐ”์šด๋“œ IP ์ฃผ์†Œ

URL ๊ฐ€์ ธ์˜ค๊ธฐ, Socket, Mail API์™€ ๊ฐ™์€ App Engine ํ‘œ์ค€ ํ™˜๊ฒฝ์˜ ์•„์›ƒ๋ฐ”์šด๋“œ ์„œ๋น„์Šค๋Š” ๋Œ€๊ทœ๋ชจ IP ์ฃผ์†Œ ํ’€์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ํ’€์˜ IP ์ฃผ์†Œ ๋ฒ”์œ„๋Š” ๊ณ„์† ๋ณ€ํ•ฉ๋‹ˆ๋‹ค. ์‹ค์ œ๋กœ, ๋™์ผํ•œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์—์„œ ์—ฐ๋‹ฌ์•„ ์‹คํ–‰๋œ 2๊ฐœ์˜ API ํ˜ธ์ถœ์€ ์„œ๋กœ ๋‹ค๋ฅธ 2๊ฐœ์˜ IP ์ฃผ์†Œ์—์„œ ์‹œ์ž‘๋œ ๊ฒƒ์ฒ˜๋Ÿผ ๋ณด์ผ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์„œ๋น„์Šค์˜ ์•„์›ƒ๋ฐ”์šด๋“œ ํŠธ๋ž˜ํ”ฝ๊ณผ ์—ฐ๊ฒฐ๋œ IP ์ฃผ์†Œ๋ฅผ ์•Œ์•„์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์„œ๋น„์Šค์˜ ํ˜„์žฌ IP ์ฃผ์†Œ ๋ฒ”์œ„๋ฅผ ์ฐพ๊ฑฐ๋‚˜ ์„œ๋น„์Šค์˜ ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์„ค์ •ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

App Engine ์„œ๋น„์Šค์˜ IP ์ฃผ์†Œ

Google์ด ๊ฒŒ์‹œํ•˜๋Š” IP ๋ฒ”์œ„ ์ •๋ณด๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ App Engine ์„œ๋น„์Šค์˜ ํ˜„์žฌ IP ์ฃผ์†Œ ๋ฒ”์œ„๋ฅผ ์ฐพ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • Google์€ ์ธํ„ฐ๋„ท ์‚ฌ์šฉ์ž์—๊ฒŒ ์ œ๊ณต๋˜๋Š” IP ๋ฒ”์œ„์˜ ์ „์ฒด ๋ชฉ๋ก์„ goog.json์— ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค.

  • ๋˜ํ•œ Google์€ ๊ณ ๊ฐ Google Cloud ๋ฆฌ์†Œ์Šค์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์ „์—ญ ๋ฐ ๋ฆฌ์ „๋ณ„ ์™ธ๋ถ€ IP ์ฃผ์†Œ ๋ฒ”์œ„ ๋ชฉ๋ก์„ cloud.json์— ๊ฒŒ์‹œํ•ฉ๋‹ˆ๋‹ค.

Google API ๋ฐ ์„œ๋น„์Šค์—์„œ ์‚ฌ์šฉ๋˜๋Š” IP ์ฃผ์†Œ๋Š” goog.json์˜ ๋ฒ”์œ„์—์„œ cloud.json์˜ ๋ชจ๋“  ๋ฒ”์œ„๋ฅผ ์‚ญ์ œํ•˜์—ฌ ๊ณ„์‚ฐํ•œ ๋ฒ”์œ„ ๋ชฉ๋ก์— ํฌํ•จ๋ฉ๋‹ˆ๋‹ค. ์ด ๋ชฉ๋ก์€ ์ž์ฃผ ์—…๋ฐ์ดํŠธ๋ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ Python ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Google API ๋ฐ ์„œ๋น„์Šค์—์„œ ์‚ฌ์šฉ๋˜๋Š” IP ์ฃผ์†Œ ๋ฒ”์œ„ ๋ชฉ๋ก์„ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด ์Šคํฌ๋ฆฝํŠธ ์‹คํ–‰์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์‹คํ–‰ ๋ฐฉ๋ฒ•์„ ์ฐธ์กฐํ•˜์„ธ์š”.

from __future__ import print_function

import json

try:
    from urllib import urlopen
except ImportError:
    from urllib.request import urlopen
    from urllib.error import HTTPError

import netaddr

IPRANGE_URLS = {
    "goog": "https://www.gstatic.com/ipranges/goog.json",
    "cloud": "https://www.gstatic.com/ipranges/cloud.json",
}


def read_url(url):
    try:
        return json.loads(urlopen(url).read())
    except (IOError, HTTPError):
        print("ERROR: Invalid HTTP response from %s" % url)
    except json.decoder.JSONDecodeError:
        print("ERROR: Could not parse HTTP response from %s" % url)


def get_data(link):
    data = read_url(link)
    if data:
        print("{} published: {}".format(link, data.get("creationTime")))
        cidrs = netaddr.IPSet()
        for e in data["prefixes"]:
            if "ipv4Prefix" in e:
                cidrs.add(e.get("ipv4Prefix"))
            if "ipv6Prefix" in e:
                cidrs.add(e.get("ipv6Prefix"))
        return cidrs


def main():
    cidrs = {group: get_data(link) for group, link in IPRANGE_URLS.items()}
    if len(cidrs) != 2:
        raise ValueError("ERROR: Could process data from Google")
    print("IP ranges for Google APIs and services default domains:")
    for ip in (cidrs["goog"] - cidrs["cloud"]).iter_cidrs():
        print(ip)


if __name__ == "__main__":
    main()

๊ณ ์ • ์•„์›ƒ๋ฐ”์šด๋“œ IP ์ฃผ์†Œ ์„ค์ •

App Engine ํ‘œ์ค€ ํ™˜๊ฒฝ ์„œ๋น„์Šค์˜ ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์„ค์ •ํ•˜๋ ค๋ฉด Cloud Router ๋ฐ Cloud NAT์—์„œ ์„œ๋ฒ„๋ฆฌ์Šค VPC ์•ก์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์„œ๋ฒ„๋ฆฌ์Šค VPC ์•ก์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ด๊ทธ๋ ˆ์Šค ํŠธ๋ž˜ํ”ฝ์„ Virtual Private Cloud(VPC) ๋„คํŠธ์›Œํฌ๋กœ ์ „์†กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. VPC์—์„œ ๋„คํŠธ์›Œํฌ ์ฃผ์†Œ ๋ณ€ํ™˜(NAT) ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ „์šฉ IP ์ฃผ์†Œ๋ฅผ ํ†ตํ•ด App Engine ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Cloud NAT ๊ฒŒ์ดํŠธ์›จ์ด ๋ฐ Cloud Router๊ฐ€ ์ œ์–ด ์˜์—ญ๋งŒ ์ œ๊ณตํ•˜๊ณ , ํŒจํ‚ท์ด Cloud NAT ๊ฒŒ์ดํŠธ์›จ์ด ๋˜๋Š” Cloud Router๋ฅผ ํ†ต๊ณผํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— Cloud NAT๋ฅผ ํ†ตํ•ด ํŠธ๋ž˜ํ”ฝ์„ ๋ผ์šฐํŒ…ํ•ด๋„ ๋„คํŠธ์›Œํ‚น ์Šคํƒ์— ์ถ”๊ฐ€ ํ™‰์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋‹ค์Œ ๋‹จ๊ณ„๋Š” App Engine ํ‘œ์ค€ ํ™˜๊ฒฝ ์„œ๋น„์Šค์˜ ๊ณ ์ • ์•„์›ƒ๋ฐ”์šด๋“œ IP ์ฃผ์†Œ๋ฅผ ์„ค์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ์ค๋‹ˆ๋‹ค.

  1. roles/compute.networkAdmin ์—ญํ•  ๋˜๋Š” ๋™์ผํ•œ ๊ถŒํ•œ์„ ๊ฐ€์ง„ ์ปค์Šคํ…€ ์—ญํ• ์ด ์žˆ์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

  2. App Engine ํŠธ๋ž˜ํ”ฝ์˜ VPC ๋„คํŠธ์›Œํฌ ๋‚ด์— ์„œ๋ธŒ๋„คํŠธ์›Œํฌ(์„œ๋ธŒ๋„ท)๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ฉด VPC ๋„คํŠธ์›Œํฌ์˜ ๋‹ค๋ฅธ ๋ฆฌ์†Œ์Šค๊ฐ€ ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

    gcloud compute networks subnets create SUBNET_NAME \
        --range=RANGE \
        --network=NETWORK_NAME \
        --region=REGION

    ์œ„์˜ ๋ช…๋ น์–ด์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    • SUBNET_NAME: ์„œ๋ธŒ๋„ท์— ์ง€์ •ํ•  ์ด๋ฆ„
    • RANGE: ์ด ์„œ๋ธŒ๋„ท์— ํ• ๋‹นํ•˜๋ ค๋Š” CIDR ํ˜•์‹์˜ IP ๋ฒ”์œ„(์˜ˆ๋ฅผ ๋“ค์–ด 10.124.0.0/28)
    • NETWORK_NAME: VPC ๋„คํŠธ์›Œํฌ์˜ ์ด๋ฆ„
    • REGION: App Engine ์„œ๋น„์Šค์˜ ๋ฆฌ์ „
  3. App Engine ์„œ๋น„์Šค๋ฅผ ์„œ๋ธŒ๋„ท์— ์—ฐ๊ฒฐํ•ฉ๋‹ˆ๋‹ค.

    VPC ๋„คํŠธ์›Œํฌ์— ์—ฐ๊ฒฐ ๊ฐ€์ด๋“œ๋ฅผ ๋”ฐ๋ฅด๊ณ  ์ด์ „ ๋‹จ๊ณ„์—์„œ ๋งŒ๋“  ์„œ๋ธŒ๋„ท์˜ ์ด๋ฆ„์„ ์ปค๋„ฅํ„ฐ ์„œ๋ธŒ๋„ท์— ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

  4. ์ƒˆ Cloud Router๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. Cloud Router๋Š” Cloud NAT์— ํ•„์š”ํ•œ ์ œ์–ด ์˜์—ญ ๊ตฌ์„ฑ์š”์†Œ์ž…๋‹ˆ๋‹ค.

    gcloud compute routers create ROUTER_NAME \
        --network=NETWORK_NAME \
        --region=REGION

    ์œ„์˜ ๋ช…๋ น์–ด์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    • ROUTER_NAME: ๋งŒ๋“ค๋ ค๋Š” Cloud Router ๋ฆฌ์†Œ์Šค ์ด๋ฆ„
    • NETWORK_NAME: VPC ๋„คํŠธ์›Œํฌ์˜ ์ด๋ฆ„
    • REGION: NAT ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ๋งŒ๋“ค๋ ค๋Š” ๋ฆฌ์ „
  5. ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์˜ˆ์•ฝํ•ฉ๋‹ˆ๋‹ค.

    ์ด๋Š” ์„œ๋น„์Šค๊ฐ€ ๋ฐœ์‹  ํŠธ๋ž˜ํ”ฝ์„ ์ „์†กํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ฃผ์†Œ์ž…๋‹ˆ๋‹ค. ์˜ˆ์•ฝ๋œ IP ์ฃผ์†Œ ๋ฆฌ์†Œ์Šค์—๋Š” ์—ฐ๊ด€๋œ ๋ฆฌ์†Œ์Šค๊ฐ€ ์‚ญ์ œ๋˜๊ณ  ๋‹ค์‹œ ์ƒ์„ฑ๋  ๋•Œ ๊ธฐ๋ณธ IP ์ฃผ์†Œ๊ฐ€ ์œ ์ง€๋ฉ๋‹ˆ๋‹ค. ์ด IP ์ฃผ์†Œ๋Š” Google Cloud ํ”„๋กœ์ ํŠธ์˜ ๊ณ ์ • IP ์ฃผ์†Œ ํ• ๋‹น๋Ÿ‰์— ๋ฐ˜์˜๋ฉ๋‹ˆ๋‹ค.

    gcloud compute addresses create ORIGIN_IP_NAME \
        --region=REGION

    ์œ„์˜ ๋ช…๋ น์–ด์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    • ORIGIN_IP_NAME์„ IP ์ฃผ์†Œ ๋ฆฌ์†Œ์Šค์— ํ• ๋‹นํ•˜๋ ค๋Š” ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
    • REGION์„ Cloud NAT ๋ผ์šฐํ„ฐ๋ฅผ ์‹คํ–‰ํ•  ๋ฆฌ์ „์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค. ์ง€์—ฐ ์‹œ๊ฐ„ ๋ฐ ๋„คํŠธ์›Œํฌ ๋น„์šฉ์„ ์ตœ์†Œํ™”ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” App Engine ์„œ๋น„์Šค์™€ ๋™์ผํ•œ ๋ฆฌ์ „์ด ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค.

    ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด compute addresses describe ๋ช…๋ น์–ด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    gcloud compute addresses describe ORIGIN_IP_NAME
  6. Cloud NAT ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ๋งŒ๋“ค๊ณ  IP ์ฃผ์†Œ๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    ์„œ๋ธŒ๋„ท์—์„œ ์‹œ์ž‘๋˜๋Š” ํŠธ๋ž˜ํ”ฝ์€ ์ด ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ํ†ต๊ณผํ•˜๊ณ  ์ด์ „ ๋‹จ๊ณ„์—์„œ ์˜ˆ์•ฝํ•œ ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

    gcloud compute routers nats create NAT_NAME \
        --router=ROUTER_NAME \
        --region=REGION \
        --nat-custom-subnet-ip-ranges=SUBNET_NAME \
        --nat-external-ip-pool=ORIGIN_IP_NAME
      

    ์œ„์˜ ๋ช…๋ น์–ด์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    • NAT_NAME: ๋งŒ๋“ค๋ ค๋Š” Cloud NAT ๊ฒŒ์ดํŠธ์›จ์ด ๋ฆฌ์†Œ์Šค์˜ ์ด๋ฆ„
    • ROUTER_NAME: Cloud Router ์ด๋ฆ„
    • REGION: NAT ๊ฒŒ์ดํŠธ์›จ์ด๋ฅผ ๋งŒ๋“ค๋ ค๋Š” ๋ฆฌ์ „
    • ORIGIN_IP_NAME: ์ด์ „ ๋‹จ๊ณ„์—์„œ ๋งŒ๋“  ์˜ˆ์•ฝ๋œ IP ์ฃผ์†Œ ๋ฆฌ์†Œ์Šค์˜ ์ด๋ฆ„
  7. ์„œ๋ฒ„๋ฆฌ์Šค VPC ์•ก์„ธ์Šค ์ด๊ทธ๋ ˆ์Šค ์„ค์ •์„ all-traffic์œผ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

    ๊ธฐ๋ณธ์ ์œผ๋กœ ์„œ๋ฒ„๋ฆฌ์Šค VPC ์•ก์„ธ์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” App Engine ์„œ๋น„์Šค๋Š” ๋‚ด๋ถ€ ํŠธ๋ž˜ํ”ฝ์„ VPC ๋„คํŠธ์›Œํฌ๋กœ๋งŒ ์ „์†กํ•ฉ๋‹ˆ๋‹ค ์™ธ๋ถ€ ๋Œ€์ƒ์œผ๋กœ ๋ณด๋‚ผ ํŠธ๋ž˜ํ”ฝ์„ VPC ๋„คํŠธ์›Œํฌ๋กœ ์ „์†กํ•˜๋ ค๋ฉด VPC ๋„คํŠธ์›Œํฌ์—์„œ ์ง€์ •๋œ ๊ณ ์ • IP ์ฃผ์†Œ๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ์ด๊ทธ๋ ˆ์Šค ์„ค์ •์„ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

    ์„œ๋น„์Šค์˜ app.yaml ํŒŒ์ผ์—์„œ ์ด๊ทธ๋ ˆ์Šค ์„ค์ •์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.

    vpc_access_connector:
    name: projects/PROJECT_ID/locations/REGION/connectors/CONNECTOR_NAME
    egress_setting: all-traffic

    ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

    • PROJECT_ID๋ฅผ Google Cloud ํ”„๋กœ์ ํŠธ ID๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
    • REGION์„ ์ปค๋„ฅํ„ฐ๊ฐ€ ์žˆ๋Š” ๋ฆฌ์ „์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.
    • CONNECTOR_NAME์„ ์ปค๋„ฅํ„ฐ ์ด๋ฆ„์œผ๋กœ ๋ฐ”๊ฟ‰๋‹ˆ๋‹ค.

์„œ๋น„์Šค๋ฅผ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

 gcloud app deploy