32

I'm using Search Guard plugin to secure an elasticsearch cluster composed of multiple nodes. Here is my Dockerfile:

#!/bin/sh
FROM docker.elastic.co/elasticsearch/elasticsearch:5.6.3

USER root

# Install search guard
RUN bin/elasticsearch-plugin install --batch com.floragunn:search-guard-5:5.6.3-16 \
    && chmod +x \
        plugins/search-guard-5/tools/hash.sh \
        plugins/search-guard-5/tools/sgadmin.sh \
        bin/init_sg.sh \
    && chown -R elasticsearch:elasticsearch /usr/share/elasticsearch

USER elasticsearch

To initialize SearchGuard (create internal users and assign roles). I need to run the script init_sg.sh after the container startup. Here is the problem: Unless elasticsearch is running, the script will not initialize any security index.

The script's content is :

sleep 10
plugins/search-guard-5/tools/sgadmin.sh -cd config/ -ts config/truststore.jks -ks config/kirk-keystore.jks -nhnv -icl

Now, I just run the script manually after the container startup but since I'm running it on Kubernetes.. Pods may get killed or fail and get recreated automatically for some reason. In this case, the plugin have to be initialized automatically after the container startup!

So how to accomplish this? Any help or hint would be really appreciated.

3
  • 1
    You will only need to initialize SG once per cluster. Re-initialize SG on every restart of a pod will overwrite any custom roles/permissions not saved.
    – nafooesi
    Commented Sep 11, 2019 at 22:51
  • @nafooesi Absolutely, you are right! The SG should be initialized only once per cluster. Commented Sep 15, 2019 at 18:17
  • This question is also addressed here
    – ManishM
    Commented Nov 20, 2020 at 19:17

8 Answers 8

30

The image itself has an entrypoint ENTRYPOINT ["/run/entrypoint.sh"] specified in the Dockerfile. You can replace it by your own script. So for example create a new script, mount it and first call /run/entrypoint.sh and then wait for start of elasticsearch before running your init_sg.sh.

4
  • 3
    I'm trying to do the exact same thing, but the /run/entrypoint.sh is not present in the container. I can't find where it sits. Any help?
    – RedGiant
    Commented May 25, 2018 at 12:37
  • Found it at /entrypoint.sh for elasticsearch:1.5.2
    – Marieke
    Commented Jun 24, 2021 at 16:50
  • Can you please specify how to mount it?
    – Gulzar
    Commented Nov 27, 2021 at 19:41
  • 1
    You can use ADD in Dockerfile command to add script to the container statically. If you change the script often and do not want to rebuild the image every time, you can make a script which is called from entrypoint.sh, and make all changes there.
    – Do-do-new
    Commented Mar 12, 2023 at 9:51
8

Not sure this will solves your problem, but its worth check my repo'sDockerfile

I have created a simple run.sh file copied to docker image and in the Dockerfile I wrote CMD ["run.sh"]. In the same way define whatever you want in run.sh and write CMD ["run.sh"]. You can find another example like below

Dockerfile

FROM java:8

RUN apt-get update && apt-get install stress-ng -y 
ADD target/restapp.jar /restapp.jar 
COPY dockerrun.sh /usr/local/bin/dockerrun.sh 
RUN chmod +x /usr/local/bin/dockerrun.sh 
CMD ["dockerrun.sh"]

dockerrun.sh

#!/bin/sh
java -Dserver.port=8095 -jar /restapp.jar &
hostname="hostname: `hostname`"
nohup stress-ng --vm 4 &
while true; do
  sleep 1000
done
1
  • 2
    thanks for the reply, the elasticsearch image has already an entrypoint.sh file, using CMD like this will override it and elasticsearch search won't start at all :/ Commented Dec 6, 2017 at 15:09
7

This is addressed in the documentation here: https://docs.docker.com/config/containers/multi-service_container/

If one of your processes depends on the main process, then start your helper process FIRST with a script like wait-for-it, then start the main process SECOND and remove the fg %1 line.

#!/bin/bash
  
# turn on bash's job control
set -m
  
# Start the primary process and put it in the background
./my_main_process &
  
# Start the helper process
./my_helper_process
  
# the my_helper_process might need to know how to wait on the
# primary process to start before it does its work and returns
  
  
# now we bring the primary process back into the foreground
# and leave it there
fg %1
1
5

I was trying to solve the exact problem. Here's the approach that worked for me.

  1. Create a separate shell script that checks for ES status, and only start initialization of SG when ES is ready:

Shell Script

#!/bin/sh

echo ">>>>  Right before SG initialization <<<<"
# use while loop to check if elasticsearch is running 
while true
do
    netstat -uplnt | grep :9300 | grep LISTEN > /dev/null
    verifier=$?
    if [ 0 = $verifier ]
        then
            echo "Running search guard plugin initialization"
            /elasticsearch/plugins/search-guard-6/tools/sgadmin.sh -h 0.0.0.0 -cd plugins/search-guard-6/sgconfig -icl -key config/client.key -cert config/client.pem -cacert config/root-ca.pem -nhnv
            break
        else
            echo "ES is not running yet"
            sleep 5
    fi
done

Install script in Dockerfile

You will need to install the script in container so it's accessible after it starts.

COPY sginit.sh /
RUN chmod +x /sginit.sh

Update entrypoint script

You will need to edit the entrypoint script or run script of your ES image. So that it starts the sginit.sh in the background BEFORE starting ES process.

# Run sginit in background waiting for ES to start
/sginit.sh &

This way the sginit.sh will start in the background, and will only initialize SG after ES is started.

