맨땅에 헤딩하는 개바른자

[런타임X] 리얼타임 Vault 값 사용하기 본문

개발 TIP

[런타임X] 리얼타임 Vault 값 사용하기

앵낄낄 2023. 5. 16. 21:30
반응형

서론

본 포스팅에 앞서 Vault가 이미 구성 된 프로젝트에서 리얼타임으로 Vault의 값을 사용하기위한 가이드 내용으로 Vault구성이 궁금하시다면 다른 포스팅글을 참고 부탁드립니다.

Vault 소개와 기본 개념 이해하기

Vault는 현대적인 시스템 및 애플리케이션의 비밀 관리를 위한 도구로 널리 사용되고 있습니다. 그러나 Vault의 설정은 복잡하고 다양한 요소가 있어 초보자에게는 약간의 어려움을 줄 수 있습니다. 이 포스팅은 Java SpringBoot 기반의 프로젝트에서 Vault 설정을 실시간으로 구성하는 방법에 대한 포괄적인 가이드를 제공하여 사용자가 보다 간편하게 Vault를 사용할 수 있도록 도와줍니다.

  • Vault의 기본 개념 및 원리 소개
    1. 시크릿(Secrets): Vault는 시크릿(암호, 키, 패스워드 등)의 안전한 저장소로 작동합니다. 이러한 시크릿은 보안 관련 정보로 사용되며, Vault를 통해 안전하게 액세스 및 관리됩니다.
    2. 액세스 제어(Access Control): Vault는 각 시크릿에 대한 액세스 제어를 제공합니다. 엄격한 액세스 규칙을 설정하여 허가되지 않은 액세스로부터 시크릿을 보호합니다. 이를 통해 보안을 강화하고 시크릿에 대한 액세스 권한을 관리할 수 있습니다.
    3. 시크릿 엔진(Secret Engine): Vault는 시크릿 엔진을 사용하여 시크릿을 생성, 저장 및 관리합니다. 시크릿 엔진은 다양한 유형의 시크릿(예: 데이터베이스 암호, 클라이언트 API 키 등)을 지원하며, 암호화 및 복호화 작업을 수행합니다.
    4. 인증(Authentication): Vault는 클라이언트의 신원을 확인하기 위해 다양한 인증 방법을 지원합니다. 사용자 이름과 비밀번호, 액세스 토큰, 클라이언트 인증서 등을 사용하여 클라이언트의 인증을 처리할 수 있습니다.
    5. 암호화 및 복호화(Encryption and Decryption): Vault는 저장된 시크릿을 안전하게 암호화하여 저장합니다. 클라이언트가 시크릿을 요청하면 Vault는 암호화를 해제하여 시크릿을 제공합니다. 이를 통해 보안성을 유지하면서도 필요에 따라 시크릿을 사용할 수 있습니다.
  • VaultTemplate 소개
    1. VaultTemplate는 RestTemplate를 사용하여 Vault 서버에 인증/인가를 걸친 후 데이터를 조회할 수 있는 기능을 제공합니다.
    2. API기능을 통해 Vault의 Key/Vaule 데이터를 응답받을 수 있습니다.

 

학습예제

https://github.com/ymwoo88/vault.git

 

학습목표

  • 연동 된 vault정보를 갱신된 최신데이터를 실시간 사용하기
  • 연동방식
    • (미선택) AbstractVaultConfiguration 상속한 설정파일을 통해 이벤트리스터를 통한 실시간 동기화 된 데이터 사용
      • 특정 springboot 버전에서 방식이 대거 변경 되어 버전에 따라 방식이 다양한 단점이 존재
    • (선택) AbstractVaultConfiguration 상속한 설정파일을 통해 VaultTemplate를 통한 실시간 통신으로 최신 데이터 사용
      • VaultTemplate에 필요한 property만주입하면되어 여러 환경에서 쉽게 구성할 수 있는 장점이 존재

 

구성하기 앞서 Vault사용 환경

  • vault UI 셋팅

  • 인증
    • 프로세스에서는 ROLE_ID, SECRET_ID를 사용한 AppRole인증을 사용
  • Vault의 경우 API기능이 제공되며 API 기능에 다라 응답데이터를 받을 수 있습니다.
    • 로그인 curl 테스트
// [REQUEST]
curl --location --request POST '<https://test.ymwoo.com:8200/v1/auth/approle/login>' \\
--header 'Content-Type: application/json' \\
--data-raw '{
    "role_id" : "some..",
    "secret_id" : "some.."
}'


// [RESPONSE]
{
    "request_id": "123123-32da-123-86b7-123123123",
    "lease_id": "",
    "renewable": false,
    "lease_duration": 0,
    "data": null,
    "wrap_info": null,
    "warnings": null,
    "auth": {
        "client_token": "s.asdasdasdasdasd",
        "accessor": "zzz",
        "policies": [
            "app-fnd",
            "default"
        ],
        "token_policies": [
            "app-fnd",
            "default"
        ],
        "metadata": {
            "role_name": "appfnd"
        },
        "lease_duration": 2764800,
        "renewable": true,
        "entity_id": "123123123-c4bf-1233-89d7-e10af9ce58df",
        "token_type": "service",
        "orphan": true
    }
}
  • 데이터 호출 curl 테스트
    1. 추가 param으로 version이란게 존재하는데 이것은 vault설정이 반영 될 때 마다 version이 자동으로 업데이트가 됩니다.
    2. 예를들어 현재 vault 설정 버전이 version=5라고하면
      • {url}?version=3 으로 param을 추가하게되면 이전이력의 version=3의 데이터가 호출 됩니다.
      • 저는 해당 param을 사용하지않고 호출 시 항상 최신version의 vault 값을 가져오게 하였습니다.
