Browsing Tag

object storage

NCLOUD

[NCLOUD] 암호화된 Object Storage 버킷에 대한 수명 주기 관리 전략

안녕하세요 MANVSCLOUD 김수현입니다.

네이버 클라우드의 Object Storage 서비스는 데이터 저장과 관리에 있어서 탁월한 선택지로 자리 잡고 있습니다. 그러나 많은 사용자들이 한 가지 중요한 문제에 직면하고 있는데요.
바로 버킷 암호화 설정을 했을 때 수명 주기 관리 기능의 제한입니다.

이는 데이터 보안을 강화하고자 하는 사용자들에게 큰 도전과제로 다가올 수 있습니다.그렇다면 이러한 제한을 어떻게 극복할 수 있을까요?

다행히도 네이버 클라우드에서는 Object Storage API를 제공하고 있어 Cloud Functions과 함께 사용한다면 이 문제를 쉽게 해결할 수 있습니다. 이 포스트에서는 암호화된 Object Storage 버킷에 대해 수명 주기 관리를 효과적으로 수행하는 방법을 안내해 드리려고 합니다.

이를 통해 데이터 보안을 유지하면서도 효율적으로 데이터를 관리할 수 있는 방법을 알아보겠습니다.


Object Storage 암호화

Object Storage 버킷을 암호화하는 것은 현대 클라우드 컴퓨팅 환경에서 필요로 하는 보안 조치 중 하나입니다.

많은 보안 표준과 프레임워크는 데이터를 저장할 때 암호화를 기본 요구사항으로 삼고 있는데요. ISO 27001, NIST, PCI DSS 등과 같은 표준들은 암호화를 통해 데이터 보호를 강조합니다.

이런 보안 심사는 기업이나 기관이 사이버 공격, 데이터 유출, 해킹 시도 등 다양한 위협으로부터 자신의 데이터를 어떻게 보호하는지를 평가하며 데이터 암호화는 중요한 방어 수단으로 간주하고 있는데 이와 같이 Object Storage 버킷을 암호화하는 것은 데이터 보안을 강화하고 다양한 규정과 보안 표준을 충족시키며 기업과 기관이 보안 심사에서 긍정적인 평가를 받을 수 있도록 합니다.

네이버 클라우드의 Object Storage도 암호화 관리 설정을 통해 버킷 내 객체를 자동으로 암호화할 수 있습니다. 하지만 이와 같이 암호화 설정을 할 경우 Object Storage 기능 중 하나인 ‘Lifecycle Management’를 사용할 수 없는 점인데요.

위 그림과 같이 암호화된 mvsc-sec-test 버킷에 대해서는 수명 주기 관리 대상 버킷으로 선택할 수 없는 것을 확인할 수 있습니다.

그렇다면 암호화된 버킷 내 객체들은 어떻게 수명 주기를 설정할 수 있을지 Hands On을 통해 알아보도록 하겠습니다.


Hands On

# VPC, Server 생성에 대한 가이드는 생략합니다.
# Python은 3.11 버전이 사용되었습니다.

  • 주의사항 (1) : Cloud Functions에서는 Boto3 라이브러리를 내장하고 있지 않기때문에 boto3 라이브러리를 포함시켜 압축한 후 .zip 파일로 업로드할 것입니다.
"2023-12-26T03:52:10.60211601Z  stderr: Traceback (most recent call last):"
"2023-12-26T03:52:10.602137684Z stderr: File "exec__.py", line 42, in <module>"
"2023-12-26T03:52:10.602141788Z stderr: from main__ import main as main"
"2023-12-26T03:52:10.602144077Z stderr: File "/action/1/bin/main__.py", line 3, in <module>"
"2023-12-26T03:52:10.602147112Z stderr: import boto3"
"2023-12-26T03:52:10.602149144Z stderr: ModuleNotFoundError: No module named 'boto3'"
"2023-12-26T03:52:10.607916982Z stderr: Command exited abruptly during initialization."
  • 주의사항 (2) : Boto3는 2023년 12월 13일부터 Python 3.7을 더 이상 지원하지 않기때문에 3.8 이상 버전으로 사용이 필요합니다.
