elasticsearch를 설치하고 클러스터에 접근할 때 credential 관련 에러가 발생할 수 있다.
curl http://localhost:9200/_cluster/health
curl: (52) Empty reply from server
ES에서 empty reply 증상이 발생하면 ES 클러스터가 아예 시작하지 않았거나 https로 접근하지 않아서 요청을 무시하는 것이 원인이다.
https로 변경하고 -k 옵션을 추가해서 자세한 원인을 살펴보면 다음과 같다.
curl -k https://10.152.183.118:9200/_cluster/health
{
"error": {
"root_cause": [
{
"type": "security_exception",
"reason": "missing authentication credentials for REST request [/_cluster]",
"header": {
"WWW-Authenticate": [
"Basic realm=\"security\" charset=\"UTF-8\"",
"Bearer realm=\"security\"",
"ApiKey"
]
}
}
],
"type": "security_exception",
"reason": "missing authentication credentials for REST request [/_cluster]",
"header": {
"WWW-Authenticate": [
"Basic realm=\"security\" charset=\"UTF-8\"",
"Bearer realm=\"security\"",
"ApiKey"
]
}
},
"status": 401
}
직접적인 원인은 credential(id/password)를 전달하지 않아서 발생한 것이다.
간단한 해결방법으로는 ElasticSearch를 7.x로 낮춰서 설치하고 사용하는 방법이 있다. ElasticSearch 7에서는 credential을 필수로 요구하지 않는다.
다소 복잡한 해결방법으로는 https로 접근하고 인증서 파일들을 찾아서 클라이언트가 사용할 수 있도록 설정하는 것이다.
https로 접근하려면 도메인을 사용하고 해당 도메인으로 발급된 TLS 인증서를 사용해야 한다. curl과 같은 클라이언트에서 -k 옵션을 이용해서 유효하지 않은 인증서를 무시하는 방법도 있지만, 애플리케이션에서는 이런 옵션을 사용할 수 없기 때문에 인증서를 파일로 저장해두고 사용해야 한다.
일반적으로 인증서는 1) 자체 서명을 해서 직접 만드는 방법이 있는가 하면 2) 공인 인증 기관에서 발급받은 인증서를 사용하는 방법도 있고 3) K8s 환경에서 cert-manager를 이용해서 자가 발급받는 방법도 있다.
openssl genrsa -out ca.key 2048
openssl req -new -x509 -key ca.key -sha256 -subj "/CN=내인증기관이름" -days 3650 -out ca.crt
openssl genrsa -out tls.key 2048
openssl req -new -key tls.key -out tls.csr -subj "/CN=내ES서버이름"
openssl x509 -req -in tls.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out tls.crt -days 3650 -sha256
이제 ca.crt, ca.key, tls.crt, tls.csr, tls.key 인증서 파일이 생겼다. 인증서 파일을 ElasticSearch 서비스가 접근할 수 있는 위치로 잘 옮겨두고, elasticsearch.yml 파일에 해당 위치를 지정하면 된다.
xpack.security.enabled: true
xpack.security.transport.ssl.enabled: true
xpack.security.transport.ssl.verification_mode: certificate
xpack.security.transport.ssl.key: /path/to/tls.key
xpack.security.transport.ssl.certificate: /path/to/tls.crt
xpack.security.transport.ssl.certificate_authorities: ["/path/to/ca.crt"]
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: /path/to/tls.key
xpack.security.http.ssl.certificate: /path/to/tls.crt
xpack.security.http.ssl.certificate_authorities: ["/path/to/ca.crt"]
ElasticSearch 서비스를 재시작한 다음에, curl로 인증서 파일을 이용해서 접근할 수 있다. curl과 같은 클라이언트가 접근할 수 있는 위치에 ca.crt 파일을 복사해둘 필요가 있다.
curl --cacert /path/to/ca.crt -u "id:password" https://localhost:9200/_cluster/health
공인 인증서 파일을 ElasticSearch 서비스가 접근할 수 있는 위치에 놓고 위의 방법과 동일하게 elasticsearch.yml을 설정하고 재시작하면 된다.
cert-manager가 잘 설치되어 있고, issuer나 clusterissuer도 잘 설정되어 있다고 가정하겠다.
다음과 같은 manifest를 이용하여 clusterissuer에게 인증서 발급 요청을 한다. 그러면 certificate 타입의 secret이 생성된다. elasticsearch-tls라는 이름의 secret으로 이용할 수 있다.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: elasticsearch-certificate
namespace: default
spec:
secretName: elasticsearch-tls
duration: 2160h # 90d
renewBefore: 360h # 15d
commonName: "elasticsearch.yourdomain.com"
dnsNames:
- "elasticsearch.yourdomain.com"
issuerRef:
name: letsencrypt-prod
kind: ClusterIssuer
kubectl apply -f es-certificate.yaml
xpack.security.enabled: true
xpack.security.http.ssl.enabled: true
xpack.security.http.ssl.key: /usr/share/elasticsearch/config/certificates/tls.key
xpack.security.http.ssl.certificate: /usr/share/elasticsearch/config/certificates/tls.crt
xpack.security.http.ssl.certificate_authorities: ["/usr/share/elasticsearch/config/certificates/ca.crt"]
elasticsearch.yml을 이렇게 마련해두고, 해당 경로에 certificate이 파일 형태로 마운트되도록 manifest를 작성한다. (K8s에서 certificate 타입의 secret은 보통 파일로 마운트해서 사용한다.)
spec:
containers:
- name: elasticsearch
image: elasticsearch:7.10.0
volumeMounts:
- name: cert
mountPath: "/usr/share/elasticsearch/config/certificates"
readOnly: true
volumes:
- name: cert
secret:
secretName: elasticsearch-tls