// [REQUEST]
curl --location --request GET '<https://test.ymwoo.com:8200/v1/app-kv/data/ymwoo/api/vault>' \\
--header 'X-Vault-Token: s.asdasdasdasdasd' // 헤더에 로그인에서 응답받았던 client_token사용



// [RESPONSE]
{
    "request_id": "123123-c3f4-0b86-70ea-123123123",
    "lease_id": "",
    "renewable": false,
    "lease_duration": 0,
    "data": {
        "data": {
            "fnd.database": "zzzzzzzzz",
            "fnd_database": "바뀌었당",
            "password": "pass123",
            "username": "aasdfsdfasdfasdfasdf#123"
        },
        "metadata": {
            "created_time": "2023-05-10T16:45:13.471923859Z",
            "custom_metadata": null,
            "deletion_time": "",
            "destroyed": false,
            "version": 10
        }
    },
    "wrap_info": null,
    "warnings": null,
    "auth": null
}

 

코드 구성하기

  • vault설정 시 추가되었던 dependency가 추가되어있어야 한다.
<dependency>
	<groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-vault-config</artifactId>
</dependency>
  • application.yml 파일에 Vault 정보 기입
spring:
  cloud:
    vault:
      app-role:
        app-role-path: approle
        role: appfnd
        role-id: ${VAULT_ROLE_ID}
        secret-id: ${VAULT_SECRET_ID}
      uri: https://{url}:8200
  • VaultTemplate를 사용하기위한 VaultConfiguration 등록
    1. AbstractVaultConfiguration 상속받아 vaultEndpoint와 clientAuthentication을 작성합니다.
    2. 위 2메소드를 작성하기위해 VaultProperties를 주입받아서 사용합니다.
    3. 해당 데이터는 application.yml에 작성한 데이터를 읽어오게 됩니다.
@Configuration
public class VaultConfiguration extends AbstractVaultConfiguration {

    private final VaultProperties vaultProperties;

    public VaultConfiguration(VaultProperties vaultProperties) {
        this.vaultProperties = vaultProperties;
    }

    @Override
    public VaultEndpoint vaultEndpoint() {
        try {
            return VaultEndpoint.from(new URI(Objects.requireNonNull(vaultProperties.getUri())));
        } catch (URISyntaxException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public ClientAuthentication clientAuthentication() {
        VaultProperties.AppRoleProperties appRole = vaultProperties.getAppRole();
        AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
                .appRole(appRole.getRole())
                .roleId(AppRoleAuthenticationOptions.RoleId.provided(Objects.requireNonNull(appRole.getRoleId())))
                .secretId(AppRoleAuthenticationOptions.SecretId.provided(Objects.requireNonNull(appRole.getSecretId())))
                .build();
        return new AppRoleAuthentication(options, restOperations());
    }
}
  • VaultTemplate를 주입받아 데이터 응답받기
    1. 응답 Dto는 cUrl로 응답받은 샘플을 통해서 작성하였습니다.
    2. 쩜(.)이나 _언더바로 구분된 key명은 @JsonAlias로 설정하여 주입받았습니다.
    3. 꼭 사용에 필요한 데이터만 변수로 선언하여 사용합니다.
      1. password는 제외
@Slf4j
@RestController
@RequiredArgsConstructor
public class VaultController {

    private final VaultTemplate vaultTemplate;

    @GetMapping("webjars/test")
    public VaultDTO test() {
        VaultDTO result = null;
        try {
            VaultResponseSupport<VaultDTO> response = vaultTemplate.read("app-kv/data/fnd/api/vault", VaultDTO.class);
            result = response.getData();
            log.info("Retrieved data {} from Vault", result);
        } catch (Exception e) {
            log.error("error = {}", e.getMessage());
        }
        return result;
    }

    @Getter
    @Setter
    @ToString
    public static class VaultDTO {
        private Data data;
    }

    @Getter
    @Setter
    @ToString
    public static class Data {
        private String username;
        @JsonAlias("fnd.database")
        private String fndDatabase;
    }
}
  • 결과
[
    {
        "data": {
            "username": "aasdfsdfasdfasdfasdf#123",
            "fndDatabase": "zzzzzzzzz"
        }
    }
]

 

맺음말

이 포스팅을 통해 실시간 Vault 값을 참조하여 사용할 수 있도록 하였습니다. 보안이 중요한 기능이다보니 너무 남용하면 오히려 보안에 취약해지고 Vault를 도입한 취지가 무용지물되는 상황이 올 수 있습니다.

저 같은 경우는 applicaiotn.yml에 설정 된 항목이 보안상 민감하진 않지만 변경이 자주 발생되다보니 서비스 배포를 통해서 적용하는 케이스를 줄일 수 있는 방법이 없을까 생각하다가 고안해낸 방안입니다.

글을 보시는 분들도 업무에 유용하게 사용하셨으면 좋겠습니다.

감사합니다.

반응형