Skip to content

Commit

Permalink
Merge pull request #23 from nhnacademy-be6-5ritang/feature/config
Browse files Browse the repository at this point in the history
feat: Secure Key Manager 추가
  • Loading branch information
dlrudgjs104 authored Aug 1, 2024
2 parents 6c32ff3 + ffab8da commit 0d329c3
Show file tree
Hide file tree
Showing 14 changed files with 426 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
Expand All @@ -10,6 +11,7 @@
@EnableWebSecurity
@EnableFeignClients
@EnableDiscoveryClient
@ConfigurationPropertiesScan
public class BookStoreAccountApplication {

public static void main(String[] args) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,24 +10,25 @@
import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.nhnacademy.bookstoreaccount.keymanager.property.RedisProperty;
import com.nhnacademy.bookstoreaccount.keymanager.service.KeyManagerService;

import lombok.RequiredArgsConstructor;

@Configuration
@RequiredArgsConstructor
public class RedisConfig {
@Value("${spring.data.redis.host}")
private String host;
@Value("${spring.data.redis.port}")
private int port;
@Value("${spring.data.redis.password}")
private String password;
@Value("${spring.data.redis.database}")
private int database;
private final RedisProperty redisProperty;
private final KeyManagerService keyManagerService;

@Bean
public RedisConnectionFactory redisConnectionFactory() {
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
redisStandaloneConfiguration.setPassword(password);
redisStandaloneConfiguration.setDatabase(database);
redisStandaloneConfiguration.setHostName(keyManagerService.getSecret(redisProperty.getHost()));
redisStandaloneConfiguration.setPort(Integer.parseInt(keyManagerService.getSecret(redisProperty.getPort())));
redisStandaloneConfiguration.setPassword(keyManagerService.getSecret(redisProperty.getPassword()));
redisStandaloneConfiguration.setDatabase(
Integer.parseInt(keyManagerService.getSecret(redisProperty.getDatabase())));
return new LettuceConnectionFactory(redisStandaloneConfiguration);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.nhnacademy.bookstoreaccount.global.config;

import java.nio.charset.StandardCharsets;

import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.web.client.RestTemplate;

@Configuration
public class RestTemplateConfig {

@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.nhnacademy.bookstoreaccount.global.config;
package com.nhnacademy.bookstoreaccount.global.util;

import java.util.HashMap;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.nhnacademy.bookstoreaccount.keymanager.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
public class KeyResponse {
private Header header;
private Body body;

@Getter
@NoArgsConstructor
public static class Body {
private String secret;
}

@Getter
@NoArgsConstructor
public static class Header {
private Integer resultCode;
private String resultMessage;
private boolean isSuccessful;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.nhnacademy.bookstoreaccount.keymanager.property;

import org.springframework.boot.context.properties.ConfigurationProperties;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
@ConfigurationProperties("oritang.redis")
public class RedisProperty {
private String host;
private String port;
private String password;
private String database;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package com.nhnacademy.bookstoreaccount.keymanager.service;

import java.util.List;

import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;

import com.nhnacademy.bookstoreaccount.keymanager.dto.KeyResponse;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

/**
* @author 이경헌
* KeyManager는 인증서를 사용하여 클라우드에서 기밀 데이터를 가져오는 클래스입니다.
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class KeyManagerService {
private final RestTemplate restTemplate;

/**
* 인증서를 사용하여 클라우드에서 기밀 데이터를 가져오는 메서드
*
* @param keyId 조회를 원하는 데이터의 Key value
* @return key value 에 해당하는 데이터
*/
public String getSecret(String keyId) {
try {
// HTTP 요청을 위한 헤더 설정
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.setAccept(List.of(MediaType.APPLICATION_JSON));
headers.set("X-TC-AUTHENTICATION-ID", "3bTA7VD3xkZzLXPnt31X");
headers.set("X-TC-AUTHENTICATION-SECRET", "WuXXhpYwgdSoE3mY");

// URI 생성
String url =
"https://api-keymanager.nhncloudservice.com/keymanager/v1.2/appkey/2SxwmBzUfnqJaA2A/secrets/" + keyId;

// HttpEntity를 사용하여 헤더 포함
HttpEntity<String> entity = new HttpEntity<>(headers);

// 데이터 요청 및 반환
ResponseEntity<KeyResponse> responseEntity = restTemplate.exchange(url, HttpMethod.GET, entity,
KeyResponse.class);
KeyResponse responseBody = responseEntity.getBody();
if (responseBody != null) {
return responseBody.getBody().getSecret();
} else {
log.error("응답 본문이 비어있습니다.");
return null;
}
} catch (Exception e) {
log.error("키매니저 에러: {}", e.getMessage());
return null;
}
}
}
13 changes: 6 additions & 7 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,6 @@ spring:
profiles:
active: dev

data:
redis:
host: 133.186.241.167
port: 6379
password: ${REDIS_PASSWORD}
database: 24

jwt:
secret: 33085d3cf14717d13f122979944e1e886636cc19e0f749dfbce423bb0031beae8b8758243ce109259c32a83a9325ef2c64433bc0339321d9540036a11884f8fc
access-token:
Expand All @@ -41,3 +34,9 @@ logging:
com.netflix.discovery: ERROR
com.netflix.eureka: ERROR

oritang:
redis:
host: 4c4a07c6d57c41cba23cf12de6c87559
port: 0de6a31493de4282830c2dab83e7264c
password: d9b767e1131b4434b75d1fb52444ee1e
database: 63bd8d5b9fe046058cedd2feb66fc253
2 changes: 1 addition & 1 deletion src/main/resources/logback-spring.xml
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@

<!-- LogNCrashAppender 추가 -->
<springProfile name="prod">
<appender name="LOG_N_CRASH" class="com.nhnacademy.bookstoreaccount.global.config.LogNCrashAppender"/>
<appender name="LOG_N_CRASH" class="com.nhnacademy.bookstoreaccount.global.util.LogNCrashAppender"/>

<root level="info">
<appender-ref ref="LOG_N_CRASH"/>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.nhnacademy.bookstoreaccount.global;

import static org.mockito.Mockito.*;

import java.util.HashMap;
import java.util.Map;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;

import com.nhnacademy.bookstoreaccount.global.util.LogNCrashAppender;

import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;

class LogNCrashAppenderTest {

@Mock
private RestTemplate restTemplate;

@InjectMocks
private LogNCrashAppender logNCrashAppender;

@BeforeEach
void setUp() {
MockitoAnnotations.openMocks(this);

// Create a LoggerContext
LoggerContext loggerContext = new LoggerContext();
logNCrashAppender.setContext(loggerContext);
logNCrashAppender.start();
}

@Test
void testAppend() {
ILoggingEvent loggingEvent = mock(ILoggingEvent.class);

when(loggingEvent.getFormattedMessage()).thenReturn("Test log message");
when(loggingEvent.getLevel()).thenReturn(ch.qos.logback.classic.Level.INFO);

Map<String, Object> logData = new HashMap<>();
logData.put("projectName", "Xyx7DoyszcG66ULx");
logData.put("projectVersion", "1.0.0");
logData.put("logVersion", "v2");
logData.put("body", loggingEvent.getFormattedMessage());
logData.put("logSource", "http");
logData.put("logType", "log");
logData.put("platform", "5ritang-Gateway");
logData.put("host", "192.168.0.75");
logData.put("logLevel", loggingEvent.getLevel().toString());

when(restTemplate.postForEntity("https://api-logncrash.nhncloudservice.com/v2/log", logData, String.class)).thenReturn(new ResponseEntity<>(HttpStatus.OK));

logNCrashAppender.doAppend(loggingEvent);

}

}
Loading

0 comments on commit 0d329c3

Please sign in to comment.