diff --git a/bin/v-add-letsencrypt-domain b/bin/v-add-letsencrypt-domain index c69a21f1..a6946695 100755 --- a/bin/v-add-letsencrypt-domain +++ b/bin/v-add-letsencrypt-domain @@ -1,13 +1,8 @@ #!/bin/bash -# info: adding letsencrypt ssl cetificate for domain -# options: USER DOMAIN [ALIASES] [RESTART] [NOTIFY] +# info: check letsencrypt domain +# options: USER DOMAIN [ALIASES] # -# The function turns on SSL support for a domain. Parameter ssl_dir is a path -# to directory where 2 or 3 ssl files can be found. Certificate file -# domain.tld.crt and its key domain.tld.key are mandatory. Certificate -# authority domain.tld.ca file is optional. If home directory parameter -# (ssl_home) is not set, https domain uses public_shtml as separate -# documentroot directory. +# The function check and validates domain with Let's Encript #----------------------------------------------------------# @@ -18,39 +13,67 @@ user=$1 domain=$2 aliases=$3 -restart=$4 -notify=$5 + +# LE API +API='https://acme-v02.api.letsencrypt.org' # Includes source $VESTA/func/main.sh source $VESTA/func/domain.sh source $VESTA/conf/vesta.conf -# Additional argument formatting -format_domain_idn +# encode base64 +encode_base64() { + cat |base64 |tr '+/' '-_' |tr -d '\r\n=' +} + +# Let's Encrypt v2 curl function +query_le_v2() { + + protected='{"nonce": "'$3'",' + protected=''$protected' "url": "'$1'",' + protected=''$protected' "alg": "RS256", "kid": "'$KID'"}' + content="Content-Type: application/jose+json" + + payload_=$(echo -n "$2" |encode_base64) + protected_=$(echo -n "$protected" |encode_base64) + signature_=$(printf "%s" "$protected_.$payload_" |\ + openssl dgst -sha256 -binary -sign $USER_DATA/ssl/user.key |\ + encode_base64) + + post_data='{"protected":"'"$protected_"'",' + post_data=$post_data'"payload":"'"$payload_"'",' + post_data=$post_data'"signature":"'"$signature_"'"}' + + curl -s -i -d "$post_data" "$1" -H "$content" +} + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# -check_args '2' "$#" 'USER DOMAIN [ALIASES] [RESTART] [NOTIFY]' -is_format_valid 'user' 'domain' +check_args '2' "$#" 'USER DOMAIN [ALIASES]' +is_format_valid 'user' 'domain' 'aliases' is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' -is_system_enabled "$WEB_SSL" 'SSL_SUPPORT' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" is_object_valid 'web' 'DOMAIN' "$domain" is_object_unsuspended 'web' 'DOMAIN' "$domain" +get_domain_values 'web' +for alias in $(echo "$aliases" |tr ',' '\n' |sort -u); do + check_alias="$(echo $ALIAS |tr ',' '\n' |grep ^$alias$)" + if [ -z "$check_alias" ]; then + check_result $E_NOTEXIST "domain alias $alias doesn't exist" + fi +done #----------------------------------------------------------# # Action # #----------------------------------------------------------# -# Parsing domain data -get_domain_values 'web' - # Registering LetsEncrypt user account $BIN/v-add-letsencrypt-user $user if [ "$?" -ne 0 ]; then @@ -62,53 +85,152 @@ fi # Parsing LetsEncrypt account data source $USER_DATA/ssl/le.conf -email=$EMAIL -# Validating domain and aliases -i=1 -for alias in $(echo $domain,$aliases |tr ',' '\n' |sort -u); do - $BIN/v-check-letsencrypt-domain $user $alias - if [ "$?" -ne 0 ]; then - touch $VESTA/data/queue/letsencrypt.pipe - sed -i "/ $domain /d" $VESTA/data/queue/letsencrypt.pipe - send_notice "LETSENCRYPT" "$alias validation failed" - check_result $E_INVALID "LE domain validation" >/dev/null +# Checking wildcard alias +if [ "$aliases" = "*.$domain" ]; then + wildcard='yes' + proto="dns-01" + if [ ! -e "$VESTA/data/users/$user/dns/$domain.conf" ]; then + check_result $E_NOTEXIST "DNS domain $domain doesn't exist" + fi +else + proto="http-01" +fi + +# Requesting nonce / STEP 1 +answer=$(curl -s -I "$API/directory") +nonce=$(echo "$answer" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') +status=$(echo "$answer"|grep HTTP/1.1 |tail -n1 |cut -f 2 -d ' ') +if [[ "$status" -ne 200 ]]; then + check_result $E_CONNECT "Let's Encrypt nonce request status $status" +fi + +# Placing new order / STEP 2 +url="$API/acme/new-order" +payload='{"identifiers":[' +for identifier in $(echo $domain,$aliases |tr ',' '\n' |sort -u); do + payload=$payload'{"type":"dns","value":"'$identifier'"},' +done +payload=$(echo "$payload"|sed "s/,$//") +payload=$payload']}' +answer=$(query_le_v2 "$url" "$payload" "$nonce") +nonce=$(echo "$answer" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') +authz=$(echo "$answer" |grep "acme/authz" |cut -f2 -d '"') +finalize=$(echo "$answer" |grep 'finalize":' |cut -f4 -d '"') +status=$(echo "$answer" |grep HTTP/1.1 |tail -n1 |cut -f2 -d ' ') +if [[ "$status" -ne 201 ]]; then + check_result $E_CONNECT "Let's Encrypt new auth status $status" +fi + +# Requesting authorization token / STEP 3 +for auth in $authz; do + payload='' + answer=$(query_le_v2 "$auth" "$payload" "$nonce") + url=$(echo "$answer" |grep -A3 $proto |grep url |cut -f 4 -d \") + token=$(echo "$answer" |grep -A3 $proto |grep token |cut -f 4 -d \") + nonce=$(echo "$answer" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') + status=$(echo "$answer"|grep HTTP/1.1 |tail -n1 |cut -f 2 -d ' ') + if [[ "$status" -ne 200 ]]; then + check_result $E_CONNECT "Let's Encrypt acme/authz bad status $status" fi - # Checking LE limits per account - if [ "$i" -gt 100 ]; then - touch $VESTA/data/queue/letsencrypt.pipe - sed -i "/ $domain /d" $VESTA/data/queue/letsencrypt.pipe - send_notice 'LETSENCRYPT' 'Limit of domains per account is reached' - check_result $E_LIMIT "LE can't sign more than 100 domains" + # Accepting challenge / STEP 4 + if [ "$wildcard" = 'yes' ]; then + record=$(printf "%s" "$token.$THUMB" |\ + openssl dgst -sha256 -binary |encode_base64) + old_records=$($BIN/v-list-dns-records $user $domain plain|grep 'TXT') + old_records=$(echo "$old_records" |grep _acme-challenge |cut -f 1) + for old_record in $old_records; do + $BIN/v-delete-dns-record $user $domain $old_record + done + $BIN/v-add-dns-record $user $domain "_acme-challenge" "TXT" $record + check_result $? "DNS _acme-challenge record wasn't created" + else + if [ "$WEB_SYSTEM" = 'nginx' ] || [ ! -z "$PROXY_SYSTEM" ]; then + conf="$HOMEDIR/$user/conf/web/nginx.$domain.conf_letsencrypt" + sconf="$HOMEDIR/$user/conf/web/snginx.$domain.conf_letsencrypt" + if [ ! -e "$conf" ]; then + echo 'location ~ "^/\.well-known/acme-challenge/(.*)$" {' \ + > $conf + echo ' default_type text/plain;' >> $conf + echo ' return 200 "$1.'$THUMB'";' >> $conf + echo '}' >> $conf + fi + if [ ! -e "$sconf" ]; then + ln -s "$conf" "$sconf" + fi + $BIN/v-restart-proxy + check_result $? "Proxy restart failed" >/dev/null + + else + well_known="$HOMEDIR/$user/web/$rdomain/public_html/.well-known" + acme_challenge="$well_known/acme-challenge" + mkdir -p $acme_challenge + echo "$token.$THUMB" > $acme_challenge/$token + chown -R $user:$user $well_known + fi + $BIN/v-restart-web + check_result $? "Web restart failed" >/dev/null + fi + + # Requesting ACME validation / STEP 5 + validation_check=$(echo "$answer" |grep '"valid"') + if [[ ! -z "$validation_check" ]]; then + validation='valid' + else + validation='pending' + fi + + # Doing pol check on status + i=1 + while [ "$validation" = 'pending' ]; do + payload='{}' + answer=$(query_le_v2 "$url" "$payload" "$nonce") + validation=$(echo "$answer"|grep -A1 $proto |tail -n1|cut -f4 -d \") + nonce=$(echo "$answer" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') + status=$(echo "$answer"|grep HTTP/1.1 |tail -n1 |cut -f 2 -d ' ') + if [[ "$status" -ne 200 ]]; then + check_result $E_CONNECT "Let's Encrypt vvalidation status $status" + fi + + i=$((i + 1)) + if [ "$i" -gt 10 ]; then + check_result $E_CONNECT "Let's Encrypt domain validation timeout" + fi + sleep 1 + done + if [ "$validation" = 'invalid' ]; then + check_result $E_CONNECT "Let's Encrypt domain verification failed" fi - i=$((i++)) done -# Generating CSR -ssl_dir=$($BIN/v-generate-ssl-cert "$domain" "$email" "US" "California" \ + +# Generating new ssl certificate +ssl_dir=$($BIN/v-generate-ssl-cert "$domain" "info@$domain" "US" "California"\ "San Francisco" "Vesta" "IT" "$aliases" |tail -n1 |awk '{print $2}') -# Signing CSR -crt=$($BIN/v-sign-letsencrypt-csr $user $domain $ssl_dir) -if [ "$?" -ne 0 ]; then - touch $VESTA/data/queue/letsencrypt.pipe - sed -i "/ $domain /d" $VESTA/data/queue/letsencrypt.pipe - send_notice "LETSENCRYPT" "$alias validation failed" - check_result "$E_INVALID" "LE $domain validation" +# Sedning CSR to finalize order / STEP 6 +csr=$(openssl req -in $ssl_dir/$domain.csr -outform DER |encode_base64) +payload='{"csr":"'$csr'"}' +answer=$(query_le_v2 "$finalize" "$payload" "$nonce") +nonce=$(echo "$answer" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') +status=$(echo "$answer"|grep HTTP/1.1 |tail -n1 |cut -f 2 -d ' ') +certificate=$(echo "$answer"|grep 'certificate":' |cut -f4 -d '"') +if [[ "$status" -ne 200 ]]; then + check_result $E_CONNECT "Let's Encrypt finalize bad status $status" fi -echo "$crt" > $ssl_dir/$domain.crt -# Dowloading CA certificate -le_certs='https://letsencrypt.org/certs' -x1='lets-encrypt-x1-cross-signed.pem.txt' -x3='lets-encrypt-x3-cross-signed.pem.txt' -issuer=$(openssl x509 -text -in $ssl_dir/$domain.crt |grep "Issuer:") -if [ -z "$(echo $issuer|grep X3)" ]; then - curl -s $le_certs/$x1 > $ssl_dir/$domain.ca -else - curl -s $le_certs/$x3 > $ssl_dir/$domain.ca -fi +# Downloading signed certificate / STEP 7 +curl -s "$certificate" -o $ssl_dir/$domain.pem + +# Splitting up downloaded pem +crt_end=$(grep -n END $ssl_dir/$domain.pem |head -n1 |cut -f1 -d:) +head -n $crt_end $ssl_dir/$domain.pem > $ssl_dir/$domain.crt + +pem_lines=$(wc -l $ssl_dir/$domain.pem |cut -f 1 -d ' ') +ca_end=$(grep -n "BEGIN" $ssl_dir/$domain.pem |tail -n1 |cut -f 1 -d :) +ca_end=$(( pem_lines - crt_end + 1 )) +tail -n $ca_end $ssl_dir/$domain.pem > $ssl_dir/$domain.ca # Adding SSL ssl_home=$(search_objects 'web' 'LETSENCRYPT' 'yes' 'SSL_HOME') @@ -140,18 +262,13 @@ update_object_value 'web' 'DOMAIN' "$domain" '$LETSENCRYPT' 'yes' # Vesta # #----------------------------------------------------------# -# Restarting web -$BIN/v-restart-web $restart -if [ "$?" -ne 0 ]; then - send_notice 'LETSENCRYPT' "web server needs to be restarted manually" -fi +# Deleteing task from queue +touch $VESTA/data/queue/letsencrypt.pipe +sed -i "/ $domain /d" $VESTA/data/queue/letsencrypt.pipe # Notifying user send_notice 'LETSENCRYPT' "$domain SSL has been installed successfully" -# Deleteing task from queue -touch $VESTA/data/queue/letsencrypt.pipe -sed -i "/ $domain /d" $VESTA/data/queue/letsencrypt.pipe # Logging log_event "$OK" "$ARGUMENTS" diff --git a/bin/v-add-letsencrypt-user b/bin/v-add-letsencrypt-user index 6bd3c560..f3a19163 100755 --- a/bin/v-add-letsencrypt-user +++ b/bin/v-add-letsencrypt-user @@ -1,8 +1,8 @@ #!/bin/bash # info: register letsencrypt user account -# options: USER [TYPE] +# options: USER # -# The function creates and register LetsEncript account key +# The function creates and register LetsEncript account #----------------------------------------------------------# @@ -11,8 +11,9 @@ # Argument definition user=$1 -type=${2-1} -key_size=4096 + +# LE API +API='https://acme-v02.api.letsencrypt.org' # Includes source $VESTA/func/main.sh @@ -23,22 +24,39 @@ encode_base64() { cat |base64 |tr '+/' '-_' |tr -d '\r\n=' } +# Let's Encrypt v2 curl function +query_le_v2() { + protected='{"nonce": "'$3'",' + protected=''$protected' "url": "'$1'",' + protected=''$protected' "alg": "RS256", "jwk": '$jwk'}' + content="Content-Type: application/jose+json" + + payload_=$(echo -n "$2" |encode_base64) + protected_=$(echo -n "$protected" |encode_base64) + signature_=$(printf "%s" "$protected_.$payload_" |\ + openssl dgst -sha256 -binary -sign $USER_DATA/ssl/user.key |\ + encode_base64) + + post_data='{"protected":"'"$protected_"'",' + post_data=$post_data'"payload":"'"$payload_"'",' + post_data=$post_data'"signature":"'"$signature_"'"}' + + curl -s -i -d "$post_data" "$1" -H "$content" +} + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# -check_args '1' "$#" 'USER [TYPE]' +check_args '1' "$#" 'USER' is_format_valid 'user' is_object_valid 'user' 'USER' "$user" if [ -e "$USER_DATA/ssl/le.conf" ]; then source "$USER_DATA/ssl/le.conf" - if [ "$type" -eq 1 ] && [ ! -z "$EMAIL" ]; then - exit - fi - if [ "$type" -eq 2 ] && [ ! -z "$KID" ]; then - exit - fi +fi +if [ ! -z "$KID" ]; then + exit fi @@ -46,108 +64,57 @@ fi # Action # #----------------------------------------------------------# -# Defining LE API endpoint -if [ "$type" -eq 1 ]; then - api='https://acme-v01.api.letsencrypt.org' -else - api='https://acme-v02.api.letsencrypt.org' -fi # Defining user email -if [ $type -eq 1 ]; then - email=$(get_user_value '$CONTACT') +if [[ -z "$EMAIL" ]]; then + EMAIL=$(get_user_value '$CONTACT') fi # Defining user agreement -if [ "$type" -eq 1 ]; then - agreement=$(curl -s -I "$api/terms" |grep Location |\ - cut -f 2 -d \ |tr -d '\r\n') -else - #agreement=$(curl -s "$api/directory" |grep termsOfService |\ - # cut -f 4 -d '"') - agreement='' -fi +agreement='' # Generating user key -key="$USER_DATA/ssl/user.key" -if [ ! -e "$key" ]; then - openssl genrsa -out $key $key_size >/dev/null 2>&1 - chmod 600 $key +KEY="$USER_DATA/ssl/user.key" +if [ ! -e "$KEY" ]; then + openssl genrsa -out $KEY 4096 >/dev/null 2>&1 + chmod 600 $KEY fi # Defining key exponent if [ -z "$EXPONENT" ]; then - exponent=$(openssl pkey -inform pem -in "$key" -noout -text_pub |\ + EXPONENT=$(openssl pkey -inform pem -in "$KEY" -noout -text_pub |\ grep Exponent: |cut -f 2 -d '(' |cut -f 1 -d ')' |sed -e 's/x//' |\ xxd -r -p |encode_base64) -else - exponent="$EXPONENT" fi # Defining key modulus if [ -z "$MODULUS" ]; then - modulus=$(openssl rsa -in "$key" -modulus -noout |\ + MODULUS=$(openssl rsa -in "$KEY" -modulus -noout |\ sed -e 's/^Modulus=//' |xxd -r -p |encode_base64) -else - modulus="$MODULUS" fi -# Defining JWK token -jwk='{"e":"'$exponent'","kty":"RSA","n":"'"$modulus"'"}' +# Defining JWK +jwk='{"e":"'$EXPONENT'","kty":"RSA","n":"'"$MODULUS"'"}' # Defining key thumbnail if [ -z "$THUMB" ]; then - thumb="$(echo -n "$jwk" |openssl dgst -sha256 -binary |encode_base64)" -else - thumb="$THUMB" + THUMB="$(echo -n "$jwk" |openssl dgst -sha256 -binary |encode_base64)" fi + # Requesting ACME nonce -nonce=$(curl -s -I "$api/directory" |grep Nonce |cut -f 2 -d \ |tr -d '\r\n') +nonce=$(curl -s -I "$API/directory" |grep Nonce |cut -f 2 -d \ |tr -d '\r\n') -# Defining payload and protected data for v1 and v2 -if [ "$type" -eq 1 ]; then - header='{"alg":"RS256","jwk":'"$jwk"'}' - protected='{"nonce":"'"$nonce"'"}' - payload='{"resource":"new-reg","contact":["mailto:'"$email"'"],' - payload=$payload'"agreement":"'$agreement'"}' +# Creating ACME account +url="$API/acme/new-acct" +payload='{"termsOfServiceAgreed": true}' +answer=$(query_le_v2 "$url" "$payload" "$nonce") +kid=$(echo "$answer" |grep Location: |cut -f2 -d ' '|tr -d '\r') -else - protected='{"nonce": "'$nonce'",' - protected=''$protected' "url": "'$api/acme/new-acct'",' - protected=''$protected' "alg": "RS256", "jwk": '$jwk'}' - payload='{"termsOfServiceAgreed": true}' -fi - -# Encoding data -protected=$(echo -n "$protected" |encode_base64) -payload=$(echo -n "$payload" |encode_base64) - -# Signing request -signature=$(printf "%s" "$protected.$payload" |\ - openssl dgst -sha256 -binary -sign "$key" |\ - encode_base64) - -if [ "$type" -eq 1 ]; then - data='{"header":'"$header"',"protected":"'"$protected"'",' - data=$data'"payload":"'"$payload"'","signature":"'"$signature"'"}' - - answer=$(curl -s -i -d "$data" "$api/acme/new-reg") - status=$(echo "$answer" |grep HTTP/1.1 |tail -n1 |cut -f2 -d ' ') -else - data='{"protected":"'"$protected"'",' - data=$data'"payload":"'"$payload"'",' - data=$data'"signature":"'"$signature"'"}' - - answer=$(curl -s -i -d "$data" "$api/acme/new-acct" \ - -H "Content-Type: application/jose+json") - status=$(echo "$answer" |grep HTTP/1.1 |tail -n1 |cut -f2 -d ' ') - kid=$(echo "$answer" |grep Location: |cut -f2 -d ' '|tr -d '\r') -fi - -# Checking http answer status -if [[ "${status:0:2}" -ne "20" ]] && [[ "$status" -ne "409" ]]; then - check_result $E_CONNECT "LetsEncrypt account registration $status" +# Checking answer status +status=$(echo "$answer" |grep HTTP/1.1 |tail -n1 |cut -f2 -d ' ') +if [[ "${status:0:2}" -ne "20" ]]; then + check_result $E_CONNECT "Let's Encrypt acc registration failed $status" fi @@ -157,23 +124,15 @@ fi # Adding le.conf if [ ! -e "$USER_DATA/ssl/le.conf" ]; then - echo "EXPONENT='$exponent'" > $USER_DATA/ssl/le.conf - echo "MODULUS='$modulus'" >> $USER_DATA/ssl/le.conf - echo "THUMB='$thumb'" >> $USER_DATA/ssl/le.conf - if [ "$type" -eq 1 ]; then - echo "EMAIL='$email'" >> $USER_DATA/ssl/le.conf - else - echo "KID='$kid'" >> $USER_DATA/ssl/le.conf - fi + echo "EXPONENT='$EXPONENT'" > $USER_DATA/ssl/le.conf + echo "MODULUS='$MODULUS'" >> $USER_DATA/ssl/le.conf + echo "THUMB='$THUMB'" >> $USER_DATA/ssl/le.conf + echo "EMAIL='$EMAIL'" >> $USER_DATA/ssl/le.conf + echo "KID='$kid'" >> $USER_DATA/ssl/le.conf chmod 660 $USER_DATA/ssl/le.conf else - if [ "$type" -eq 1 ]; then - sed -i '/^EMAIL=/d' $USER_DATA/ssl/le.conf - echo "EMAIL='$email'" >> $USER_DATA/ssl/le.conf - else - sed -i '/^KID=/d' $USER_DATA/ssl/le.conf - echo "KID='$kid'" >> $USER_DATA/ssl/le.conf - fi + sed -i '/^KID=/d' $USER_DATA/ssl/le.conf + echo "KID='$kid'" >> $USER_DATA/ssl/le.conf fi # Logging diff --git a/bin/v-check-letsencrypt-domain b/bin/v-check-letsencrypt-domain deleted file mode 100755 index 97e24878..00000000 --- a/bin/v-check-letsencrypt-domain +++ /dev/null @@ -1,162 +0,0 @@ -#!/bin/bash -# info: check letsencrypt domain -# options: USER DOMAIN -# -# The function check and validates domain with LetsEncript - - -#----------------------------------------------------------# -# Variable&Function # -#----------------------------------------------------------# - -# Argument definition -user=$1 -domain=$2 - -# Includes -source $VESTA/func/main.sh -source $VESTA/conf/vesta.conf - -# encode base64 -encode_base64() { - cat |base64 |tr '+/' '-_' |tr -d '\r\n=' -} - -# Additional argument formatting -format_domain_idn - - -#----------------------------------------------------------# -# Verifications # -#----------------------------------------------------------# - -check_args '2' "$#" 'USER DOMAIN' -is_format_valid 'user' 'domain' -is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' -is_object_valid 'user' 'USER' "$user" -is_object_unsuspended 'user' 'USER' "$user" -if [ ! -e "$USER_DATA/ssl/le.conf" ]; then - check_result $E_NOTEXIST "LetsEncrypt key doesn't exist" -fi -rdomain=$(egrep "'$domain'|'$domain,|,$domain,|,$domain'" $USER_DATA/web.conf) -if [ -z "$rdomain" ]; then - check_result $E_NOTEXIST "domain $domain doesn't exist" -fi - - -#----------------------------------------------------------# -# Action # -#----------------------------------------------------------# - -source $USER_DATA/ssl/le.conf -api='https://acme-v01.api.letsencrypt.org' -r_domain=$(echo "$rdomain" |cut -f 2 -d \') -key="$USER_DATA/ssl/user.key" -exponent="$EXPONENT" -modulus="$MODULUS" -thumb="$THUMB" - -# Defining JWK header -header='{"e":"'$exponent'","kty":"RSA","n":"'"$modulus"'"}' -header='{"alg":"RS256","jwk":'"$header"'}' - -# Requesting nonce -nonce=$(curl -s -I "$api/directory" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') -protected=$(echo -n '{"nonce":"'"$nonce"'"}' |encode_base64) - -# Defining ACME query (request challenge) -query='{"resource":"new-authz","identifier"' -query=$query':{"type":"dns","value":"'"$domain_idn"'"}}' -payload=$(echo -n "$query" |encode_base64) -signature=$(printf "%s" "$protected.$payload" |\ - openssl dgst -sha256 -binary -sign "$key" |encode_base64) -data='{"header":'"$header"',"protected":"'"$protected"'",' -data=$data'"payload":"'"$payload"'","signature":"'"$signature"'"}' - -# Sending request to LetsEncrypt API -answer=$(curl -s -i -d "$data" "$api/acme/new-authz") - -# Checking http answer status -status=$(echo "$answer" |grep HTTP/1.1 |tail -n1 |cut -f2 -d ' ') -if [[ "$status" -ne "201" ]]; then - check_result $E_CONNECT "LetsEncrypt challenge request $status" -fi - -# Parsing domain nonce,token and uri -nonce=$(echo "$answer" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') -protected=$(echo -n '{"nonce":"'"$nonce"'"}' |encode_base64) -token=$(echo "$answer" |grep -A 3 http-01 |grep token |cut -f 4 -d \") -uri=$(echo "$answer" |grep -A 3 http-01 |grep uri |cut -f 4 -d \") - -# Adding location wrapper for request challenge -if [ "$WEB_SYSTEM" = 'nginx' ] || [ "$PROXY_SYSTEM" = 'nginx' ]; then - conf="$HOMEDIR/$user/conf/web/nginx.$r_domain.conf_letsencrypt" - sconf="$HOMEDIR/$user/conf/web/snginx.$r_domain.conf_letsencrypt" - if [ ! -e "$conf" ]; then - echo 'location ~ "^/\.well-known/acme-challenge/(.*)$" {' > $conf - echo ' default_type text/plain;' >> $conf - echo ' return 200 "$1.'$thumb'";' >> $conf - echo '}' >> $conf - fi - if [ ! -e "$sconf" ]; then - ln -s "$conf" "$sconf" - fi -else - acme="$HOMEDIR/$user/web/$r_domain/public_html/.well-known/acme-challenge" - if [ ! -d "$acme" ]; then - mkdir -p $acme - fi - echo "$token.$thumb" > $acme/$token - chown -R $user:$user $HOMEDIR/$user/web/$r_domain/public_html/.well-known -fi - -# Restarting web server -if [ -z "$PROXY_SYSTEM" ]; then - $BIN/v-restart-web - check_result $? "Proxy restart failed" >/dev/null -else - $BIN/v-restart-proxy - $BIN/v-restart-web - check_result $? "Web restart failed" >/dev/null -fi - -# Defining ACME query (request validation) -query='{"resource":"challenge","type":"http-01","keyAuthorization"' -query=$query':"'$token.$thumb'","token":"'$token'"}' -payload=$(echo -n "$query" |encode_base64) -signature=$(printf "%s" "$protected.$payload" |\ - openssl dgst -sha256 -binary -sign "$key" |encode_base64) -data='{"header":'"$header"',"protected":"'"$protected"'",' -data=$data'"payload":"'"$payload"'","signature":"'"$signature"'"}' - -# Sending request to LetsEncrypt API -answer=$(curl -s -i -d "$data" "$uri") - -# Checking domain validation status -i=1 -status=$(echo $answer |tr ',' '\n' |grep status |cut -f 4 -d \") -location=$(echo "$answer" |grep Location: |awk '{print $2}' |tr -d '\r\n') -while [ "$status" = 'pending' ]; do - answer=$(curl -s -i "$location") - detail="$(echo $answer |tr ',' '\n' |grep detail |cut -f 4 -d \")" - status=$(echo "$answer" |tr ',' '\n' |grep status |cut -f 4 -d \") - sleep 1 - i=$((i + 1)) - if [ "$i" -gt 60 ]; then - check_result $E_CONNECT "$detail" - fi -done -if [ "$status" = 'invalid' ]; then - detail="$(echo $answer |tr ',' '\n' |grep detail |cut -f 4 -d \")" - check_result $E_CONNECT "$detail" -fi - - -#----------------------------------------------------------# -# Vesta # -#----------------------------------------------------------# - -# Logging -log_event "$OK" "$ARGUMENTS" - -exit diff --git a/bin/v-sign-letsencrypt-csr b/bin/v-sign-letsencrypt-csr deleted file mode 100755 index 414eb8b3..00000000 --- a/bin/v-sign-letsencrypt-csr +++ /dev/null @@ -1,110 +0,0 @@ -#!/bin/bash -# info: sing letsencrypt csr -# options: USER DOMAIN CSR_DIR [FORMAT] -# -# The function signs certificate request using LetsEncript API - - -#----------------------------------------------------------# -# Variable&Function # -#----------------------------------------------------------# - -# Argument definition -user=$1 -domain=$2 -csr="$3/$domain.csr" -format=$4 - -# Includes -source $VESTA/func/main.sh -source $VESTA/conf/vesta.conf - -# encode base64 -encode_base64() { - cat |base64 |tr '+/' '-_' |tr -d '\r\n=' -} - - -#----------------------------------------------------------# -# Verifications # -#----------------------------------------------------------# - -check_args '3' "$#" 'USER DOMAIN CSR' -is_format_valid 'user' 'domain' -is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' -is_object_valid 'user' 'USER' "$user" -is_object_unsuspended 'user' 'USER' "$user" -if [ ! -e "$USER_DATA/ssl/le.conf" ]; then - check_result $E_NOTEXIST "LetsEncrypt key doesn't exist" -fi -check_domain=$(grep -w "$domain'" $USER_DATA/web.conf) -if [ -z "$check_domain" ]; then - check_result $E_NOTEXIST "domain $domain doesn't exist" -fi -if [ ! -e "$csr" ]; then - check_result $E_NOTEXIST "$csr doesn't exist" -fi - - -#----------------------------------------------------------# -# Action # -#----------------------------------------------------------# - -source $USER_DATA/ssl/le.conf -api='https://acme-v01.api.letsencrypt.org' -key="$USER_DATA/ssl/user.key" -exponent="$EXPONENT" -modulus="$MODULUS" -thumb="$THUMB" - -# Defining JWK header -header='{"e":"'$exponent'","kty":"RSA","n":"'"$modulus"'"}' -header='{"alg":"RS256","jwk":'"$header"'}' - -# Requesting nonce -nonce=$(curl -s -I "$api/directory" |grep Nonce |cut -f2 -d \ |tr -d '\r\n') -protected=$(echo -n '{"nonce":"'"$nonce"'"}' |encode_base64) - -# Defining ACME query (request challenge) -csr=$(openssl req -in $csr -outform DER |encode_base64) -query='{"resource":"new-cert","csr":"'$csr'"}' -payload=$(echo -n "$query" |encode_base64) -signature=$(printf "%s" "$protected.$payload" |\ - openssl dgst -sha256 -binary -sign "$key" |encode_base64) -data='{"header":'"$header"',"protected":"'"$protected"'",' -data=$data'"payload":"'"$payload"'","signature":"'"$signature"'"}' - -# Sending request to LetsEncrypt API -answer=$(mktemp) -curl -s -d "$data" "$api/acme/new-cert" -o $answer -if [ ! -z "$(grep Error $answer)" ]; then - detail="$(cat $answer |tr ',' '\n' |grep detail |cut -f 4 -d \")" - detail=$(echo "$detail" |awk -F "::" '{print $2}') - rm $answer - check_result $E_LIMIT "$detail" -fi - -# Printing certificate -crt=$(cat "$answer" |openssl base64 -e) -rm $answer -if [ "$format" != 'json' ]; then - echo "-----BEGIN CERTIFICATE-----" - echo "$crt" - echo "-----END CERTIFICATE-----" -else - echo -e "{\n\t\"$domain\": {\n\t\t\"CRT\":\"" - echo -n '-----BEGIN CERTIFICATE-----\n' - echo -n "$crt" |sed ':a;N;$!ba;s/\n/\\n/g' - echo -n '-----END CERTIFICATE-----' - echo -e "\"\n\t\t}\n\t}" -fi - - -#----------------------------------------------------------# -# Vesta # -#----------------------------------------------------------# - -# Logging -log_event "$OK" "$ARGUMENTS" - -exit diff --git a/bin/v-update-letsencrypt-ssl b/bin/v-update-letsencrypt-ssl index 17f8f0f6..a5cbc286 100755 --- a/bin/v-update-letsencrypt-ssl +++ b/bin/v-update-letsencrypt-ssl @@ -22,51 +22,32 @@ source $VESTA/conf/vesta.conf # Action # #----------------------------------------------------------# -# Defining user list -users=$($BIN/v-list-users | tail -n+3 | awk '{ print $1 }') - -lecounter=0 - -# Checking users -for user in $users; do +# Checking user certificates +for user in $($BIN/v-list-users plain |cut -f 1); do USER_DATA=$VESTA/data/users/$user - # Checking user certificates + lecounter=0 + for domain in $(search_objects 'web' 'LETSENCRYPT' 'yes' 'DOMAIN'); do - # Working on Web domain check - if is suspended - webSuspended=$(grep "DOMAIN='$domain'" $USER_DATA/web.conf |grep "SUSPENDED='yes") - if [ ! -z "$webSuspended" ]; then - continue; - fi; - crt="$VESTA/data/users/$user/ssl/$domain.crt" - crt_data=$(openssl x509 -text -in "$crt") - expire=$(echo "$crt_data" |grep "Not After") - expire=$(echo "$expire" |cut -f 2,3,4 -d :) - expire=$(date -d "$expire" +%s) + crt_data=$(openssl x509 -text -in $USER_DATA/ssl/$domain.crt) + not_after=$(echo "$crt_data" |grep "Not After" |cut -f 2,3,4 -d :) + expiration=$(date -d "$not_after" +%s) now=$(date +%s) - expire=$((expire - now)) - expire=$((expire / 86400)) - domain=$(basename $crt |sed -e "s/.crt$//") - if [[ "$expire" -lt 31 ]]; then - if [ $lecounter -gt 0 ]; then - sleep 10 - fi - ((lecounter++)) + seconds_valid=$((expiration - now)) + days_valid=$((seconds_valid / 86400)) + if [[ "$days_valid" -lt 31 ]]; then aliases=$(echo "$crt_data" |grep DNS:) aliases=$(echo "$aliases" |sed -e "s/DNS://g" -e "s/,//") aliases=$(echo "$aliases" |tr ' ' '\n' |sed "/^$/d") aliases=$(echo "$aliases" |grep -v "^$domain$") - if [ ! -z "$aliases" ]; then - aliases=$(echo "$aliases" |sed -e ':a;N;$!ba;s/\n/,/g') - msg=$($BIN/v-add-letsencrypt-domain $user $domain $aliases) - if [ $? -ne 0 ]; then - echo "$domain $msg" - fi - else - msg==$($BIN/v-add-letsencrypt-domain $user $domain) - if [ $? -ne 0 ]; then - echo "$domain $msg" - fi + aliases=$(echo "$aliases" |sed -e ':a;N;$!ba;s/\n/,/g') + msg=$($BIN/v-add-letsencrypt-domain $user $domain $aliases) + if [ $? -ne 0 ]; then + echo "$domain $msg" fi + if [ $lecounter -gt 0 ]; then + sleep 10 + fi + ((lecounter++)) fi done done