How to add initial users when starting a RabbitMQ

2019-01-31 03:35发布

问题:

Currently i am starting RabbitMQ Docker container using the default RabbitMQ image from DockerHub. Using the following commands.

docker run --restart=always \
-d \
-e RABBITMQ_NODENAME=rabbitmq \
-v /opt/docker/rabbitmq/data:/var/lib/rabbitmq/mnesia/rabbitmq \
-p 5672:5672 \
-p 15672:15672 \
--name rabbitmq rabbitmq:3-management

I have a need where i want to provide defaults users / and virtual-hosts when the image is first started. For example to create a default 'test-user'.

Currently i have to do that manually by using the management plugin and adding the users / virtual-hosts via the web ui. Is there a way i can provide default settings when starting the RabbitMQ image?

回答1:

You can create a simple Dockerfile that extends the functionality of the basic image and creates a default user. The Docker file you need is the following:

FROM rabbitmq

# Define environment variables.
ENV RABBITMQ_USER user
ENV RABBITMQ_PASSWORD user

ADD init.sh /init.sh
EXPOSE 15672

# Define default command
CMD ["/init.sh"]

And the init.sh:

#!/bin/sh

# Create Rabbitmq user
( sleep 5 ; \
rabbitmqctl add_user $RABBITMQ_USER $RABBITMQ_PASSWORD 2>/dev/null ; \
rabbitmqctl set_user_tags $RABBITMQ_USER administrator ; \
rabbitmqctl set_permissions -p / $RABBITMQ_USER  ".*" ".*" ".*" ; \
echo "*** User '$RABBITMQ_USER' with password '$RABBITMQ_PASSWORD' completed. ***" ; \
echo "*** Log in the WebUI at port 15672 (example: http:/localhost:15672) ***") &

# $@ is used to pass arguments to the rabbitmq-server command.
# For example if you use it like this: docker run -d rabbitmq arg1 arg2,
# it will be as you run in the container rabbitmq-server arg1 arg2
rabbitmq-server $@

This script also initialize and expose the RabbitMQ webadmin at port 15672.



回答2:

Came up with a solution that suits my needs, leaving it here in case anybody else needs it.

Summary

The idea is to take a standard rabbitmq container with management plugin enabled and use it to create the required configuration, then export and use it to start new containers. The below solution creates a derived docker image but it also works to just mount the two files at runtime (e.g. using docker compose).

References

  • the info I started from
  • complete rabbitmq.config example

Components

  • official rabbitmq image, management plugin version (rabbitmq:management)
  • custom image based on the original one, with this Dockerfile (using version 3.6.6):

    FROM rabbitmq:3.6.6-management
    ADD rabbitmq.config /etc/rabbitmq/
    ADD definitions.json /etc/rabbitmq/
    RUN chown rabbitmq:rabbitmq /etc/rabbitmq/rabbitmq.config /etc/rabbitmq/definitions.json
    CMD ["rabbitmq-server"]
    
  • rabbitmq.config just tells rabbitmq to load definitions from the json file
  • definitions.json contains the users, vhosts, etc. and can be generated by the export function of the management web interface

rabbitmq.config example:

[
  {rabbit, [
    {loopback_users, []}
  ]},
  {rabbitmq_management, [
    {load_definitions, "/etc/rabbitmq/definitions.json"}
  ]}
].

definitions.json example:

{
 "rabbit_version": "3.6.6",
 "users": [
  {
   "name": "user1",
   "password_hash": "pass1",
   "hashing_algorithm": "rabbit_password_hashing_sha256",
   "tags": ""
  },
  {
   "name": "adminuser",
   "password_hash": "adminpass",
   "hashing_algorithm": "rabbit_password_hashing_sha256",
   "tags": "administrator"
  }
 ],
 "vhosts": [
  {
   "name": "\/vhost1"
  },
  {
   "name": "\/vhost2"
  }
 ],
 "permissions": [
  {
   "user": "user1",
   "vhost": "\/vhost1",
   "configure": ".*",
   "write": ".*",
   "read": ".*"
  }
 ],
 "parameters": [],
 "policies": [],
 "queues": [],
 "exchanges": [],
 "bindings": []
}

Alternave version

Deriving a new docker image is just one solution and works best when portability is key, since it avoids including host-based file management in the picture.

In some situations using the official image and providing configuration files from storage local to the host might be preferred.

The rabbitmq.config and definitions.json files are produced the same way, then mounted at runtime.

Notes:

  • I'm assuming they have been placed in /etc/so/ for the sake of these examples
  • files need to either be world readable or owned by the rabbitmq user or group (numerical id inside the docker container is 999), this needs to be handled by the host's sysadmin

docker run example:

    docker run --rm -it \
        -v /etc/so/rabbitmq.config:/etc/rabbitmq/rabbitmq.config:ro \
        -v /etc/so/definitions.json:/etc/rabbitmq/definitions.json:ro \
        rabbitmq:3.6-management

docker compose example:

    version: '2.1'
    services:
        rabbitmq:
            image: "rabbitmq:3.6-management"
            ports:
                - 5672:5672
                - 15672:15672
            volumes:
                - /etc/so/rabbitmq.config:/etc/rabbitmq/rabbitmq.config:ro
                - /etc/so/definitions.json:/etc/rabbitmq/definitions.json:ro


回答3:

I would like to add that sudo's response helped me a lot. But that it still missed a command to be added to the Dockerfile.

