Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

only first domain works #70

Open
hanscees opened this issue May 20, 2020 · 8 comments
Open

only first domain works #70

hanscees opened this issue May 20, 2020 · 8 comments

Comments

@hanscees
Copy link

hanscees commented May 20, 2020

Hi,

I tried the init script with 6 domains. Only the first one works because the dummy cert is only created for the first domain.

The script does this:

`echo "### Creating dummy certificate for $domains ..."
path="/etc/letsencrypt/live/$domains"
mkdir -p "$data_path/conf/live/$domains"
docker-compose run --rm --entrypoint "
openssl req -x509 -nodes -newkey rsa:1024 -days 1
-keyout '$path/privkey.pem'
-out '$path/fullchain.pem'
-subj '/CN=localhost'" certbot
echo

`

but should include a for i in loop of some kind like the script does use later

domain_args="" for domain in "${domains[@]}"; do domain_args="$domain_args -d $domain" done

unless I am mistaken of course

@hanscees
Copy link
Author

hanscees commented May 21, 2020

I use another docker volume:

certbot:
image: certbot/certbot
volumes: - certbot_conf:/etc/letsencrypt - certbot_www:/var/www/certbot

to make it work for my I adapted the script like so:

`
#!/bin/bash

if ! [ -x "$(command -v docker-compose)" ]; then
echo 'Error: docker-compose is not installed.' >&2
exit 1
fi

domains=(www.example.com www.example.net www.tieser.com )
rsa_key_size=4096
#data_path="./data/certbot"
data_path="/var/lib/docker/volumes/nginx_certbot_conf/_data"
data_path_container="/etc/letsencrypt" # the openssl looks in the comtainer
email="[email protected]" # Adding a valid address is strongly recommended
staging=1 # Set to 1 if you're testing your setup to avoid hitting request limits

if [ -d "$data_path" ]; then
read -p "Existing data found for $domains. Continue and replace existing certificate? (y/N) " decision
if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
exit
fi
fi

if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
echo "### Downloading recommended TLS parameters ..."
mkdir -p "$data_path/conf"
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
echo
fi

echo "### Creating dummy certificate for $domains ..."
for domain in "${domains[@]}"; do
#path="/etc/letsencrypt/live/$domain"
path="$data_path/conf/live/$domain"
path_container="$data_path_container/live/$domain"
mkdir -p "$data_path/live/$domain"
docker-compose run --rm --entrypoint "
openssl req -x509 -nodes -newkey rsa:1024 -days 1
-keyout '$path_container/privkey.pem'
-out '$path_container/fullchain.pem'
-subj '/CN=localhost'" certbot
done
echo

`
Difficult because your script runs openssl in the container, so there is a difference between the datapad on the docker host and in the container.

@oxr463
Copy link

oxr463 commented May 27, 2020

Possible duplicate of #65

@fredo994
Copy link

I've also walked right into this problem. I wanted to have two separate certificates for two different websites, but one reverse proxy. I've refactored this init-letsencrypt.sh script to support different domains.

#!/bin/bash

###################################################################
############### Function declarations #############################
###################################################################

function check_if_docker_compose_installed {
  if ! [ -x "$(command -v docker-compose)" ]; then
    echo 'Error: docker-compose is not installed.' >&2
    exit 1
  fi
}

function setup_tls_parameters {
  local data_path=$1
  if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
    echo "### Downloading recommended TLS parameters ..."
    mkdir -p "$data_path/conf"
    curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
    curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
    echo
  fi
}

function should_renew_certificate {
  local data_path=$1
  local domains=("$2") # This will convert the string values into an array
    # Check if certificates already exists
  if [ -d "$data_path" ]; then
    read -p "Existing data found for ${domains[*]}. Continue and replace existing certificate? (y/N) " decision
    if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
      echo "0" #Return  empty value, which signifies that we did not create dummy certificate.
    fi
  fi
  echo "1"
}

