

# CloudFront Functions 및 KeyValueStore를 사용하여 상호 TLS(뷰어)에 대한 인증서 해지 구현
<a name="implement-certificate-revocation"></a>

CloudFront 연결 함수를 KeyValueStore와 함께 사용하여 인증서 해지 확인을 구현할 수 있습니다. 이렇게 하면 해지된 인증서 일련 번호 목록을 유지하고 TLS 핸드셰이크 중에 이 목록과 비교하여 클라이언트 인증서를 확인할 수 있습니다.

인증서 해지를 구현하려면 다음 구성 요소가 필요합니다.
+ 뷰어 mTLS로 구성된 배포
+ 해지된 인증서 일련 번호가 포함된 KeyValueStore
+ KeyValueStore를 쿼리하여 인증서 상태를 확인하는 연결 함수

클라이언트가 연결되면 CloudFront는 트러스트 스토어에 대해 인증서를 검증한 다음 연결 함수를 실행합니다. 함수는 KeyValueStore에 대해 인증서 일련 번호를 확인하고 연결을 허용하거나 거부합니다.

**Topics**
+ [1단계: 해지된 인증서에 대한 KeyValueStore 생성](create-kvs-revoked-certificates.md)
+ [2단계: 해지 연결 함수 생성](create-revocation-connection-function.md)
+ [3단계: 해지 함수 테스트](test-revocation-function.md)
+ [4단계: 배포에 함수 연결](associate-function-distribution.md)
+ [고급 해지 시나리오](advanced-revocation-scenarios.md)

# 1단계: 해지된 인증서에 대한 KeyValueStore 생성
<a name="create-kvs-revoked-certificates"></a>

KeyValueStore를 생성하여 mTLS 연결 중에 연결 함수가 확인할 수 있는 해지된 인증서 일련 번호를 저장합니다.

먼저 해지된 인증서 일련 번호를 JSON 형식으로 준비합니다.

```
{
  "data": [
    {
      "key": "ABC123DEF456",
      "value": ""
    },
    {
      "key": "789XYZ012GHI", 
      "value": ""
    }
  ]
}
```

이 JSON 파일을 S3 버킷에 업로드한 다음 KeyValueStore를 생성합니다.

```
aws s3 cp revoked-serials.json s3://your-bucket-name/revoked-serials.json
aws cloudfront create-key-value-store \
  --name revoked-serials-kvs \
  --import-source '{
    "SourceType": "S3",
    "SourceARN": "arn:aws:s3:::your-bucket-name/revoked-serials.json"
  }'
```

KeyValueStore가 프로비저닝을 완료할 때까지 기다립니다. 다음을 통해 상태를 확인합니다.

```
aws cloudfront get-key-value-store --name "revoked-serials-kvs"
```

# 2단계: 해지 연결 함수 생성
<a name="create-revocation-connection-function"></a>

KeyValueStore에 대한 인증서 일련 번호를 확인하여 인증서가 해지되었는지 확인하는 연결 함수를 생성합니다.

KeyValueStore에 대해 인증서 일련 번호를 확인하는 연결 함수를 생성합니다.

```
aws cloudfront create-connection-function \
  --name "revocation-control" \
  --connection-function-config file://connection-function-config.json \
  --connection-function-code file://connection-function-code.txt
```

구성 파일은 KeyValueStore 연결을 지정합니다.

```
{
  "Runtime": "cloudfront-js-2.0",
  "Comment": "A function that implements revocation control via KVS",
  "KeyValueStoreAssociations": {
    "Quantity": 1,
    "Items": [
      {
        "KeyValueStoreArn": "arn:aws:cloudfront::account-id:key-value-store/kvs-id"
      }
    ]
  }
}
```

