💻문제점
공식 사이트를 참고했다.
1. Docker-Compost 파일 작성
.env
ELASTIC_PASSWORD=elastic
KIBANA_PASSWORD=kibana_system
STACK_VERSION=8.7.1
CLUSTER_NAME=docker-cluster
LICENSE=basic
ES_PORT=9200
KIBANA_PORT=5601
docker-compose.yml
version: "3.8"
volumes:
certs:
driver: local
esdata01:
driver: local
kibanadata:
driver: local
networks:
default:
name: elastic
external: false
services:
setup:
image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
volumes:
- certs:/usr/share/elasticsearch/config/certs
user: "0"
command: >
bash -c '
if [ x${ELASTIC_PASSWORD} == x ]; then
echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
exit 1;
elif [ x${KIBANA_PASSWORD} == x ]; then
echo "Set the KIBANA_PASSWORD environment variable in the .env file";
exit 1;
fi;
if [ ! -f config/certs/ca.zip ]; then
echo "Creating CA";
bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
unzip config/certs/ca.zip -d config/certs;
fi;
if [ ! -f config/certs/certs.zip ]; then
echo "Creating certs";
echo -ne \
"instances:\n"\
" - name: es01\n"\
" dns:\n"\
" - es01\n"\
" - localhost\n"\
" ip:\n"\
" - 127.0.0.1\n"\
" - name: kibana\n"\
" dns:\n"\
" - kibana\n"\
" - localhost\n"\
" ip:\n"\
" - 127.0.0.1\n"\
> config/certs/instances.yml;
bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
unzip config/certs/certs.zip -d config/certs;
fi;
echo "Setting file permissions"
chown -R root:root config/certs;
find . -type d -exec chmod 750 \{\} \;;
find . -type f -exec chmod 640 \{\} \;;
echo "Waiting for Elasticsearch availability";
until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
echo "Setting kibana_system password";
until curl -s -X POST --cacert config/certs/ca/ca.crt -u "elastic:${ELASTIC_PASSWORD}" -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
echo "All done!";
'
healthcheck:
test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"]
interval: 1s
timeout: 5s
retries: 120
es01:
depends_on:
setup:
condition: service_healthy
image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
labels:
co.elastic.logs/module: elasticsearch
volumes:
- certs:/usr/share/elasticsearch/config/certs
- esdata01:/usr/share/elasticsearch/data
ports:
- ${ES_PORT}:9200
environment:
- node.name=es01
- cluster.name=${CLUSTER_NAME}
- discovery.type=single-node
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- bootstrap.memory_lock=true
- xpack.security.enabled=true
- ES_JAVA_OPTS=-Xms1g -Xmx1g
- xpack.security.http.ssl.enabled=true
- xpack.security.http.ssl.key=certs/es01/es01.key
- xpack.security.http.ssl.certificate=certs/es01/es01.crt
- xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
- xpack.security.transport.ssl.enabled=true
- xpack.security.transport.ssl.key=certs/es01/es01.key
- xpack.security.transport.ssl.certificate=certs/es01/es01.crt
- xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
- xpack.security.transport.ssl.verification_mode=certificate
- xpack.license.self_generated.type=${LICENSE}
ulimits:
memlock:
soft: -1
hard: -1
healthcheck:
test:
[
"CMD-SHELL",
"curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
]
interval: 10s
timeout: 10s
retries: 120
kibana:
depends_on:
es01:
condition: service_healthy
image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
labels:
co.elastic.logs/module: kibana
volumes:
- certs:/usr/share/kibana/config/certs
- kibanadata:/usr/share/kibana/data
ports:
- ${KIBANA_PORT}:5601
environment:
- SERVERNAME=kibana
- ELASTICSEARCH_HOSTS=https://es01:9200
- ELASTICSEARCH_USERNAME=kibana_system
- ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
- ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
healthcheck:
test:
[
"CMD-SHELL",
"curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
]
interval: 10s
timeout: 10s
retries: 120
docker-compose up을 통해 컨테이너를 실행한다.
2. Nori 분석기 설치
# es container 확인
$ docker ps
# es컨테이너 접속
$ docker exec -it {containerId} /bin/bash
$ cd /usr/share/elasticsearch/bin/
$ ./elasticsearch-plugin install analysis-nori
docker-compose stop > docker-compose up
3. Elasticsearch Config 설정
ElasticsearchConfig
@Configuration
@EnableElasticsearchRepositories
public class ElasticsearchConfig extends ElasticsearchConfiguration {
@Value("${spring.elasticsearch.username}")
private String username;
@Value("${spring.elasticsearch.password}")
private String password;
@Value("${spring.elasticsearch.host}")
private String host;
@Override
public ClientConfiguration clientConfiguration() {
return ClientConfiguration.builder()
.connectedTo(host)
.usingSsl()
.withBasicAuth(username, password)
.build();
}
@Bean
public ElasticsearchClient elasticsearchClient(RestClient restClient) {
ObjectMapper objectMapper =
new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
ElasticsearchTransport transport =
new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
return new ElasticsearchClient(transport);
}
}
애플리케이션 실행 시, SSL 인증서 등록 관련 에러가 발생했다.
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
📃시도
인증서 객체를 생성하여 키스토어에 등록
docker-compose.yml의 setup 서비스 command를 살펴보면, 컨테이너 내에서 SSL/TLS 인증서 및 보안 설정을 구성하여 Elasticsearch와 Kibana가 통신할 수 있도록 한다.
여기서 SSL/TLS 인증을 위한 CA를 생성하는 부분이 있다.
echo "Creating CA";
bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
unzip config/certs/ca.zip -d config/certs;
PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
이 오류는 SSL 인증서 검증 과정에서 발생하는 문제로, 애플리케이션이 Elasticsearch 서버의 인증서에 대해 신뢰할 수 있는 인증서 경로( PKIX path building failed)를 찾지 못했을 때 발생할 수 있다.
이를 해결하기 위해 클라이언트 측에서 해당 인증서를 신뢰하도록 설정해줘야 한다.
Java Keystore에 해당 인증서를 추가하자.
Stack Overflow에서 ca.crt를 디코딩하고 인증서 객체를 생성해 KeyStore에 저장 하는 방법을 찾았다.
How to configure security in elasticsearch 8.5.3 using Starter Data Elasticsearch 3.0.1 in maven java springboot
I was able to create an elasticsearch 8.5.3 server as a docker image, but with security completely disabled, and in my springboot application I am using ElasticsearchRepository to perform insert,up...
stackoverflow.com
elasticsearch 컨테이너의 /usr/share/elasticsearch/config/certs/ca/ca.crt를 열면 다음과 같은 내용이 있다.
-----BEGIN CERTIFICATE-----
MIIDSTCCAjGgAwIBAgIUR1gPSYQEiqCeWaI7ir1hQf9BZ9EwDQYJKoZIhvcNAQEL
(생략)
OL01woHZE+jHFz4i/31pARjL6Fvq8vsrjXJc2Uo=
-----END CERTIFICATE-----
첫 번째 줄(BEGIN)과 마지막 줄(END)를 제외하고 복사하여 application.yml에 추가한다.
나는 환경 변수를 만들어 추가했다.
spring:
elasticsearch:
host: ${ELASTIC_HOST}
username: ${ELASTIC_USER}
password: ${ELASTIC_PASSWORD}
client:
certificate: ${ELASTIC_CA}
다음과 같은 방식으로 해도 된다. 개행이 없어야 하기 때문에 '|' 을 추가하여 여러 줄의 텍스트를 하나의 문자열로 취급하도록 한다.
client:
certificate: |
MIIDSTCCAjGgAwIBAgIUR1gPSYQEiqCeWaI7ir1hQf9BZ9EwDQYJKoZIhvcNAQEL
(생략)
OL01woHZE+jHFz4i/31pARjL6Fvq8vsrjXJc2Uo=
ElasticsearchConfig
@Configuration
@EnableElasticsearchRepositories
public class ElasticsearchConfig extends ElasticsearchConfiguration {
@Value("${spring.elasticsearch.client.certificate}")
private String certificateBase64;
@Value("${spring.elasticsearch.username}")
private String username;
@Value("${spring.elasticsearch.password}")
private String password;
@Value("${spring.elasticsearch.host}")
private String host;
@Override
public ClientConfiguration clientConfiguration() {
try {
return ClientConfiguration.builder()
.connectedTo("localhost:9200")
.usingSsl(getSSLContext())
.withBasicAuth(username, password)
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
@Bean
public ElasticsearchClient elasticsearchClient(RestClient restClient) {
ObjectMapper objectMapper =
new ObjectMapper()
.registerModule(new JavaTimeModule())
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
ElasticsearchTransport transport =
new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
return new ElasticsearchClient(transport);
}
private SSLContext getSSLContext() throws Exception {
byte[] decodedCertificate = Base64.getMimeDecoder().decode(certificateBase64);
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
X509Certificate ca;
try (InputStream certificateInputStream = new ByteArrayInputStream(decodedCertificate)) {
ca = (X509Certificate) certificateFactory.generateCertificate(certificateInputStream);
}
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmf.init(keyStore);
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tmf.getTrustManagers(), new java.security.SecureRandom());
return sslContext;
}
}
🔍해결
cmd로 인증서 등록
1. ca.crt 로컬에 복사
C드라이브 하위에 elasticsearch 폴더를 생성하고, cmd를 관리자 권한으로 실행했다.
(폴더를 생성하지 않고 C드라이브에 복사해도 된다)
$ docker ps
$ docker cp {es container id}:/usr/share/elasticsearch/config/certs/ca/ca.crt /elasticsearch
elasticsearch 폴더에 ca.crt 파일이 복사된 것을 확인할 수 있다.
2. 인증서 등록
자바가 설치된 경로의 lib 폴더로 이동해서 자바 인증서 저장소에 ca.crt를 등록해주자
$ cd {JAVA_HOME}\lib
$ keytool.exe -import -alias {별칭} -keystore "{JAVA_HOME}\lib\security\cacerts" -storepass changeit -file "인증서 경로"
예시: keytool.exe -import -alias es -keystore "{JAVA_HOME}\lib\security\cacerts" -storepass changeit -file "C:\elasticsearch\ca.crt"
등록한 인증서를 삭제하고 싶다면 다음 명령을 사용한다.
$ keytool -delete -alias es -keystore "{JAVA_HOME}\lib\security\cacerts" -storepass changeit
참고
[에러] PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certificati
🚫 SSL 인증서 등록 관련 에러 발생 고객사 쪽의 API를 호출을 하고 있었는데, 이 사이트에 https를 적용하면서 정상적으로 호출이 되지 않고 아래와 같은 에러가 발생하였다. 이에 관련해서 조치
ssow93.tistory.com