13년차의 서버실

서버, 인프라, 홈랩 기술 블로그

[태그:] 인프라 보안

  • [인프라] Vault를 활용한 클라우드 환경 보안 강화: 실제 적용 사례와 팁

    [인프라] Vault를 활용한 클라우드 환경 보안 강화: 실제 적용 사례와 팁

    [인프라] Vault를 활용한 클라우드 환경 보안 강화: 실제 적용 사례와 팁

    안녕하세요, 13년차 서버실 지킴이입니다. 오늘도 클라우드 환경에서 겪었던 재미난(?) 삽질과 그 해결 과정을 공유해 볼까 합니다. 요즘 클라우드 환경에서 인프라를 운영하다 보면, 비밀(Secrets) 관리가 정말 큰 골칫덩이가 되곤 하죠? 데이터베이스 비밀번호, API 키, 각종 인증서… 이런 민감한 정보들을 어떻게 안전하고 효율적으로 관리할지 늘 고민이었거든요. 특히 애플리케이션이 늘어나고, 마이크로서비스 아키텍처로 전환하면서 이 문제는 더 심각해졌습니다.

    처음에는 환경 변수에 넣거나, 암호화된 파일로 저장하고, 심지어 코드 안에 박아 넣는(?) 무모한 시도도 했었죠. (부끄럽지만 다들 한 번쯤은 해보셨을 겁니다 ㅎㅎ) 근데 이렇게 하다 보니 보안 사고 위험은 물론이고, 비밀번호를 주기적으로 바꿔야 할 때마다 여기저기 찾아다니며 수정하는 것도 엄청난 작업이더라고요. 이러다간 언젠가 큰 사고가 터지겠다 싶어, 중앙 집중식 비밀 관리(Centralized Secrets Management) 솔루션을 찾아 나섰고, 그 과정에서 HashiCorp Vault를 만나게 되었습니다. 오늘은 이 Vault를 활용해서 클라우드 보안(Cloud Security)을 어떻게 강화했는지, 실제 적용 사례와 제가 직접 겪었던 팁들을 솔직하게 풀어보겠습니다.

    Vault 클라우드 보안 강화를 위한 인프라 아키텍처 개요

    Vault를 활용한 클라우드 환경 보안 강화 아키텍처 개요. 중앙의 Vault가 각종 클라우드 리소스 및 애플리케이션의 비밀을 안전하게 관리하는 모습을 보여줍니다.

    Vault, 도대체 뭘 하는 녀석일까요? (비밀 관리 개념 설명)

    쉽게 말해 Vault는 비밀 관리(Secrets Management)를 위한 금고 같은 존재입니다. 애플리케이션이 접근해야 하는 민감한 정보들, 예를 들어 데이터베이스 자격 증명, API 키, 클라우드 제공업체 자격 증명 등을 한곳에 모아두고 안전하게 관리해 주는 도구인 거죠.

    Vault의 핵심 기능은 크게 네 가지로 볼 수 있습니다:

    • 안전한 비밀 저장소 (Secure Secrets Storage): 모든 비밀을 암호화하여 저장합니다. 그냥 저장하는 게 아니라, 데이터를 암호화해서 저장하고, 접근 제어까지 확실하게 해주죠.
    • 동적 비밀 (Dynamic Secrets): 이게 진짜 마법 같은 기능인데요. 요청이 들어올 때마다 일회성으로 사용할 수 있는 자격 증명을 동적으로 생성해줍니다. 예를 들어, DB에 접속할 임시 계정을 만들어주고, 정해진 시간이 지나면 자동으로 만료시키거나 삭제하는 식이죠. 이걸 통해 비밀 유출 위험을 획기적으로 줄일 수 있습니다.
    • 데이터 암호화 (Data Encryption): 단순히 비밀을 저장하는 것을 넘어, 애플리케이션이 저장해야 할 일반 데이터도 Vault를 거쳐 암호화/복호화할 수 있도록 API를 제공합니다. 애플리케이션이 직접 암호화 로직을 구현할 필요가 없어지니 개발도 편해지더라고요.
    • 세밀한 접근 제어 및 감사 (Fine-grained Access Control & Auditing): 누가 어떤 비밀에 접근했는지, 언제 접근했는지 모든 기록을 남길 수 있습니다. 감사 로그(Audit Log)를 통해 보안 취약점을 파악하고 규정 준수에도 큰 도움이 됩니다.

    이런 기능들 덕분에 Vault는 인프라 보안(Infrastructure Security)과 DevSecOps를 구현하는 데 없어서는 안 될 핵심 도구로 자리 잡았습니다. 처음엔 그저 비밀번호 관리 도구인 줄 알았는데, 파고들수록 정말 똑똑한 녀석이더라고요.

    Vault와 함께하는 클라우드 보안 강화: AWS 동적 자격 증명 활용하기 (실전 구현)

    제가 직접 Vault를 도입하면서 가장 크게 체감했던 부분은 AWS 동적 자격 증명(Dynamic AWS Credentials) 관리였습니다. 기존에는 IAM User를 만들어 Access Key와 Secret Key를 발급받아 환경 변수나 설정 파일에 넣어 사용했거든요. 근데 이게 키가 유출되면 답이 없잖아요? Vault를 쓰면서 이 문제가 깔끔하게 해결됐습니다.

    자, 그럼 실제 어떻게 설정하고 사용했는지 단계별로 보여드릴게요.

    1. Vault 설치 및 초기화 (개발 환경 기준)

    저는 로컬에서 개발용으로 테스트할 때는 Docker를 활용했습니다. 프로덕션 환경에서는 HA 구성 등 고려할 게 많지만, 여기서는 간단하게 실습해볼게요.

    
    # Docker로 Vault 개발 서버 실행
    docker run --cap-add=IPC_LOCK -e 'VAULT_DEV_ROOT_TOKEN_ID=root' -p 8200:8200 --name dev-vault hashicorp/vault:latest
    
    # Vault CLI 접속 및 환경 변수 설정
    export VAULT_ADDR='http://127.0.0.1:8200'
    vault login root
    

    VAULT_DEV_ROOT_TOKEN_ID=root는 개발용으로 루트 토큰을 ‘root’로 설정하는 겁니다. 실제 운영 환경에서는 절대로 이렇게 설정하면 안 됩니다! ⚠️ 운영 환경에서는 반드시 초기화(Init) 및 봉인 해제(Unseal) 과정을 거쳐야 합니다. 이 과정은 아래 주의사항 섹션에서 다시 다룰게요.

    2. AWS Secret Backend 활성화

    Vault가 AWS 자격 증명을 관리하려면 AWS Secret Backend를 활성화해야 합니다.

    
    vault secrets enable aws
    

    이 명령어로 aws/ 경로에 AWS 관련 설정과 비밀을 관리할 수 있게 됩니다.

    3. Vault가 AWS와 통신할 루트 자격 증명 설정

    Vault가 AWS IAM에서 동적 자격 증명을 생성하려면, Vault 자체도 AWS에 접근할 수 있는 권한이 필요합니다. 저는 특정 IAM User의 Access Key와 Secret Key를 Vault에 등록했습니다. 이 키는 Vault가 IAM User, Role 등을 생성/삭제할 권한을 가져야 합니다. 최소 권한의 원칙을 지켜서 필요한 권한만 부여해야 해요.

    
    vault write aws/config/root \
        access_key="YOUR_AWS_ACCESS_KEY_ID" \
        secret_key="YOUR_AWS_SECRET_ACCESS_KEY" \
        region="ap-northeast-2"
    

    YOUR_AWS_ACCESS_KEY_ID와 YOUR_AWS_SECRET_ACCESS_KEY는 Vault가 AWS에 접근할 수 있는 권한을 가진 IAM User의 키입니다.

    4. Vault Role 생성 (AWS IAM 정책 매핑)

    이제 애플리케이션이 요청할 동적 자격 증명에 어떤 권한을 부여할지 정의하는 Role(역할)을 Vault에 생성합니다. 예를 들어, S3 버킷에만 읽기/쓰기 권한을 가진 자격 증명을 만들고 싶다면, 해당 IAM 정책을 정의해줍니다.

    저는 S3에만 접근할 수 있는 정책을 만들고 싶어서 다음과 같이 JSON 정책 문서를 준비했습니다. (AWS IAM 콘솔에서 만들 수도 있습니다.)

    
    {
      "Version": "2012-10-17",
      "Statement": [
        {
          "Effect": "Allow",
          "Action": [
            "s3:GetObject",
            "s3:ListBucket",
            "s3:PutObject",
            "s3:DeleteObject"
          ],
          "Resource": [
            "arn:aws:s3:::my-secure-bucket-1234",
            "arn:aws:s3:::my-secure-bucket-1234/*"
          ]
        }
      ]
    }
    

    이 정책을 Vault Role에 연결해줍니다. Vault는 이 Role을 바탕으로 AWS IAM에서 임시 자격 증명을 생성합니다.

    
    vault write aws/roles/s3-writer \
        credential_type="iam_user" \
        policy_document='{
          "Version": "2012-10-17",
          "Statement": [
            {
              "Effect": "Allow",
              "Action": [
                "s3:GetObject",
                "s3:ListBucket",
                "s3:PutObject",
                "s3:DeleteObject"
              ],
              "Resource": [
                "arn:aws:s3:::my-secure-bucket-1234",
                "arn:aws:s3:::my-secure-bucket-1234/*"
              ]
            }
          ]
        }' \
        max_ttl="1h" # 최대 유효 기간 1시간
    

    여기서 max_ttl은 생성된 자격 증명의 최대 유효 기간을 설정하는 겁니다. 1시간으로 설정하면, 1시간 뒤에는 자동으로 만료되거나 삭제됩니다. 💡 최소 권한(Least Privilege)과 최소 유효 기간(Least Lifetime) 원칙은 보안의 핵심입니다!

    Vault AWS Secret Backend 설정 흐름 다이어그램. Vault가 AWS IAM과 연동하여 동적 자격 증명을 생성하는 과정을 시각적으로 보여줍니다.

    5. 애플리케이션에서 Vault를 통해 동적 자격 증명 발급받기

    이제 애플리케이션은 직접 AWS 키를 들고 있을 필요 없이, Vault에 요청해서 임시 자격 증명을 발급받을 수 있습니다. 예를 들어, Python 애플리케이션에서 boto3를 사용한다고 가정해볼게요.

    
    import hvac # HashiCorp Vault API 클라이언트
    import os
    import boto3
    
    # Vault 클라이언트 초기화
    client = hvac.Client(url=os.environ.get('VAULT_ADDR'), token=os.environ.get('VAULT_TOKEN'))
    
    # Vault에서 AWS 자격 증명 요청
    try:
        read_response = client.secrets.aws.generate_credentials(name='s3-writer')
        
        aws_access_key_id = read_response['data']['access_key']
        aws_secret_access_key = read_response['data']['secret_key']
        aws_session_token = read_response['data']['security_token'] # 임시 자격 증명에만 존재
    
        print(f"✅ AWS Access Key ID: {aws_access_key_id}")
        print(f"✅ AWS Secret Key: {aws_secret_access_key}")
        print(f"✅ AWS Session Token: {aws_session_token}")
        print(f"✅ Lease Duration: {read_response['lease_duration']} seconds")
    
        # Boto3 클라이언트 초기화 (임시 자격 증명 사용)
        s3_client = boto3.client(
            's3',
            aws_access_key_id=aws_access_key_id,
            aws_secret_access_key=aws_secret_access_key,
            aws_session_token=aws_session_token,
            region_name='ap-northeast-2'
        )
        
        # S3 작업 수행 예시
        s3_client.put_object(Bucket='my-secure-bucket-1234', Key='test-file.txt', Body='Hello from Vault!')
        print("🎉 Successfully uploaded 'test-file.txt' to S3!")
    
    except Exception as e:
        print(f"⚠️ Error getting AWS credentials from Vault or interacting with S3: {e}")
    
    

    애플리케이션은 이제 Vault 토큰만 안전하게 관리하면 됩니다. AWS 키는 Vault가 알아서 생성하고, 정해진 시간이 지나면 자동으로 사라지니, 키 유출 걱정이 훨씬 줄어들죠. 이거 진짜 써보니까 너무 편하더라고요!

    ⚠️ 삽질 경험 공유: Vault 운영 시 주의사항 및 트러블슈팅

    Vault를 처음 도입했을 때, 저도 꽤 삽질을 많이 했습니다. 특히 운영 환경에서 Vault를 안정적으로 돌리는 건 개발 환경과는 차원이 다르더라고요. 몇 가지 중요한 포인트들을 공유해볼게요.

    1. Vault 봉인 해제 (Unseal)의 중요성

    Vault는 보안을 위해 기동 시 봉인(Sealed)된 상태로 시작합니다. 마치 금고 문이 잠겨 있는 것과 같죠. 데이터를 읽거나 쓰려면 반드시 봉인 해제(Unseal)를 해야 합니다. 이때 샤미르의 비밀 공유 알고리즘(Shamir’s Secret Sharing Algorithm)이라는 것이 사용되는데, 초기화 시 생성되는 마스터 키를 여러 개의 ‘봉인 해제 키 조각(Unseal Key Shares)’으로 나누어 보관합니다. 예를 들어, 5개의 조각 중 3개 이상이 모여야 봉인 해제가 가능한 식이죠.

    
    # Vault 초기화 (운영 환경에서 최초 1회)
    vault operator init \
        -key-shares=5 \
        -key-threshold=3
    
    # 봉인 해제 키 조각들 (Unseal Key Shares) 출력됨. 이 키들을 안전하게 보관해야 합니다!
    # Root Token도 함께 출력되니, 이것도 잘 보관해야 합니다.
    
    # 봉인 해제 (Vault 재시작 시마다)
    vault operator unseal <KEY_SHARE_1>
    vault operator unseal <KEY_SHARE_2>
    vault operator unseal <KEY_SHARE_3>
    # 필요한 키 조각 개수(threshold)만큼 입력하면 봉인 해제됩니다.
    

    처음엔 이게 왜 이렇게 복잡한가 싶었는데, 단일 장애점(Single Point of Failure)과 단일 주체에 의한 비밀 유출을 방지하기 위한 강력한 보안 장치더라고요. 저는 처음에 이 키 조각들을 안전하게 보관하는 방법을 고민하느라 애먹었습니다. KMS(Key Management Service)나 HSM(Hardware Security Module)과 연동하여 자동 봉인 해제(Auto-unseal)를 구성하는 것이 가장 이상적입니다.

    2. 토큰 관리 및 ACL (Access Control List)

    root 토큰은 말 그대로 모든 권한을 가집니다. 개발 환경에서야 편하지만, 운영에서는 절대로 사용하면 안 됩니다. 애플리케이션이나 사용자마다 필요한 권한만 부여한 ACL 정책(Policy)을 만들고, 해당 정책이 적용된 토큰이나 인증 방식을 사용해야 합니다. 예를 들어, Kubernetes 환경에서는 Kubernetes Auth Method를 사용해서 파드(Pod)에 자동으로 Vault 토큰을 주입하는 방식으로 많이 사용합니다.

    
    # Vault ACL Policy 예시 (s3-reader.hcl)
    path "aws/creds/s3-writer" {
      capabilities = ["read"]
    }
    # 이 정책은 aws/creds/s3-writer 경로에서 자격 증명을 '읽을' 권한만 부여합니다.
    
    # 정책 적용
    vault policy write s3-reader s3-reader.hcl
    

    이렇게 세밀하게 접근 제어를 하는 게 처음엔 번거로워도, 나중에 보안 사고 발생 시 파급력을 최소화하는 데 큰 도움이 됩니다.

    3. 네트워크 보안 및 고가용성(High Availability)

    Vault는 모든 비밀을 관리하는 핵심 인프라입니다. 따라서 네트워크 접근을 엄격하게 통제하고, Vault 인스턴스 자체의 보안에도 신경 써야 합니다. 또한, Vault 서버가 다운되면 모든 애플리케이션이 비밀을 얻지 못해 장애로 이어질 수 있으므로, 고가용성(High Availability, HA) 구성은 필수입니다. 저는 클라우드의 로드 밸런서와 여러 인스턴스를 활용해 HA 구성을 했었습니다.

    Vault 적용 결과 및 검증 (Verification & Results)

    Vault를 도입하고 나서 가장 크게 달라진 점은 바로 마음의 평화였습니다. 😅

    • 보안 강화: 하드코딩된 비밀이나 환경 변수에 노출된 키가 사라지면서 공격 표면(Attack Surface)이 획기적으로 줄어들었습니다. 동적 비밀 덕분에 키 유출의 위험도 거의 없어졌고요.
    • 운영 효율성 증대: 비밀번호 변경이나 키 로테이션(Rotation)이 필요한 경우, Vault 정책만 수정하면 되니 운영팀의 부담이 크게 줄었습니다. 애플리케이션은 Vault에 요청만 하면 되니, 설정 변경 없이도 최신 비밀을 사용할 수 있게 되었죠.
    • 감사 용이성: 누가 언제 어떤 비밀에 접근했는지 Vault의 감사 로그를 통해 쉽게 파악할 수 있게 되어, 규제 준수(Compliance)에도 큰 도움이 되었습니다.

    실제로 애플리케이션에서 동적으로 발급받은 AWS 자격 증명으로 S3에 접근하는 것을 검증했을 때, 드디어 됐다! 하는 쾌감을 잊을 수 없습니다. 🥳

    Vault를 통한 AWS 동적 자격 증명 생성 및 취소 결과 화면

    Vault 동적 AWS 자격 증명 발급 및 취소 CLI 결과. CLI에서 Vault를 통해 AWS 임시 자격 증명을 요청하고, 그 후 해당 자격 증명이 성공적으로 취소되는 과정을 보여줍니다.

    마무리하며: Vault와 클라우드 보안, 선택이 아닌 필수

    오늘은 Vault를 활용한 클라우드 환경 보안 강화에 대해 제가 직접 경험한 사례와 팁들을 공유해드렸습니다. 처음엔 도입이 다소 복잡하게 느껴질 수도 있지만, 한 번 구축하고 나면 얻을 수 있는 보안 강화와 운영 효율성은 그 노력을 충분히 보상하고도 남습니다.

    특히 DevSecOps 문화가 확산되면서 보안을 개발 프로세스 초기부터 고려하는 것이 중요해졌습니다. Vault는 이런 환경에서 비밀 관리(Secrets Management)의 표준처럼 자리 잡아가고 있습니다. 여러분의 인프라 환경에서도 Vault를 도입하여 더욱 안전하고 효율적인 시스템을 구축해보시길 강력히 추천합니다.

    다음 글에서는 Vault를 Kubernetes 환경에 통합하여 CI/CD 파이프라인에서 동적 비밀을 사용하는 방법에 대해 더 깊이 다뤄볼 예정입니다. 궁금한 점이나 공유하고 싶은 삽질 경험이 있다면 언제든 댓글 남겨주세요! 😊

    Vault와 기존 비밀 관리 방식의 보안 및 효율성 비교

    Vault와 전통적인 비밀 관리 방식 비교 인포그래픽. 보안, 효율성, 자동화 측면에서 Vault의 장점을 시각적으로 강조합니다.

  • [Proxmox] Proxmox VE 보안 강화 체크리스트 10가지: 안전한 가상화 환경 구축

    [Proxmox] Proxmox VE 보안 강화 체크리스트 10가지: 안전한 가상화 환경 구축

    안녕하세요! 13년차 인프라 엔지니어, 13년차의 서버실 운영자입니다. 오늘은 제가 홈랩(Homelab)에서 Proxmox VE를 운영하면서 겪었던 일들과 함께, 여러분의 소중한 가상화 환경을 더욱 안전하게 보호할 수 있는 Proxmox 보안 강화 체크리스트 10가지를 공유해볼까 합니다. 사실 처음엔 저도 ‘내 개인 서버인데 뭐 그렇게까지 해야 하나?’ 싶었거든요. 근데 한번 호되게 당하고 나서는 생각이 싹 바뀌었습니다. 인터넷에 연결된 모든 시스템은 잠재적인 공격 대상이 될 수 있다는 걸 뼈저리게 느꼈죠. 이 글을 통해 여러분은 저처럼 삽질하지 마시고, 처음부터 튼튼한 Proxmox VE 환경을 구축하시길 바랍니다. 이 체크리스트만 따라 해도 웬만한 위협에서는 훨씬 안전해질 거예요! 자, 그럼 시작해볼까요? 🎉

    Proxmox VE의 구성 요소와 각 영역에 적용될 보안 조치들을 시각적으로 보여주는 다이어그램입니다.

    1. 강력한 관리자 비밀번호와 SSH 키 인증 ✅

    보안의 가장 기본은 뭐니 뭐니 해도 비밀번호거든요. Proxmox VE 설치 시 설정하는 root 계정 비밀번호는 물론, 추가로 생성하는 모든 사용자 계정 비밀번호는 길고 복잡하게 만들어야 합니다. 숫자, 특수문자, 대소문자를 섞어서 최소 12자리 이상이죠. 저는 보통 16자리 이상으로 만듭니다.

    그리고 SSH(Secure Shell) 접속 시에는 비밀번호 인증 대신 SSH 키 인증(SSH Key Authentication)을 사용하는 게 훨씬 안전해요. 비밀번호는 무작위 대입 공격(Brute-force attack)에 취약하거든요. 제가 직접 해보니 키 인증 한 번 설정해두면 훨씬 편하고 안전하더라고요. 처음엔 좀 번거롭지만, 한 번 해두면 두고두고 안심입니다.

    # 1. SSH 키 생성 (클라이언트 PC에서 실행)
    ssh-keygen -t rsa -b 4096 -C "[email protected]"
    
    # 2. Proxmox VE 서버로 공개 키 복사
    ssh-copy-id -i ~/.ssh/id_rsa.pub root@your_proxmox_ip
    
    # 3. 비밀번호 없이 SSH 접속 확인
    ssh root@your_proxmox_ip

    이렇게 하면 다음부터는 비밀번호 입력 없이 SSH에 접속할 수 있습니다. 💡 팁: ssh-agent를 활용하면 키 비밀번호(passphrase)도 한 번만 입력해도 되더라고요.

    2. Proxmox VE 웹 UI 2단계 인증 (2FA) 🛡️

    Proxmox VE의 웹 관리 UI는 모든 설정의 핵심이거든요. 이곳이 뚫리면 모든 게 끝장입니다. 그래서 2단계 인증(Two-Factor Authentication, 2FA)은 선택이 아닌 필수죠. 구글 OTP(Google Authenticator) 같은 TOTP(Time-based One-Time Password) 앱을 연동해서 로그인할 때마다 일회용 코드를 입력하게 하는 거예요.

    제가 직접 설정해보니 생각보다 간단하더라고요. Proxmox VE 웹 UI에 로그인해서 [Datacenter] > [Permissions] > [Users]로 가서 여러분의 사용자 계정을 선택한 다음, [TFA] 탭에서 [Add] > [TOTP Factor]를 선택하면 됩니다. 그러면 QR 코드가 나오는데, 이걸 OTP 앱으로 스캔하면 끝! 로그인할 때 아이디/비밀번호 입력 후 OTP 코드를 한 번 더 입력하게 됩니다.

    Proxmox VE 웹 UI 2단계 인증 (TOTP) 설정 화면

    Proxmox VE 웹 인터페이스에서 TOTP(Time-based One-Time Password) 2단계 인증을 활성화하는 과정을 보여주는 스크린샷입니다.

    3. SSH 기본 설정 강화 🔒

    앞서 SSH 키 인증을 설정했다면, 이제 sshd 설정을 강화할 차례예요. 기본 설정을 그대로 두면 보안에 취약할 수 있거든요. 특히 root 계정으로 바로 로그인하는 것을 막고, 비밀번호 인증도 비활성화하는 것이 좋습니다. 저는 이 설정을 안 했다가 한동안 SSH 무작위 대입 공격 시도 로그를 보면서 식겁했던 경험이 있습니다. ㅎㅎ

    # Proxmox VE 서버에서 실행
    
    # 1. SSH 설정 파일 백업 (중요!)
    cp /etc/ssh/sshd_config /etc/ssh/sshd_config.bak
    
    # 2. 설정 파일 수정
    nano /etc/ssh/sshd_config
    
    # 다음 라인들을 찾아서 수정하거나 추가합니다.
    # PermitRootLogin no             # root 계정 직접 로그인 금지
    # PasswordAuthentication no      # 비밀번호 인증 비활성화 (키 인증 필수)
    # Port 2222                      # SSH 기본 포트 22번 대신 다른 포트 사용 (예: 2222)
    
    # 3. SSH 서비스 재시작
    systemctl restart sshd

    ⚠️ 주의사항: PermitRootLogin no와 PasswordAuthentication no를 설정하기 전에, 반드시 일반 사용자 계정을 만들고 SSH 키 인증으로 로그인 가능한지 확인해야 합니다. 안 그러면 서버에 접속하지 못하는 불상사가 발생할 수 있거든요! 저도 한 번 실수로 root 계정으로만 접속 가능한 상태에서 이 설정을 했다가 콘솔에 매달려야 했던 적이 있습니다… 😅

    4. Proxmox VE 내장 방화벽 활용 🔥

    Proxmox VE는 자체적으로 방화벽(Firewall) 기능을 제공하는데, 정말 강력해요. 호스트 레벨과 VM(Virtual Machine)/컨테이너(Container) 레벨 모두에서 네트워크 보안(Network Security)을 구현할 수 있거든요. Ingress(인그레스, 외부에서 내부로 들어오는 트래픽)와 Egress(이그레스, 내부에서 외부로 나가는 트래픽) 규칙을 세밀하게 제어할 수 있습니다.

    제가 홈랩에서 가장 먼저 하는 일 중 하나가 바로 이 방화벽 설정입니다. 불필요한 포트는 모두 막아두고, 필요한 포트(예: Proxmox 웹 UI 8006, SSH 변경 포트)만 열어두는 거죠. VM마다 다른 보안 정책을 적용할 수 있어서 정말 유용하더라고요.

    # Proxmox VE 호스트 레벨 방화벽 활성화
    pve-firewall start
    pve-firewall enable
    
    # 방화벽 규칙 설정은 Web UI를 권장합니다:
    # [Datacenter] > [Firewall] 또는 [Node] > [Firewall]
    # [VM/Container] > [Firewall]

    이건 Proxmox VE의 꽃 같은 기능이라고 생각합니다. 네트워크 세그멘테이션과 함께 사용하면 시너지가 정말 엄청나거든요!

    5. 네트워크 세그멘테이션 🌐

    네트워크 세그멘테이션(Network Segmentation)은 물리적 또는 가상적으로 네트워크를 여러 개의 작은 구역으로 나누는 것을 의미해요. 예를 들어, Proxmox VE 관리용 네트워크, VM 서비스용 네트워크, 스토리지용 네트워크 등을 분리하는 거죠. 이렇게 하면 한 구역이 뚫려도 다른 구역으로의 확산을 막을 수 있습니다.

    저는 보통 물리적 네트워크 인터페이스 카드(NIC)를 여러 개 사용하거나, VLAN(Virtual Local Area Network)을 활용해서 네트워크를 나눕니다. 관리 네트워크는 외부 인터넷과 직접 연결되지 않도록 하고, VM 서비스 네트워크만 필요한 포트만 열어두죠. 처음엔 귀찮아서 한데 뭉쳐놨었는데, 나중에 문제가 생겼을 때 트러블슈팅도 훨씬 어렵고, 보안적으로도 너무 취약하더라고요. 그래서 지금은 무조건 나누는 걸 원칙으로 합니다.

    Proxmox VE에서는 vmbr0, vmbr1 등 리눅스 브릿지(Linux Bridge)를 활용해서 가상 네트워크를 구성해요. 물리 NIC와 연결하거나, VLAN 태그를 지정할 수 있죠.

    6. Proxmox VE 시스템 최신 유지 🔄

    소프트웨어는 항상 최신 상태로 유지하는 게 중요합니다. Proxmox VE도 마찬가지거든요. 개발팀은 지속적으로 보안 취약점을 패치하고 새로운 기능을 추가하니까요. 오래된 버전은 알려진 취약점에 노출될 위험이 크거든요.

    저는 정기적으로 업데이트를 확인하고 적용하는 루틴을 가지고 있습니다. 한 달에 한 번 정도는 꼭 확인해서 업데이트를 진행하죠. 물론 업데이트 전에 백업은 필수예요! 업데이트하다가 예기치 않은 문제가 생길 수도 있거든요. 이 부분은 제가 나중에 백업 관련 글로 자세히 다룰 예정입니다.

    # Proxmox VE 업데이트
    apt update
    apt dist-upgrade
    
    # 재부팅이 필요한 경우 (커널 업데이트 등)
    reboot

    apt dist-upgrade는 새로운 커널이나 주요 패키지 업데이트를 포함하므로, 항상 변경 내용을 확인하고 신중하게 진행해야 합니다. 특히 프로덕션 환경이라면 더더욱 그렇고요!

    7. 백업 및 복구 전략 보안 💾

    아무리 보안을 잘해도 사고는 언제든 일어날 수 있어요. 랜섬웨어 공격, 하드웨어 고장, 실수로 인한 데이터 손실 등 다양한 위협으로부터 데이터를 보호하기 위해 백업은 필수입니다. 그리고 이 백업 데이터 자체도 안전하게 관리해야 합니다.

    저는 Proxmox Backup Server (PBS)를 활용해서 백업을 하더라고요. PBS는 효율적인 증분 백업(Incremental Backup)과 중복 제거(Deduplication)는 물론, 백업 암호화(Backup Encryption) 기능까지 제공해서 데이터를 안전하게 보관할 수 있게 해줍니다. 백업 데이터를 외부(Off-site) 스토리지에 보관하는 것도 중요해요. 물리적으로 다른 위치에 두는 거죠.

    그리고 백업이 잘 되는지 주기적으로 복구 테스트를 해보는 것도 정말 중요합니다. 백업만 있고 복구가 안 되면 아무 소용 없잖아요? 제가 예전에 백업은 잘 했는데, 복구 테스트를 안 했다가 막상 필요할 때 복구가 안 돼서 식은땀 흘렸던 적이 있어요… 😅

    Proxmox VE 보안 강화 체크리스트 10가지 요약 인포그래픽

    Proxmox VE 보안 강화의 핵심 10가지 항목을 요약하고 시각적으로 강조한 인포그래픽입니다.

    8. 사용자 및 권한 관리 (RBAC) 👥

    Proxmox VE는 역할 기반 접근 제어(Role-Based Access Control, RBAC)를 지원해요. 관리자 계정 하나로 모든 걸 다 하는 것보다는, 필요한 최소한의 권한만 부여하는 최소 권한 원칙(Principle of Least Privilege)을 지키는 게 중요합니다. 예를 들어, VM만 관리하는 사용자에게는 스토리지나 네트워크 설정 권한을 주지 않는 거죠.

    Proxmox VE 웹 UI에서 [Datacenter] > [Permissions]로 들어가면 사용자(Users), 그룹(Groups), 역할(Roles)을 정의할 수 있습니다. 처음엔 좀 복잡하게 느껴질 수도 있지만, 익숙해지면 훨씬 체계적으로 관리할 수 있더라고요.

    저도 처음엔 그냥 root 계정 하나로 모든 걸 했다가, 실수로 중요한 설정을 건드릴 뻔한 적이 여러 번 있습니다. 그때마다 ‘아, 이러면 안 되겠구나’ 싶어서 권한을 쪼개기 시작했죠. 특히 여러 사람이 함께 Proxmox VE를 관리하는 환경이라면 이 기능은 정말 필수예요.

    9. 불필요한 서비스 비활성화 🛑

    운영체제에는 기본적으로 다양한 서비스(Daemon)들이 설치되어 있어요. 이 중에는 Proxmox VE 운영에 반드시 필요하지 않거나, 사용하지 않는 서비스들도 있을 수 있습니다. 불필요한 서비스를 비활성화하면 공격 표면(Attack Surface)을 줄이고 시스템 자원을 절약할 수 있거든요.

    예를 들어, Proxmox VE를 설치하면 apt-cacher-ng 서비스가 자동으로 설치되는 경우가 있는데, 만약 여러 대의 Proxmox 서버를 운영하는 환경이 아니라면 굳이 필요 없을 수 있습니다. 이런 서비스들을 확인하고 비활성화하는 것이 좋아요.

    # 현재 실행 중인 서비스 목록 확인
    systemctl list-units --type=service --state=running
    
    # 특정 서비스 비활성화 (예시: apt-cacher-ng)
    systemctl stop apt-cacher-ng
    systemctl disable apt-cacher-ng

    ⚠️ 경고: 어떤 서비스가 시스템에 필요한지 확실히 모른다면 섣불리 비활성화하지 마세요. 잘못하면 시스템이 부팅되지 않거나 정상적으로 작동하지 않을 수 있거든요. Proxmox 관련 핵심 서비스는 절대 건드리면 안 됩니다!

    10. 정기적인 보안 감사 및 모니터링 🔍

    마지막으로, 모든 보안 설정이 잘 작동하는지 정기적으로 확인하고 모니터링하는 게 정말 중요합니다. 시스템 로그를 확인하고, 이상 징후는 없는지 살펴보는 거죠. fail2ban 같은 도구를 사용하면 SSH 무작위 대입 공격 시도를 자동으로 차단하는 데 큰 도움이 됩니다.

    저는 journalctl 명령어를 자주 사용해서 로그를 살펴봐요. 특히 SSH 접속 시도 로그나 방화벽 차단 로그 같은 것들이죠. 처음엔 그냥 지나쳤는데, 자세히 보니 수상한 IP에서 계속 접속 시도가 있더라고요. 그때부터 fail2ban을 설치해서 사용하기 시작했습니다. 이거 진짜 편하더라고요!

    # 최근 로그 확인
    journalctl -f
    
    # fail2ban 설치 및 설정
    apt install fail2ban
    cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
    nano /etc/fail2ban/jail.local
    # [sshd] 섹션에서 enabled = true 로 변경하거나 원하는 설정 적용
    systemctl enable fail2ban
    systemctl start fail2ban

    ⚠️ 삽질 경험과 해결 과정

    솔직히 이 모든 과정을 한 번에 완벽하게 해내기란 쉽지 않습니다. 저도 여러 번 삽질 좀 했거든요. 특히 SSH 설정을 강화하다가 제 발등을 찍었던 적이 많아요. PermitRootLogin no와 PasswordAuthentication no를 설정하고 sshd를 재시작했는데, 그만 SSH 키 인증이 제대로 안 되어 있어서 서버에 접속하지 못했던 거죠. 다행히 Proxmox VE는 콘솔 접속(Keyboard & Monitor)이 가능해서 직접 가서 복구했습니다만… 원격으로만 관리하는 서버였다면 정말 큰일 날 뻔했어요.

    이런 경험을 통해 배운 건, ‘변경 전에 항상 백업하고, 변경 후에는 반드시 검증하라’는 거예요. 특히 원격 접속 설정은 더더욱 신중해야 합니다. 콘솔 접속이 불가능한 환경이라면 ‘Rollback Plan(롤백 계획)’을 미리 세워두는 것이 좋아요.

    🎉 안전한 Proxmox 환경, 직접 확인해보니

    위에 언급된 체크리스트를 하나씩 적용하고 나면, 여러분의 Proxmox VE 환경은 훨씬 견고해질 겁니다. SSH 포트가 바뀌고, 키 인증으로만 접속되며, 웹 UI는 2단계 인증을 거쳐야만 들어갈 수 있게 되죠. 방화벽 덕분에 불필요한 트래픽은 차단되고, 시스템은 항상 최신 상태를 유지하게 됩니다. 이런 변화들을 직접 체감하면 정말 뿌듯하더라고요!

    각 설정이 제대로 적용되었는지 확인하는 것도 중요합니다. 예를 들어, SSH 포트 변경은 netstat -tuln | grep <새로운 포트> 명령어로 확인할 수 있고, 방화벽 규칙은 Proxmox VE 웹 UI의 [Datacenter] > [Firewall] 섹션에서 확인할 수 있어요.

    보안 강화 설정이 완료된 Proxmox VE 환경 대시보드

    Proxmox VE의 보안 설정이 강화된 후의 상태를 보여주는 대시보드 또는 주요 보안 설정 활성화 여부를 시각적으로 나타낸 이미지입니다.

    💡 마무리하며: 13년차 서버실의 다음 이야기

    오늘은 Proxmox VE 보안 강화 체크리스트 10가지를 통해 안전한 가상화 환경을 구축하는 방법을 알아봤습니다. 기본적인 비밀번호부터 시작해서 SSH 보안, 2FA Proxmox, Proxmox 방화벽 설정, 네트워크 세그멘테이션까지, 이 모든 과정이 처음엔 어렵게 느껴질 수 있지만, 하나씩 따라 하다 보면 어느새 튼튼한 서버실을 만들고 있는 자신을 발견할 수 있을 거예요. 이 경험들이 여러분의 인프라 엔지니어링 여정에 큰 도움이 되기를 바랍니다. 다음번에는 Proxmox VE의 백업 전략에 대해 좀 더 깊이 있는 이야기를 나눠볼까 합니다. 그때까지 여러분의 서버실이 안전하길 바랍니다! 궁금한 점이 있다면 언제든지 댓글로 남겨주세요. 저도 함께 고민해드릴게요! 😊

  • [k8s] GKE WireGuard 보안 취약점 분석: 매니지드 쿠버네티스 보안 강화 전략

    [k8s] GKE WireGuard 보안 취약점 분석: 매니지드 쿠버네티스 보안 강화 전략

    GKE WireGuard 보안 취약점 분석: 매니지드 쿠버네티스 보안 강화 전략

    안녕하세요, 13년차 서버실 지킴이입니다. 요즘 클라우드 환경에서 쿠버네티스(Kubernetes)를 운영하는 분들이 정말 많으시더라고요. 저도 홈랩(Homelab)에서 이것저것 만져보면서 GKE(Google Kubernetes Engine)의 편리함에 푹 빠져 있는데, 이 편리함 뒤에는 우리가 꼭 알아야 할 보안의 그림자가 숨어있다는 걸 아시나요?

    특히 온프레미스(On-premise) 환경이나 다른 클라우드에 있는 시스템과 GKE 클러스터를 안전하게 연결할 때 WireGuard(와이어가드) 같은 VPN(Virtual Private Network)을 많이 쓰는데요. 오늘은 이 GKE와 WireGuard 조합에서 발생할 수 있는 잠재적인 보안 취약점들을 어떻게 분석하고 효과적으로 보안을 강화할 수 있을지 저의 삽질 경험을 바탕으로 솔직하게 풀어보려 합니다. 매니지드 쿠버네티스(Managed Kubernetes)의 보안, 과연 어디까지가 우리의 책임일까요? 함께 파헤쳐 봅시다! 💡

    GKE와 WireGuard, 왜 함께 쓸까요?

    먼저, GKE와 WireGuard가 무엇이고 왜 이 둘을 함께 쓰는지 간단히 짚고 넘어갈게요. 이미 잘 아시는 분들도 계시겠지만, 쉽게 말해드릴게요.

    • GKE (Google Kubernetes Engine): 구글에서 제공하는 매니지드 쿠버네티스 서비스예요. 복잡한 쿠버네티스 클러스터(Cluster) 구축과 운영을 구글이 대신 해주니까, 우리는 애플리케이션 개발에만 집중할 수 있죠. 노드(Node) 관리, 업그레이드, 확장성 등 많은 부분이 자동화되어 있어서 정말 편합니다.
    • WireGuard (와이어가드): 최근 각광받는 오픈소스 VPN 프로토콜이에요. 기존 VPN(예: OpenVPN, IPSec)보다 훨씬 가볍고 빠르면서도 강력한 암호화를 제공하거든요. 설정도 간단해서 저도 홈랩에서 자주 써먹고 있습니다.

    그럼 이 둘을 왜 같이 쓸까요? 주로 이런 시나리오에서 빛을 발합니다:

    • 하이브리드 클라우드(Hybrid Cloud) 환경 구축: 온프레미스 데이터센터나 다른 클라우드에 있는 시스템이 GKE 클러스터 내부의 서비스와 안전하게 통신해야 할 때요.
    • 개발자/운영자 원격 접속: 관리자들이 집이나 외부에서 GKE 클러스터의 프라이빗(Private) 네트워크에 안전하게 접속하여 kubectl(쿠버네티스 커맨드라인 툴) 명령어를 실행하거나 내부 서비스에 접근해야 할 때죠.
    • 보안 강화: GKE 클러스터의 외부 노출을 최소화하고, 특정 허용된 경로(VPN)를 통해서만 접근을 허용하여 전반적인 보안 수준을 높일 때입니다.

    이렇게 들으면 정말 만능 조합 같죠? 근데 여기서부터 우리의 보안 취약점 분석이 시작됩니다.

    GKE와 WireGuard를 연동하여 안전한 접근 경로를 확보하는 일반적인 아키텍처 다이어그램

    GKE와 WireGuard를 연동하여 안전한 접근 경로를 확보하는 일반적인 아키텍처 다이어그램입니다.

    “취약점 분석”의 의미: 잠재적 위험 요소 파악

    제목에 “GKE WireGuard 보안 취약점 분석”이라고 했더니, 혹시 특정 버그(Bug)나 제로데이(Zero-day) 취약점을 떠올리셨나요? 사실 오늘 다룰 내용은 특정 소프트웨어의 버그보다는, GKE와 WireGuard를 연동하는 과정에서 발생할 수 있는 설계적, 설정적 미흡함으로 인한 잠재적 보안 문제들에 대한 분석입니다. 이걸 저는 넓은 의미의 ‘취약점’으로 보고 접근해요.

    매니지드 서비스라고 마냥 안심할 수 없는 이유는 바로 공동 책임 모델 (Shared Responsibility Model) 때문입니다. GKE 자체의 인프라(Infrastructure), 즉 쿠버네티스 컨트롤 플레인(Control Plane)이나 노드 운영체제(Operating System) 등은 구글이 관리하지만, 그 위에서 돌아가는 우리의 워크로드(Workload), 네트워크 구성, 데이터 보안 등은 전적으로 사용자의 책임이거든요. WireGuard 설정 역시 마찬가지예요. 이 경계를 명확히 이해하는 것이 보안 강화의 첫걸음입니다. ⚠️

    WireGuard 구성 시 고려해야 할 보안 취약점

    제가 실제로 GKE에 WireGuard를 연결하면서 “아차!” 했던 부분들을 중심으로 잠재적 취약점들을 짚어볼게요.

    1. 키 관리 (Key Management)의 허술함

    WireGuard는 공개키 암호화(Public-key cryptography)를 사용해요. 각 피어(Peer)마다 고유한 프라이빗 키(Private Key)와 퍼블릭 키(Public Key)를 가지죠. 이 프라이빗 키가 유출된다면 어떻게 될까요? 해당 키를 가진 누구나 VPN을 통해 우리 GKE 네트워크에 접근할 수 있게 됩니다. 홈랩에서는 제가 직접 관리하니 괜찮지만, 여러 팀원이나 시스템이 사용하는 프로덕션(Production) 환경에서는 정말 치명적입니다.

    • 문제점: 프라이빗 키를 코드 저장소에 올리거나, 관리 서버에 평문(Plaintext)으로 저장하는 경우가 있어요.
    • 강화 전략: Google Secret Manager(시크릿 매니저) 같은 안전한 키 관리 서비스(KMS, Key Management Service)를 활용하여 키를 안전하게 보관하고, 필요한 인스턴스(Instance)나 서비스 계정(Service Account)에만 최소한의 권한으로 접근을 허용해야 합니다.

    2. 과도한 접근 제어 (Access Control)

    WireGuard 서버를 설정할 때, `AllowedIPs` 옵션으로 허용할 IP 대역을 지정하죠. “일단 다 열어두고 나중에 좁혀야지” 하는 생각으로 0.0.0.0/0이나 너무 넓은 대역을 설정하면 곤란합니다. GKE 내부의 민감한 서비스까지 접근이 허용될 수 있거든요.

    • 문제점: WireGuard 피어가 GKE 클러스터의 모든 서브넷(Subnet)에 접근 가능하게 설정되는 경우예요.
    • 강화 전략: 최소 권한의 원칙 (Principle of Least Privilege)을 적용해야 해요. 특정 WireGuard 피어는 필요한 GKE 내부 서비스의 IP 대역이나 특정 파드(Pod)의 IP 대역에만 접근할 수 있도록 `AllowedIPs`를 정확하게 지정해야 하니까요.

    3. 네트워크 분리 (Network Segmentation)의 부재

    WireGuard VPN으로 GKE VPC(Virtual Private Cloud)에 연결했다고 해서 모든 것이 끝나는 게 아닙니다. VPN 터널(Tunnel)은 단지 ‘길’을 만들어줄 뿐, 그 ‘길’을 통해 어디까지 갈 수 있는지는 GKE와 GCP(Google Cloud Platform)의 네트워크 규칙이 결정하거든요.

    • 문제점: WireGuard 서버가 위치한 서브넷과 GKE 클러스터 서브넷 간의 방화벽 규칙(Firewall Rule)이 너무 느슨하거나, GKE 내부의 네트워크 정책(Network Policy)이 없는 경우를 말해요.
    • 강화 전략: Shared VPC(공유 VPC)를 활용하여 네트워크를 중앙에서 관리하고, GCP 방화벽 규칙을 통해 WireGuard 서버에서 GKE 클러스터로 들어오는 트래픽을 세밀하게 제어해야 합니다. 예를 들어, 특정 포트(Port)와 프로토콜(Protocol)만 허용하는 식이죠. 또한, GKE 내부에서는 쿠버네티스 네트워크 정책(Kubernetes Network Policies)을 사용하여 파드 간 통신을 제어해야 한답니다.

    4. 모니터링 및 로깅 (Monitoring & Logging)의 부족

    아무리 보안을 강화해도 사고는 발생할 수 있어요. 중요한 건 사고 발생 시 얼마나 빠르게 인지하고 대응하느냐죠. WireGuard 트래픽이나 GKE 내부 네트워크 트래픽에 대한 가시성(Visibility)이 없다면 문제가 생겨도 알 수가 없습니다.

    • 문제점: WireGuard 서버의 접속 로그(Log)나 GKE의 감사 로깅(Audit Logging)을 제대로 설정하지 않거나, 모니터링 시스템과 연동하지 않는 경우가 많아요.
    • 강화 전략: WireGuard 서버의 접속 및 트래픽 로그를 Cloud Logging(클라우드 로깅)으로 연동하고, GKE의 Cloud Audit Logs(클라우드 감사 로그)를 통해 클러스터 내의 모든 활동을 기록해야 해요. Cloud Monitoring(클라우드 모니터링)으로 관련 지표를 대시보드(Dashboard)에 띄우고, 비정상적인 활동에 대한 알림(Alert)을 설정하는 것이 필수입니다.

    GKE 환경에서 WireGuard 보안 강화 전략 (실전 팁)

    그럼 이제 위에서 언급한 취약점들을 보완하면서 실제로 어떻게 GKE와 WireGuard의 보안을 강화할 수 있는지 제가 사용해본 몇 가지 실전 팁을 공유해드릴게요.

    1. 프라이빗 GKE 클러스터 (Private GKE Cluster) 활용

    GKE 클러스터의 마스터(Master)와 노드(Node) 모두 프라이빗 IP 주소만 가지도록 설정하여 인터넷에 노출되지 않게 하는 것이 가장 강력한 보안 조치 중 하나예요. WireGuard VPN을 통해서만 접근 가능하게 만들 수 있거든요.

    
    gcloud container clusters create my-private-gke \
        --region=asia-east1 \
        --enable-private-nodes \
        --enable-private-endpoint \
        --master-ipv4-cidr=172.16.0.0/28 \
        --create-subnetwork name=my-gke-subnet,range=10.10.0.0/20
    

    이렇게 하면 외부에서는 프라이빗 IP로만 접근할 수 있기 때문에, WireGuard VPN을 통해 VPC 네트워크에 연결된 경우에만 GKE API 서버에 접근할 수 있어요. 🔒

    2. Shared VPC(공유 VPC)와 세분화된 방화벽 규칙

    WireGuard 서버는 별도의 서브넷에 두거나, 아예 다른 GCP 프로젝트(Project)의 Shared VPC에 위치시켜 GKE 클러스터와 네트워크를 분리하는 것을 권장합니다. 그리고 반드시 필요한 트래픽만 허용하는 방화벽 규칙을 적용해야 해요.

    
    gcloud compute firewall-rules create allow-wireguard-to-gke-api \
        --network=my-shared-vpc \
        --allow=tcp:443 \
        --source-ranges=YOUR_WIREGUARD_SERVER_IP/32 \
        --target-tags=gke-master-api-endpoint
    
    gcloud compute firewall-rules create allow-wireguard-to-gke-nodes \
        --network=my-shared-vpc \
        --allow=tcp:10250,tcp:30000-32767 \
        --source-ranges=YOUR_WIREGUARD_SERVER_IP/32 \
        --target-tags=gke-node
    

    위 예시처럼 WireGuard 서버 IP에서 GKE 마스터 API(tcp:443)와 노드(tcp:10250, NodePort 범위)로만 접근을 허용하는 방화벽 규칙을 만들 수 있어요. `YOUR_WIREGUARD_SERVER_IP` 부분은 실제 WireGuard 서버의 IP 주소로 바꿔주세요.

    GCP 콘솔에서 GKE와 WireGuard 간의 통신을 제어하는 방화벽 규칙을 설정하는 화면 예시

    GCP 콘솔에서 GKE와 WireGuard 간의 통신을 제어하는 방화벽 규칙을 설정하는 화면 예시입니다.

    3. Kubernetes Network Policies(네트워크 정책) 활용

    WireGuard VPN을 통해 GKE 내부 네트워크에 접근하더라도, 클러스터 내부의 파드 간 통신은 네트워크 정책으로 다시 한번 제어해야 합니다. 특정 네임스페이스(Namespace)나 파드에만 접근을 허용하는 식으로 말이에요. 이건 GKE 내부 보안의 핵심이거든요.

    
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: allow-wireguard-access-to-backend
      namespace: default
    spec:
      podSelector:
        matchLabels:
          app: backend-service
      policyTypes:
        - Ingress
      ingress:
        - from:
            - ipBlock:
                cidr: 10.128.0.0/14  # GKE 클러스터의 Pod CIDR 범위 (WireGuard 서버에서 접근할 수 있는 IP 대역)
            - namespaceSelector:
                matchLabels:
                  name: wireguard-client-namespace # WireGuard 클라이언트 Pod가 있다면 해당 네임스페이스
          ports:
            - protocol: TCP
              port: 8080
    

    이 정책은 `backend-service` 레이블을 가진 파드가 특정 IP 대역(WireGuard를 통해 접근하는 클라이언트 IP 범위) 또는 특정 네임스페이스의 파드로부터 8080 포트로만 트래픽을 허용하도록 한답니다.

    WireGuard 키를 안전하게 생성하고 Secret Manager를 통해 관리하는 프로세스 다이어그램

    WireGuard 키를 안전하게 생성하고 Secret Manager를 통해 관리하는 프로세스 다이어그램입니다.

    삽질 경험: “나는 괜찮겠지” 하다가 겪은 일

    저도 처음엔 “GKE는 구글이 관리해주니까 보안은 기본으로 되겠지? WireGuard는 빠르고 가볍다니까 그냥 연결만 하면 되겠지!” 하는 안일한 생각으로 시작했었어요. 홈랩에서 대충 WireGuard 서버 하나 띄워서 GKE VPC에 연결하고 `AllowedIPs = 0.0.0.0/0` 해놓고 썼습니다. 처음엔 잘 되더라고요. 🎉

    근데 어느 날, 개발자 한 분이 “특정 마이크로서비스(Microservice)에만 접속해서 디버깅(Debugging)해야 하는데, 뭔가 불안해요”라는 피드백을 주시더라고요. 그때 정신이 번쩍 들었습니다. WireGuard VPN을 통해 들어오면 마치 데이터센터 안에 있는 것처럼 GKE 클러스터의 모든 파드와 서비스에 접근이 가능한 상태였던 거죠. 😱

    이거 진짜 삽질 좀 했습니다 ㅎㅎ. 처음엔 방화벽 규칙을 좁혔는데, GKE 노드들이 서로 통신을 못 해서 클러스터가 깨지는 일도 있었고요. Network Policy를 적용하는 과정에서도 파드 셀렉터(Pod Selector)를 잘못 지정해서 멀쩡한 서비스가 통신 불가가 되는 상황도 겪었죠. 결국은 GCP 방화벽 규칙과 K8s 네트워크 정책을 촘촘하게, 그리고 단계적으로 적용하면서 테스트하고 또 테스트하는 과정을 거쳤어요. 이 과정에서 최소 권한의 원칙과 네트워크 분리의 중요성을 뼈저리게 느꼈습니다. 💡

    결과 및 검증

    위에 설명드린 보안 강화 전략들을 적용하고 나면, 반드시 검증 과정을 거쳐야 해요. 저는 주로 다음과 같은 방법으로 확인했습니다.

    • 접근 테스트: WireGuard VPN을 통해 접속한 후, 허용되지 않은 IP 대역이나 포트로 접근을 시도하여 차단되는지 확인합니다.
    • GCP Security Command Center(보안 명령 센터): GKE Security Posture(보안 상태) 대시보드를 통해 클러스터의 전반적인 보안 상태를 점검하고, 취약점이 없는지 확인했어요.
    • 로깅 확인: Cloud Logging에서 WireGuard 서버 로그와 GKE 감사 로그를 확인하여 비정상적인 접근 시도나 차단 기록이 잘 남는지 확인해요.

    이 과정을 통해 “아, 이제야 좀 안심하고 쓸 수 있겠구나” 하는 생각이 들더라고요. ✅

    GCP Security Command Center의 GKE Security Posture 대시보드를 통해 클러스터의 보안 상태를 점검하는 화면 예시

    GCP Security Command Center의 GKE Security Posture 대시보드를 통해 클러스터의 보안 상태를 점검하는 화면 예시입니다.

    마무리: 결국 보안은 지속적인 관리입니다

    오늘은 GKE와 WireGuard를 연동할 때 발생할 수 있는 잠재적인 보안 취약점들을 분석하고, 이를 강화하기 위한 전략들을 저의 경험을 바탕으로 이야기해봤습니다.

    핵심은 이거예요. 매니지드 서비스라고 해도 ‘내 책임’ 영역은 분명히 존재한다는 점, 그리고 그 영역에서 우리는 ‘최소 권한의 원칙’과 ‘네트워크 분리’를 철저히 지켜야 한다는 것. WireGuard 키 관리부터 시작해서 GCP 방화벽, K8s 네트워크 정책까지, 겹겹이 보안을 쌓아 올리는 것이 중요합니다.

    보안은 한 번 설정해두면 끝나는 게 아니라, 지속적인 관심과 관리가 필요해요. 정기적인 구성 감사, 취약점 스캔, 그리고 최신 보안 패치 적용을 게을리하지 마세요. 우리 서버실의 평화를 위해서 말이에요! 🛡️

    다음번엔 GKE 보안 모범 사례를 좀 더 깊게 파고들어볼까 합니다. 기대해주세요!

    GKE WireGuard 보안 강화 전략의 주요 내용을 요약한 인포그래픽입니다.

  • [Cloud] Cloudflare Zero Trust 구축: VPN 대체 및 보안 강화 실전 가이드

    [Cloud] Cloudflare Zero Trust 구축: VPN 대체 및 보안 강화 실전 가이드

    VPN의 한계를 넘어, Cloudflare Zero Trust로 보안을 강화하다

    안녕하세요, 13년차 서버실 지킴이입니다. 제가 13년 동안 인프라 엔지니어로 일하면서 가장 많이 들었던 말 중 하나가 "보안"이에요. 특히 재택근무가 일상화되고 클라우드 전환이 가속화되면서, 기존 VPN(Virtual Private Network)만으로는 한계를 느끼는 순간이 많아졌습니다. 저도 홈랩을 운영하면서 내부 서비스에 안전하게 접속하는 방법을 항상 고민했거든요. 기존 VPN은 설정도 복잡하고, 모든 트래픽을 내부망으로 보내는 방식이라 보안 위협에 취약할 수 있다는 생각도 들었고요. 그러다 문득, Cloudflare Zero Trust가 떠올랐습니다. "이거다!" 싶었죠. VPN을 대체하고 보안 강화까지 가능한 실전 가이드를 제가 직접 구축해본 경험을 바탕으로 여러분께 공유해볼까 합니다.

    Cloudflare Zero Trust, 그게 대체 뭔데요?

    Cloudflare Zero Trust(클라우드플레어 제로 트러스트)는 이름 그대로 "아무것도 신뢰하지 않는다"는 전제에서 시작하는 보안 모델이에요. 기존 보안 모델은 내부 네트워크에 들어오면 신뢰하고, 외부 네트워크는 불신하는 "성곽과 해자(Castle and Moat)" 방식이었죠. 하지만 Zero Trust는 내부든 외부든 모든 접근 시도를 "기본적으로 신뢰하지 않고(Never Trust), 항상 검증한다(Always Verify)"는 원칙을 따릅니다.

    쉽게 말해, 어떤 사용자가 어떤 기기에서 어떤 애플리케이션에 접근하려 할 때마다, 그 사용자가 누구인지, 기기가 안전한지, 접근하려는 애플리케이션에 대한 권한이 있는지 등 모든 요소를 실시간으로 검증하는 방식이에요. Cloudflare Zero Trust는 이 원칙을 구현하기 위한 다양한 도구와 서비스를 제공하는데, 특히 Cloudflare Access(클라우드플레어 액세스)와 Cloudflare WARP(클라우드플레어 워프)가 핵심적인 역할을 합니다.

    Cloudflare Zero Trust는 기존 VPN과 달리, 모든 접근을 개별적으로 검증하여 보안을 한층 더 높입니다.

    왜 Zero Trust 보안이 필요한가요?

    • 보안 강화: 내부 네트워크 침투 시 측면 이동(Lateral Movement) 공격을 어렵게 만듭니다. 각 리소스 접근 시마다 인증과 권한 검증을 거치니까요.
    • 유연한 접근: 사용자가 어디에 있든(사무실, 재택, 카페 등) 안전하게 필요한 리소스에 접근할 수 있습니다.
    • VPN 대체: 복잡한 VPN 설정 없이도 특정 애플리케이션에 대한 접근 제어가 가능해요. 저처럼 홈랩에서 특정 서비스만 외부에서 접근하고 싶을 때 정말 유용하더라고요.
    • 성능 향상: 모든 트래픽을 데이터 센터로 모으는 VPN과 달리, 최적의 경로로 리소스에 연결되므로 성능 저하가 훨씬 덜합니다.

    Cloudflare Zero Trust, 실전 구축 가이드

    이제 본격적으로 Cloudflare Zero Trust 구축 과정을 살펴볼까요? 저도 처음엔 좀 헤맸는데, 막상 해보니 별거 아니더라고요. 단계별로 차근차근 따라오시면 됩니다.

    Step 1: Cloudflare Teams 계정 활성화

    1. Cloudflare 대시보드에 로그인합니다.
    2. 왼쪽 메뉴에서 "Zero Trust"를 클릭하세요.
    3. 처음 접근하는 경우, Cloudflare Teams 계정을 활성화하라는 메시지가 나타날 거예요. "Launch Zero Trust" 버튼을 눌러 활성화하고, 팀 이름(Team Name)을 설정해줍니다. 저는 제 홈랩 이름으로 만들었죠.
    4. 요금제는 우선 무료 플랜인 "Free"로 시작하시면 됩니다. 소규모 팀에 충분한 수의 사용자를 지원하거든요.

    Step 2: Cloudflare WARP 클라이언트 설정

    WARP는 사용자 기기와 Cloudflare Edge 네트워크 사이에 암호화된 터널을 생성해주는 클라이언트 프로그램입니다. 이걸 설치해야 Zero Trust 정책이 적용돼요.

    1. Cloudflare Zero Trust 대시보드에서 "Settings" > "WARP Client"로 이동합니다.
    2. "Device enrollment" 탭에서 "Manage"를 클릭하고, 팀 도메인(예: <code>myhomelab.cloudflareaccess.com)을 확인해둡니다.
    3. 사용자 기기(PC, 모바일)에 맞는 WARP 클라이언트를 다운로드하여 설치합니다. 저는 리눅스 서버에 설치할 거라 아래 명령어를 사용했어요.
    # Debian/Ubuntu 기준
    curl https://pkg.cloudflareclient.com/pubkey.gpg | sudo gpg --yes --dearmor --output /usr/share/keyrings/cloudflare-warp-archive-keyring.gpg
    echo "deb [arch=amd64 signed-by=/usr/share/keyrings/cloudflare-warp-archive-keyring.gpg] https://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list
    sudo apt update
    sudo apt install cloudflare-warp
    
    # WARP 등록
    warp-cli register
    warp-cli set-mode tunnel
    warp-cli set-gateway-mode proxy
    warp-cli set-license <YOUR_LICENSE_KEY_IF_PAID> # 무료 플랜은 필요 없음
    warp-cli connect
    

    설치 후에는 웹 브라우저가 열리면서 Cloudflare Zero Trust 계정으로 로그인하라고 할 거예요. 로그인하면 WARP가 활성화됩니다. WARP 대시보드에서 "Connected" 상태를 확인하면 성공!

    Cloudflare Zero Trust 대시보드에서 Access 애플리케이션을 손쉽게 관리할 수 있습니다.

    Step 3: Access 애플리케이션 생성

    이제 특정 내부 서비스에 접근할 수 있도록 Cloudflare Access 애플리케이션을 만들어볼까요? 저는 홈랩의 Prometheus(프로메테우스) 대시보드에 접근하는 시나리오를 예로 들겠습니다.

    1. Zero Trust 대시보드에서 "Access" > "Applications"로 이동합니다.
    2. "Add an application" 버튼을 클릭하고, "Self-hosted"를 선택합니다.
    3. "Subdomain"에 접근할 도메인(예: prometheus.myhomelab.com)을 입력하고, "Session Duration" 등 필요한 설정을 합니다.
    4. 여기서 중요한 것이 Cloudflare Tunnel(클라우드플레어 터널)입니다. 내부망에 있는 서비스를 외부로 노출하지 않고 Cloudflare Edge 네트워크로 연결해주는 방식이죠. 저는 cloudflared라는 클라이언트 프로그램을 사용해서 터널을 만들었어요.
    # cloudflared 설치 (Debian/Ubuntu 기준)
    curl -L --output cloudflared.deb https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb
    sudo dpkg -i cloudflared.deb
    
    # 터널 생성 및 인증
    cloudflared tunnel login
    cloudflared tunnel create prometheus-tunnel # 터널 이름 지정
    
    # 터널 설정 파일 (config.yaml) 생성
    # nano ~/.cloudflared/config.yaml
    # --- (아래 내용 붙여넣기) ---
    tunnel: <YOUR_TUNNEL_UUID> # tunnel create 명령 실행 후 나오는 UUID
    credentials-file: /root/.cloudflared/<YOUR_TUNNEL_UUID>.json
    
    ingress:
      - hostname: prometheus.myhomelab.com
        service: http://localhost:9090 # Prometheus가 실행되는 내부 주소
      - service: http_status:404 # 일치하는 규칙이 없으면 404 반환
    # --- (여기까지) ---
    
    # 터널 실행 (서비스로 등록하여 자동 시작 설정 권장)
    sudo cloudflared tunnel run prometheus-tunnel
    

    Cloudflare Tunnel을 통해 내부의 Prometheus 서비스(http://localhost:9090)가 prometheus.myhomelab.com 도메인으로 Cloudflare Edge 네트워크에 안전하게 연결됩니다.

    Step 4: 정책 설정 및 사용자 인증

    이제 누가 이 애플리케이션에 접근할 수 있는지 정책(Policy)을 설정할 차례입니다. 이게 바로 Zero Trust 보안의 핵심이에요!

    1. Access 애플리케이션 설정 화면에서 "Policies" 탭으로 이동합니다.
    2. "Add a policy"를 클릭하여 새로운 정책을 만듭니다.
    3. 저는 다음과 같은 정책을 설정했어요:

      필드 (Field) 조건 (Rule) 값 (Value) 설명
      Emails in ['[email protected]'] 특정 이메일 주소만 허용
      Countries in ['KR'] 대한민국 IP에서만 접근 허용
      Device Posture Requires ['WARP is connected'] WARP 클라이언트가 연결된 기기에서만 허용
    4. "Action"은 "Allow"로 설정합니다. 이렇게 하면 지정된 조건(제 이메일, 한국 IP, WARP 연결 기기)을 모두 만족하는 사용자만 prometheus.myhomelab.com에 접근할 수 있게 됩니다.
    5. 사용자 인증은 Cloudflare Access가 제공하는 다양한 IDP(Identity Provider)와 연동할 수 있어요. 저는 주로 Google Workspace(구 G Suite)나 GitHub를 사용하는데, 무료 플랜에서도 연동이 가능해서 정말 편하더라고요.

    ⚠️ 삽질 경험 공유: 저도 이거 때문에 고생 좀 했습니다

    13년차 엔지니어라고 삽질을 안 하는 건 아니죠! 저도 몇 가지 문제 때문에 머리를 쥐어뜯었거든요. 혹시 여러분도 겪을 수 있는 문제들이니 참고하세요.

    • Cloudflare Tunnel 서비스 문제: cloudflared tunnel run 명령어로 터널을 실행했는데, 서버 재부팅 후 터널이 끊어져 있는 경우가 있었습니다. 당연히 서비스로 등록해서 자동 시작되도록 해야 하는데, 처음엔 수동으로 실행하고 "왜 안되지?" 했지 뭐예요. 꼭 systemd 등으로 서비스 등록해서 관리해주세요.

      # 서비스 파일 예시 (/etc/systemd/system/cloudflared-prometheus.service)
      [Unit]
      Description=Cloudflared Tunnel for Prometheus
      After=network.target
      
      [Service]
      TimeoutStartSec=0
      Type=notify
      ExecStart=/usr/local/bin/cloudflared tunnel run --config /root/.cloudflared/config.yaml prometheus-tunnel
      Restart=on-failure
      RestartSec=5s
      
      [Install]
      WantedBy=multi-user.target
      
      # 서비스 활성화 및 시작
      sudo systemctl enable cloudflared-prometheus
      sudo systemctl start cloudflared-prometheus
      sudo systemctl status cloudflared-prometheus
      
    • DNS 설정 누락: Cloudflare Access 애플리케이션을 만들면, 해당 서브도메인에 대한 CNAME 레코드를 Cloudflare DNS에 추가하라고 안내 메시지가 나옵니다. 이걸 빼먹으면 외부에서 접근이 안 돼요. prometheus.myhomelab.com CNAME -> <UUID>.cfargotunnel.com 이런 식으로요.

    • 정책 순서와 조건: Zero Trust 정책은 순서가 중요합니다. 특정 조건을 먼저 허용하고 다음 조건에서 거부하면, 의도치 않게 접근이 허용될 수 있어요. 항상 가장 엄격한 규칙을 위에 두는 것이 좋습니다. 또한, AND/OR 조건 조합을 잘 활용해야 하는데, 저는 처음에 이메일과 국가 조건을 OR로 묶어서 "왜 다른 사람이 접근되지?" 하고 당황했거든요. 각 조건이 모두 충족되어야 할 때는 AND처럼 동작하도록 여러 Require 규칙을 추가해야 합니다.

    🎉 드디어 성공! Cloudflare Zero Trust의 위력

    모든 설정을 마치고 제 홈랩의 Prometheus 대시보드에 접속해보니… 드디어 됐다! 처음에 웹 브라우저가 Cloudflare Access 인증 페이지로 리디렉션되고, 제 Google 계정으로 로그인한 후에야 Prometheus 화면이 나타났습니다. WARP 클라이언트가 연결되지 않은 상태에서는 아예 접근이 차단되는 것을 확인했고요.

    이거 진짜 편하더라고요! 기존 VPN 접속처럼 전체 트래픽이 느려지는 느낌도 없고, 필요한 서비스에만 딱 접근할 수 있으니 훨씬 직관적이고 빨랐어요. 게다가 Cloudflare의 글로벌 네트워크를 통해 트래픽이 처리되니 안정성도 높고요. 기존 VPN에서 발생했던 IP 충돌 문제나 복잡한 라우팅 설정이 필요 없다는 점도 정말 큰 장점입니다.

    Cloudflare Zero Trust를 통해 안전하게 내부 Prometheus 대시보드에 접속한 모습.

    마무리하며: Zero Trust, 이제 선택이 아닌 필수!

    오늘은 제가 직접 Cloudflare Zero Trust를 구축하여 VPN을 대체하고 보안을 한층 더 높인 실전 경험을 공유해드렸습니다. 처음엔 낯설게 느껴질 수 있지만, 한번 구축하고 나면 기존 VPN 방식보다 훨씬 강력하고 유연한 보안 환경을 갖출 수 있다는 것을 몸소 느꼈습니다.

    Cloudflare Zero Trust는 단순히 VPN을 대체하는 것을 넘어, 사용자와 애플리케이션, 데이터의 위치에 상관없이 일관된 보안 정책을 적용할 수 있게 해주는 미래 지향적인 보안 모델입니다. 특히 재택근무가 활성화되고 클라우드 환경이 보편화된 요즘에는 선택이 아닌 필수가 되어가고 있다고 생각해요. 여러분도 저처럼 삽질 좀 하시면서 직접 경험해보시면, 이 강력한 보안 솔루션의 매력에 푹 빠지실 겁니다. 혹시 이 글을 읽고 궁금한 점이 있다면 언제든지 댓글로 물어봐 주세요!

    Cloudflare Zero Trust와 전통 VPN의 주요 특징 비교.

    다음 글에서는 Cloudflare Zero Trust의 또 다른 강력한 기능인 WARP Gateway(워프 게이트웨이)를 활용한 DNS 필터링과 L7 방화벽 설정에 대해 더 깊이 다뤄볼게요. 기대해주세요!

  • [Cloud] GitHub Actions 자체 호스팅 러너 보안 강화: 백도어 방지 및 2026년 최신 가이드

    [Cloud] GitHub Actions 자체 호스팅 러너 보안 강화: 백도어 방지 및 2026년 최신 가이드

    CI/CD 파이프라인의 약한 고리, 자체 호스팅 러너

    몇 달 전에 팀 내에서 꽤 아찔한 일이 있었습니다. 동료 엔지니어가 운영하던 GitHub Actions 자체 호스팅 러너(Self-hosted Runner)가 공급망 공격(Supply Chain Attack)의 타깃이 될 뻔했거든요. 다행히 조기에 발견했지만, 그때 이후로 저도 홈랩이랑 실무 환경 전체를 다시 들여다보게 됐습니다.

    솔직히 말씀드리면, 저도 처음에는 “GitHub에서 제공하는 거니까 그냥 쓰면 되겠지”라고 생각했었어요. 근데 자체 호스팅 러너는 얘기가 완전히 다릅니다. 여러분 서버 위에 올라가는 순간, 그 보안 책임은 100% 여러분 몫이거든요.

    이번 글에서는 GitHub Actions 자체 호스팅 러너 보안을 실질적으로 강화하는 방법을 단계별로 정리해 드릴게요. 2025~2026년 기준으로 GitHub에서 권고하는 최신 가이드라인과 제가 직접 적용해본 경험을 섞어서 써봤습니다.

    GitHub Actions 자체 호스팅 러너 보안 아키텍처 전체 개요 다이어그램

    ▲ GitHub Actions 자체 호스팅 러너의 전체 보안 아키텍처 개요 — 러너, 리포지토리, 네트워크, 시크릿 관리 레이어별 보안 포인트를 한눈에 보여줍니다.


    자체 호스팅 러너(Self-hosted Runner)란 무엇인가요?

    GitHub Actions에는 두 가지 러너 타입이 있습니다.

    • GitHub 호스팅 러너(GitHub-hosted Runner): GitHub이 관리하는 클라우드 VM. 워크플로우 실행 후 바로 폐기됩니다.
    • 자체 호스팅 러너(Self-hosted Runner): 여러분이 직접 서버를 준비하고, 그 위에 러너 에이전트를 설치해서 운영하는 방식입니다.

    쉽게 말해, 자체 호스팅 러너는 “내 서버를 GitHub CI/CD 파이프라인에 연결하는 것”입니다. 비용 절감, 내부망 접근, 특수 하드웨어 활용 등 장점이 많죠. 근데 바로 그 유연성 때문에 보안 리스크도 같이 따라옵니다.

    왜 위험한가요?

    핵심 문제는 워크플로우 파일(.yml)이 코드 저장소에 존재한다는 것입니다. 누군가 PR(Pull Request)을 통해 악성 워크플로우를 심으면, 그게 여러분 서버 위에서 실행될 수 있어요. GitHub 호스팅 러너라면 그 VM은 바로 폐기되지만, 자체 호스팅 러너는 여러분 서버가 그대로 남아있고, 내부 네트워크에도 접근 가능하죠.

    구분 GitHub 호스팅 러너 자체 호스팅 러너
    인프라 관리 GitHub 책임 사용자 책임
    보안 패치 자동 직접 관리
    실행 후 환경 VM 폐기 (격리) 서버 유지 (잔류 위험)
    내부망 접근 불가 가능 (위험 요소)
    비용 분당 과금 자체 서버 비용
    공급망 공격 위험 낮음 높음

    GitHub Actions 자체 호스팅 러너 보안 강화 — 단계별 실전 가이드

    1단계: 러너 접근 범위를 최소화하세요

    제가 처음 자체 호스팅 러너를 설정할 때 Organization 레벨로 등록했었어요. 편하긴 한데, 나중에 생각해보니 이게 꽤 위험한 설정이더라고요. 모든 리포지토리의 워크플로우가 그 러너에 접근할 수 있으니까요.

    권장 설정: 러너를 특정 리포지토리 레벨에만 등록하거나, Runner Group으로 접근을 제한하세요.

    GitHub Enterprise나 Organization을 쓰신다면 Runner Group(러너 그룹) 기능을 꼭 활용하세요.

    1. GitHub Organization → Settings → Actions → Runner groups
    2. 새 그룹 생성 후, 접근 허용할 리포지토리만 선택
    3. “Allow public repositories” 옵션은 반드시 비활성화
    # 러너 등록 시 특정 리포지토리 레벨로 등록하는 예시
    # Organization 레벨 대신 리포지토리 레벨 토큰 사용
    ./config.sh \
      --url https://github.com/your-org/specific-repo \
      --token YOUR_REPO_LEVEL_TOKEN \
      --name "secure-runner-01" \
      --labels "self-hosted,linux,secure"

    2단계: 에페머럴 러너(Ephemeral Runner) 도입 — 이게 진짜 핵심입니다

    이거 처음 알았을 때 “왜 진작 이걸 안 썼지?” 싶었어요. 에페머럴(Ephemeral, 일회성)이라는 단어처럼, 하나의 잡(Job)을 처리하고 나면 러너가 자동으로 폐기되는 방식입니다. GitHub 호스팅 러너처럼요.

    이렇게 하면 이전 잡에서 심어진 악성 코드나 환경 오염이 다음 잡으로 이어지지 않습니다. 공급망 공격 방어에 가장 효과적인 방법 중 하나거든요.

    # 에페머럴 모드로 러너 실행
    ./config.sh \
      --url https://github.com/your-org/your-repo \
      --token YOUR_TOKEN \
      --ephemeral  # 이 플래그 하나로 일회성 러너가 됩니다
    
    ./run.sh

    컨테이너 환경이라면 Actions Runner Controller(ARC)를 활용하면 Kubernetes 위에서 에페머럴 러너를 자동으로 스케일링할 수 있어요. 저도 홈랩 k3s 클러스터에 ARC 올려서 쓰고 있는데, 진짜 편하더라고요.

    # Helm으로 Actions Runner Controller 설치
    helm install arc \
      --namespace "arc-systems" \
      --create-namespace \
      oci://ghcr.io/actions/actions-runner-controller-charts/gha-runner-scale-set-controller
    
    # 에페머럴 러너 스케일셋 설정 (values.yaml)
    githubConfigUrl: "https://github.com/your-org/your-repo"
    githubConfigSecret: "arc-github-secret"
    
    containerMode:
      type: "dind"  # Docker-in-Docker로 격리 강화
    
    template:
      spec:
        containers:
          - name: runner
            image: ghcr.io/actions/actions-runner:latest
            resources:
              limits:
                cpu: "2"
                memory: "4Gi"
    GitHub Actions 에페머럴 러너 라이프사이클 다이어그램 - 잡 실행 후 자동 폐기 과정

    ▲ 에페머럴 러너의 라이프사이클 — 잡 수신 → 실행 → 자동 폐기 과정을 통해 환경 오염을 원천 차단합니다.

    3단계: 워크플로우 권한(Permissions)을 최소화하세요

    이게 은근히 놓치기 쉬운 부분이에요. GitHub Actions 워크플로우는 기본적으로 꽤 넓은 권한을 가질 수 있거든요. 최소 권한 원칙(Principle of Least Privilege)을 워크플로우에도 적용해야 합니다.

    # .github/workflows/ci.yml
    name: CI Pipeline
    
    on:
      push:
        branches: [main]
      pull_request:
        branches: [main]
    
    # 워크플로우 전체 기본 권한을 최소화
    permissions:
      contents: read  # 코드 읽기만 허용
    
    jobs:
      build:
        runs-on: [self-hosted, linux, secure]
        
        # 잡별로 필요한 권한만 추가
        permissions:
          contents: read
          packages: write  # 패키지 빌드가 필요한 경우에만
        
        steps:
          - name: Checkout
            uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
            with:
              persist-credentials: false  # 크리덴셜 잔류 방지!
          
          - name: Build
            run: make build

    특히 persist-credentials: false 옵션, 저도 처음엔 그냥 넘겼다가 나중에 알고 깜짝 놀랐어요. 이게 없으면 체크아웃 후 GitHub 토큰이 git 설정에 남아있을 수 있거든요.

    4단계: 외부 액션(Third-party Actions) SHA 핀닝(Pinning)

    공급망 공격(Supply Chain Attack)에서 가장 많이 노출되는 지점이 바로 여기입니다. uses: some-action/checkout@main처럼 브랜치 태그를 쓰면, 그 액션 저장소가 해킹당했을 때 여러분 파이프라인도 같이 당하게 돼요.

    해결책: 액션을 커밋 SHA 해시로 고정(Pin)하세요.

    # ❌ 이렇게 쓰면 위험합니다
    - uses: actions/checkout@v4
    - uses: actions/setup-node@main
    
    # ✅ 커밋 SHA로 고정하는 것이 안전합니다
    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683  # v4.2.2
    - uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af  # v4.1.0

    SHA 해시를 일일이 관리하기 귀찮으시다면 Dependabot을 활용하세요. .github/dependabot.yml에 설정하면 액션 버전 업데이트를 자동으로 PR로 올려줍니다.

    # .github/dependabot.yml
    version: 2
    updates:
      - package-ecosystem: "github-actions"
        directory: "/"
        schedule:
          interval: "weekly"
        # SHA 핀닝된 액션도 자동으로 업데이트 PR 생성
        groups:
          actions:
            patterns:
              - "*"

    5단계: 시크릿(Secrets) 관리와 환경 보호 규칙

    시크릿 관리도 허술하면 다 무너져요. 몇 가지 중요한 포인트를 짚어드릴게요.

    • 환경(Environment) 보호 규칙 설정: Production 환경에는 반드시 Reviewer 승인 단계를 추가하세요.
    • 시크릿 범위 최소화: Organization 시크릿보다는 리포지토리 시크릿, 그것보다는 환경 시크릿이 더 안전합니다.
    • OIDC(OpenID Connect) 활용: 장기 자격증명(Long-lived Credentials) 대신 단기 토큰을 사용하세요.
    # OIDC로 AWS 인증하는 예시 — 장기 Access Key 불필요!
    jobs:
      deploy:
        runs-on: [self-hosted, linux]
        environment: production  # 환경 보호 규칙 적용
        
        permissions:
          id-token: write  # OIDC 토큰 발급에 필요
          contents: read
        
        steps:
          - name: Configure AWS Credentials via OIDC
            uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502  # v4
            with:
              role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
              aws-region: ap-northeast-2
              # Access Key/Secret Key가 전혀 필요 없습니다!

    6단계: 러너 서버 자체 하드닝(Hardening)

    러너가 올라가는 서버 자체도 단단하게 만들어야 하거든요. 이건 일반적인 리눅스 서버 보안과 겹치는 부분도 있는데, 자체 호스팅 러너 특화 포인트만 짚어드릴게요.

    # 러너를 전용 비권한 사용자로 실행
    sudo useradd -m -s /bin/bash github-runner
    sudo usermod -aG docker github-runner  # Docker 사용이 필요한 경우만
    
    # 러너 디렉토리 권한 설정
    sudo chown -R github-runner:github-runner /opt/actions-runner
    
    # systemd 서비스로 등록 (루트 실행 방지)
    cd /opt/actions-runner
    sudo ./svc.sh install github-runner  # 전용 유저로 서비스 설치
    sudo ./svc.sh start
    # /etc/systemd/system/actions.runner.*.service 에서 확인
    # User=github-runner 로 설정되어 있어야 합니다
    
    # Docker 소켓 접근 제한 (필요한 경우 rootless Docker 고려)
    # /etc/docker/daemon.json
    {
      "userns-remap": "github-runner",  # 사용자 네임스페이스 리매핑
      "no-new-privileges": true,
      "log-driver": "json-file",
      "log-opts": {
        "max-size": "10m",
        "max-file": "3"
      }
    }

    그리고 네트워크 측면에서는, 러너 서버가 외부 인터넷에 직접 노출되지 않도록 해야 해요. GitHub Actions 자체 호스팅 러너는 아웃바운드 연결만 사용합니다. 인바운드 포트를 열 필요가 없거든요. 방화벽 규칙을 아웃바운드 443(HTTPS)만 허용하는 식으로 좁힐 수 있습니다.

    GitHub Actions 자체 호스팅 러너 보안 강화 설정 체크리스트 대시보드

    ▲ 자체 호스팅 러너 보안 강화 체크리스트 — 네트워크, OS, 워크플로우, 시크릿 관리 등 레이어별 적용 현황을 확인하세요.


    ⚠️ 실제로 삽질했던 문제들과 해결법

    문제 1: pull_request 이벤트와 포크 리포지토리

    이거 진짜 주의하셔야 해요. 퍼블릭 리포지토리에서 자체 호스팅 러너를 쓰면 포크(Fork)된 리포지토리의 PR도 트리거될 수 있거든요. 외부 기여자가 악성 워크플로우를 PR에 담아서 보내면… 생각만 해도 아찔하죠.

    해결책: 퍼블릭 리포지토리에는 자체 호스팅 러너를 절대 사용하지 마세요. GitHub도 공식적으로 이를 권고하지 않아요. 꼭 써야 한다면 pull_request_target 이벤트 대신 pull_request를 쓰고, 환경 보호 규칙으로 외부 기여자 PR은 수동 승인 후 실행되도록 설정하세요.

    # 외부 기여자 PR 실행 제한 예시
    on:
      pull_request:
        branches: [main]
    
    jobs:
      build:
        # 포크 PR의 경우 시크릿 접근 불가 (GitHub 기본 동작)
        # 자체 호스팅 러너는 내부 PR만 처리하도록 조건 추가
        if: github.event.pull_request.head.repo.full_name == github.repository
        runs-on: [self-hosted, linux]

    문제 2: 러너 업데이트 누락

    홈랩 운영하다 보면 러너 에이전트 업데이트를 깜빡하기 쉬워요. 저도 몇 달 방치했다가 나중에 보니 꽤 여러 버전이 밀려있더라고요. 자동 업데이트 설정을 켜두는 게 좋습니다.

    # 러너 자동 업데이트 설정 확인
    # .runner 파일에서 disableUpdate 옵션 확인
    cat /opt/actions-runner/.runner
    
    # 자동 업데이트가 비활성화되어 있다면
    # 주기적으로 업데이트 스크립트를 cron으로 실행
    # 에페머럴 러너라면 이미지 자체를 최신으로 유지하는 것이 더 좋습니다

    문제 3: 워크플로우 로그에 시크릿 노출

    워크플로우 실행 로그가 공개되는 경우, 시크릿이 실수로 echo 되거나 에러 메시지에 포함되는 경우가 있어요. GitHub은 등록된 시크릿 값을 자동으로 마스킹해주지만, 파생된 값(예: base64 인코딩된 시크릿)은 마스킹이 안 될 수 있거든요.

    # 파생 값도 마스킹하려면 add-mask 명령을 사용하세요
    - name: Mask derived secret
      run: |
        DERIVED_SECRET=$(echo "${{ secrets.MY_SECRET }}" | base64)
        echo "::add-mask::$DERIVED_SECRET"  # 이 값도 로그에서 마스킹됨
        echo "DERIVED_SECRET=$DERIVED_SECRET" >> $GITHUB_ENV

    보안 검증 — 설정이 제대로 됐는지 확인하기

    설정을 다 했다면 실제로 제대로 동작하는지 확인해봐야 하거든요. 제가 주기적으로 체크하는 항목들입니다.

    GitHub의 보안 권고 확인

    리포지토리 → Security → Code scanning alerts에서 워크플로우 파일의 보안 문제를 스캔할 수 있어요. CodeQL이나 actionlint를 CI에 넣어두면 자동으로 체크됩니다.

    # actionlint로 워크플로우 파일 정적 분석
    # 로컬에서 실행
    docker run --rm -v $(pwd):/repo rhysd/actionlint:latest -color /repo/.github/workflows/
    
    # CI에 통합
    - name: Lint GitHub Actions workflows
      uses: raven-actions/actionlint@v2
      with:
        files: .github/workflows/*.yml

    보안 강화 체크리스트 최종 점검

    • ✅ 러너가 특정 리포지토리/그룹에만 등록되어 있는가?
    • ✅ 에페머럴 모드로 실행되는가?
    • ✅ 워크플로우 기본 권한이 read-all 또는 그 이하인가?
    • ✅ 외부 액션이 SHA 해시로 핀닝되어 있는가?
    • ✅ Dependabot으로 액션 업데이트를 추적하는가?
    • ✅ 프로덕션 환경에 보호 규칙이 설정되어 있는가?
    • ✅ 장기 자격증명 대신 OIDC를 사용하는가?
    • ✅ 러너가 비권한 사용자로 실행되는가?
    • ✅ 러너 서버의 인바운드 포트가 닫혀 있는가?
    • ✅ 퍼블릭 리포지토리에 자체 호스팅 러너를 사용하지 않는가?
    GitHub Actions 자체 호스팅 러너 보안 강화 10가지 핵심 체크리스트 인포그래픽

    ▲ GitHub Actions 자체 호스팅 러너 보안 강화 요약 인포그래픽 — 10가지 핵심 체크리스트를 한눈에 확인하세요.


    마무리 — CI/CD 보안은 한 번이 아닙니다

    긴 글 읽어주셔서 감사합니다. 정리하자면, GitHub Actions 자체 호스팅 러너 보안의 핵심은 이렇습니다.

    1. 접근 최소화: 러너 범위를 필요한 리포지토리로만 제한
    2. 에페머럴 러너: 잡 실행 후 환경을 폐기해서 오염 방지
    3. 최소 권한: 워크플로우 권한을 필요한 것만 허용
    4. 공급망 공격 방어: 외부 액션을 SHA로 핀닝하고 Dependabot으로 관리
    5. 시크릿 보호: OIDC 활용, 환경 보호 규칙 설정
    6. 서버 하드닝: 비권한 사용자 실행, 네트워크 제한

    CI/CD 보안은 “한 번 설정하면 끝”이 아니거든요. 공급망 공격 기법은 계속 진화하고 있고, GitHub도 꾸준히 새로운 보안 기능을 추가하고 있어요. 저도 이 글 쓰면서 다시 한 번 제 홈랩 설정을 점검했는데, 고쳐야 할 부분이 몇 개 보이더라고요.

    💡 다음 글에서는 Kubernetes 기반의 Actions Runner Controller(ARC)를 활용한 에페머럴 러너 자동 스케일링 설정을 더 자세히 다룰 예정이에요. 홈랩에 k3s 올려서 직접 구성해본 경험을 공유해드릴 거니까 기대해주세요.

    궁금한 점이나 추가로 다뤄줬으면 하는 내용이 있으면 댓글로 남겨주세요. 제 경험이 여러분의 파이프라인을 조금 더 안전하게 만드는 데 도움이 됐으면 좋겠습니다. 🎉