The rabbitmq.config and definitions.json file should be owned by the rabbitmq user & group. So after adding the files run chown.

The full Dockerfile in my case was the following:

FROM rabbitmq:3-management-alpine
ADD definitions.json /etc/rabbitmq/
ADD rabbitmq.config /etc/rabbitmq/
RUN chown rabbitmq:rabbitmq /etc/rabbitmq/rabbitmq.config /etc/rabbitmq/definitions.json

EXPOSE 4369 5671 5672 15671 15672 25672

CMD ["rabbitmq-server"]

The rabbitmq.config file has the following content being a merge from the default image's config and the added definitions loading:

[
    { rabbit, [
        {loopback_users, []},
        { tcp_listeners, [ 5672 ]},
        { ssl_listeners, [ ]},
        { hipe_compile, false } 
    ]},
    { rabbitmq_management, [
        { load_definitions, "/etc/rabbitmq/definitions.json"},
        { listeners, [
            { port, 15672 },
            { ssl, false } 

        ]}
    ]}
].

The definitions file can be exported from the management interface in the overview tab.

So you would first create a normal 'empty' rabbitmq container. Define whatever users, exchanges and queues you like. Then enter the management interface, export the definitions and create your own image using the file as described above.

Downloading the definitions is the easiest way to get the right password hashes in the definitions file for your own passwords. If you do not wish to do that you should follow the instructions as noted here (https://www.rabbitmq.com/passwords.html) to generate the correct hashes.



回答4:

The newest version of the RabbitMQ image on Dockerhub has in-built functionality for changing the default username / password from "guest" / "guest" to something else.

Simply set the environment variables "RABBITMQ_DEFAULT_USER" and "RABBITMQ_DEFAULT_PASS" when starting the image.

As a docker command, you would run the image like this:

docker run \
-e RABBITMQ_DEFAULT_USER=test-user \
-e RABBITMQ_DEFAULT_PASS=test-user \
-p 5672:5672 \
rabbitmq


回答5:

In my case sleep 5 solution above did not work because RabbitMQ startup time was much longer and not predictable. Posting solution which waits until RabbitMQ is up and running:

  • Dockerfile

    FROM rabbitmq:3-management
    ADD init.sh /
    ADD config_rabbit.sh /
    RUN chmod +x /init.sh /config_rabbit.sh
    ENTRYPOINT ["/init.sh"]
    
  • init.sh

    #!/bin/bash
    
    # Launch config script in background
    # Note there is no RabbitMQ Docker image support for executing commands after server (PID 1) is running (something like "ADD schema.sql /docker-entrypoint-initdb.d" in MySql image), so we are using this trick
    /config_rabbit.sh &
    
    # Launch
    /docker-entrypoint.sh rabbitmq-server
    
  • config_rabbit.sh

    #!/bin/bash
    
    # This script needs to be executed just once
    if [ -f /$0.completed ] ; then
      echo "$0 `date` /$0.completed found, skipping run"
      exit 0
    fi
    
    # Wait for RabbitMQ startup
    for (( ; ; )) ; do
      sleep 5
      rabbitmqctl -q node_health_check > /dev/null 2>&1
      if [ $? -eq 0 ] ; then
        echo "$0 `date` rabbitmq is now running"
        break
      else
        echo "$0 `date` waiting for rabbitmq startup"
      fi
    done
    
    # Execute RabbitMQ config commands here
    
    # Create user
    rabbitmqctl add_user USER PASSWORD
    rabbitmqctl set_permissions -p / USER ".*" ".*" ".*"
    echo "$0 `date` user USER created"
    
    # Create queue
    rabbitmqadmin declare queue name=QUEUE durable=true
    echo "$0 `date` queues created"
    
    # Create mark so script is not ran again
    touch /$0.completed
    


回答6:

Here is an example of how I add an unprivileged user gg RUN useradd -d /home/gg -m -s /bin/bash gg RUN echo gg:gg | chpasswd RUN echo 'gg ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/gg RUN chmod 0440 /etc/sudoers.d/gg



回答7:

I had to make a few changes to the script in the accepted answer to get it working based on the comments above.

Dockerfile

FROM rabbitmq

# Define environment variables.
ENV RABBITMQ_USER user
ENV RABBITMQ_PASSWORD user

ADD init.sh /init.sh
EXPOSE 15672

# Define default command
CMD ["/init.sh"]

init.sh

#!/bin/sh
( sleep 10 && \
rabbitmqctl add_user $RABBITMQ_USER $RABBITMQ_PASSWORD && \
rabbitmqctl set_user_tags $RABBITMQ_USER administrator && \
rabbitmqctl set_permissions -p / $RABBITMQ_USER  ".*" ".*" ".*" ) & \
rabbitmq-server


回答8:

You can create a new image and use the rabbitmqctl command line.

For example using this Dockerfile:

FROM rabbitmq
# rabbitmqctl command requires to start the rabbitmq server 
RUN service rabbitmq-server start
RUN /usr/lib/rabbitmq/bin/rabbitmqctl add_user test-user mypassword
RUN /usr/lib/rabbitmq/bin/rabbitmqctl add_vhost myvhost 
RUN /usr/lib/rabbitmq/bin/rabbitmqctl set_permissions -p /myvhost test-user ".*" ".*" ".*"

And build the image using

sudo docker build -t anImageName .

I have not test my answer, I cannot use docker at work