Skip to content

Instantly share code, notes, and snippets.

@shantanoo-desai
Last active July 15, 2022 15:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save shantanoo-desai/b5d971c533509fdf978f9e97e584b0d0 to your computer and use it in GitHub Desktop.
Save shantanoo-desai/b5d971c533509fdf978f9e97e584b0d0 to your computer and use it in GitHub Desktop.
Exploring the marshland of Docker Compose Versions with single quotes, special characters and environment variables using Vagrant and Ansible
# Encrypting plain-text password with value `password` with bcyrpt
NODERED_ADMIN_PASSWORD=$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.

Docker Compose Discrepancy

Vagrant Machine with Ubuntu 20.04 provisioned with both Docker Compose V1 and Compose V2 highlighting the problem of using / not using single-quotes for passwords with $

Environment

OS: Windows 10 with Windows Subsystem for Linux 2 (Ubuntu 20.04 LTS)

Tool Version
vagrant (on WSL2 + Windows 10) 2.2.19
Hyper-V 10.0.19041.1
ansible (on WSL2) 2.12.5

Docker Version

within the provisioned Vagrant Machine

Tool Version
docker-compose v1.29.2 build 5b3cea4c, v1.25.4 build 8d51620a
docker compose 2.6.0
docker 20.10.17

Reproduction

  1. Bring the Vagrant machine up using:

    vagrant up
  2. Within the vagrant machine change to the sync folder

    cd /vagrant/

Checks: without Single Quotes

For the following encrypted password via bcrypt (plain-text value password)

cat .env.node-red
NODERED_ADMIN_PASSWORD=$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.

Compose V1.25.4

Let Compose generate the configuration on STDOUT for you:

docker-compose-125 config

This will result in:

services:
  node-red:
    container_name: test-nodered
    environment:
      NODERED_ADMIN_PASSWORD: $$2a$$08$$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.
    image: nodered/node-red:2.2.2
version: '3.9'

Compose V1 will escape the $ characters in the resolved password.

THIS WILL WORK

Compose V1.29.2

Let Compose generate the configuration on STDOUT for you:

docker-compose-129 config

This will result in:

services:
  node-red:
    container_name: test-nodered
    environment:
      NODERED_ADMIN_PASSWORD: $$2a$$08$$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.
    image: nodered/node-red:2.2.2
version: '3.9'

Compose V1 will escape the $ characters in the resolved password.

THIS WILL WORK

Compose V2

Let Compose v2 generate the configuration STDOUT for you:

docker compose config

This will result in:

name: node-red-app
services:
  node-red:
    container_name: test-nodered
    environment:
      NODERED_ADMIN_PASSWORD: a$$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.
    image: nodered/node-red:2.2.2
    networks:
      default: null
networks:
  default:
    name: node-red-app_default

Observe: the resolved password is broken. Compose V2 understands that the initial $ character is to be somehow resolved hence when the container is up and running the password hashes won't match and you won't be able to login to your system (here node-red Editor)

THIS WILL NOT WORK!

Checks: with Single Quotes

assuming the same password in the .env.node-red file is enclosed in single quotes ''

cat conf/.env.node-red
NODERED_ADMIN_PASSWORD='$2a$08$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.'

Compose V1.25.4

docker-compose-125 config

will result in:

services:
  node-red:
    container_name: test-nodered
    environment:
      NODERED_ADMIN_PASSWORD: '''$$2a$$08$$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.'''
    image: nodered/node-red:2.2.2
version: '3.7'

Observe that the resolved password has more single quotes which will be part of the container's environment and hence the password hashes won't match and you won't be able to login to your system (here node-red Editor)

THIS WILL NOT WORK!

Compose V1.29.2

docker-compose-129 config

will result in:

services:
  node-red:
    container_name: test-nodered
    environment:
      NODERED_ADMIN_PASSWORD: $$2a$$08$$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.
    image: nodered/node-red:2.2.2
version: '3.7'

This will work because the password is preserved

THIS WILL WORK

Compose V2

docker compose config

will result in:

name: node-red-app
services:
  node-red:
    container_name: test-nodered
    environment:
      NODERED_ADMIN_PASSWORD: $$2a$$08$$zZWtXTja0fB1pzD4sHCMyOCMYz2Z6dNbM6tl8sJogENOMcxWV9DN.
    image: nodered/node-red:2.2.2
    networks:
      default: null
networks:
  default:
    name: node-red-app_default

THIS WILL WORK

Compatibility Matrix

