구독해서 새 게시물에 대한 알림을 받으세요.

Cloudflare Zero Trust를 이용한 Kubectl

2022. 06. 24.

9분 읽기
Kubectl with Cloudflare Zero Trust

Cloudflare는 엔지니어링 워크로드에 쿠버네티스를 많이 사용합니다. 쿠버네티스는 API 백엔드를 구동하고 분석 집계 및 봇 감지와 같은 일괄 처리를 수행하며 CI/CD 파이프라인과 같은 엔지니어링 도구를 다루는 데 이용됩니다. 하지만 쿠버네티스는 로드 밸런서, API 서버 등, 수신, 포드 간 표면적을 다소 크게 노출시킬 수 있습니다.

이 게시물에서는 엔지니어링 팀이 Cloudflare Zero Trust를 시험적으로 사용해 쿠버네티스를 보호하고 프록시 없이 kubectl을 이용하는 방법을 조금 나누어보겠습니다.

쿠버네티스 보안에 대한 Cloudflare의 일반적인 접근 방식

보안 조치의 일환으로, Cloudflare는 네트워크를 통해 클러스터에 액세스할 수 있는 항목을 크게 제한합니다. 네트워크 서비스가 노출되는 곳에는 다른 보호 기능을 추가합니다. 수신 리소스에 액세스하기 위해 Cloudflare Access 인증 또는 상호 TLS(또는 둘 다)를 요구하는 것이 그 예입니다.

클러스터가 API 서버에 액세스 하려는 경우에도 이와 같이 네트워크 제한이 적용됩니다. Cloudflare의 엔지니어에게 이 액세스 권한이 없으면 kubectl 등의 도구를 사용해 팀 리소스를 직접 살펴볼 수가 없습니다. 지속적으로 배포하고 GitOps 방식을 적용하는 게 가장 좋지만, 개발자가 쿠버네티스 API를 사용할 수 있게 하면 문제를 해결하고 개발자의 속도를 높일 수 있습니다. 액세스할 수 없다면 일이 진행되지 않을 것입니다.

Cloudflare는 보안 요구 사항을 충족하기 위해 Cloudflare Zero Trust를 사용하고 있습니다. 당사에서 Zero Trust를 사용하는 방법과 여기까지 온 과정을 나누고 싶었습니다.

Zero Trust 이전

Zero Trust가 없었을 때 엔지니어는 VPN 장비에 연결하여 쿠버네티스 API에 액세스할 수 있었습니다. 전체 업계에서 일반적인 방식이고 API 액세스 권한을 부여할 수 있게 되지만, 엔지니어를 클라이언트로 내부 네트워크에 포함시키는 방식이라 필요한 것보다 훨씬 많은 네트워크 액세스 권한이 주어졌습니다.

만족스럽지는 않았지만 몇 년 동안 이 상황은 지속되었습니다. 2020년 초 Cloudflare는 VPN 사용을 중지했기 때문에 쿠버네티스 팀은 다른 솔루션을 찾아야 했습니다.

Cloudflare Tunnel을 사용한 쿠버네티스

당시, Cloudflare Tunnel 개발 팀과 긴밀히 협력하여 Cloudflare를 적용한 터널과 Access를 사용한 kubectl 연결을 처리하도록 지원을 추가했습니다.

엔지니어링 사용자에게는 괜찮았지만 신입 직원이 온보딩하는 데는 큰 장애물이었습니다. 엔지니어의 장치에서 쿠버네티스 클러스터 각각에 자체적으로 터널을 연결해야 했기 때문에 클러스터 간의 셔플링은 골칫거리가 되었습니다. kubectl에서 SOCKS 프록시를 통해 연결할 수 있긴 했지만 쿠버네티스 생태계 내 모든 도구에 보편적으로 지원되지는 않았습니다.

내부적으로 이 솔루션을 계속 이용하며 더 나은 솔루션을 찾아보았습니다.

Zero Trust를 갖춘 쿠버네티스

Cloudflare One 출시 이후, Cloudflare는 다양한 구성으로 Zero Trust 에이전트를 시험해보았습니다. 처음에는 1.1.1.1로 보안 DNS를 구현했습니다. 이후, Zero Trust 기능을 추가로 시험해보는 데 사용하기 시작했습니다.