"2023-12-26T04:24:05.996379353Z stderr: /action/1/bin/boto3/compat.py:82: PythonDeprecationWarning: Boto3 will no longer support Python 3.7 starting December 13, 2023. To continue receiving service updates, bug fixes, and security updates please upgrade to Python 3.8 or later. More information can be found here: https://aws.amazon.com/blogs/developer/python-support-policy-updates-for-aws-sdks-and-tools/"
"2023-12-26T04:24:05.996387502Z stderr: warnings.warn(warning, PythonDeprecationWarning)"

아래 예시는 Linux 환경을 기준으로 작성되었습니다.

  • 코드 작성 및 .zip 파일 생성
mkdir obj_lifecycle
cd obj_lifecycle
python -m venv venv
source venv/bin/activate
pip install boto3
pip install requests
cp -r venv/lib/python3.11/site-packages/* .
vi main__.py

venv를 이용하여 가상 환경을 만들어준 뒤 boto3를 install하고 main__.py를 작성하였습니다.

import json
import base64
import boto3
import requests
from datetime import datetime, timedelta

service_name = "s3"
endpoint_url = "https://kr.object.ncloudstorage.com"
region_name = "kr-standard"

S3 = None

def set_response(status_code, body):
    return {
        "Content-Type": "application/json",
        "statusCode": status_code,
        "body": json.dumps(body),
    }

def set_S3_client(access_key, secret_key):
    print("--- new object storage client ---")
    return boto3.client(
        service_name,
        endpoint_url=endpoint_url,
        aws_access_key_id=access_key,
        aws_secret_access_key=secret_key,
    )

def delete_old_files(bucket_name, prefix, days_old):
    try:
        global S3
        response = S3.list_objects_v2(Bucket=bucket_name, Prefix=prefix)
        if 'Contents' not in response:
            return []

        deleted_files = []
        for item in response['Contents']:
            last_modified = item['LastModified']
            if datetime.now(last_modified.tzinfo) - last_modified > timedelta(days=days_old):
                S3.delete_object(Bucket=bucket_name, Key=item['Key'])
                deleted_files.append(item['Key'])
        return deleted_files

    except Exception as e:
        print("Error in deleting old files.")
        raise e

def main(args):
    try:
        global S3
        if S3 is None:
            S3 = set_S3_client(args["access_key"], args["secret_key"])

        method = args["__ow_method"]
        path = args["__ow_path"]
        path_components = path.split("/")

        # 처리할 경로와 버킷 이름 지정
        bucket_name = args["bucket_name"]
        target_prefix = args["bucket_path"]

        # 파일 삭제 기능 실행
        if method == "delete" and len(path_components) == 2 and path_components[1] == "cleanup":
            days_old = int(args.get('expiration_date', 180))  # 기본값 180일
            deleted_files = delete_old_files(bucket_name, target_prefix, days_old)
            return set_response(200, {"deleted_files": deleted_files})

        # 기타 다른 HTTP 메소드 처리 부분을 여기에 추가할 수 있습니다.

    except Exception as e:
        print(e)
        return set_response(500, {"error": "An unexpected error occurred."})

작성된 main__.py의 코드는 위와 같고 이후 압축되어야할 파일들은 아래 목록이 될 것입니다.
(venv 디렉토리는 삭제해버립시다 용량만 차지하니까요?)

total 76
drwxr-xr-x 10 root root  4096 Dec 26 13:34 boto3
drwxr-xr-x  2 root root   133 Dec 26 13:34 boto3-1.34.7.dist-info
drwxr-xr-x  8 root root  4096 Dec 26 13:34 botocore
drwxr-xr-x  2 root root   120 Dec 26 13:34 botocore-1.34.7.dist-info
drwxr-xr-x  3 root root   112 Dec 26 13:34 certifi
drwxr-xr-x  2 root root   102 Dec 26 13:34 certifi-2023.11.17.dist-info
drwxr-xr-x  4 root root  4096 Dec 26 13:34 charset_normalizer
drwxr-xr-x  2 root root   126 Dec 26 13:34 charset_normalizer-3.3.2.dist-info
drwxr-xr-x  6 root root  4096 Dec 26 13:34 dateutil
drwxr-xr-x  3 root root    63 Dec 26 13:34 _distutils_hack
-rw-r--r--  1 root root   151 Dec 26 13:34 distutils-precedence.pth
drwxr-xr-x  3 root root   190 Dec 26 13:34 idna
drwxr-xr-x  2 root root    84 Dec 26 13:34 idna-3.6.dist-info
drwxr-xr-x  3 root root   167 Dec 26 13:34 jmespath
drwxr-xr-x  2 root root   106 Dec 26 13:34 jmespath-1.0.1.dist-info
-rw-r--r--  1 root root  2447 Dec 26 13:35 main__.py
drwxr-xr-x  5 root root   136 Dec 26 13:34 pip
drwxr-xr-x  2 root root   166 Dec 26 13:34 pip-23.2.1.dist-info
drwxr-xr-x  5 root root    73 Dec 26 13:34 pkg_resources
drwxr-xr-x  2 root root    33 Dec 26 13:34 __pycache__
drwxr-xr-x  2 root root   118 Dec 26 13:34 python_dateutil-2.8.2.dist-info
drwxr-xr-x  3 root root  4096 Dec 26 13:34 requests
drwxr-xr-x  2 root root   119 Dec 26 13:34 requests-2.31.0.dist-info
drwxr-xr-x  3 root root  4096 Dec 26 13:34 s3transfer
drwxr-xr-x  2 root root   124 Dec 26 13:34 s3transfer-0.10.0.dist-info
drwxr-xr-x  8 root root  4096 Dec 26 13:34 setuptools
drwxr-xr-x  2 root root   143 Dec 26 13:34 setuptools-65.5.0.dist-info
drwxr-xr-x  2 root root   102 Dec 26 13:34 six-1.16.0.dist-info
-rw-r--r--  1 root root 34549 Dec 26 13:34 six.py
drwxr-xr-x  5 root root  4096 Dec 26 13:34 urllib3
drwxr-xr-x  3 root root    82 Dec 26 13:34 urllib3-2.0.7.dist-info
zip -r obj_lifecycle.zip *

Cloud Functions으로 업로드할 수 있도록 .zip 파일로 압축이 되었다면 Cloud Functions Action에서 파일을 업로드 해줍시다.

  • Cloud Functions
    디폴트 파라미터는 아래와 같이 설정해줄 수 있습니다.

    VPC ID는 Access Control Group(ACG)가 존재하는 VPC에서 확인할 수 있으며 OBJ_ACCESS_KEY와 OBJ_SECRET_KEY는 Object Storage의 권한이 있는 최소한의 권한이 부여된 API용 Sub Account의 Access Key와 Secret key 등을 입력해줍니다.
{"expiration_date":"180","bucket_path":"cat-tracer/","__ow_method":"delete","access_key":"AAAAAAAAAAAAAAAAA","secret_key":"BBBBBBBBBBBBBBBBBBBBBBBBBBBB","__ow_path":"/cleanup","bucket_name":"mvsc-sec-test"}
  • expiration_date : 만료일
  • bucket_path : 버킷 내 수명 주기 경로
  • __ow_method : 요청 메소드
  • access_key : Object Storage 객체 삭제가 가능한 권한이 있는 ACCESS KEY
  • secret_key : Object Storage 객체 삭제가 가능한 권한이 있는 SECRET KEY
  • __ow_path : Cloud Functions 환경에서 HTTP 요청의 경로(URL의 일부)를 나타내는 변수
  • bucket_name : 암호화 설정된 Object Storage의 버킷 이름

KMS 키를 생성하고 Cloud Functions 내 암호화 설정을 이용하여 설정한 파라미터의 값을 암호화할 수 있습니다. 즉, 디폴트 파라미터에 입력된 ACCESS KEY, SECRET KEY 등을 암호화할 수 있게됩니다.

위 Action을 Cron과 같은 트리거를 함께 사용한다면 하루에 한번씩 특정 기간이 넘어가는 파일을 삭제하여 암호화된 Object Storage의 비용을 최적화할 수 있습니다.


Personal Comments

네이버 클라우드를 사용하면서 Object Storage의 가격이 Block Storage보다 저렴하지만 지속적으로 관리되지 않고 파일이 쌓이게 된다면 비용은 어느순간 기하급수적으로 늘어나있을 것입니다. 이때 암호화된 Object Storage를 Lifecycle Management로 관리할 수 없다면 이를 대안으로 요청이 1,000,000 (건) 이하일 경우, 소요시간이 400,000 (GB-초) 이하인 경우 무료로 사용할 수 있고 그 이상 사용하더라도 사용한만큼만 비용이 발생하는 Serverless 서비스인Cloud Functions을 활용하여 Object Storage 버킷 내 파일들을 쉽게 관리하고 비용을 최적화할 수 있습니다.

긴 글 읽어주셔서 감사합니다.