Compose Version / Values 1.25.4 1.29.2 2.6.0
with Single Quotes ✔️ ✔️
without Single Quotes ✔️ ✔️
---
- hosts: all
become: yes
tasks:
- name: install prerequisites
apt:
name:
- apt-transport-https
- ca-certificates
- curl
- gnupg-agent
- software-properties-common
update_cache: yes
- name: add apt-key
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
- name: add docker repo
apt_repository:
repo: deb https://download.docker.com/linux/ubuntu focal stable
- name: install docker
apt:
name:
- docker-ce
- docker-ce-cli
- containerd.io
- docker-compose-plugin
update_cache: yes
- name: add userpermissions
shell: "usermod -aG docker vagrant"
- name: Install docker-compose v1.29.2 from official github repo
get_url:
url : https://github.com/docker/compose/releases/download/1.29.2/docker-compose-Linux-x86_64
dest: /usr/local/bin/docker-compose-129
mode: 'u+x,g+x'
- name: change compose v1.29.2 user permissions
shell: "chmod +x /usr/local/bin/docker-compose-129"
- name: Install docker-compose v1.25.4 from official github repo
get_url:
url: https://github.com/docker/compose/releases/download/1.25.4/docker-compose-Linux-x86_64
dest: /usr/local/bin/docker-compose-125
mode: 'u+x,g+x'
- name: change compose v1.25.4 user permissions
shell: "chmod +x /usr/local/bin/docker-compose-125"
version: '3.7'
services:
node-red:
image: nodered/node-red:2.2.2
container_name: test-nodered
env_file:
- .env.node-red
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile for:
# - Windows 10 Machine with WSL2 (Ubuntu 20.04 flavor)
# - Vagrant 2.2.19 + Ansible
Vagrant.configure("2") do |config|
config.vm.define "ubuntu2004" do |subconfig|
subconfig.vm.box = "generic/ubuntu2004"
subconfig.vm.hostname = "hyperv-vagrant"
subconfig.vm.provider "hyperv"
subconfig.vm.network "public_network", bridge: "Vagrant Switch"
subconfig.vm.synced_folder ".", "/vagrant"
subconfig.vm.provider "hyperv" do |h|
h.enable_virtualization_extensions = true
h.linked_clone = true
h.vmname = "ubuntu_vagrant_pacedge"
end
subconfig.vm.provision "ansible" do |a|
a.verbose = "v"
a.playbook = "./docker-compose-playbook.yml"
end
end
end
# -*- mode: ruby -*-
# vi: set ft=ruby :
# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure("2") do |config|
# The most common configuration options are documented and commented below.
# For a complete reference, please see the online documentation at
# https://docs.vagrantup.com.
# Every Vagrant development environment requires a box. You can search for
# boxes at https://vagrantcloud.com/search.
config.vm.box = "ubuntu/focal64"
# Disable automatic box update checking. If you disable this, then
# boxes will only be checked for updates when the user runs
# `vagrant box outdated`. This is not recommended.
config.vm.box_check_update =false
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine. In the example below,
# accessing "localhost:8080" will access port 80 on the guest machine.
# NOTE: This will enable public access to the opened port
# config.vm.network "forwarded_port", guest: 80, host: 8080
# Create a forwarded port mapping which allows access to a specific port
# within the machine from a port on the host machine and only allow access
# via 127.0.0.1 to disable public access
# config.vm.network "forwarded_port", guest: 80, host: 8080, host_ip: "127.0.0.1"
# Create a private network, which allows host-only access to the machine
# using a specific IP.
# config.vm.network "private_network", ip: "192.168.33.10"
# Create a public network, which generally matched to bridged network.
# Bridged networks make the machine appear as another physical device on
# your network.
# config.vm.network "public_network"
# Share an additional folder to the guest VM. The first argument is
# the path on the host to the actual folder. The second argument is
# the path on the guest to mount the folder. And the optional third
# argument is a set of non-required options.
config.vm.synced_folder ".", "/vagrant"
# Provider-specific configuration so you can fine-tune various
# backing providers for Vagrant. These expose provider-specific options.
# Example for VirtualBox:
#
config.vm.provider "virtualbox" do |vb|
# # Display the VirtualBox GUI when booting the machine
# vb.gui = true
#
# # Customize the amount of memory on the VM:
vb.memory = "2048"
end
#
# View the documentation for the provider you are using for more
# information on available options.
# Enable provisioning with a shell script. Additional provisioners such as
# Ansible, Chef, Docker, Puppet and Salt are also available. Please see the
# documentation for more information about their specific syntax and use.
# config.vm.provision "shell", inline: <<-SHELL
# apt-get update
# apt-get install -y apache2
# SHELL
config.vm.provision "ansible" do |a|
a.verbose = "v"
a.playbook = "./docker-compose-playbook.yml"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment