Merge pull request #24 from cdalvaro/issue-23/add-salt-api-support

Add salt-api support
This commit is contained in:
Carlos D. Álvaro
2020-05-17 14:34:27 +02:00
committed by GitHub
13 changed files with 373 additions and 108 deletions

23
.shellcheckrc Normal file
View File

@@ -0,0 +1,23 @@
# Check shellcheck wiki at:
# https://github.com/koalaman/shellcheck/wiki/SCXXXX
# Redirections are performed by the current shell before sudo is started.
disable=SC2024
# foo appears unused. Verify it or export it.
disable=SC2034
# Double quote array expansions to avoid re-splitting elements.
disable=SC2068
# Argument mixes string and array.
disable=SC2145
# Declare and assign separately to avoid masking return values.
disable=SC2155
# This redirection doesn't have a command.
disable=SC2188
# This is a file redirection.
disable=SC2210

View File

@@ -3,6 +3,13 @@
This file only reflects the changes that are made in this image. This file only reflects the changes that are made in this image.
Please refer to the SaltStack [Release Notes](https://docs.saltstack.com/en/latest/topics/releases/3000.3.html) for the list of changes in SaltStack. Please refer to the SaltStack [Release Notes](https://docs.saltstack.com/en/latest/topics/releases/3000.3.html) for the list of changes in SaltStack.
**3000.3_1**
- Add support for `salt-api` service
- Add entrypoint support to restart services
- Use previous image as Docker cache
- Add `build-arg` to Makefile
**3000.3** **3000.3**
- Upgrade SaltStack Master to `3000.3` - Upgrade SaltStack Master to `3000.3`

View File

@@ -57,7 +57,7 @@ COPY entrypoint.sh /sbin/entrypoint.sh
RUN chmod +x /sbin/entrypoint.sh RUN chmod +x /sbin/entrypoint.sh
# Shared resources # Shared resources
EXPOSE 4505/tcp 4506/tcp EXPOSE 4505 4506 8000
RUN mkdir -p ${SALT_DATA_DIR} ${SALT_BASE_DIR} ${SALT_KEYS_DIR} ${SALT_CONFS_DIR} ${SALT_LOGS_DIR} RUN mkdir -p ${SALT_DATA_DIR} ${SALT_BASE_DIR} ${SALT_KEYS_DIR} ${SALT_CONFS_DIR} ${SALT_LOGS_DIR}
VOLUME [ "${SALT_BASE_DIR}" "${SALT_KEYS_DIR}" "${SALT_CONFS_DIR}" "${SALT_LOGS_DIR}" ] VOLUME [ "${SALT_BASE_DIR}" "${SALT_KEYS_DIR}" "${SALT_CONFS_DIR}" "${SALT_LOGS_DIR}" ]

View File

@@ -11,10 +11,14 @@ help:
@echo " 5. make logs - view logs" @echo " 5. make logs - view logs"
build: build:
@docker build --tag=cdalvaro/saltstack-master . @docker build --tag=cdalvaro/saltstack-master . \
--build-arg=BUILD_DATE="$(shell date +"%Y-%m-%d %H:%M:%S%:z")" \
--build-arg=VCS_REF="$(shell git rev-parse --short HEAD)" \
release: build release: build
@docker build --tag=cdalvaro/saltstack-master:$(shell cat VERSION) . @docker build --tag=cdalvaro/saltstack-master:$(shell cat VERSION) . \
--build-arg=BUILD_DATE="$(shell date +"%Y-%m-%d %H:%M:%S%:z")" \
--build-arg=VCS_REF="$(shell git rev-parse --short HEAD)" \
quickstart: quickstart:
@echo "Starting saltstack-master container..." @echo "Starting saltstack-master container..."

157
README.md
View File

@@ -1,4 +1,9 @@
# SaltStack Master v3000.3 [![SaltStack][saltstack_badge]][saltstack_release_notes]
[![Ubuntu Image][ubuntu_badge]][ubuntu_hub_docker]
[![Docker Build Status][docker_build_badge]][docker_hub]
[![CodeFactor][codefactor_badge]][codefactor_score]
# SaltStack Master v3000.3_1
Dockerfile to build a [SaltStack](https://www.saltstack.com) Master image for the Docker opensource container platform. Dockerfile to build a [SaltStack](https://www.saltstack.com) Master image for the Docker opensource container platform.
@@ -15,6 +20,8 @@ For other methods to install SaltStack please refer to the [Official SaltStack I
- [Custom Recipes](#custom-recipes) - [Custom Recipes](#custom-recipes)
- [Minion Keys](#minion-keys) - [Minion Keys](#minion-keys)
- [Master Signed Keys](#master-signed-keys) - [Master Signed Keys](#master-signed-keys)
- [Salt API](#salt-api)
- [Salt Pepper](#salt-pepper)
- [Host Mapping](#host-mapping) - [Host Mapping](#host-mapping)
- [Git Fileserver](#git-fileserver) - [Git Fileserver](#git-fileserver)
- [GitPython](#gitpython) - [GitPython](#gitpython)
@@ -23,6 +30,7 @@ For other methods to install SaltStack please refer to the [Official SaltStack I
- [Available Configuration Parameters](#available-configuration-parameters) - [Available Configuration Parameters](#available-configuration-parameters)
- [Usage](#usage) - [Usage](#usage)
- [Shell Access](#shell-access) - [Shell Access](#shell-access)
- [Restart Services](#restart-services)
- [References](#references) - [References](#references)
## Installation ## Installation
@@ -30,7 +38,7 @@ For other methods to install SaltStack please refer to the [Official SaltStack I
Automated builds of the image are available on [Dockerhub](https://hub.docker.com/r/cdalvaro/saltstack-master/) and is the recommended method of installation. Automated builds of the image are available on [Dockerhub](https://hub.docker.com/r/cdalvaro/saltstack-master/) and is the recommended method of installation.
```sh ```sh
docker pull cdalvaro/saltstack-master:3000.3 docker pull cdalvaro/saltstack-master:3000.3_1
``` ```
You can also pull the latest tag which is built from the repository `HEAD` You can also pull the latest tag which is built from the repository `HEAD`
@@ -69,63 +77,149 @@ Alternatively, you can manually launch the `saltstack-master` container:
```sh ```sh
docker run --name salt_master --detach \ docker run --name salt_master --detach \
--publish 4505:4505/tcp --publish 4506:4506/tcp \ --publish 4505:4505 --publish 4506:4506 \
--env 'SALT_LOG_LEVEL=info' \ --env 'SALT_LOG_LEVEL=info' \
--volume $(pwd)/roots/:/home/salt/data/srv/ \ --volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
cdalvaro/saltstack-master:3000.3 cdalvaro/saltstack-master:3000.3_1
``` ```
## Configuration ## Configuration
### Custom Recipes ### Custom Recipes
In order to provide salt with your custom recipes you must mount the volume `/home/salt/data/srv/` with your `roots` directory. In order to provide salt with your custom recipes you must mount the volume `/home/salt/data/srv/`
with your `roots` directory.
### Minion Keys ### Minion Keys
Minion keys can be added automatically on startup to SaltStack master by mounting the volume `/home/salt/data/keys` and copying the minion keys inside `keys/minions/` directory. Minion keys can be added automatically on startup to SaltStack master by mounting the volume
`/home/salt/data/keys` and copying the minion keys inside `keys/minions/` directory.
It is also important to know that, in order to keep your keys after removing the container, the keys directory must be mounted. It is also important to know that, in order to keep your keys after removing the container,
the keys directory must be mounted.
```sh ```sh
mkdir -p keys/minions mkdir -p keys/minions
rsync root@minion1:/etc/salt/pki/minion/minion.pub keys/minions/minion1 rsync root@minion1:/etc/salt/pki/minion/minion.pub keys/minions/minion1
docker run --name salt_master -d \ docker run --name salt_master -d \
--publish 4505:4505/tcp --publish 4506:4506/tcp \ --publish 4505:4505 --publish 4506:4506 \
--env 'SALT_LOG_LEVEL=info' \ --env 'SALT_LOG_LEVEL=info' \
--volume $(pwd)/roots/:/home/salt/data/srv/ \ --volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
cdalvaro/saltstack-master:3000.3 cdalvaro/saltstack-master:3000.3_1
``` ```
### Master Signed Keys ### Master Signed Keys
It is possible to use signed master keys by establishing the environment variable `SALT_MASTER_SIGN_PUBKEY` to `True`. It is possible to use signed master keys by establishing the environment variable
`SALT_MASTER_SIGN_PUBKEY` to `True`.
```sh ```sh
docker run --name salt_stack --detach \ docker run --name salt_stack --detach \
--publish 4505:4505/tcp --publish 4506:4506/tcp \ --publish 4505:4505 --publish 4506:4506 \
--env 'SALT_LOG_LEVEL=info' \ --env 'SALT_LOG_LEVEL=info' \
--env 'SALT_MASTER_SIGN_PUBKEY=True' --env 'SALT_MASTER_SIGN_PUBKEY=True'
--volume $(pwd)/roots/:/home/salt/data/srv/ \ --volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
cdalvaro/saltstack-master:3000.3 cdalvaro/saltstack-master:3000.3_1
``` ```
The container will create the `master_sign` key and its signature. More information about how to configure the minion service can be found [here](https://docs.saltstack.com/en/latest/topics/tutorials/multimaster_pki.html#prepping-the-minion-to-verify-received-public-keys). The container will create the `master_sign` key and its signature.
More information about how to configure the minion service can be found
[here](https://docs.saltstack.com/en/latest/topics/tutorials/multimaster_pki.html#prepping-the-minion-to-verify-received-public-keys).
Additionally, you can generate new keys by executing the following command: Additionally, you can generate new keys by executing the following command:
```sh ```sh
docker run --name salt_stack -it --rm \ docker run --name salt_stack -it --rm \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
cdalvaro/saltstack-master:3000.3 app:gen-signed-keys other_master_sign cdalvaro/saltstack-master:3000.3_1 app:gen-signed-keys other_master_sign
``` ```
The newly created keys will appear inside `keys/generated/other_master_sign` directory. The newly created keys will appear inside `keys/generated/other_master_sign` directory.
### Salt API
You can enable `salt-api` service by setting env variable `SALT_API_SERVICE_ENABLED` to `true`.
A self-signed SSL certificate will be automatically generated and the following configuration
will be added to the master configuration file:
```yml
rest_cherrypy:
port: 8000
ssl_crt: /etc/pki/tls/certs/docker-salt-master.crt
ssl_key: /etc/pki/tls/certs/docker-salt-master.key
```
The container exposes port `8000` by default, although you can map this port to whatever port you like in
your `docker run` command or in your `docker-compose.yml` file.
```sh
docker run --name salt_stack --detach \
--publish 4505:4505 --publish 4506:4506 --publish 8000:8000 \
--env 'SALT_API_SERVICE_ENABLED=true' \
--env 'SALT_API_USER_PASS=SuperCool/Password10'
--volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \
cdalvaro/saltstack-master:3000.3_1
```
By default, user `salt_api` is created and you can set its password by setting the environment variable
`SALT_API_USER_PASS`.
You can also change the salt-api _username_ by setting `SALT_API_USER`.
It is possible to disable this user by explicitly setting this variable to an empty string: `SALT_API_USER=''` if you are going to use an `LDAP` server.
As a security measure, if `SALT_API_USER_PASS` is set to `true` and you don't disable `SALT_API_USER`,
you'll be required to set `SALT_API_USER_PASS`. Otherwise initialization will fail and your Docker image won't work.
With all that set, you'll be able to provide your _salt-api_ custom configuration by creating the `salt-api.conf`
file inside your `conf` directory:
```yml
external_auth:
pam:
salt_api:
- .*
```
More information is available in the following link: [External Authentication System (eAuth)](https://docs.saltstack.com/en/latest/topics/eauth/index.html#acl-eauth).
Now you have your saltstack-master docker image ready to accept external authentications and to connect external tools such as [`saltstack/pepper`](https://github.com/saltstack/pepper).
#### Salt Pepper
The pepper CLI script allows users to execute Salt commands from computers that are external to computers running the salt-master or salt-minion daemons as though they were running Salt locally
##### Installation:
```sh
pip3 install salt-pepper
```
##### Configuration
Then configure pepper by filling your `~/.pepperrc` file with your salt-api credentials:
```conf
[main]
SALTAPI_URL=https://your.salt-master.hostname:8000/
SALTAPI_USER=salt_api
SALTAPI_PASS=SuperCool/Password10
SALTAPI_EAUTH=pam
```
##### Usage
Beging executing salt recipes with `pepper`:
```sh
pepper '*' test.ping
```
### Host Mapping ### Host Mapping
Per default the container is configured to run `salt-master` as user and group `salt` with `uid` and `gid` `1000`. From the host it appears as if the mounted data volumes are owned by the host's user/group `1000` and maybe leading to unfavorable effects. Per default the container is configured to run `salt-master` as user and group `salt` with `uid` and `gid` `1000`. From the host it appears as if the mounted data volumes are owned by the host's user/group `1000` and maybe leading to unfavorable effects.
@@ -137,7 +231,7 @@ docker run --name salt_stack -it --rm \
--env "USERMAP_UID=$(id -u)" --env "USERMAP_GID=$(id -g)" \ --env "USERMAP_UID=$(id -u)" --env "USERMAP_GID=$(id -g)" \
--volume $(pwd)/roots/:/home/salt/data/srv/ \ --volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
cdalvaro/saltstack-master:3000.3 cdalvaro/saltstack-master:3000.3_1
``` ```
### Git Fileserver ### Git Fileserver
@@ -188,12 +282,12 @@ Inside that directory you could find `supervisor/` logs and `salt/` logs:
```sh ```sh
docker run --name salt_master --detach \ docker run --name salt_master --detach \
--publish 4505:4505/tcp --publish 4506:4506/tcp \ --publish 4505:4505 --publish 4506:4506 \
--env 'SALT_LOG_LEVEL=info' \ --env 'SALT_LOG_LEVEL=info' \
--volume $(pwd)/roots/:/home/salt/data/srv/ \ --volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
--volume $(pwd)/logs/:/home/salt/data/logs/ \ --volume $(pwd)/logs/:/home/salt/data/logs/ \
cdalvaro/saltstack-master:3000.3 cdalvaro/saltstack-master:3000.3_1
``` ```
Check [Available Configuration Parameters](#available-configuration-parameters) section for configuring logrotate. Check [Available Configuration Parameters](#available-configuration-parameters) section for configuring logrotate.
@@ -212,6 +306,9 @@ Below is the list of available options that can be used to customize your SaltSt
| `SALT_LOG_ROTATE_FREQUENCY` | Logrotate frequency for salt logs. Available options are 'daily', 'weekly', 'monthly', and 'yearly'. Default: `weekly` | | `SALT_LOG_ROTATE_FREQUENCY` | Logrotate frequency for salt logs. Available options are 'daily', 'weekly', 'monthly', and 'yearly'. Default: `weekly` |
| `SALT_LOG_ROTATE_RETENTION` | Keep x files before deleting old log files. Defaults: `52` | | `SALT_LOG_ROTATE_RETENTION` | Keep x files before deleting old log files. Defaults: `52` |
| `SALT_LEVEL_LOGFILE` | The level of messages to send to the log file. One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. Default: `warning` | | `SALT_LEVEL_LOGFILE` | The level of messages to send to the log file. One of 'garbage', 'trace', 'debug', info', 'warning', 'error', 'critical'. Default: `warning` |
| `SALT_API_SERVICE_ENABLED` | Enable `salt-api` service. Default: `false` |
| `SALT_API_USER` | Set username for `salt-api` service. Default: `salt_api` |
| `SALT_API_USER_PASS` | `SALT_API_USER` password. Required if `SALT_API_SERVICE_ENBALED` is `true` and `SALT_API_USER` is not empty. _Unset_ by default |
| `SALT_MASTER_SIGN_PUBKEY` | Sign the master auth-replies with a cryptographic signature of the master's public key. Possible values: 'True' or 'False'. Default: `False` | | `SALT_MASTER_SIGN_PUBKEY` | Sign the master auth-replies with a cryptographic signature of the master's public key. Possible values: 'True' or 'False'. Default: `False` |
| `SALT_MASTER_USE_PUBKEY_SIGNATURE` | Instead of computing the signature for each auth-reply, use a pre-calculated signature. This option requires `SALT_MASTER_SIGN_PUBKEY` set to 'True'. Possible values: 'True' or 'False'. Default: `True` | | `SALT_MASTER_USE_PUBKEY_SIGNATURE` | Instead of computing the signature for each auth-reply, use a pre-calculated signature. This option requires `SALT_MASTER_SIGN_PUBKEY` set to 'True'. Possible values: 'True' or 'False'. Default: `True` |
| `SALT_MASTER_SIGN_KEY_NAME` | The customizable name of the signing-key-pair without suffix. Default: `master_sign` | | `SALT_MASTER_SIGN_KEY_NAME` | The customizable name of the signing-key-pair without suffix. Default: `master_sign` |
@@ -234,12 +331,12 @@ ret_port: 3506
EOF EOF
docker run --name salt_master -d \ docker run --name salt_master -d \
--publish 3505:3505/tcp --publish 3506:3506/tcp \ --publish 3505:3505 --publish 3506:3506 \
--env 'SALT_LOG_LEVEL=info' \ --env 'SALT_LOG_LEVEL=info' \
--volume $(pwd)/roots/:/home/salt/data/srv/ \ --volume $(pwd)/roots/:/home/salt/data/srv/ \
--volume $(pwd)/keys/:/home/salt/data/keys/ \ --volume $(pwd)/keys/:/home/salt/data/keys/ \
--volume $(pwd)/config/:/home/salt/data/config/ \ --volume $(pwd)/config/:/home/salt/data/config/ \
cdalvaro/saltstack-master:3000.3 cdalvaro/saltstack-master:3000.3_1
``` ```
## Usage ## Usage
@@ -264,8 +361,30 @@ For debugging and maintenance purposes you may want access the container shell.
docker exec -it salt_master bash docker exec -it salt_master bash
``` ```
## Restart Services
You can restart containers services by running the following command:
```sh
docker exec -it salt_master entrypoint.sh app:restart [salt-service]
```
Where `salt-service` is one of: `salt-master` os `salt-api` (if `SALT_API_SERVICE_ENABLED` is set to `true`)
## References ## References
- https://docs.saltstack.com/en/latest/topics/installation/index.html - https://docs.saltstack.com/en/latest/topics/installation/index.html
- https://docs.saltstack.com/en/latest/topics/tutorials/salt_bootstrap.html - https://docs.saltstack.com/en/latest/topics/tutorials/salt_bootstrap.html
- https://github.com/saltstack/salt/releases - https://github.com/saltstack/salt/releases
[saltstack_badge]: https://img.shields.io/badge/SaltStack-v3000.3-lightgrey.svg?style=flat-square&logo=Saltstack
[saltstack_release_notes]: https://docs.saltstack.com/en/latest/topics/releases/3000.3.html "SaltStack Release Notes"
[ubuntu_badge]: https://img.shields.io/badge/ubuntu-bionic--20200403-E95420.svg?style=flat-square&logo=Ubuntu
[ubuntu_hub_docker]: https://hub.docker.com/_/ubuntu/ "Ubuntu Image"
[docker_build_badge]: https://img.shields.io/docker/build/cdalvaro/saltstack-master?logo=docker&style=flat-square
[docker_hub]: https://hub.docker.com/r/cdalvaro/saltstack-master/builds
[codefactor_badge]: https://www.codefactor.io/repository/github/cdalvaro/saltstack-master/badge?style=flat-square
[codefactor_score]: https://www.codefactor.io/repository/github/cdalvaro/saltstack-master

View File

@@ -1 +1 @@
3000.3 3000.3_1

View File

@@ -5,9 +5,9 @@ set -e
# Execute a command as SALT_USER # Execute a command as SALT_USER
function exec_as_salt() function exec_as_salt()
{ {
if [[ $(whoami) == ${SALT_USER} ]]; then if [[ $(whoami) == "${SALT_USER}" ]]; then
$@ $@
else else
sudo -HEu ${SALT_USER} "$@" sudo -HEu "${SALT_USER}" "$@"
fi fi
} }

View File

@@ -2,39 +2,41 @@
set -e set -e
source ${SALT_BUILD_DIR}/functions.sh source "${SALT_BUILD_DIR}/functions.sh"
# Install build dependencies # Install build dependencies
echo "Installing dependencies ..." echo "Installing dependencies ..."
BUILD_DEPENDENCIES="cmake gcc g++ make \ BUILD_DEPENDENCIES=(
libhttp-parser-dev libssl-dev zlib1g-dev \ cmake gcc g++ make \
libcurl4-openssl-dev libffi-dev swig" libhttp-parser-dev libssl-dev zlib1g-dev \
libcurl4-openssl-dev libffi-dev swig \
)
apt-get update apt-get update
DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet --no-install-recommends ${BUILD_DEPENDENCIES} DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet --no-install-recommends "${BUILD_DEPENDENCIES[@]}"
# Create salt user # Create salt user
echo "Creating ${SALT_USER} user ..." echo "Creating ${SALT_USER} user ..."
useradd -d ${SALT_HOME} -ms /bin/bash -U -G root,sudo ${SALT_USER} useradd -d "${SALT_HOME}" -ms /bin/bash -U -G root,sudo,shadow "${SALT_USER}"
# Set PATH # Set PATH
exec_as_salt cat >> ${SALT_HOME}/.profile <<EOF exec_as_salt cat >> "${SALT_HOME}/.profile" <<EOF
PATH=/usr/local/sbin:/usr/local/bin:\$PATH PATH=/usr/local/sbin:/usr/local/bin:\$PATH
EOF EOF
# Compile libssh2 # Compile libssh2
echo "Building libssh2 v${LIBSSH2_VERSION} ..." echo "Building libssh2 v${LIBSSH2_VERSION} ..."
wget https://github.com/libssh2/libssh2/archive/libssh2-${LIBSSH2_VERSION}.tar.gz wget "https://github.com/libssh2/libssh2/archive/libssh2-${LIBSSH2_VERSION}.tar.gz"
tar xzf libssh2-${LIBSSH2_VERSION}.tar.gz tar xzf "libssh2-${LIBSSH2_VERSION}.tar.gz"
cd libssh2-libssh2-${LIBSSH2_VERSION}/ cd "libssh2-libssh2-${LIBSSH2_VERSION}/"
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DENABLE_ZLIB_COMPRESSION=ON . cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DENABLE_ZLIB_COMPRESSION=ON .
cmake --build . --target install cmake --build . --target install
# Compile libgit2 # Compile libgit2
echo "Building libgit2 v${LIBGIT2_VERSION} ..." echo "Building libgit2 v${LIBGIT2_VERSION} ..."
wget https://github.com/libgit2/libgit2/archive/v${LIBGIT2_VERSION}.tar.gz wget "https://github.com/libgit2/libgit2/archive/v${LIBGIT2_VERSION}.tar.gz"
tar xzf v${LIBGIT2_VERSION}.tar.gz tar xzf "v${LIBGIT2_VERSION}.tar.gz"
cd libgit2-${LIBGIT2_VERSION}/ cd "libgit2-${LIBGIT2_VERSION}/"
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_CLAR=OFF -DTHREADSAFE=ON . cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=ON -DBUILD_CLAR=OFF -DTHREADSAFE=ON .
cmake --build . --target install cmake --build . --target install
@@ -42,7 +44,7 @@ cmake --build . --target install
echo "Installing python3 packages ..." echo "Installing python3 packages ..."
DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet --no-install-recommends \ DEBIAN_FRONTEND=noninteractive apt-get install --yes --quiet --no-install-recommends \
python3-mako python3-pycryptodome python3-cherrypy3 python3-git python3-u-msgpack \ python3-mako python3-pycryptodome python3-cherrypy3 python3-git python3-u-msgpack \
python3-redis python3-gnupg python3-mysqldb python3-dateutil python3-libnacl python3-redis python3-gnupg python3-mysqldb python3-dateutil python3-libnacl python3-openssl
# Install pip3 python packages # Install pip3 python packages
echo "Installing pip3 python packages ..." echo "Installing pip3 python packages ..."
@@ -54,21 +56,26 @@ pip3 install "pygit2==v${PYGIT2_VERSION}" \
## -M: install Salt Master by default ## -M: install Salt Master by default
## -N: Do not install salt-minion ## -N: Do not install salt-minion
## -X: Do not start daemons after installation ## -X: Do not start daemons after installation
## -d: Disables checking if Salt services are enabled to start on system boot
## -P: Allow pip based installations ## -P: Allow pip based installations
## -p: Extra-package to install
## -x: Changes the python version used to install a git version of salt ## -x: Changes the python version used to install a git version of salt
SALT_BOOTSTRAP_OPTS="-M -N -X -P -x python${PYTHON_VERSION}" SALT_BOOTSTRAP_OPTS=( -M -N -X -d -P -p salt-api -x "python${PYTHON_VERSION}" )
echo "Installing saltstack ..." echo "Installing saltstack ..."
echo "Option: ${SALT_BOOTSTRAP_OPTS[@]}"
wget -O bootstrap-salt.sh https://bootstrap.saltstack.com wget -O bootstrap-salt.sh https://bootstrap.saltstack.com
sh bootstrap-salt.sh ${SALT_BOOTSTRAP_OPTS} git v${SALT_VERSION} sh bootstrap-salt.sh ${SALT_BOOTSTRAP_OPTS[@]} git "v${SALT_VERSION}"
chown -R ${SALT_USER}: ${SALT_ROOT_DIR} chown -R "${SALT_USER}": "${SALT_ROOT_DIR}"
# Configure ssh # Configure ssh
echo "Configuring ssh ..." echo "Configuring ssh ..."
sed -i -e "s|^[# ]*StrictHostKeyChecking.*$| StrictHostKeyChecking no|" /etc/ssh/ssh_config sed -i -e "s|^[# ]*StrictHostKeyChecking.*$| StrictHostKeyChecking no|" /etc/ssh/ssh_config
echo " UserKnownHostsFile /dev/null" >> /etc/ssh/ssh_config {
echo " LogLevel ERROR" >> /etc/ssh/ssh_config echo " UserKnownHostsFile /dev/null"
echo "# IdentityFile salt_ssh_key" >> /etc/ssh/ssh_config echo " LogLevel ERROR"
echo "# IdentityFile salt_ssh_key"
} >> /etc/ssh/ssh_config
# Configure logrotate # Configure logrotate
echo "Configuring logrotate ..." echo "Configuring logrotate ..."
@@ -89,7 +96,7 @@ priority=5
directory=${SALT_HOME} directory=${SALT_HOME}
environment=HOME=${SALT_HOME} environment=HOME=${SALT_HOME}
command=/usr/local/bin/salt-master command=/usr/local/bin/salt-master
user=${SALT_USER} user=root
autostart=true autostart=true
autorestart=true autorestart=true
stopsignal=QUIT stopsignal=QUIT

View File

@@ -3,6 +3,9 @@
DEBUG=${DEBUG:-false} DEBUG=${DEBUG:-false}
TIMEZONE=${TIMEZONE:-UTC} TIMEZONE=${TIMEZONE:-UTC}
SALT_API_SERVICE_ENABLED=${SALT_API_SERVICE_ENABLED:-false}
SALT_API_USER=${SALT_API_USER:-salt_api}
SALT_LOG_ROTATE_FREQUENCY=${SALT_LOG_ROTATE_FREQUENCY:-weekly} SALT_LOG_ROTATE_FREQUENCY=${SALT_LOG_ROTATE_FREQUENCY:-weekly}
SALT_LOG_ROTATE_RETENTION=${SALT_LOG_ROTATE_RETENTION:-52} SALT_LOG_ROTATE_RETENTION=${SALT_LOG_ROTATE_RETENTION:-52}

View File

@@ -1,30 +1,36 @@
#!/usr/bin/env bash #!/usr/bin/env bash
set -e set -e
source ${SALT_RUNTIME_DIR}/env-defaults.sh source "${SALT_RUNTIME_DIR}/env-defaults.sh"
# Execute a command as SALT_USER # Execute a command as SALT_USER
function exec_as_salt() function exec_as_salt()
{ {
if [[ $(whoami) == ${SALT_USER} ]]; then if [[ $(whoami) == "${SALT_USER}" ]]; then
$@ $@
else else
sudo -HEu ${SALT_USER} "$@" sudo -HEu "${SALT_USER}" "$@"
fi fi
} }
# Log error
function log_error()
{
(>2& echo "ERROR: $*")
}
# Map salt user with host user # Map salt user with host user
function map_uidgid() function map_uidgid()
{ {
USERMAP_ORIG_UID=$(id -u ${SALT_USER}) USERMAP_ORIG_UID=$(id -u "${SALT_USER}")
USERMAP_ORIG_GID=$(id -g ${SALT_USER}) USERMAP_ORIG_GID=$(id -g "${SALT_USER}")
USERMAP_GID=${USERMAP_GID:-${USERMAP_UID:-$USERMAP_ORIG_GID}} USERMAP_GID=${USERMAP_GID:-${USERMAP_UID:-$USERMAP_ORIG_GID}}
USERMAP_UID=${USERMAP_UID:-$USERMAP_ORIG_UID} USERMAP_UID=${USERMAP_UID:-$USERMAP_ORIG_UID}
if [[ ${USERMAP_UID} != ${USERMAP_ORIG_UID} ]] || [[ ${USERMAP_GID} != ${USERMAP_ORIG_GID} ]]; then if [[ "${USERMAP_UID}" != "${USERMAP_ORIG_UID}" ]] || [[ "${USERMAP_GID}" != "${USERMAP_ORIG_GID}" ]]; then
echo "Mapping UID and GID for ${SALT_USER}:${SALT_USER} to ${USERMAP_UID}:${USERMAP_GID} ..." echo "Mapping UID and GID for ${SALT_USER}:${SALT_USER} to ${USERMAP_UID}:${USERMAP_GID} ..."
groupmod -o -g ${USERMAP_GID} ${SALT_USER} groupmod -o -g "${USERMAP_GID}" "${SALT_USER}"
sed -i -e "s|:${USERMAP_ORIG_UID}:${USERMAP_GID}:|:${USERMAP_UID}:${USERMAP_GID}:|" /etc/passwd sed -i -e "s|:${USERMAP_ORIG_UID}:${USERMAP_GID}:|:${USERMAP_UID}:${USERMAP_GID}:|" /etc/passwd
find ${SALT_HOME} -path ${SALT_DATA_DIR}/\* \( ! -uid ${USERMAP_ORIG_UID} -o ! -gid ${USERMAP_ORIG_GID} \) -print0 | xargs -0 chown -h ${SALT_USER}: ${SALT_HOME} find "${SALT_HOME}" -path "${SALT_DATA_DIR}/*" \( ! -uid "${USERMAP_ORIG_UID}" -o ! -gid "${USERMAP_ORIG_GID}" \) -print0 | xargs -0 chown -h "${SALT_USER}": "${SALT_HOME}"
fi fi
} }
@@ -36,25 +42,25 @@ function update_template()
local FILE=${1?missing argument} local FILE=${1?missing argument}
shift shift
[[ ! -f ${FILE} ]] && return 1 [[ ! -f "${FILE}" ]] && return 1
local VARIABLES=($@) local VARIABLES=( "$@" )
local USR=$(stat -c %U ${FILE}) local USR=$(stat -c %U "${FILE}")
local tmp_file=$(mktemp) local tmp_file=$(mktemp)
cp -a "${FILE}" ${tmp_file} cp -a "${FILE}" "${tmp_file}"
local variables local variables
for variable in ${VARIABLES[@]}; do for variable in "${VARIABLES[@]}"; do
sed -ri "s|[{}]{2}$variable[}]{2}|\${$variable}|g" ${tmp_file} sed -ri "s|[{}]{2}${variable}[}]{2}|\${${variable}}|g" "${tmp_file}"
done done
# Replace placeholders # Replace placeholders
( (
export ${VARIABLES[@]} export "${VARIABLES[@]}"
local IFS=":"; sudo -HEu ${USR} envsubst "${VARIABLES[*]/#/$}" < ${tmp_file} > ${FILE} local IFS=":"; sudo -HEu "${USR}" envsubst "${VARIABLES[*]/#/$}" < "${tmp_file}" > "${FILE}"
) )
rm -f ${tmp_file} rm -f "${tmp_file}"
} }
# This function configures containers timezone # This function configures containers timezone
@@ -63,14 +69,14 @@ function configure_timezone()
echo "Configuring container timezone ..." echo "Configuring container timezone ..."
# Perform sanity check of provided timezone value # Perform sanity check of provided timezone value
if [ -e /usr/share/zoneinfo/${TIMEZONE} ]; then if [ -e "/usr/share/zoneinfo/${TIMEZONE}" ]; then
echo "Setting TimeZone -> ${TIMEZONE} ..." echo "Setting TimeZone -> ${TIMEZONE} ..."
# Set localtime # Set localtime
ln -snf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime ln -snf "/usr/share/zoneinfo/${TIMEZONE}" /etc/localtime
# Set timezone # Set timezone
echo ${TIMEZONE} > /etc/timezone echo "${TIMEZONE}" > /etc/timezone
else else
echo "Timezone: '${TIMEZONE}' is not valid. Check available timezones at: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones" echo "Timezone: '${TIMEZONE}' is not valid. Check available timezones at: https://en.wikipedia.org/wiki/List_of_tz_database_time_zones"
return 1 return 1
@@ -82,39 +88,40 @@ function gen_signed_keys()
{ {
local key_name=${1:-master} local key_name=${1:-master}
mkdir -p ${SALT_KEYS_DIR}/generated/ mkdir -p "${SALT_KEYS_DIR}/generated/"
GENERATED_KEYS_DIR=$(mktemp -d -p ${SALT_KEYS_DIR}/generated/ -t ${key_name}.XXXXX) GENERATED_KEYS_DIR=$(mktemp -d -p "${SALT_KEYS_DIR}/generated/" -t "${key_name}.XXXXX")
salt-key --gen-keys ${key_name} --gen-keys-dir ${GENERATED_KEYS_DIR} > /dev/null 2>&1 salt-key --gen-keys "${key_name}" --gen-keys-dir "${GENERATED_KEYS_DIR}" > /dev/null 2>&1
salt-key --gen-signature --auto-create --pub ${GENERATED_KEYS_DIR}/${key_name}.pub --signature-path ${GENERATED_KEYS_DIR} > /dev/null 2>&1 salt-key --gen-signature --auto-create --pub "${GENERATED_KEYS_DIR}/${key_name}.pub" --signature-path "${GENERATED_KEYS_DIR}" > /dev/null 2>&1
echo -n ${GENERATED_KEYS_DIR} echo -n "${GENERATED_KEYS_DIR}"
} }
# This function repairs keys permissions and creates keys if neaded # This function repairs keys permissions and creates keys if neaded
function setup_salt_keys() function setup_salt_keys()
{ {
echo "Setting up salt keys ..." echo "Setting up salt keys ..."
if [ ! -f ${SALT_KEYS_DIR}/master.pem ]; then if [ ! -f "${SALT_KEYS_DIR}/master.pem" ]; then
echo "Generating keys ..." echo "Generating keys ..."
salt-key --gen-keys master --gen-keys-dir ${SALT_KEYS_DIR} salt-key --gen-keys master --gen-keys-dir "${SALT_KEYS_DIR}"
fi fi
if [ ! -f "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.pem" ] && [ ${SALT_MASTER_SIGN_PUBKEY} == True ]; then if [ ! -f "${SALT_KEYS_DIR}/${SALT_MASTER_SIGN_KEY_NAME}.pem" ] && [ "${SALT_MASTER_SIGN_PUBKEY}" == True ]; then
echo "Generating signed keys ..." echo "Generating signed keys ..."
salt-key --gen-signature --auto-create --pub ${SALT_KEYS_DIR}/master.pub --signature-path ${SALT_KEYS_DIR} salt-key --gen-signature --auto-create --pub "${SALT_KEYS_DIR}/master.pub" --signature-path "${SALT_KEYS_DIR}"
fi fi
for pub_key in $(find ${SALT_KEYS_DIR} -maxdepth 1 -type f); do while IFS= read -r -d '' pub_key
if [[ ${pub_key} =~ .*\.pem$ ]]; then do
chmod 400 ${pub_key} if [[ "${pub_key}" =~ .*\.pem$ ]]; then
chmod 400 "${pub_key}"
else else
chmod 644 ${pub_key} chmod 644 "${pub_key}"
fi fi
done done < <(find "${SALT_KEYS_DIR}" -maxdepth 1 -type f -print0)
find ${SALT_KEYS_DIR}/minions* -maxdepth 1 -type f -exec chmod 644 {} \; find "${SALT_KEYS_DIR}/minions"* -maxdepth 1 -type f -exec chmod 644 {} \;
find ${SALT_HOME} -path ${SALT_KEYS_DIR}/\* -prune -o -print0 | xargs -0 chown -h ${SALT_USER}: find "${SALT_HOME}" -path "${SALT_KEYS_DIR}/*" -prune -o -print0 | xargs -0 chown -h "${SALT_USER}":
} }
# This function configures ssh keys # This function configures ssh keys
@@ -135,16 +142,16 @@ function setup_ssh_keys()
fi fi
} }
# This functions cofigures master service # This function cofigures master service
function configure_salt_master() function configure_salt_master()
{ {
echo "Configuring salt-master ..." echo "Configuring salt-master service ..."
# https://docs.saltstack.com/en/latest/ref/configuration/master.html # https://docs.saltstack.com/en/latest/ref/configuration/master.html
exec_as_salt cp -p ${SALT_RUNTIME_DIR}/config/master.yml ${SALT_ROOT_DIR}/master exec_as_salt cp -p "${SALT_RUNTIME_DIR}/config/master.yml" "${SALT_ROOT_DIR}/master"
# Update main configuration # Update main configuration
update_template ${SALT_ROOT_DIR}/master \ update_template "${SALT_ROOT_DIR}/master" \
SALT_USER \ SALT_USER \
SALT_LOG_LEVEL \ SALT_LOG_LEVEL \
SALT_LEVEL_LOGFILE \ SALT_LEVEL_LOGFILE \
@@ -155,13 +162,73 @@ function configure_salt_master()
SALT_KEYS_DIR SALT_KEYS_DIR
# Update keys configuration # Update keys configuration
update_template ${SALT_ROOT_DIR}/master \ update_template "${SALT_ROOT_DIR}/master" \
SALT_MASTER_SIGN_PUBKEY \ SALT_MASTER_SIGN_PUBKEY \
SALT_MASTER_SIGN_KEY_NAME \ SALT_MASTER_SIGN_KEY_NAME \
SALT_MASTER_PUBKEY_SIGNATURE \ SALT_MASTER_PUBKEY_SIGNATURE \
SALT_MASTER_USE_PUBKEY_SIGNATURE SALT_MASTER_USE_PUBKEY_SIGNATURE
} }
# This function configures salt-api if service is set to be enabled
function configure_salt_api()
{
[[ ${SALT_API_SERVICE_ENABLED} == true ]] || return 0
if [[ -n "${SALT_API_USER}" ]]; then
if [[ ${SALT_API_USER} == "${SALT_USER}" ]]; then
log_error "SALT_API_USER cannot be the same as '${SALT_USER}'"
return 1
fi
if [[ -z "${SALT_API_USER_PASS}" ]]; then
log_error "SALT_API_USER_PASS env variable must be set to create '${SALT_API_USER}' user"
return 2
fi
echo "Creating '${SALT_API_USER}' user for salt-api ..."
adduser --quiet --disabled-password --gecos "Salt API" "${SALT_API_USER}"
echo "${SALT_API_USER}:${SALT_API_USER_PASS}" | chpasswd
unset SALT_API_USER_PASS
fi
echo "Configuring salt-api service ..."
CERTS_PATH=/etc/pki
rm -rf "${CERTS_PATH}/tls/certs/*"
salt-call --local tls.create_self_signed_cert cacert_path="${CERTS_PATH}" CN=docker-salt-master
cat >> "${SALT_ROOT_DIR}/master" <<EOF
##### salt-api settings #####
##########################################
# Basic configuration for salt-api
api_logfile: ${SALT_LOGS_DIR}/salt/api
rest_cherrypy:
port: 8000
ssl_crt: /etc/pki/tls/certs/docker-salt-master.crt
ssl_key: /etc/pki/tls/certs/docker-salt-master.key
EOF
# configure supervisord to start salt-api
cat > /etc/supervisor/conf.d/salt-api.conf <<EOF
[program:salt-api]
priority=5
directory=${SALT_HOME}
environment=HOME=${SALT_HOME}
command=/usr/local/bin/salt-api
user=root
autostart=true
autorestart=true
stopsignal=QUIT
stdout_logfile=${SALT_LOGS_DIR}/supervisor/%(program_name)s.log
stderr_logfile=${SALT_LOGS_DIR}/supervisor/%(program_name)s.log
EOF
}
# Initializes main directories # Initializes main directories
function initialize_datadir() function initialize_datadir()
{ {
@@ -169,32 +236,32 @@ function initialize_datadir()
# This symlink simplifies paths for loading sls files # This symlink simplifies paths for loading sls files
[[ -d /srv ]] && [[ ! -L /srv ]] && rm -rf /srv [[ -d /srv ]] && [[ ! -L /srv ]] && rm -rf /srv
ln -sfnv ${SALT_BASE_DIR} /srv ln -sfnv "${SALT_BASE_DIR}" /srv
# Set Salt root permissions # Set Salt root permissions
chown -R ${SALT_USER}: ${SALT_ROOT_DIR} chown -R "${SALT_USER}": "${SALT_ROOT_DIR}"
# Set Salt run permissions # Set Salt run permissions
mkdir -p /var/run/salt mkdir -p /var/run/salt
chown -R ${SALT_USER}: /var/run/salt chown -R "${SALT_USER}": /var/run/salt
# Set cache permissions # Set cache permissions
mkdir -p /var/cache/salt/master mkdir -p /var/cache/salt/master
chown -R ${SALT_USER}: /var/cache/salt chown -R "${SALT_USER}": /var/cache/salt
# Keys directories # Keys directories
mkdir -p ${SALT_KEYS_DIR}/minions mkdir -p "${SALT_KEYS_DIR}/minions"
chown -R ${SALT_USER}: ${SALT_KEYS_DIR} chown -R "${SALT_USER}": "${SALT_KEYS_DIR}"
# Logs directory # Logs directory
mkdir -p ${SALT_LOGS_DIR}/salt ${SALT_LOGS_DIR}/supervisor mkdir -p "${SALT_LOGS_DIR}/salt" "${SALT_LOGS_DIR}/supervisor"
chmod -R 0755 ${SALT_LOGS_DIR}/supervisor chmod -R 0755 "${SALT_LOGS_DIR}/supervisor"
chown -R root: ${SALT_LOGS_DIR}/supervisor chown -R root: "${SALT_LOGS_DIR}/supervisor"
[[ -d /var/log/salt ]] && [[ ! -L /var/log/salt ]] && rm -rf /var/log/salt [[ -d /var/log/salt ]] && [[ ! -L /var/log/salt ]] && rm -rf /var/log/salt
mkdir -p ${SALT_LOGS_DIR}/salt /var/log mkdir -p "${SALT_LOGS_DIR}/salt" /var/log
ln -sfnv ${SALT_LOGS_DIR}/salt /var/log/salt ln -sfnv "${SALT_LOGS_DIR}/salt" /var/log/salt
chown -R ${SALT_USER}: ${SALT_LOGS_DIR}/salt chown -R "${SALT_USER}": "${SALT_LOGS_DIR}/salt"
} }
# Configures logrotate # Configures logrotate
@@ -243,6 +310,21 @@ ${SALT_LOGS_DIR}/salt/key {
notifempty notifempty
} }
EOF EOF
if [[ "${SALT_API_SERVICE_ENABLED}" == true ]]; then
# configure salt-api log rotation
cat >> /etc/logrotate.d/salt <<EOF
${SALT_LOGS_DIR}/salt/api {
${SALT_LOG_ROTATE_FREQUENCY}
missingok
rotate ${SALT_LOG_ROTATE_RETENTION}
compress
notifempty
}
EOF
fi
} }
# Initializes the system # Initializes the system
@@ -253,6 +335,7 @@ function initialize_system()
configure_logrotate configure_logrotate
configure_timezone configure_timezone
configure_salt_master configure_salt_master
configure_salt_api
setup_salt_keys setup_salt_keys
setup_ssh_keys setup_ssh_keys
rm -rf /var/run/supervisor.sock rm -rf /var/run/supervisor.sock

View File

@@ -3,7 +3,7 @@ version: '3'
services: services:
master: master:
container_name: salt_master container_name: salt_master
image: cdalvaro/saltstack-master:3000.3 image: cdalvaro/saltstack-master:3000.3_1
restart: always restart: always
volumes: volumes:
- "roots/:/home/salt/data/srv" - "roots/:/home/salt/data/srv"

View File

@@ -1,29 +1,45 @@
#!/usr/bin/env bash #!/bin/bash
set -e set -e
set -o pipefail
source "${SALT_RUNTIME_DIR}/functions.sh" source "${SALT_RUNTIME_DIR}/functions.sh"
[[ ${DEBUG} == true ]] && set -x [[ "${DEBUG}" == true ]] && set -x
case ${1} in case "${1}" in
app:start|app:init|app:gen-signed-keys) app:start|app:gen-signed-keys)
initialize_system initialize_system
case ${1} in case "${1}" in
app:start) app:start)
echo "Starting salt-master..." echo "Starting supervisord ..."
exec /usr/bin/supervisord -nc /etc/supervisor/supervisord.conf exec /usr/bin/supervisord -nc /etc/supervisor/supervisord.conf
;; ;;
app:gen-signed-keys) app:gen-signed-keys)
shift 1 shift 1
gen_signed_keys ${1} gen_signed_keys "${1}"
;;
esac
;;
app:restart)
shift 1
case "${1}" in
salt-master|salt-api)
echo "Restarting ${1} service ..."
exec pkill "${1}"
;;
*)
log_error "Unable to restart ${1} serice. Service is unknown"
exit 1
;; ;;
esac esac
;; ;;
app:help) app:help)
echo "Available options:" echo "Available options:"
echo " app:start - Start salt-master service. (default)" echo " app:start - Start configured services. (default)"
echo " app:restart - Restart the specified service on a running container. Choices: salt-master, salt-api"
echo " app:gen-signed-keys <key_name> - Create a master_sign key pair and its signature inside ${SALT_KEYS_DIR}/generated/" echo " app:gen-signed-keys <key_name> - Create a master_sign key pair and its signature inside ${SALT_KEYS_DIR}/generated/"
echo " app:help - Displays this help." echo " app:help - Displays this help."
echo " [command] - Execute the specified command, eg. bash." echo " [command] - Execute the specified command, eg. bash."

View File

@@ -3,7 +3,10 @@
# Docker Daemon Build Hook # Docker Daemon Build Hook
# $IMAGE_NAME var is injected into the build so the tag is correct. # $IMAGE_NAME var is injected into the build so the tag is correct.
docker pull ${DOCKER_REPO}:latest
docker build \ docker build \
--cache-from=${DOCKER_REPO}:latest \
--build-arg=BUILD_DATE="$(date +"%Y-%m-%d %H:%M:%S%:z")" \ --build-arg=BUILD_DATE="$(date +"%Y-%m-%d %H:%M:%S%:z")" \
--build-arg=VCS_REF="$(git rev-parse --short HEAD)" \ --build-arg=VCS_REF="$(git rev-parse --short HEAD)" \
-t ${IMAGE_NAME} . -t ${IMAGE_NAME} .