연결 함수 코드는 KeyValueStore에서 해지된 인증서를 확인합니다.

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    
    // Get parsed client serial number from client certificate
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    
    // Check KVS to see if serial number exists as a key
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    // Deny connection if serial number exists in KVS
    if (serialNumberExistsInKvs) {
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Allow connections that don't exist in kvs
    console.log("Connection allowed");
    return connection.allow();
}
```

# 3단계: 해지 함수 테스트
<a name="test-revocation-function"></a>

CloudFront 콘솔을 사용하여 샘플 인증서로 연결 함수를 테스트합니다. 콘솔에서 연결 함수로 이동하여 테스트 탭을 사용합니다.

**샘플 인증서로 테스트**

1. 테스트 인터페이스에 PEM 형식의 샘플 인증서 붙여넣기

1. 선택적으로 IP 기반 로직을 테스트하기 위한 클라이언트 IP 주소 지정

1. **함수 테스트**를 선택하여 실행 결과를 확인합니다.

1. 실행 로그를 검토하여 함수 로직 확인

유효한 인증서와 해지된 인증서를 모두 사용해 테스트하여 함수가 두 시나리오를 올바르게 처리하는지 확인합니다. 실행 로그에는 console.log 출력과 함수 실행 중에 발생하는 모든 오류가 표시됩니다.

# 4단계: 배포에 함수 연결
<a name="associate-function-distribution"></a>

연결 함수를 게시한 후 mTLS 지원 배포와 연결하여 인증서 해지 확인을 활성화합니다.

배포 설정 페이지 또는 연결 함수의 연결된 배포 테이블에서 함수를 연결할 수 있습니다. 배포 설정으로 이동하여 **뷰어 상호 인증(mTLS)** 섹션으로 스크롤한 후 연결 함수를 선택하고 변경 사항을 저장합니다.

# 고급 해지 시나리오
<a name="advanced-revocation-scenarios"></a>

더 복잡한 인증서 해지 요구 사항을 보려면 다음과 같은 추가 구성을 고려하세요.

**Topics**
+ [인증서 해지 목록(CRL)을 KeyValueStore 형식으로 변환](#convert-crl-kvs-format)
+ [여러 인증 기관 처리](#handle-multiple-cas)
+ [연결 로그에 사용자 지정 데이터 추가](#add-custom-data-logs)
+ [CRL 업데이트 관리](#manage-crl-updates)
+ [KeyValueStore 용량 계획](#plan-kvs-capacity)

## 인증서 해지 목록(CRL)을 KeyValueStore 형식으로 변환
<a name="convert-crl-kvs-format"></a>

인증서 해지 목록(CRL) 파일이 있는 경우 OpenSSL 및 jq를 사용하여 KeyValueStore JSON 형식으로 변환할 수 있습니다.

**CRL을 KeyValueStore 형식으로 변환**

다음과 같이 CRL 파일에서 일련 번호를 추출합니다.

```
openssl crl -text -noout -in rfc5280_CRL.crl | \
  awk '/Serial Number:/ {print $3}' | \
  cut -d'=' -f2 | \
  sed 's/../&:/g;s/:$//' >> serialnumbers.txt
```

일련 번호를 KeyValueStore JSON 형식으로 변환합니다.

```
jq -R -s 'split("\n") | map(select(length > 0)) | {data: map({"key": ., "value": ""})}' \
  serialnumbers.txt >> serialnumbers_kvs.json
```

형식이 지정된 파일을 S3에 업로드하고 1단계에 설명된 대로 KeyValueStore를 생성합니다.

## 여러 인증 기관 처리
<a name="handle-multiple-cas"></a>

TrustStore에 여러 인증 기관(CA)이 포함된 경우 KeyValueStore 키에 발급자 정보를 포함하여 일련 번호가 같을 수 있는 다른 CA의 인증서 간 충돌을 방지합니다.

다중 CA 시나리오의 경우 발급자의 SHA1 해시와 일련 번호를 키로 조합하여 사용합니다.

```
import cf from 'cloudfront';