function make_dummy_certificate {
  local data_path=$1
  local domains=("$2") # This will convert the string values into an array

  # In the case user wants to replace old certificate we will replace it with the new one.
  echo "### Creating dummy certificate for domains ${domains[*]} ..."
  path="/etc/letsencrypt/live/${domains[0]}"
  mkdir -p "$data_path/conf/live/${domains[0]}"
  docker-compose run --rm --entrypoint " \
    openssl req -x509 -nodes -newkey rsa:1024 -days 1 \
      -keyout '$path/privkey.pem' \
      -out '$path/fullchain.pem' \
      -subj '/CN=localhost'" certbot
  echo
}

function start_nginx {
  echo "### Starting nginx ..."
  docker-compose up --force-recreate -d nginx
  echo
}


function delete_dummy_certificate {
  local dummy_certificate_domain=$1
  echo "### Deleting dummy certificate for $dummy_certificate_domain ..."
  docker-compose run --rm --entrypoint " \
    rm -Rf /etc/letsencrypt/live/$dummy_certificate_domain && \
    rm -Rf /etc/letsencrypt/archive/$dummy_certificate_domain && \
    rm -Rf /etc/letsencrypt/renewal/$dummy_certificate_domain.conf" certbot
  echo
}

function request_new_certificate {
  local domains=( "$1" )
  local email="$2"
  local rsa_key_size="$3"
  local staging="$4"

  echo "### Requesting Let's Encrypt certificate for ${domains[*]} ..."
  #Join $domains to -d args
  domain_args=""
  for domain in "${domains[@]}"; do
    domain_args="$domain_args -d $domain"
  done

  # Select appropriate email arg
  case "$email" in
    "") email_arg="--register-unsafely-without-email" ;;
    *) email_arg="--email $email" ;;
  esac

  # Enable staging mode if needed
  if [ "$staging" != "0" ]; then
    staging_arg="--staging";
  fi

  docker-compose run --rm --entrypoint " \
    certbot certonly --webroot -w /var/www/certbot \
      $staging_arg \
      $email_arg \
      $domain_args \
      --rsa-key-size $rsa_key_size \
      --agree-tos \
      --force-renewal" certbot
  echo
}

function reload_nginx {
  echo "### Reloading nginx ..."
  docker-compose exec nginx nginx -s reload
}


###################################################################
##################### Script start ################################
###################################################################


domains_list=("example.com www.example.com" "api.example.com www.api.example.com")
rsa_key_size=4096
data_path="/home/example/certbot"
email="[email protected]" # Adding a valid address is strongly recommended
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits

check_if_docker_compose_installed

setup_tls_parameters $data_path

# Create dummy certificates if needed.
n_renewals=0
dummy_certificate_domains=()
for domains in "${domains_list[@]}"; do
    renew_certificate=$(should_renew_certificate "$data_path" "$domains")
    if [ "$renew_certificate" -eq "1" ]; then
      n_renewals=$(( n_renewals+1 ))
      make_dummy_certificate "$data_path" "$domains"
      dummy_certificate_domains+=( "${domains[0]}" )
    else
      dummy_certificate_domains+=( "" )
    fi
done

if [ "$n_renewals" -eq  "0" ]; then
  echo "No new renewals, quitting."
  exit
fi

start_nginx

# For each domain renew certificate (if needed).
n_domains="${#domains_list[@]}"
for (( i=0; i<"$n_domains"; i++ )); do
  dummy_certificate_domain="${dummy_certificate_domains[$i]}"
  if [ -z "$dummy_certificate_domain" ]; then
    # if we did not create dummy certificate continue to the next domain.
    continue
  fi

  delete_dummy_certificate "$dummy_certificate_domain"

  domains="${domains_list[$i]}"

  request_new_certificate "$domains" "$email" "$rsa_key_size" "$staging"
done

# Reload nginx with new certificates.
reload_nginx

domains variable now acts as "array of arrays" where sub-arrays are encoded as as string (values separated by whitespace).

I've tested this with my workflow and it worked.
Hope, somebody else will find this useful.

@sugenk
Copy link

sugenk commented Jun 17, 2020

another small fix for init-letsencrypt.sh

@oem2
Copy link

oem2 commented Jul 14, 2020

@fredo994 This solved my problem. Thanks so much

@EdwardDiehl
Copy link

EdwardDiehl commented Sep 27, 2020

@fredo994 thanks for sharing your init-letsencrypt.sh

