diff --git a/.github/workflows/dockerimage.yml b/.github/workflows/dockerimage.yml index 6003798..84011c0 100644 --- a/.github/workflows/dockerimage.yml +++ b/.github/workflows/dockerimage.yml @@ -12,3 +12,6 @@ jobs: - uses: actions/checkout@v2 - name: Build the latest Docker image run: make build + - name: Build the TLS Docker image + run: make build-tls + diff --git a/Dockerfile b/Dockerfile index cafda1b..6bb6f52 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,19 +4,19 @@ FROM redis@sha256:e422889e156ebea83856b6ff973bfe0c86bce867d80def228044eeecf92559 LABEL maintainer="Johan Andersson " # Some Environment Variables -ENV HOME /root -ENV DEBIAN_FRONTEND noninteractive +ENV HOME=/root +ENV DEBIAN_FRONTEND=noninteractive # Install system dependencies RUN apt-get update -qq && \ apt-get install --no-install-recommends -yqq \ - net-tools supervisor ruby rubygems locales gettext-base wget gcc make g++ build-essential libc6-dev tcl && \ + net-tools supervisor ruby rubygems locales locales-all gettext-base wget gcc make g++ build-essential libc6-dev tcl && \ apt-get clean -yqq # # Ensure UTF-8 lang and locale +ENV LANG=en_US.UTF-8 +ENV LC_ALL=en_US.UTF-8 RUN locale-gen en_US.UTF-8 -ENV LANG en_US.UTF-8 -ENV LC_ALL en_US.UTF-8 # Necessary for gem installs due to SHA1 being weak and old cert being revoked ENV SSL_CERT_FILE=/usr/local/etc/openssl/cert.pem diff --git a/Dockerfile.tls b/Dockerfile.tls new file mode 100644 index 0000000..e4b870b --- /dev/null +++ b/Dockerfile.tls @@ -0,0 +1,52 @@ +# Build based on redis:7.2.5 from "2024-05-22T23:17:59Z" +FROM redis@sha256:e422889e156ebea83856b6ff973bfe0c86bce867d80def228044eeecf925592b + +LABEL maintainer="Johan Andersson " + +# Some Environment Variables +ENV HOME=/root +ENV DEBIAN_FRONTEND=noninteractive + +# Install system dependencies +RUN apt-get update -qq && \ + apt-get install --no-install-recommends -yqq \ + net-tools supervisor ruby rubygems locales locales-all gettext-base wget gcc make g++ build-essential libc6-dev tcl libssl-dev && \ + apt-get clean -yqq + +# # Ensure UTF-8 lang and locale +ENV LANG=en_US.UTF-8 +ENV LC_ALL=en_US.UTF-8 +RUN locale-gen en_US.UTF-8 + +# Necessary for gem installs due to SHA1 being weak and old cert being revoked +ENV SSL_CERT_FILE=/usr/local/etc/openssl/cert.pem + +RUN gem install redis -v 4.1.3 + +# This will always build the latest release/commit in the 7.2 branch +ARG redis_version=7.2 + +RUN wget -qO redis.tar.gz https://github.com/redis/redis/tarball/${redis_version} \ + && tar xfz redis.tar.gz -C / \ + && mv /redis-* /redis + +RUN (cd /redis && make BUILD_TLS=yes) + +RUN mkdir /redis-conf && mkdir /redis-data + +COPY redis-cluster.tmpl /redis-conf/redis-cluster.tmpl +COPY redis.tmpl /redis-conf/redis.tmpl +COPY sentinel.tmpl /redis-conf/sentinel.tmpl + +# Add startup script +COPY docker-entrypoint.sh /docker-entrypoint.sh + +# Add script that generates supervisor conf file based on environment variables +COPY generate-supervisor-conf.sh /generate-supervisor-conf.sh + +RUN chmod 755 /docker-entrypoint.sh + +EXPOSE 7000 7001 7002 7003 7004 7005 7006 7007 5000 5001 5002 + +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["redis-cluster"] diff --git a/Makefile b/Makefile index 582283f..77dc1af 100644 --- a/Makefile +++ b/Makefile @@ -8,7 +8,10 @@ help: @echo " cli run redis-cli inside the container on the server with port 7000" build: - docker-compose build + docker compose build + +build-tls: + docker compose -f docker-compose.tls.yml build up: docker-compose up diff --git a/README.md b/README.md index 3dcea6a..b51033a 100644 --- a/README.md +++ b/README.md @@ -200,6 +200,38 @@ Unfortunately Docker does not handle IPv6 NAT so, when acceptable, `--network ho # Example using plain docker docker run -e "IP=::1" -e "BIND_ADDRESS=::" --network host grokzen/redis-cluster:latest +## Enable authentication +By default, authentication using a password is disabled. +This is configurable by an enviroment variable that specifies which a password string is required to authenticate itself using the AUTH command. + +| Environment variable | Default | +| -------------------- | ------------------------------------------ | +| `PASSWORD` | "" (requirepass/masterauth not configured) | + +## Announced hostname +By default, no announced hostname is set. +This is configurable by an enviroment variable that specifies which show hostname as additional metadata in the CLUSTER SLOTS command. This valus is communicated along the clusterbus to all nodes, setting it to an empty string will remove the hostname and also propagate the removal. + +| Environment variable | Default | +| -------------------- | ------------------------------------------ | +| `CLUSTER_ANNOUNCE_HOSTNAME` | "" (empty announced hostname) | + +## Protected mode +By default, Protected mode is enabled. +This is configurable by an enviroment variable that specifies which the system administator can still ignore the error given by Redis and just disable protected mode or manually bind all the interfaces. + +| Environment variable | Default | +| -------------------- | ------------------------------------------ | +| `PROTECTED_MODE` | "" (protected-mode is yes) | + +## Clean redis-data before start redis +By default, All file in folder redis-data will be remove before start redis. +This is configurable by an enviroment variable that specifies which the system administator can handle remove/keep all file in redis-data before start redis. + +| Environment variable | Default | +| -------------------- | ------------------------------------------ | +| `RESET_DATA` | "" (redis-data will be clean) | + ## Build alternative redis versions diff --git a/docker-compose.tls.yml b/docker-compose.tls.yml new file mode 100644 index 0000000..719853f --- /dev/null +++ b/docker-compose.tls.yml @@ -0,0 +1,17 @@ +version: '2' +services: + redis-cluster: + image: redis-cluster:tls-latest + environment: + IP: ${REDIS_CLUSTER_IP} + SENTINEL: ${REDIS_USE_SENTINEL} + STANDALONE: ${REDIS_USE_STANDALONE} + build: + context: . + dockerfile: ./Dockerfile.tls + args: + redis_version: '7.2.5' + hostname: server + ports: + - '7000-7050:7000-7050' + - '5000-5010:5000-5010' diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index 0b2e67f..696be88 100644 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -42,24 +42,51 @@ if [ "$1" = 'redis-cluster' ]; then for port in $(seq $INITIAL_PORT $max_port); do mkdir -p /redis-conf/${port} mkdir -p /redis-data/${port} - - if [ -e /redis-data/${port}/nodes.conf ]; then - rm /redis-data/${port}/nodes.conf + if [ -n "$RESET_DATA" -a "$RESET_DATA" = "false" ]; then + if [ ! -e /redis-data/${port}/nodes.conf ]; then + RESET_DATA="true" + fi fi + done - if [ -e /redis-data/${port}/dump.rdb ]; then - rm /redis-data/${port}/dump.rdb + for port in $(seq $INITIAL_PORT $max_port); do + if [ -z "$RESET_DATA" -o "$RESET_DATA" = "true" ]; then + if [ -e /redis-data/${port}/nodes.conf ]; then + rm /redis-data/${port}/nodes.conf + fi + + if [ -e /redis-data/${port}/dump.rdb ]; then + rm /redis-data/${port}/dump.rdb + fi + + if [ -e /redis-data/${port}/appendonly.aof ]; then + rm /redis-data/${port}/appendonly.aof + fi fi + if [ -z "$PROTECTED_MODE" -o "$PROTECTED_MODE" = "true" ]; then + protectedmode="protected-mode yes" + elif [ "$PROTECTED_MODE" = "false" ]; then + protectedmode="protected-mode no" + fi + - if [ -e /redis-data/${port}/appendonly.aof ]; then - rm /redis-data/${port}/appendonly.aof + if [ -n "$CLUSTER_ANNOUNCE_HOSTNAME" ]; then + clusterannouncehostname="cluster-announce-hostname '${CLUSTER_ANNOUNCE_HOSTNAME}'" + clusterpreferedendpointtype="cluster-preferred-endpoint-type hostname" fi if [ "$port" -lt "$first_standalone" ]; then - PORT=${port} BIND_ADDRESS=${BIND_ADDRESS} envsubst < /redis-conf/redis-cluster.tmpl > /redis-conf/${port}/redis.conf + if [ -n "$PASSWORD" ]; then + requirepass="requirepass '${PASSWORD}'" + masterauth="masterauth '${PASSWORD}'" + fi + PORT=${port} BIND_ADDRESS=${BIND_ADDRESS} REQUIREPASS=${requirepass} MASTERAUTH=${masterauth} PROTECTED_MODE=${protectedmode} CLUSTER_ANNOUNCE_HOSTNAME=${clusterannouncehostname} CLUSTER_PREFERED_ENDPOINT_TYPE=${clusterpreferedendpointtype} envsubst < /redis-conf/redis-cluster.tmpl > /redis-conf/${port}/redis.conf nodes="$nodes $IP:$port" else - PORT=${port} BIND_ADDRESS=${BIND_ADDRESS} envsubst < /redis-conf/redis.tmpl > /redis-conf/${port}/redis.conf + if [ -n "$PASSWORD" ]; then + requirepass="requirepass '${PASSWORD}'" + fi + PORT=${port} BIND_ADDRESS=${BIND_ADDRESS} REQUIREPASS=${requirepass} PROTECTED_MODE=${protectedmode} envsubst < /redis-conf/redis.tmpl > /redis-conf/${port}/redis.conf fi if [ "$port" -lt $(($INITIAL_PORT + $MASTERS)) ]; then @@ -80,21 +107,27 @@ if [ "$1" = 'redis-cluster' ]; then ## Check the version of redis-cli and if we run on a redis server below 5.0 ## If it is below 5.0 then we use the redis-trib.rb to build the cluster # - /redis/src/redis-cli --version | grep -E "redis-cli 3.0|redis-cli 3.2|redis-cli 4.0" - - if [ $? -eq 0 ] - then - echo "Using old redis-trib.rb to create the cluster" - echo "yes" | eval ruby /redis/src/redis-trib.rb create --replicas "$SLAVES_PER_MASTER" "$nodes" - else - echo "Using redis-cli to create the cluster" - echo "yes" | eval /redis/src/redis-cli --cluster create --cluster-replicas "$SLAVES_PER_MASTER" "$nodes" - fi + if [ -z "$RESET_DATA" -o "$RESET_DATA" = "true" ]; then + /redis/src/redis-cli --version | grep -E "redis-cli 3.0|redis-cli 3.2|redis-cli 4.0" + if [ $? -eq 0 ] + then + echo "Using old redis-trib.rb to create the cluster" + echo "yes" | eval ruby /redis/src/redis-trib.rb create --replicas "$SLAVES_PER_MASTER" "$nodes" + else + echo "Using redis-cli to create the cluster" + if [ -z "$PASSWORD" ]; then + echo "yes" | eval /redis/src/redis-cli --cluster create --cluster-replicas "$SLAVES_PER_MASTER" "$nodes" + password_arg="-a $PASSWORD" + else + echo "yes" | eval /redis/src/redis-cli --cluster create --cluster-replicas "$SLAVES_PER_MASTER" -a "$PASSWORD" "$nodes" + fi + fi - if [ "$SENTINEL" = "true" ]; then - for port in $(seq $INITIAL_PORT $(($INITIAL_PORT + $MASTERS))); do - redis-sentinel /redis-conf/sentinel-${port}.conf & - done + if [ "$SENTINEL" = "true" ]; then + for port in $(seq $INITIAL_PORT $(($INITIAL_PORT + $MASTERS))); do + redis-sentinel /redis-conf/sentinel-${port}.conf & + done + fi fi tail -f /var/log/supervisor/redis*.log diff --git a/redis-cluster.tmpl b/redis-cluster.tmpl index ebc9ce7..6dc58a4 100644 --- a/redis-cluster.tmpl +++ b/redis-cluster.tmpl @@ -5,4 +5,8 @@ cluster-config-file nodes.conf cluster-node-timeout 5000 appendonly yes dir /redis-data/${PORT} -protected-mode no +${REQUIREPASS} +${MASTERAUTH} +${PROTECTED_MODE} +${CLUSTER_ANNOUNCE_HOSTNAME} +${CLUSTER_PREFERED_ENDPOINT_TYPE} \ No newline at end of file diff --git a/redis.tmpl b/redis.tmpl index 2081848..64628f6 100644 --- a/redis.tmpl +++ b/redis.tmpl @@ -2,4 +2,5 @@ bind ${BIND_ADDRESS} port ${PORT} appendonly yes dir /redis-data/${PORT} -protected-mode no +${REQUIREPASS} +${PROTECTED_MODE} \ No newline at end of file