The reason to have this sginit.sh script starts before ES in the background is so that it's not blocking ES from starting. The same logic applies if you put it after starting of ES, it will never run unless you put the starting of ES in the background.

3
  • How to wait for the ES get started, then initialize the SG?
    – Akira
    Commented Sep 10, 2019 at 21:20
  • 1
    As commented below the original question, you do NOT want to initialize SG every single time. It will overwrite any modified config when a pod restarts. SG should only be initialized once per cluster at the beginning. If you need to run some other script after ES starts, my script can be used as an example to detect if ES is running in the background before kicking off the script.
    – nafooesi
    Commented Dec 3, 2019 at 1:23
  • The general approach described here is very useful. I had a similar problem (not with ES), where I wanted to run a script after the main process had a chance to start up. This pattern worked like a charm!
    – bischoje
    Commented Feb 25, 2020 at 20:07
3

You can also use wait-for-it script. It will wait on the availability of a host and TCP port. It is useful for synchronizing the spin-up of interdependent services and works like a charm with containers. It does not have any external dependencies so you can just run it as an RUN command without doing anything else.

A Dockerfile example based on this thread:

FROM elasticsearch

# Make elasticsearch write data to a folder that is not declared as a volume in elasticsearchs' official dockerfile.
RUN mkdir /data && chown -R elasticsearch:elasticsearch /data && echo 'es.path.data: /data' >> config/elasticsearch.yml && echo 'path.data: /data' >> config/elasticsearch.yml

# Download wait-for-it
ADD https://raw.githubusercontent.com/vishnubob/wait-for-it/e1f115e4ca285c3c24e847c4dd4be955e0ed51c2/wait-for-it.sh /utils/wait-for-it.sh

# Copy the files you may need and your insert script

# Insert data into elasticsearch
RUN /docker-entrypoint.sh elasticsearch -p /tmp/epid & /bin/bash /utils/wait-for-it.sh -t 0 localhost:9200 -- path/to/insert/script.sh; kill $(cat /tmp/epid) && wait $(cat /tmp/epid); exit 0;
1
2

I would suggest to put the CMD in you docker file to execute the script when the container start

FROM debian
RUN apt-get update && apt-get install -y nano && apt-get clean
EXPOSE 8484
CMD ["/bin/bash", "/opt/your_app/init.sh"]

There is other way , but before using this look at your requirement,

    ENTRYPOINT "put your code here" && /bin/bash
    #exemple ENTRYPOINT service nginx start && service ssh start &&/bin/bash "use && to separate your code"
2
  • this solution overrides the entrypoint which is provided by Elasticsearch Image Commented Dec 6, 2017 at 15:06
  • I have no looked into elasticseacrh image installation as such, do you mean this does not work for you?
    – Sohan
    Commented Dec 7, 2017 at 6:05
1

There is a dedicated tool for this - s6-overlay.
To quote from their description:

A simple init process which allows the end-user to execute tasks like initialization (...) Multiple processes in a single container (...) Able to operate in "The Docker Way"

The repo provides lengthy explanation how it works, how to install etc. which I won't repeat here.

Example

Imo their repo lacks a working, straightforward minimal example how to run a process + a script so I provide one. I modify the example they provide in their docs.
Say we want to run nginx (or any process that runs until end of container lifetime) plus some shell script myscript.sh.

Local directory structure:

./Dockerfile
./myscript.sh
./s6-overlay/s6-rc.d/myapp/type
./s6-overlay/s6-rc.d/myapp/up
./s6-overlay/s6-rc.d/user/contents.d/myapp

Dockerfile:

FROM ubuntu
ARG S6_OVERLAY_VERSION=3.1.4.1

RUN apt-get update && apt-get install -y nginx xz-utils
RUN echo "daemon off;" >> /etc/nginx/nginx.conf

# Minimal set of dependecies required for s6
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-noarch.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-noarch.tar.xz
ADD https://github.com/just-containers/s6-overlay/releases/download/v${S6_OVERLAY_VERSION}/s6-overlay-x86_64.tar.xz /tmp
RUN tar -C / -Jxpf /tmp/s6-overlay-x86_64.tar.xz

# Overhead of files to manage processes via s6
COPY s6-overlay /etc/s6-overlay
# Copy the script we intend to run
COPY myscript.sh /home

# CMD is the main process - nothing special here
CMD ["/usr/sbin/nginx"]
# ENTRYPOINT must be /init for s6 to work
ENTRYPOINT ["/init"]

myscript.sh - make sure to make it executable:

#!/bin/bash
echo "foo" > /home/foo.txt
echo "bar" > /home/bar.txt

s6-overlay/s6-rc.d/myapp/type:

oneshot

"an up file contains a single command line" so as soon our script has >1 line, we have to outsource our script to a separate file. Therefore this is our s6-overlay/s6-rc.d/myapp/up:

/home/myscript.sh

s6-overlay/s6-rc.d/myapp/contents.d/myapp is an empty file.


Now we simply need to docker build (...) and docker run -p 80:80 (...). If you have done everything correctly, you should see a log message s6-rc: info: service myapp successfully started at container startup.
You can then visit localhost:80 and run docker exec CONTAINER bash -c "cat /home/foo.txt" to confirm it works as expected.

Note that utilizing s6-rc.d is the recommended way to do it. There's also a legacy way to accomplish this with less overhead by putting myscript.sh into folder /etc/cont-init.d/.

0

My success would be adding my script to CMD or ENTRYPOINT

Then add the bootom of the script i made run include the start application at the bottom.

apachectl -D FOREGROUND

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.