this does not work correctly

("example.com www.example.com" "api.example.com www.api.example.com")

without quotes, it works correctly

(example.com www.example.com api.example.com www.api.example.com)

@Ibsardar
Copy link

Ibsardar commented Jan 31, 2021

@EdwardDiehl @fredo994

Thank you for this awesome script.
With some tweaks, I believe I got this to work correctly:

("example.com www.example.com" "api.example.com www.api.example.com")

I also incorporated the following (useful if you use a DNS supported by certbot) based on if you provide a non-empty string to dns_cred_path:

#88 (comment)

Make sure to replace all instances of nginx_service with the name of your nginx_service, certbot_service with the name of your certbot service, and dnsprovider with the name of your DNS provider (if any).

FYI dnsprovider usually is their full name, uncapitilized, without any spaces. So for Digital Ocean, you get "digitalocean".

Boom:

#!/bin/bash

###################################################################
############### Function declarations #############################
###################################################################

function check_if_docker_compose_installed {
  if ! [ -x "$(command -v docker-compose)" ]; then
    echo 'Error: docker-compose is not installed.' >&2
    exit 1
  fi
}

function setup_tls_parameters {
  local data_path=$1
  if [ ! -e "$data_path/conf/options-ssl-nginx.conf" ] || [ ! -e "$data_path/conf/ssl-dhparams.pem" ]; then
    echo "### Downloading recommended TLS parameters ..."
    mkdir -p "$data_path/conf"
    curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot-nginx/certbot_nginx/_internal/tls_configs/options-ssl-nginx.conf > "$data_path/conf/options-ssl-nginx.conf"
    curl -s https://raw.githubusercontent.com/certbot/certbot/master/certbot/certbot/ssl-dhparams.pem > "$data_path/conf/ssl-dhparams.pem"
    echo
  fi
}

function should_renew_certificate {
  local data_path=$1
  local domains=("$2") # This will convert the string values into an array
    # Check if certificates already exists
  if [ -d "$data_path" ]; then
    read -p "Existing data found for ${domains[*]}. Continue and replace existing certificate? (y/N) " decision
    if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
      echo "0" #Return  empty value, which signifies that we did not create dummy certificate.
    fi
  fi
  echo "1"
}

function make_dummy_certificate {
  local data_path=$1
  local domains=("$2") # This will convert the string values into an array
  local rsa_key_size=$3

  # In the case user wants to replace old certificate we will replace it with the new one.
  echo "### Creating dummy certificate for domains ${domains[*]} ..."
  path="/etc/letsencrypt/live/${domains[0]}"
  mkdir -p "$data_path/conf/live/${domains[0]}"
  docker-compose run --rm --entrypoint " \
    openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1 \
      -keyout '$path/privkey.pem' \
      -out '$path/fullchain.pem' \
      -subj '/CN=localhost'" certbot_service
  echo
}

function start_nginx {
  echo "### Starting nginx ..."
  docker-compose up --force-recreate -d nginx_service
  echo
}


function delete_dummy_certificate {
  local dummy_certificate_domain=$1
  echo "### Deleting dummy certificate for $dummy_certificate_domain ..."
  docker-compose run --rm --entrypoint " \
    rm -Rf /etc/letsencrypt/live/$dummy_certificate_domain && \
    rm -Rf /etc/letsencrypt/archive/$dummy_certificate_domain && \
    rm -Rf /etc/letsencrypt/renewal/$dummy_certificate_domain.conf" certbot_service
  echo
}

function request_new_certificate {
  local domains=( "$1" ) # convert to array
  local email="$2"
  local rsa_key_size="$3"
  local staging="$4"
  local creds=$5

  echo "### Requesting Let's Encrypt certificate for ${domains[*]} ..."
  #Join $domains to -d args
  domain_args=""
  for domain in $domains; do
    domain_args="$domain_args -d $domain"
  done

  # Select appropriate email arg
  case "$email" in
    "") email_arg="--register-unsafely-without-email" ;;
    *) email_arg="--email $email" ;;
  esac

  # Enable staging mode if needed
  if [ "$staging" != "0" ]; then
    staging_arg="--staging";
  fi

  if [ -z "$creds" ]; then
    echo "###### (requesting without a DNS challange...)"
    docker-compose run --rm --entrypoint " \
      certbot certonly --webroot -w /var/www/certbot \
      $staging_arg \
      $email_arg \
      $domain_args \
      --rsa-key-size $rsa_key_size \
      --agree-tos \
      --force-renewal" certbot_service
  else
    echo "###### (requesting with a DNS challange...)"
    docker-compose run --rm --entrypoint " \
      certbot certonly \
      $staging_arg \
      $email_arg \
      $domain_args \
      --rsa-key-size $rsa_key_size \
      --no-eff-email \
      --agree-tos \
      --force-renewal \
      --dns-dnsprovider \
      --dns-dnsprovider-credentials $creds \
      --dns-dnsprovider-propagation-seconds 30" certbot_service
  fi
  echo
}

function reload_nginx {
  echo "### Reloading nginx ..."
  if $(docker-compose exec nginx_service nginx -s reload); then
    echo "### Reloaded."
  else
    YELO='\033[1;33m'
    CYAN='\033[0;36m'
    NO='\033[0m' # No Color
    echo "### ${YELO}Nothing to Reload${NO}...(you probably just ran this before running '${CYAN}docker-compose build${NO}')"
  fi
}


###################################################################
##################### Script start ################################
###################################################################



# PUT STUFF BELOW:



# FYI: One certificate per quoted string.
# FYI: Dummy certificates often will be named incorrectly - just ignore those.
# FYI: Actual certificates will usually prefer www.blah.com rather than blah.com
domains_list=("example.com www.example.com" "anotha.one www.anotha.one")

rsa_key_size=4096

data_path="./server/certbot" # <-- change this to wherever you like

# if using a dns, you need to create your credential file first...
# see all dns plugins for certbot: https://certbot.eff.org/docs/using.html?highlight=certbot%20certonly#dns-plugins
# digitalocean plugin for example: https://certbot-dns-digitalocean.readthedocs.io/en/stable/
dns_cred_path="/etc/letsencrypt/dnsprovider.ini" # set equal to empty string if not using a dns provider

email="[email protected]" # Adding a valid address is strongly recommended

# FYI: will get RemoteCertificateNameMismatch error in browser if this is set to 1
staging=0 # Set to 1 if you're testing your setup to avoid hitting request limits



# STOP PUTTING STUFF !



check_if_docker_compose_installed

setup_tls_parameters $data_path

# Create dummy certificates if needed.
n_renewals=0
dummy_certificate_domains=()
for domains in "${domains_list[@]}"; do
    renew_certificate=$(should_renew_certificate "$data_path" "$domains")
    if [ "$renew_certificate" -eq "1" ]; then
      n_renewals=$(( n_renewals+1 ))
      make_dummy_certificate "$data_path" "$domains" "$rsa_key_size"
      dummy_certificate_domains+=( "${domains[0]}" )
    else
      dummy_certificate_domains+=( "" )
    fi
done

if [ "$n_renewals" -eq  "0" ]; then
  echo "No new renewals, quitting."
  exit
fi

start_nginx

# For each domain renew certificate (if needed).
n_domains="${#domains_list[@]}"
for (( i=0; i<"$n_domains"; i++ )); do
  dummy_certificate_domain="${dummy_certificate_domains[$i]}"
  if [ -z "$dummy_certificate_domain" ]; then
    # if we did not create dummy certificate continue to the next domain.
    continue
  fi

  delete_dummy_certificate "$dummy_certificate_domain"

  domains="${domains_list[$i]}"

  request_new_certificate "$domains" "$email" "$rsa_key_size" "$staging" $dns_cred_path
done

# Reload nginx with new certificates.
reload_nginx

@Ibsardar
Copy link

Ibsardar commented Feb 1, 2021

On another note, I have made my own boilerplate (based on this repo) that worked much better for me than this repo, mainly because I needed to incorporate digitalocean DNS stuff, additional Dockerfile steps, and custom Dockerfile entrypoint shell scripts.

dennisgermany added a commit to dennisgermany/nginx-certbot that referenced this issue Apr 19, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants