關於redis在docker-compose中免密配置的討論

遇到個頗有趣的討論,先看一下這個docker-compose配置腳本:linux

version: '2'

services:
  redis:
    image: 'redis:5.0.3-stretch'
    restart: always
    command: redis-server --requirepass redis
    environment:
      # ALLOW_EMPTY_PASSWORD is recommended only for development.
      - ALLOW_EMPTY_PASSWORD=yes
      - REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
    ports:
      - '6379:6379'
    volumes:
      - 'redis_data:/bitnami/redis/data'

volumes:
  redis_data:
    driver: local

這裏先交代一下出現這樣腳本的背景,由於一直開始的時候使用的是bitnami版本,由於其支持免密配置,在配置開發環境具備很棒的優點,後來爲了安全起見,使用了官方提供的鏡像文件redis:5.0.3-stretch,以後就有了這麼奇怪的配置腳本,既有requirepass,也寫了ALLOW_EMPTY_PASSWORD=yesgit

在這個配置中,在commend中配置passwd:redis,然而又在enviroment中配置了allow_empty_password=yes,那麼這樣的配置,究竟是須要密碼仍是不須要呢?理由又是怎樣呢?github

關於redis在docker-compose中免密配置的討論

解決這個問題,先知道,問題確定不是在docker-compose上,這玩意充其量就是個腳本解析器,一般狀況下,運行指定的參數優先級大於環境變量,環境變量大於配置文件,可事實上真的是這樣嗎?那麼咱們就須要去閱讀redis源碼了。有興趣的能夠本身讀讀看redis源碼redis

src/config.c中寫到,若是是用了requirepass這個命令,則會把password的內容寫進sds中,不過好像仍是沒有說明到environment對於redis配置文件的影響,甚至是說在整份config文件中沒有介紹到對於environment這個變量的調用,所以能夠推斷到開發者應該是沒有考慮使用environmentdocker

關於redis在docker-compose中免密配置的討論

爲了求證這個猜想,由於並不想大海撈針去發掘源碼,因此去看下官方對於config的介紹shell

Passing arguments via the command line
Since Redis 2.6 it is possible to also pass Redis configuration parameters using the command line directly. This is very useful for testing purposes. The following is an example that starts a new Redis instance using port 6380 as a slave of the instance running at 127.0.0.1 port 6379.centos

./redis-server --port 6380 --slaveof 127.0.0.1 6379安全

The format of the arguments passed via the command line is exactly the same as the one used in the redis.conf file, with the exception that the keyword is prefixed with --.bash

Note that internally this generates an in-memory temporary config file (possibly concatenating the config file passed by the user if any) where arguments are translated into the format of redis.conf.ide

Changing Redis configuration while the server is running
It is possible to reconfigure Redis on the fly without stopping and restarting the service, or querying the current configuration programmatically using the special commands CONFIG SET and CONFIG GET

Not all the configuration directives are supported in this way, but most are supported as expected. Please refer to the CONFIG SET and CONFIG GET pages for more information.

Note that modifying the configuration on the fly has no effects on the redis.conf file so at the next restart of Redis the old configuration will be used instead.

Make sure to also modify the redis.conf file accordingly to the configuration you set using CONFIG SET. You can do it manually, or starting with Redis 2.8, you can just use CONFIG REWRITE, which will automatically scan your redis.conf file and update the fields which don't match the current configuration value. Fields non existing but set to the default value are not added. Comments inside your configuration file are retained.

經過介紹,咱們中能夠,知道對於redis的配置有兩種方式:

  • 經過命令行,這個方式就是在經過commend指令進行配置的,屬於初始化配置,會被寫入到redis.conf文件中
  • 在運行過程當中,這個方式是在redis運行的過程當中進行配置的,屬於暫時配置,也不會寫入到redis.conf,當服務重啓的時候,原來的配置就失效,須要從新輸入指令config set xxx

至此,咱們知道environment對於正版redis是無效的,那麼爲何bitnami版本有這個效果呢?這個時候咱們就須要看下這哥們是怎麼設計的了。
先看下人家的docker-compose.yml,確實是容許免密進入,這個好是好,本身一我的小打小鬧可還行,遇到團隊協助或者在生產環境中分分鐘被噴

version: '2'

services:
  redis:
    image: 'bitnami/redis:5.0-centos-7'
    environment:
      # ALLOW_EMPTY_PASSWORD is recommended only for development.
      - ALLOW_EMPTY_PASSWORD=yes
      - REDIS_DISABLE_COMMANDS=FLUSHDB,FLUSHALL
    ports:
      - '6379:6379'
    volumes:
      - 'redis_data:/bitnami/redis/data'

volumes:
  redis_data:
    driver: local

以後是分析他的dockerfile,前面的打包內容確實沒啥好看的,主要是看最後兩步ENTRYPOINT [ "/entrypoint.sh" ] && CMD [ "/run.sh" ],這個是其容許免密配置的關鍵,咱們繼續探究源碼

