XKS는 AWS KMS의 기능중 하나로, 직접 제어 가능한 외부 키 관리 시스템에 저장된 암호화 키로 데이터를 보호하려는 사용자가 이용할 수 있는 기능입니다. 사용자는 이 기능을 통해 암호화 키를 사용한 데이터 암호화/복호화, 독립적인 권한 부여, AWS 외부의 키 관리 시스템에서 감사 등의 작업을 유동적으로 수행할 수 있습니다.
AWS에서 제공하는 서비스를 이용하여 환경을 다음 사진과 같이 구성하였습니다.
subnet_1, subnet_2는 Private 망으로 구축되어 있으며, 구성도에 나와있지 않은 Subnet_3을 Public 망으로 구축하여 bastion 서버를 통해 접속했습니다.
AWS의 XKS 기능을 사용하려면 xks-proxy 서버에 AWS가 인정하는 CA에서 발급받은 인증서가 적용되어 있어야 합니다. 이 부분은 쉽게 해결할 수 없기 때문에 Hashicorp의 가이드에서는 ngork를 사용합니다. ngrok는 로컬에서 실행되고 있는 서비스를 ngrok가 가지고 있는 인증서를 사용하여 TLS 통신을 해주기 때문에 이 문제를 해결할 수 있습니다.
하지만 ngrok를 사용하면 다른 문제가 발생하게 됩니다. XKS 기능을 사용하려면 35ms 안에 통신이 완료되야 하는데, ngrok는 기본적으로 100ms의 대기시간을 가지고 있기 때문에 제한시간 내에 통신이 불가능하여 에러가 발생하게 됩니다.
그래서 ngrok 대신 AWS의 NLB를 사용하여 proxy 기능을 대체하였습니다. NLB에는 AWS의 ACM으로 생성한 인증서를 적용시킬 수 있고, 대기시간도 길지 않아서 에러 없이 사용할 수 있었습니다.
vault secrets enable kmip
KMIP secret engine을 사용하기 위해 활성화합니다.
vault write kmip/config listen_addrs=0.0.0.0:5696 server_ips=<Vault 서버 IP 주소> server_hostnames=<Vault 서버 Domain 주소>
listen_addrs
: 수신받을 주소입니다.server_ips
: KMIP를 활용해 생성할 TLS 인증서에 SAN IP 주소로 포함할 IP 목록입니다.server_hostnames
: KMIP를 활용해 생성할 TLS 인증서에 SAN DNS 이름으로 포함할 Domain 목록입니다.인증서를 사용하여 Vault 서버를 구동했을 경우 server_hostnames
를, 인증서를 사용하지 않았을 경우 server_ips
를 입력하시면 됩니다.
vault write -f kmip/scope/my-service
액세스할 수 있는 AES 키를 포함할 KMIP scope를 생성합니다.
vault write kmip/scope/my-service/role/admin operation_all=true
scope에 대한 액세스 권한이 있는 KMIP role을 생성합니다.
vault write -f -format=json kmip/scope/my-service/role/admin/credential/generate | tee kmip.json
KMIP role에 대한 인증서를 생성하여 파일로 추출합니다.
jq --raw-output --exit-status '.data.ca_chain[]' kmip.json > ca.pem
jq --raw-output --exit-status '.data.certificate' kmip.json > cert.pem
ca.pem
과 cert.pem
을 kmip.json
에서 추출합니다. ca.pem
과 cert.pem
은 xks proxy 서버에서 검증할 때 사용됩니다. 그러므로, xks proxy 서버로 파일을 전송합니다.
scp ca.pem cert.pem <xks proxy 서버>
여기까지 Vault 서버에서의 작업은 모두 끝났습니다. 나머지 작업은 xks proxy 서버에서 진행됩니다.
Cargo는 Rust 프로그래밍 언어의 공식 의존성 관리 및 빌드 도구로, 패키지 관리와 프로젝트 빌드 프로세스를 효율적으로 빌드 및 배포할 수 있도록 도와주는 도구입니다. 여기서는 Cargo를 이용하여 Vault와 AWS의 KMS와의 통신을 위한 서비스를 올릴 예정입니다.
sudo -i
이유를 아직 파악하지 못했지만, Cargo를 설치할 때 root
로 직접 접속하여 설치하지 않으면, 실행이 안되는 문제가 발생해서 Cargo를 설치, 실행할 때만 root
유저로 진행합니다. (문제가 해결되면 업데이트 하겠습니다.)
yum install gcc
cargo를 빌드하기 위해 gcc를 설치합니다.
curl https://sh.rustup.rs -sSf | sh
curl을 사용하여 cargo를 설치할 스크립트를 가져와 실행합니다.
1) Proceed with installation (default)
2) Customize installation
3) Cancel installation
1번을 선택하여 cargo를 설치합니다.
cp /root/.cargo/bin/cargo /usr/bin/cargo
설치가 완료되면 root
유저의 홈경로에 .cargo
라는 디렉토리가 생성됩니다. 디렉토리 내부에 있는 Cargo를 복사하여 전역에서 사용할 수 있도록 설정합니다.
Vault PKCS11 Provider는 Vault를 다른 PKCS11 장치처럼 취급하여 키, 개체를 관리하고 PKCS11 호출을 사용하여 Vault에서 암호화 및 암호 해독을 수행할 수 있습니다. Vault의 KMIP에 연결되어 암호화 작업 및 객체 스토리지를 제공합니다.
wget https://releases.hashicorp.com/vault-pkcs11-provider/0.2.0/vault-pkcs11-provider_0.2.0_linux-el7_amd64.zip
https://releases.hashicorp.com/vault-pkcs11-provider 에서 버전을 선택하여 설치할 수 있습니다.
unzip vault-pkcs11-provider_0.2.0_linux-el7_amd64.zip
다운받은 zip 파일의 압축을 해제합니다.
mv libvault-pkcs11.so /usr/local/lib/
압축 해제한 Provider를 실행 가능하도록 하기 위해 위치를 옮겨줍니다.
git clone https://github.com/aws-samples/aws-kms-xks-proxy
Cargo를 사용하여 서비스를 올리기 위해 aws-mks-xks-proxy Repository를 가져옵니다.
cd aws-kms-xks-proxy/xks-axum
작업을 수행하기 위해 해당 위치로 이동합니다.
vi ./configuration/settings.toml
Cargo가 읽어야 할 toml 파일을 생성합니다. 기존에 있던 settings.toml
의 내용을 지우고 작성합니다.
[server]
ip = "0.0.0.0"
port = 8000
region = "ap-northeast-2"
service = "kms-xks-proxy"
[server.tcp_keepalive]
tcp_keepalive_secs = 60
tcp_keepalive_retries = 3
tcp_keepalive_interval_secs = 1
[tracing]
is_stdout_writer_enabled = true
is_file_writer_enabled = true
level = "DEBUG"
directory = "/var/local/xks-proxy/logs"
file_prefix = "xks-proxy.log"
rotation_kind = "HOURLY"
[security]
is_sigv4_auth_enabled = true
is_tls_enabled = false
is_mtls_enabled = false
[tls]
tls_cert_pem = "tls/server_cert.pem"
tls_key_pem = "tls/server_key.pem"
mtls_client_ca_pem = "tls/client_ca.pem"
mtls_client_dns_name = "ap-northeast-2.alpha.cks.kms.aws.internal.amazonaws.com"
[[external_key_stores]]
uri_path_prefix = "/xyz"
sigv4_access_key_id = "AKIA4GBY3I6JCE5M2HPM"
sigv4_secret_access_key = "1234567890123456789012345678901234567890123="
xks_key_id_set = ["abc123"]
[pkcs11]
session_pool_max_size = 30
session_pool_timeout_milli = 0
session_eager_close = false
user_pin = ""
PKCS11_HSM_MODULE = "/usr/local/lib/libvault-pkcs11.so"
context_read_timeout_milli = 100
[limits]
max_plaintext_in_base64 = 8192
max_aad_in_base64 = 16384
[hsm_capabilities]
can_generate_iv = false
is_zero_iv_required = false
중요한 설정에 대한 설명은 아래와 같습니다.
is_tls_enabled
: xks proxy 서버가 TLS를 필요로 할 경우, 활성화합니다. tls_cert_pem
: is_tls_enabled
가 true일 경우에 적용되며, 인증서의 경로를 입력합니다.tls_key_pem
: is_tls_enabled
가 true일 경우에 적용되며, 인증서에 해당하는 개인키의 경로를 입력합니다.mtls_client_ca_pem
: is_mtls_enabled
가 true일 경우에 적용되며, mTLS 통신 대상의 CA 인증서의 경로를 입력합니다.mtls_client_dns_name
: is_mtls_enabled
가 true일 경우에 적용되며, mTLS 통신 대상의 Domain을 입력합니다.uri_path_prefix
: URI 경로의 접두사를 지정합니다.sigv4_access_key_id
: xks proxy 서버의 Access Key를 지정합니다. AWS에서 xks proxy 서버와 상호작용을 하기 위해 사용됩니다.sigv4_secret_access_key
: xks proxy 서버의 Secret Key를 지정합니다. AWS에서 xks proxy 서버와 상호작용을 하기 위해 사용됩니다.xks_key_id_set
: xks proxy 서버에 적용할 수 있는 Key 레이블을 지정합니다.PKCS11_HSM_MODULE
: Vault PKCS11 Provider의 위치를 지정합니다.export XKS_PROXY_SETTINGS_TOML=<이전 경로>/aws-kms-xks-proxy/xks-axum/configuration/settings_vault.toml
환경 변수로 toml 파일을 지정해줍니다.
XKS_PROXY_SETTINGS_TOML=<이전 경로>/aws-kms-xks-proxy/xks-axum/configuration/settings_vault.toml sudo cargo run
settings_vault.toml
를 적용시킨 Cargo 서비스를 실행합니다.
2023-08-16T07:33:05.920971Z INFO main xks_proxy: 276: Tracing level="DEBUG" is_file_writer_enabled=true
2023-08-16T07:33:05.921137Z INFO main xks_proxy: 278: Tracing file rotation_kind="HOURLY"
2023-08-16T07:33:05.921172Z INFO main xks_proxy: 67: Starting service="kms-xks-proxy" region="ap-northeast-2"
2023-08-16T07:33:05.922107Z INFO tokio-runtime-worker xks_proxy: 195: http://0.0.0.0:80/ping available for health check
2023-08-16T07:33:05.923144Z INFO tokio-runtime-worker xks_proxy: 113: is_sigv4_enabled=true is_tls_enabled=false is_mtls_enabled=false secondary_auth=None
2023-08-16T07:33:05.923372Z INFO tokio-runtime-worker xks_proxy: 133: Ciphertext Metadata is not configured.
2023-08-16T07:33:05.923431Z INFO tokio-runtime-worker xks_proxy: 144: TCP Keepalive secs=Some(60s) interval_secs=Some(1s) retries=Some(3)
2023-08-16T07:33:05.923478Z INFO tokio-runtime-worker xks_proxy: 149: v3.1.2-unknown listening on 0.0.0.0:8000 for traffic
성공적으로 Cargo 서비스가 실행되었습니다.
git clone https://github.com/aws-samples/aws-kms-xks-proxy.git
xks-proxy의 rpm 파일을 생성하기 위해 Repository를 가져옵니다.
sudo yum install rpm-build
sudo yum install cargo
make를 사용하기 위한 패키지를 설치합니다.
cd ./aws-kms-xks-proxy
GitHub Repository 폴더로 이동합니다.
make
빌드를 진행합니다.
Requires: /usr/bin/env ld-linux-x86-64.so.2()(64bit) ld-linux-x86-64.so.2(GLIBC_2.3)(64bit) libc.so.6()(64bit) libc.so.6(GLIBC_2.10)(64bit) libc.so.6(GLIBC_2.14)(64bit) libc.so.6(GLIBC_2.2.5)(64bit) libc.so.6(GLIBC_2.3)(64bit) libc.so.6(GLIBC_2.3.2)(64bit) libc.so.6(GLIBC_2.3.4)(64bit) libc.so.6(GLIBC_2.4)(64bit) libc.so.6(GLIBC_2.7)(64bit) libc.so.6(GLIBC_2.9)(64bit) libdl.so.2()(64bit) libdl.so.2(GLIBC_2.2.5)(64bit) libgcc_s.so.1()(64bit) libgcc_s.so.1(GCC_3.0)(64bit) libgcc_s.so.1(GCC_3.3)(64bit) libgcc_s.so.1(GCC_4.2.0)(64bit) libm.so.6()(64bit) libm.so.6(GLIBC_2.2.5)(64bit) libpthread.so.0()(64bit) libpthread.so.0(GLIBC_2.12)(64bit) libpthread.so.0(GLIBC_2.2.5)(64bit) librt.so.1()(64bit) librt.so.1(GLIBC_2.2.5)(64bit) rtld(GNU_HASH)
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/gweowe/rpmbuild/BUILDROOT
작성: /home/gweowe/rpmbuild/SRPMS/xks-proxy-3.1.2-0.el7.src.rpm
작성: /home/gweowe/rpmbuild/RPMS/x86_64/xks-proxy-3.1.2-0.el7.x86_64.rpm
ln -s /home/gweowe/rpmbuild/RPMS/x86_64/xks-proxy-3.1.2-0.el7.x86_64.rpm xks-axum/target/release/aws-kms-xks-proxy.rpm
INFO: No command alien found
빌드가 정상적으로 완료되면 위와 같은 Output이 출력됩니다.
cd ../rpmbuild/RPMS/x86_64
make를 사용하여 생성된 rpm 파일의 위치로 이동합니다.
sudo yum install xks-proxy-3.1.2-0.el7.x86_64.rpm
xks-proxy를 설치합니다.
sudo vi /etc/systemd/system/xks-proxy.service
서비스 파일을 확인합니다.
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
[Unit]
Description=AWS External Keystore (XKS) Proxy Service
[Service]
# Specifies the configuration file for rust-xks-proxy
Environment=XKS_PROXY_SETTINGS_TOML=/var/local/xks-proxy/.secret/settings.toml
# Prints a backtrace to stderr whenever a panic occurs
Environment=RUST_BACKTRACE=1
# You can use the following environment variables to override the PKCS11 related
# configurations
# Specifies the file path to the PKCS#11 library.
# Environment=PKCS11_HSM_MODULE=/local/centos/pkcs11-logger/build/linux/pkcs11-logger-x64.so
# https://github.com/Pkcs11Interop/pkcs11-logger
# Path to the original PKCS#11 library.
# Environment=PKCS11_LOGGER_LIBRARY_PATH=/usr/safenet/lunaclient/lib/libCryptoki2_64.so
# Path to the pkcs11-logger log file.
# Environment=PKCS11_LOGGER_LOG_FILE_PATH=/var/local/xks-proxy/logs/pkcs11-logger-output.log
# Specifies a bit mask that controls multiple pkcs11-logger features.
# Environment=PKCS11_LOGGER_FLAGS=0
ExecStart=/usr/sbin/xks-proxy
[Install]
WantedBy=multi-user.target
9번 Line에 있는 Environment=XKS_PROXY_SETTINGS_TOML
의 값에 toml 파일의 위치를 지정합니다.
sudo vi /var/local/xks-proxy/.secret/settings.toml
xks-proxy가 읽어야 할 toml 파일을 생성합니다. 예제에서는 서비스 파일에 작성되어있는 위치대로 진행합니다.
[server]
ip = "0.0.0.0"
port = 8000
region = "ap-northeast-2"
service = "kms-xks-proxy"
[server.tcp_keepalive]
tcp_keepalive_secs = 60
tcp_keepalive_retries = 3
tcp_keepalive_interval_secs = 1
[tracing]
is_stdout_writer_enabled = true
is_file_writer_enabled = true
level = "DEBUG"
directory = "/var/local/xks-proxy/logs"
file_prefix = "xks-proxy.log"
rotation_kind = "HOURLY"
[security]
is_sigv4_auth_enabled = true
is_tls_enabled = false
is_mtls_enabled = false
[tls]
tls_cert_pem = "tls/server_cert.pem"
tls_key_pem = "tls/server_key.pem"
mtls_client_ca_pem = "tls/client_ca.pem"
mtls_client_dns_name = "ap-northeast-2.alpha.cks.kms.aws.internal.amazonaws.com"
[[external_key_stores]]
uri_path_prefix = "/xyz"
sigv4_access_key_id = "AKIA4GBY3I6JCE5M2HPM"
sigv4_secret_access_key = "1234567890123456789012345678901234567890123="
xks_key_id_set = ["abc123"]
[pkcs11]
session_pool_max_size = 30
session_pool_timeout_milli = 0
session_eager_close = false
user_pin = ""
PKCS11_HSM_MODULE = "/usr/local/lib/libvault-pkcs11.so"
context_read_timeout_milli = 100
[limits]
max_plaintext_in_base64 = 8192
max_aad_in_base64 = 16384
[hsm_capabilities]
can_generate_iv = false
is_zero_iv_required = false
toml 파일의 설정 내용은 [1] Cargo 이용하여 서버 구축하기
에 기재하였습니다.
Vault PKCS11 Provider는 Vault를 다른 PKCS11 장치처럼 취급하여 키, 개체를 관리하고 PKCS11 호출을 사용하여 Vault에서 암호화 및 암호 해독을 수행할 수 있습니다. Vault의 KMIP에 연결되어 암호화 작업 및 객체 스토리지를 제공합니다.
wget https://releases.hashicorp.com/vault-pkcs11-provider/0.2.0/vault-pkcs11-provider_0.2.0_linux-el7_amd64.zip
https://releases.hashicorp.com/vault-pkcs11-provider 에서 버전을 선택하여 설치할 수 있습니다.
unzip vault-pkcs11-provider_0.2.0_linux-el7_amd64.zip
다운받은 zip 파일의 압축을 해제합니다.
mv libvault-pkcs11.so /usr/local/lib/
압축 해제한 Provider를 실행 가능하도록 하기 위해 위치를 옮겨줍니다.
sudo systemctl start xks-proxy.service
xks-proxy 서버를 실행합니다.
XKS Proxy 서버에서 진행합니다.
Vault PKCS11 Provider를 사용하기 위해서는 pkcs11-tool이 필요합니다. 이를 포함하고 있는 opensc라는 패키지를 설치합니다.
sudo yum install opensc
sudo vi /etc/vault-pkcs11.hcl
Vault PKCS11 Provider를 실행하기 위해 hcl 파일을 작성합니다.
slot {
server = "<Vault 서버 주소>:5696"
tls_cert_path = "<KMIP로 생성한 cert.pem의 경로>"
ca_path = "<KMIP로 생성한 ca.pem의 경로>"
scope = "my-service"
}
Vault 서버에서 server_ips
혹은 server_hostnames
로 지정한 Vault 서버의 주소와 KMIP로 생성한 인증서 파일의 경로를 지정합니다.
pkcs11-tool --module /usr/local/lib/libvault-pkcs11.so --keygen -a abc123 --key-type AES:32
Vault의 KMIP 엔진에 Key Set을 생성하기 위해 명령어를 수행합니다. 여기서 abc123
은 settings_vault.toml
에서 지정한 xks_key_id_set
의 Key 레이블 입니다.
Key generated:
Secret Key Object; AES length 32
VALUE: 81519316b15fb834fbeb04f8a59fd7d0d78152b86775699c778e84efb635bf7c
label: abc123
Usage: encrypt, decrypt, wrap, unwrap
성공적으로 Key가 Generated 되었음을 확인할 수 있습니다.
pkcs11-tool --module /usr/local/lib/libvault-pkcs11.so --list-objects
위와 같은 명령어를 사용하여 생성된 Key Set을 확인할 수 있습니다.
pkcs11-tool --module /usr/local/lib/libvault-pkcs11.so --delete-object --type secrkey --label abc123
위와 같은 명령어를 사용하여 생성한 Key를 삭제할 수 있습니다.
XKS Proxy 서버에서 진행합니다. 이 테스트는 AWS 콘솔에서 진행해도 무관합니다.
aws kms create-custom-key-store \
--custom-key-store-name <xks 키 스토어 이름> \
--custom-key-store-type EXTERNAL_KEY_STORE \
--xks-proxy-connectivity PUBLIC_ENDPOINT
--xks-proxy-uri-endpoint https://<NLB Domain 주소> \
--xks-proxy-uri-path <toml 파일에 작성되어 있는 uri path>/kms/xks/v1 \
--xks-proxy-authentication-credential AccessKeyId=<toml 파일에 작성되어 있는 Access Key>,RawSecretAccessKey=<Toml 파일에 작성되어 있는 Secret Access Key>
CLI 명령어를 사용하여 키 스토어를 생성합니다.
{
"CustomKeyStoreId": "cks-bc2f81082b298d68d"
}
생성된 키 스토어의 아이디가 Output으로 출력됩니다.
aws kms connect-custom-key-store --custom-key-store-id cks-bc2f81082b298d68d
생성한 키 스토어와 연결합니다. 이 작업은 최대 5분정도 소요될 수 있습니다.
aws kms describe-custom-key-stores --custom-key-store-id cks-bc2f81082b298d68d
어느정도 시간이 지난 후에 연결이 되었는지 확인해볼 수 있습니다.
{
"CustomKeyStores": [
{
<키 스토어에 대한 정보 출력>
}
}
]
}
위와 같은 Output이 출력되면 정상적으로 연결이 된 것입니다.
aws kms create-key --custom-key-store-id cks-bc2f81082b298d68d --xks-key-id <toml 파일에 작성되어 있는 키 ID> --origin EXTERNAL_KEY_STORE
{
"KeyMetadata": {
<키에 대한 정보 출력>
}
}
위와 같은 Output이 출력되면 정상적으로 연결이 된 것입니다.
aws kms encrypt --key-id <생성된 키의 ID> --plaintext <base64 테스트용 텍스트 입력>
{
"CiphertextBlob": "<암호화된 테스트용 텍스트>",
"KeyId": "<키 ID>",
"EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
성공적으로 암호화가 되었음을 확인할 수 있습니다.
aws kms decrypt --ciphertext-blob <암호화된 테스트용 텍스트>
{
"KeyId": "<키 ID>",
"Plaintext": "<테스트용 텍스트>",
"EncryptionAlgorithm": "SYMMETRIC_DEFAULT"
}
키를 사용하여 복호화까지 되었음을 확인할 수 있습니다.