이제 Cloudflare Zero Trust의 사설 네트워크 라우팅을 활용한다면 Cloudflare를 적용한 터널을 설정하거나, kubectl 및 기타 쿠버네티스 생태계 도구에서 터널을 사용하도록 구성하지 않고도 엔지니어가 쿠버네티스 API에 액세스할 수 있습니다. Cloudflare만 가능한 게 아닙니다. 여러분의 팀에서도 지금 이 기능을 이용할 수 있습니다!

Zero Trust 구성

Cloudflare에서는 Zero Trust 구성에 구성 관리 도구를 사용하여 코드형 인프라를 구현하며, 수정한 내용이 아래에 있습니다. 하지만 Cloudflare Zero Trust 대시보드로도 똑같이 구성할 수 있습니다.

가장 먼저 새 터널을 만들어야 합니다. 이 터널은 Cloudflare 에지 네트워크를 쿠버네티스 API에 연결하는 데 사용됩니다. 이 포스트 뒷부분에 나오는 구성을 사용하여 쿠버네티스 내에서 터널 엔드포인트를 실행합니다.

resource "cloudflare_argo_tunnel" "k8s_zero_trust_tunnel" {
  account_id = var.account_id
  name       = "k8s_zero_trust_tunnel"
  secret     = var.tunnel_secret
}

"tunnel_secret" 암호는 32바이트 난수여야 하며, 나중에 쿠버네티스 설정 시 다시 사용해야 하니 잠깐 저장해 두어야 합니다.

터널을 만든 후에는 Cloudflare 네트워크가 터널을 통해 어떤 트래픽을 보내는지 알 수 있도록 경로를 만들어야 합니다.

resource "cloudflare_tunnel_route" "k8s_zero_trust_tunnel_ipv4" {
  account_id = var.account_id
  tunnel_id  = cloudflare_argo_tunnel.k8s_zero_trust_tunnel.id
  network    = "198.51.100.101/32"
  comment    = "Kubernetes API Server (IPv4)"
}
 
resource "cloudflare_tunnel_route" "k8s_zero_trust_tunnel_ipv6" {
  account_id = var.account_id
  tunnel_id  = cloudflare_argo_tunnel.k8s_zero_trust_tunnel.id
  network    = "2001:DB8::101/128"
  comment    = "Kubernetes API Server (IPv6)"
}

Cloudflare는 IPv4와 IPv6 주소 모두를 통한 쿠버네티스 API 액세스를 지원하므로 두 경로를 모두 설정합니다. 호스트 이름을 통해 API 서버에 연결하는 경우 이러한 IP 주소는 DNS 조회로 반환된 주소와 일치해야 합니다.

다음에는 API 서버 및 클라이언트와 호환되도록 Cloudflare Gateway 설정을 구성합니다.

resource "cloudflare_teams_list" "k8s_apiserver_ips" {
  account_id = var.account_id
  name       = "Kubernetes API IPs"
  type       = "IP"
  items      = ["198.51.100.101/32", "2001:DB8::101/128"]
}
 
resource "cloudflare_teams_rule" "k8s_apiserver_zero_trust_http" {
  account_id  = var.account_id
  name        = "Don't inspect Kubernetes API"
  description = "Allow connections from kubectl to API"
  precedence  = 10000
  action      = "off"
  enabled     = true
  filters     = ["http"]
  traffic     = format("any(http.conn.dst_ip[*] in $%s)", replace(cloudflare_teams_list.k8s_apiserver_ips.id, "-", ""))
}

클라이언트와 API 서버 간에 상호 TLS를 사용하며 kubectl과 API 서버 간의 트래픽이 모두 HTTP인 것은 아니므로 이러한 연결에 대한 HTTP 검사를 비활성화했습니다.

이러한 규칙을 장치 증명, 세션 수명, 사용자 및 그룹 액세스 정책과 같은 추가 Zero Trust 규칙과 연결하여 보안을 더욱 사용자에게 맞출 수 있습니다.

터널 배포

터널을 만들고 구성했으면 해당 엔드포인트를 네트워크에 배포할 수 있습니다. Cloudflare는 터널을 포드로 배포하기로 선택했는데, 업그레이드를 롤아웃하고 노드 오류를 처리하는 데 쿠버네티스의 배포 전략을 사용할 수 있기 때문입니다.

apiVersion: v1
kind: ConfigMap
metadata:
  name: tunnel-zt
  namespace: example
  labels:
    tunnel: api-zt