async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientCert = connection.clientCertInfo;
    
    // Create composite key with issuer hash and serial number
    const issuer = clientCert.issuer.replace(/[^a-zA-Z0-9]/g, '').substring(0, 20);
    const serialno = clientCert.serialNumber;
    const compositeKey = `${issuer}_${serialno}`;
    
    const cert_revoked = await kvsHandle.exists(compositeKey);
    
    if (cert_revoked) {
        console.log(`Blocking revoked cert: ${serialno} from issuer: ${issuer}`);
        connection.deny();
    } else {
        connection.allow();
    }
}
```

**참고**  
발급자 식별자 \$1 일련 번호를 사용하면 키가 더 길어져 KeyValueStore에 저장할 수 있는 총 항목 수가 줄어들 수 있습니다.

## 연결 로그에 사용자 지정 데이터 추가
<a name="add-custom-data-logs"></a>

연결 함수는 logCustomData 메서드를 사용하여 CloudFront 연결 로그에 사용자 지정 데이터를 추가할 수 있습니다. 이렇게 하면 해지 확인 결과, 인증서 정보 또는 기타 관련 데이터를 로그에 포함할 수 있습니다.

```
async function connectionHandler(connection) {
    const kvsHandle = cf.kvs();
    const clientSerialNumber = connection.clientCertInfo.serialNumber;
    const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber);
    
    if (serialNumberExistsInKvs) {
        // Log revocation details to connection logs
        connection.logCustomData(`REVOKED:${clientSerialNumber}:DENIED`);
        console.log("Connection denied - certificate revoked");
        return connection.deny();
    }
    
    // Log successful validation
    connection.logCustomData(`VALID:${clientSerialNumber}:ALLOWED`);
    console.log("Connection allowed");
    return connection.allow();
}
```

사용자 지정 데이터는 800바이트의 유효한 UTF-8 텍스트로 제한됩니다. 이 제한을 초과하면 CloudFront는 데이터를 가장 가까운 유효한 UTF-8 경계로 잘라냅니다.

**참고**  
사용자 지정 데이터 로깅은 배포에 연결 로그가 활성화된 경우에만 작동합니다. 연결 로그가 구성되지 않은 경우 logCustomData 메서드는 no-op입니다.

## CRL 업데이트 관리
<a name="manage-crl-updates"></a>

인증 기관은 두 가지 유형의 CRL을 발급할 수 있습니다.
+ **전체 CRL**: 해지된 모든 인증서의 전체 목록 포함
+ **델타 CRL**: 마지막 전체 CRL 이후 해지된 인증서만 나열

전체 CRL 업데이트의 경우 업데이트된 데이터로 새 KeyValueStore를 생성하고 연결 함수 연결을 새 KeyValueStore로 리디렉션합니다. 이 접근 방식은 차이를 계산하고 증분 업데이트를 수행하는 것보다 더 간단합니다.

델타 CRL 업데이트의 경우 update-keys 명령을 사용하여 해지된 새 인증서를 기존 KeyValueStore에 추가합니다.

```
aws cloudfront update-key-value-store \
  --name "revoked-serials-kvs" \
  --if-match "current-etag" \
  --put file://delta-revoked-serials.json
```

## KeyValueStore 용량 계획
<a name="plan-kvs-capacity"></a>

KeyValueStore는 크기 제한이 5MB이며 최대 1천만 개의 키-값 페어를 지원합니다. 키 형식 및 데이터 크기에 따라 취소 목록 용량을 계획합니다.
+ **일련 번호만 해당**: 간단한 해지 확인을 위한 효율적인 스토리지
+ **발급자 식별자 \$1 일련 번호**: 다중 CA 환경을 위한 더 긴 키

대규모 해지 목록의 경우 다양한 인증서 범주 또는 기간에 대해 별도의 KeyValueStores를 유지하는 계층형 접근 방식을 구현하는 것이 좋습니다.