FROM bitnami/centos-extras-base:7-r269
LABEL maintainer "Bitnami <containers@bitnami.com>"

ENV BITNAMI_PKG_CHMOD="-R g+rwX" \
    HOME="/" \
    OS_ARCH="x86_64" \
    OS_FLAVOUR="centos-7" \
    OS_NAME="linux"

# Install required system packages and dependencies
RUN install_packages glibc
RUN . ./libcomponent.sh && component_unpack "redis" "5.0.7-0" --checksum 0046ebee1870e41fe422f646d504a8ec84efb85152189ee434d8f4c9ad2917c7

COPY rootfs /
RUN /postunpack.sh
ENV BITNAMI_APP_NAME="redis" \
    BITNAMI_IMAGE_VERSION="5.0.7-centos-7-r59" \
    NAMI_PREFIX="/.nami" \
    PATH="/opt/bitnami/redis/bin:$PATH"

EXPOSE 6379

USER 1001
ENTRYPOINT [ "/entrypoint.sh" ]
CMD [ "/run.sh" ]

事實上,比較重要的就是entrypoint.sh這個文件,由於run.sh實際上是用來進行啓動初始化用的,有興趣的讀者能夠進去讀一下源碼,這裏就再也不贅述,咱們不妨看下entrypoint.sh源碼

#!/bin/bash

set -o errexit
set -o nounset
set -o pipefail
#set -o xtrace
# shellcheck disable=SC1091

# Load libraries
. /libbitnami.sh
. /libredis.sh

# Load Redis environment variables
eval "$(redis_env)"

print_welcome_page

if [[ "$*" = *"/run.sh"* ]]; then
    info "** Starting Redis setup **"
    /setup.sh
    info "** Redis setup finished! **"
fi

echo ""
exec "$@"

從代碼中知道,程序會先執行libbitnami.shlibredis.sh完成配置依賴庫的工做,而後使用eval函數將數據進行提取,以後經過run.sh這個腳本啓動redis,那問題的答案愈來愈近了:),先發現libredis.sh就先讀這份代碼

關於redis在docker-compose中免密配置的討論

在這份代碼中,能夠經過搜索關鍵字ALLOW_EMPTY_PASSWORD肯定是否是有,咱們要配置腳本,事實上也被咱們找到了

關於redis在docker-compose中免密配置的討論

就是這個函數實現了免密註冊redis的效果

redis_validate() {
    debug "Validating settings in REDIS_* env vars.."
    local error_code=0

    # Auxiliary functions
    print_validation_error() {
        error "$1"
        error_code=1
    }

    empty_password_enabled_warn() {
        warn "You set the environment variable ALLOW_EMPTY_PASSWORD=${ALLOW_EMPTY_PASSWORD}. For safety reasons, do not use this flag in a production environment."
    }
    empty_password_error() {
        print_validation_error "The $1 environment variable is empty or not set. Set the environment variable ALLOW_EMPTY_PASSWORD=yes to allow the container to be started with blank passwords. This is recommended only for development."
    }

    if is_boolean_yes "$ALLOW_EMPTY_PASSWORD"; then
        empty_password_enabled_warn
    else
        [[ -z "$REDIS_PASSWORD" ]] && empty_password_error REDIS_PASSWORD
    fi
    if [[ -n "$REDIS_REPLICATION_MODE" ]]; then
        if [[ "$REDIS_REPLICATION_MODE" =~ ^(slave|replica)$ ]]; then
            if [[ -n "$REDIS_MASTER_PORT_NUMBER" ]]; then
                if ! err=$(validate_port "$REDIS_MASTER_PORT_NUMBER"); then
                    print_validation_error "An invalid port was specified in the environment variable REDIS_MASTER_PORT_NUMBER: $err"
                fi
            fi
            if ! is_boolean_yes "$ALLOW_EMPTY_PASSWORD" && [[ -z "$REDIS_MASTER_PASSWORD" ]]; then
                empty_password_error REDIS_MASTER_PASSWORD
            fi
        elif [[ "$REDIS_REPLICATION_MODE" != "master" ]]; then
            print_validation_error "Invalid replication mode. Available options are 'master/replica'"
        fi
    fi

    [[ "$error_code" -eq 0 ]] || exit "$error_code"
}

探究源碼真的挺有趣的,讓咱們剖根究底,直面本源,知道以前拿來主義時沒有通過思考就隨便抄的不良後果,原來的docker-compose配置中,用了5.0.3-stretch這個版本以後,environment這段文字實際上就是無效的。至此擱筆,問題解決

參考資料:

  1. redis官方對於config的介紹,https://redis.io/topics/config
  2. redis源碼,https://github.com/antirez/redis/
  3. docker-compose redis:bitnami,https://github.com/bitnami/bitnami-docker-redis
相關文章
相關標籤/搜索