I am attempting to have an Elastic Stack (just one node ES and Kibana) authenticate against Keycloak using OpenID.
My docker-compose.yml file:
version: "2.2"
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"\
> 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}
volumes:
- certs:/usr/share/elasticsearch/config/certs
- esdata01:/usr/share/elasticsearch/data
- type: bind
source: ./elasticsearch.yml
target: /usr/share/elasticsearch/config/elasticsearch.yml
read_only: true
ports:
- ${ES_PORT}:9200
environment:
- node.name=es01
- cluster.name=${CLUSTER_NAME}
- cluster.initial_master_nodes=es01
# - cluster.initial_master_nodes=es01,es02,es03
# - discovery.seed_hosts=es02,es03
- ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
- bootstrap.memory_lock=true
- xpack.security.enabled=true
- 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.http.ssl.verification_mode=certificate
- 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}
mem_limit: ${MEM_LIMIT}
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}
volumes:
- certs:/usr/share/kibana/config/certs
- kibanadata:/usr/share/kibana/data
- type: bind
source: ./kibana.yml
target: /usr/share/kibana/config/kibana.yml
read_only: true
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
mem_limit: ${MEM_LIMIT}
healthcheck:
test:
[
"CMD-SHELL",
"curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
]
interval: 10s
timeout: 10s
retries: 120
volumes:
certs:
driver: local
esdata01:
driver: local
kibanadata:
driver: local
Elasticsearch.yml:
cluster.name: "docker-cluster"
network.host: 0.0.0.0
# xpack.security.authc.token.enabled: true
# xpack.security.authc.realms.oidc.oidc1:
# order: 2
# rp.client_id: "elasticsearch"
# rp.response_type: code
# rp.redirect_uri: "https://localhost:5601/api/security/v1/oidc"
# op.issuer: "http://localhost:8080/auth/realms/Elastic"
# op.authorization_endpoint: "http://localhost:8080/auth/realms/Elastic/protocol/openid-connect/auth"
# op.token_endpoint: "http://localhost:8080/auth/realms/Elastic/protocol/openid-connect/token"
# op.jwkset_path: certs.json
# op.userinfo_endpoint: "http://localhost:8080/auth/realms/Elastic/protocol/openid-connect/userinfo"
# op.endsession_endpoint: "http://localhost:8080/auth/realms/Elastic/protocol/openid-connect/logout"
# rp.post_logout_redirect_uri: "https://localhost:5601/logged_out"
# claims.principal: preferred_username
kibana.yml
# Default Kibana configuration for docker target
server.host: "0.0.0.0"
server.shutdownTimeout: "5s"
elasticsearch.hosts: [ "http://elasticsearch:9200" ]
monitoring.ui.container.elasticsearch.enabled: true
# xpack.security.authc.providers: [oidc]
# xpack.security.authc.oidc.realm: "oidc1"
# server.xsrf.whitelist: [/api/security/v1/oidc]
# xpack.security.enabled: true
In my local Keycloak instance I have created a realm "Elastic" with client "elasticsearch".
My current process is:
-
run
docker-compose up -d
This successfully sets up the elk stack, with ssl enabled. (this part of docker-compose was modified from the documentation's three node ssl example) -
Hit the Start trial API (from my understanding this necessary for xpack's OIDC features)
-
Inside the elasticsearch container:
-
Run
./bin/elasticsearch-keystore add xpack.security.authc.realms.oidc.oidc1.rp.client_secret
and add the client secret under the Keycloak Realm's Client's Credentials section. -
Uncomment all the lines in elasticsearch.yml
-
-
Restart ES container
ES then crashes and the error I see is:
{
"@timestamp": "2022-12-31T22:30:11.694Z",
"log.level": "ERROR",
"message": "fatal exception while booting Elasticsearch",
"ecs.version": "1.2.0",
"service.name": "ES_ECS",
"event.dataset": "elasticsearch.server",
"process.thread.name": "main",
"log.logger": "org.elasticsearch.bootstrap.Elasticsearch",
"elasticsearch.node.name": "es01",
"elasticsearch.cluster.name": "docker-cluster",
"error.type": "java.lang.IllegalStateException",
"error.message": "security initialization failed",
"error.stack_trace": "java.lang.IllegalStateException: security initialization failed\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.Security.createComponents(Security.java:577)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.node.Node.lambda$new$16(Node.java:709)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.plugins.PluginsService.lambda$flatMap$0(PluginsService.java:252)\n\tat java.base/java.util.stream.ReferencePipeline$7$1.accept(ReferencePipeline.java:273)\n\tat java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)\n\tat java.base/java.util.AbstractList$RandomAccessSpliterator.forEachRemaining(AbstractList.java:722)\n\tat java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)\n\tat java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)\n\tat java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:575)\n\tat java.base/java.util.stream.AbstractPipeline.evaluateToArrayNode(AbstractPipeline.java:260)\n\tat java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:616)\n\tat java.base/java.util.stream.ReferencePipeline.toArray(ReferencePipeline.java:622)\n\tat java.base/java.util.stream.ReferencePipeline.toList(ReferencePipeline.java:627)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.node.Node.<init>(Node.java:724)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.node.Node.<init>(Node.java:318)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.bootstrap.Elasticsearch$2.<init>(Elasticsearch.java:214)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.bootstrap.Elasticsearch.initPhase3(Elasticsearch.java:214)\n\tat org.elasticsearch.server@8.5.3/org.elasticsearch.bootstrap.Elasticsearch.main(Elasticsearch.java:67)\nCaused by: java.lang.IllegalStateException: Unable to create a IDTokenValidator instance\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectAuthenticator.createIdTokenValidator(OpenIdConnectAuthenticator.java:796)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectAuthenticator.<init>(OpenIdConnectAuthenticator.java:161)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectRealm.<init>(OpenIdConnectRealm.java:113)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.InternalRealms.lambda$getFactories$7(InternalRealms.java:169)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.Realms.initRealms(Realms.java:288)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.Realms.<init>(Realms.java:109)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.Security.createComponents(Security.java:685)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.Security.createComponents(Security.java:565)\n\t... 17 more\nCaused by: java.nio.file.NoSuchFileException: /usr/share/elasticsearch/config/certs.json\n\tat java.base/sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)\n\tat java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:106)\n\tat java.base/sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)\n\tat java.base/sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:218)\n\tat java.base/java.nio.file.Files.newByteChannel(Files.java:380)\n\tat java.base/java.nio.file.Files.newByteChannel(Files.java:432)\n\tat java.base/java.nio.file.Files.readAllBytes(Files.java:3287)\n\tat java.base/java.nio.file.Files.readString(Files.java:3365)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectAuthenticator.readJwkSetFromFile(OpenIdConnectAuthenticator.java:364)\n\tat org.elasticsearch.security@8.5.3/org.elasticsearch.xpack.security.authc.oidc.OpenIdConnectAuthenticator.createIdTokenValidator(OpenIdConnectAuthenticator.java:789)\n\t... 24 more\n"
}
I am unsure how to get past this point or if these steps are even the correct way of going about setting this up.