data:
  config.yaml: |
    tunnel: 8e343b13-a087-48ea-825f-9783931ff2a5
    credentials-file: /opt/zt/creds/creds.json
    metrics: 0.0.0.0:8081
    warp-routing:
        enabled: true

이렇게 하면 지정된 터널 ID에 WARP 라우팅을 활성화하는 기본 구성으로 쿠버네티스 ConfigMap이 생성됩니다. 지정된 터널 ID는 구성 관리 시스템, Cloudflare Zero Trust 대시보드, 또는 같은 계정에 로그인한 다른 장치에서 다음 명령을 실행하여 확인할 수 있습니다.

cloudflared tunnel list

다음에는 터널 자격 증명의 암호를 만들어야 합니다. 암호 관리 시스템을 사용해야 하지만 여기서는 간단하게 직접 만들겠습니다.

jq -cn --arg accountTag $CF_ACCOUNT_TAG \
       --arg tunnelID $CF_TUNNEL_ID \
       --arg tunnelName $CF_TUNNEL_NAME \
       --arg tunnelSecret $CF_TUNNEL_SECRET \
   '{AccountTag: $accountTag, TunnelID: $tunnelID, TunnelName: $tunnelName, TunnelSecret: $tunnelSecret}' | \
kubectl create secret generic -n example tunnel-creds --from-file=creds.json=/dev/stdin

이렇게 하면 "example" 네임스페이스에 새 암호 "tunnel-creds"가 터널이 예상하는 자격 증명 파일과 함께 생성됩니다.

이제 터널 자체를 배포할 수 있습니다. 노드를 비우는 동안에도 항상 일부를 사용할 수 있도록 복제본을 여러 개 배포합니다.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    tunnel: api-zt
  name: tunnel-api-zt
  namespace: example
spec:
  replicas: 3
  selector:
    matchLabels:
      tunnel: api-zt
  strategy:
    rollingUpdate:
      maxSurge: 0
      maxUnavailable: 1
  template:
    metadata:
      labels:
        tunnel: api-zt
    spec:
      containers:
        - args:
            - tunnel
            - --config
            - /opt/zt/config/config.yaml
            - run
          env:
            - name: GOMAXPROCS
              value: "2"
            - name: TZ
              value: UTC
          image: cloudflare/cloudflared:2022.5.3
          livenessProbe:
            failureThreshold: 1
            httpGet:
              path: /ready
              port: 8081
            initialDelaySeconds: 10
            periodSeconds: 10
          name: tunnel
          ports:
            - containerPort: 8081
              name: http-metrics
          resources:
            limits:
              cpu: "1"
              memory: 100Mi
          volumeMounts:
            - mountPath: /opt/zt/config
              name: config
              readOnly: true
            - mountPath: /opt/zt/creds
              name: creds
              readOnly: true
      volumes:
        - secret:
            name: tunnel-creds
          name: creds
        - configMap:
            name: tunnel-api-zt
          name: config

Cloudflare Zero Trust로 Kubectl 사용

Screenshot of the Cloudflare WARP app showing it connected to Teams

Cloudflare Zero Trust 에이전트를 배포했다면, 이제 여러분의 팀 구성원은 SOCKS 터널을 특별히 설정하지 않고도 쿠버네티스 API에 액세스할 수 있습니다!

kubectl version --short
Client Version: v1.24.1
Server Version: v1.24.1

다음은 무엇일까요?

시도해 보셨다면 피드백을 보내주세요! HTTP 이외의 워크플로에 대한 Zero Trust를 계속 개선하고 있습니다.

Cloudflare에서는 전체 기업 네트워크를 보호하고, 고객이 인터넷 규모의 애플리케이션을 효과적으로 구축하도록 지원하며, 웹 사이트와 인터넷 애플리케이션을 가속화하고, DDoS 공격을 막으며, 해커를 막고, Zero Trust로 향하는 고객의 여정을 지원합니다.

어떤 장치로든 1.1.1.1에 방문해 인터넷을 더 빠르고 안전하게 만들어 주는 Cloudflare의 무료 앱을 사용해 보세요.

더 나은 인터넷을 만들기 위한 Cloudflare의 사명을 자세히 알아보려면 여기에서 시작하세요. 새로운 커리어 경로를 찾고 있다면 채용 공고를 확인해 보세요.
Cloudflare One Week (KO)Security (KO)Zero Trust (KO)Kubernetes (KO)한국어

X에서 팔로우하기

Terin Stock|@terinjokes
Cloudflare|@cloudflare

관련 게시물