diff --git a/.drone.yml b/.drone.yml index 1a4d9d3d66..8912c91958 100644 --- a/.drone.yml +++ b/.drone.yml @@ -52,6 +52,7 @@ trigger: - refs/heads/servicing - refs/heads/tests/* - refs/heads/main + - refs/heads/ipv6 - refs/pull/*/head --- @@ -112,6 +113,7 @@ trigger: - refs/heads/servicing - refs/heads/tests/* - refs/heads/main + - refs/heads/ipv6 - refs/pull/*/head --- diff --git a/bin/v-add-dns-domain b/bin/v-add-dns-domain index 8dec55714e..505394734d 100755 --- a/bin/v-add-dns-domain +++ b/bin/v-add-dns-domain @@ -1,6 +1,9 @@ #!/bin/bash +# +# !!! WRAPPER SCRIPT FOR COMPATIBILITY PURPOSES WITH OLD IPV4 SCRIPTS AND EXTERNAL CALLS !!! +# # info: add dns domain -# options: USER DOMAIN IP [NS1] [NS2] [NS3] [NS4] [NS5] [NS6] [NS7] [NS8] [RESTART] +# options: USER DOMAIN IP [NS1] [NS2] [NS3] [NS4] [NS5] [NS6] [NS7] [NS8] [RESTART] [DNSSEC] # # example: v-add-dns-domain admin example.com ns1.example.com ns2.example.com '' '' '' '' '' '' yes # @@ -15,15 +18,16 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -ip=$3 -ns1=$4 -ns2=$5 -ns3=$6 -ns4=$7 -ns5=$8 -ns6=$9 +user=${1} +domain=${2} +ip=${3} +ipv6="" # EMPTY! Not used here, because of old IPV4 only mode +ns1=${4} +ns2=${5} +ns3=${6} +ns4=${7} +ns5=${8} +ns6=${9} ns7=${10} ns8=${11} restart=${12} @@ -32,191 +36,5 @@ dnssec=${13} # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf -# shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh -# shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh -# shellcheck source=/usr/local/hestia/func/rebuild.sh -source $HESTIA/func/rebuild.sh -# load config file -source_conf "$HESTIA/conf/hestia.conf" - -# Additional argument formatting -format_domain -format_domain_idn -domain_utf=$(idn2 --quiet -d "$domain_idn") - -#----------------------------------------------------------# -# Verifications # -#----------------------------------------------------------# - -check_args '3' "$#" 'USER DOMAIN IP [NS1] [NS2] [NS3] [..] [NS8] [RESTART]' -is_format_valid 'user' 'domain' 'ip' -is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' -is_object_valid 'user' 'USER' "$user" -is_object_unsuspended 'user' 'USER' "$user" - -if [ "$($BIN/v-list-dns-domain $user $domain_utf plain | cut -f 1) " != "$domain" ]; then - is_domain_new 'dns' "$domain_utf" -fi -if [ "$($BIN/v-list-dns-domain $user $domain_idn plain | cut -f 1) " != "$domain" ]; then - is_domain_new 'dns' "$domain_idn" -else - is_domain_new 'dns' "$domain" -fi -if [ -z "$(is_ip_format_valid $domain)" ]; then - echo "Error: Invalid domain format. IP address detected as input." - exit 1 -fi - -if [ -n "$restart" ]; then - is_format_valid 'restart' -fi - -if [ -n "$dnssec" ]; then - is_boolean_format_valid "$dnssec" 'dnssec' -fi - -is_package_full 'DNS_DOMAINS' -template=$(get_user_value '$DNS_TEMPLATE') -is_dns_template_valid "$template" - -is_base_domain_owner "$domain" - -if [ -n "$ns1" ]; then - ns1=$(echo $4 | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns1' -fi -if [ -n "$ns2" ]; then - ns2=$(echo $5 | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns2' -fi -if [ -n "$ns3" ]; then - ns3=$(echo $6 | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns3' -fi -if [ -n "$ns4" ]; then - ns4=$(echo $7 | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns4' -fi -if [ -n "$ns5" ]; then - ns5=$(echo $8 | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns5' -fi -if [ -n "$ns6" ]; then - ns6=$(echo $9 | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns6' -fi -if [ -n "$ns7" ]; then - ns7=$(echo ${10} | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns7' -fi -if [ -n "$ns8" ]; then - ns8=$(echo ${11} | sed -e 's/\.*$//g' -e 's/^\.*//g') - is_format_valid 'ns8' -fi - -# Perform verification if read-only mode is enabled -check_hestia_demo_mode - -#----------------------------------------------------------# -# Action # -#----------------------------------------------------------# - -# Defining NS variables -if [ -z $ns2 ]; then - i=1 - ns=$(get_user_value '$NS') - for nameserver in ${ns//,/ }; do - eval ns$i=$nameserver - ((++i)) - done -fi -soa="$ns1" -exp=$(date +%F -d "+ 1 year") -serial=$(date +'%Y%m%d01') -ttl=14400 - -# Reading template -template_data=$(cat "$DNSTPL/$template.tpl") - -# Deleting unused nameservers -if [ -z "$ns3" ]; then - template_data=$(echo "$template_data" | grep -v %ns3%) -fi -if [ -z "$ns4" ]; then - template_data=$(echo "$template_data" | grep -v %ns4%) -fi -if [ -z "$ns5" ]; then - template_data=$(echo "$template_data" | grep -v %ns5%) -fi -if [ -z "$ns6" ]; then - template_data=$(echo "$template_data" | grep -v %ns6%) -fi -if [ -z "$ns7" ]; then - template_data=$(echo "$template_data" | grep -v %ns7%) -fi -if [ -z "$ns8" ]; then - template_data=$(echo "$template_data" | grep -v %ns8%) -fi -if [ -z "$dnssec" ]; then - dnssec="no" -fi - -# Generating timestamp -time_n_date=$(date +'%T %F') -time=$(echo "$time_n_date" | cut -f 1 -d \ ) -date=$(echo "$time_n_date" | cut -f 2 -d \ ) - -# Adding dns zone to the user config -echo "$template_data" \ - | sed -e "s/%ip%/$ip/g" \ - -e "s/%domain_idn%/$domain_idn/g" \ - -e "s/%domain%/$domain/g" \ - -e "s/%ns1%/$ns1/g" \ - -e "s/%ns2%/$ns2/g" \ - -e "s/%ns3%/$ns3/g" \ - -e "s/%ns4%/$ns4/g" \ - -e "s/%ns5%/$ns5/g" \ - -e "s/%ns6%/$ns6/g" \ - -e "s/%ns7%/$ns7/g" \ - -e "s/%ns8%/$ns8/g" \ - -e "s/%time%/$time/g" \ - -e "s/%date%/$date/g" > $USER_DATA/dns/$domain.conf - -chmod 660 $USER_DATA/dns/$domain.conf -records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f 1 -d ' ')" - -# Adding dns.conf record -dns_rec="DOMAIN='$domain' IP='$ip' TPL='$template' TTL='$ttl' EXP='$exp'" -dns_rec="$dns_rec SOA='$soa' SERIAL='$serial' SRC='' RECORDS='$records'" -dns_rec="$dns_rec DNSSEC='$dnssec' KEY='' SLAVE='no' MASTER='' SUSPENDED='no' TIME='$time' DATE='$date'" - -echo "$dns_rec" >> $USER_DATA/dns.conf -chmod 660 $USER_DATA/dns.conf - -rebuild_dns_domain_conf - -# Updating dns-cluster queue -if [ "$DNS_CLUSTER" = "yes" ]; then - cmd="$BIN/v-add-remote-dns-domain $user $domain yes" - echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe -fi - -#----------------------------------------------------------# -# Hestia # -#----------------------------------------------------------# - -# Increasing domain value -increase_user_value "$user" '$U_DNS_DOMAINS' -increase_user_value "$user" '$U_DNS_RECORDS' "$records" - -# Restart named -$BIN/v-restart-dns $restart -check_result $? "DNS restart failed" - -# Logging -$BIN/v-log-action "$user" "Info" "DNS" "Added new DNS domain (Name: $domain)." -log_event "$OK" "$ARGUMENTS" -exit +"$HESTIA/bin/v-add-dns-domain-ipv46" "$user" "$domain" "$ip" "$ipv6" "$ns1" "$ns2" "$ns3" "$ns4" "$ns5" "$ns6" "$ns7" "$ns8" "$restart" "$dnssec" diff --git a/bin/v-add-dns-domain-ipv46 b/bin/v-add-dns-domain-ipv46 new file mode 100755 index 0000000000..c3eeeefbea --- /dev/null +++ b/bin/v-add-dns-domain-ipv46 @@ -0,0 +1,171 @@ +#!/bin/bash +# info: add dns domain +# options: USER DOMAIN IPV4 [IPV6] [NS1] [NS2] [NS3] [NS4] [NS5] [NS6] [NS7] [NS8] [RESTART] [DNSSEC] +# +# example: v-add-dns-domain admin example.com 192.168.0.1 1111:2222:3333:111 ns1.example.com ns2.example.com '' '' '' '' '' '' yes +# +# This function adds DNS zone with records defined in the template. If the exp +# argument isn't stated, the expiration date value will be set to next year. +# The soa argument is responsible for the relevant record. By default the first +# user's NS server is used. TTL is set as common for the zone and for all of +# its records with a default value of 14400 seconds. + +#----------------------------------------------------------# +# Variables & Functions # +#----------------------------------------------------------# + +# Argument definition +user=${1} +domain=${2} +ip=${3} +ipv6=${4} +ns1=$(echo ${5} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns2=$(echo ${6} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns3=$(echo ${7} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns4=$(echo ${8} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns5=$(echo ${9} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns6=$(echo ${10} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns7=$(echo ${11} | sed -e 's/\.*$//g' -e 's/^\.*//g') +ns8=$(echo ${12} | sed -e 's/\.*$//g' -e 's/^\.*//g') +restart=${13} +dnssec=${14} + +# Includes +# shellcheck source=/etc/hestiacp/hestia.conf +source /etc/hestiacp/hestia.conf +# shellcheck source=/usr/local/hestia/func/main.sh +source "$HESTIA/func/main.sh" +# shellcheck source=/usr/local/hestia/func/domain.sh +source "$HESTIA/func/domain.sh" +# shellcheck source=/usr/local/hestia/func/rebuild.sh +source "$HESTIA/func/rebuild.sh" +# load config file +source_conf "$HESTIA/conf/hestia.conf" + +# Additional argument formatting +format_domain +format_domain_idn +domain_utf=$(idn2 --quiet -d "$domain_idn") + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '3' "$#" 'USER DOMAIN [IPV4] [IPV6] [NS1] [NS2] [NS3] [..] [NS8] [RESTART] [DNSSEC]' +is_format_valid 'user' 'domain' 'ip' 'ipv6' +is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' +is_object_valid 'user' 'USER' "$user" +is_object_unsuspended 'user' 'USER' "$user" + +users_domain_utf=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain_utf/p") +if [ "$users_domain_utf" != "$domain" ]; then + is_domain_new 'dns' "$domain_utf" +fi +users_domain_idn=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain_idn/p") +if [ "$users_domain_idn" != "$domain" ]; then + is_domain_new 'dns' "$domain_idn" +else + is_domain_new 'dns' "$domain" +fi +if [ -n "$(get_ip_format "$domain")" ]; then + echo "Error: Invalid domain format. IP address detected as input." + exit 1 +fi + +if [ -n "$restart" ]; then + is_format_valid 'restart' +fi + +if [ -n "$dnssec" ]; then + is_boolean_format_valid "$dnssec" 'dnssec' +fi + +is_package_full 'DNS_DOMAINS' +template=$(get_user_value '$DNS_TEMPLATE') +is_dns_template_valid "$template" + +is_base_domain_owner "$domain" + +if [ -n "$ns1" ]; then + is_format_valid 'ns1' +fi +if [ -n "$ns2" ]; then + is_format_valid 'ns2' +fi +if [ -n "$ns3" ]; then + is_format_valid 'ns3' +fi +if [ -n "$ns4" ]; then + is_format_valid 'ns4' +fi +if [ -n "$ns5" ]; then + is_format_valid 'ns5' +fi +if [ -n "$ns6" ]; then + is_format_valid 'ns6' +fi +if [ -n "$ns7" ]; then + is_format_valid 'ns7' +fi +if [ -n "$ns8" ]; then + is_format_valid 'ns8' +fi + +# Perform verification if read-only mode is enabled +check_hestia_demo_mode + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Defining NS variables +if [ -z "$ns2" ]; then + get_dns_values # get DNS values, nameservers +fi +soa="$ns1" +exp=$(date +%F -d "+ 1 year") +serial=$(date +'%Y%m%d01') +ttl=14400 + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" | cut -f 1 -d \ ) +date=$(echo "$time_n_date" | cut -f 2 -d \ ) + +# Create DNS domain config +create_dns_domain_config +records=$(cat "$USER_DATA/dns/$domain.conf" | wc -l) + +# Adding dns.conf record +dns_rec="DOMAIN='$domain' IP='$ip' IP6='$ipv6' TPL='$template' TTL='$ttl' EXP='$exp'" +dns_rec="$dns_rec SOA='$soa' SERIAL='$serial' SRC='' RECORDS='$records'" +dns_rec="$dns_rec DNSSEC='$dnssec' KEY='' SLAVE='no' MASTER='' SUSPENDED='no' TIME='$time' DATE='$date'" + +echo "$dns_rec" >> "$USER_DATA/dns.conf" +chmod 660 "$USER_DATA/dns.conf" + +rebuild_dns_domain_conf + +# Updating dns-cluster queue +if [ "$DNS_CLUSTER" = "yes" ]; then + cmd="$BIN/v-add-remote-dns-domain $user $domain yes" + echo "$cmd" >> "$HESTIA/data/queue/dns-cluster.pipe" +fi + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Increasing domain value +increase_user_value "$user" '$U_DNS_DOMAINS' +increase_user_value "$user" '$U_DNS_RECORDS' "$records" + +# Restart named +"$BIN/v-restart-dns" "$restart" +check_result $? "DNS restart failed" + +# Logging +"$BIN/v-log-action" "$user" "Info" "DNS" "Added new DNS domain (Name: $domain)." +log_event "$OK" "$ARGUMENTS" + +exit diff --git a/bin/v-add-dns-on-web-alias b/bin/v-add-dns-on-web-alias index c4ee4a03a0..6743eff3de 100755 --- a/bin/v-add-dns-on-web-alias +++ b/bin/v-add-dns-on-web-alias @@ -1,8 +1,12 @@ #!/bin/bash # info: add dns domain or dns record after web domain alias -# options: USER ALIAS IP [RESTART] +# options: USER ALIAS IPV4 [IPV6] [RESTART] # -# example: v-add-dns-on-web-alias admin www.example.com 8.8.8.8 +# example: v-add-dns-on-web-alias admin www.example.com 8.8.8.8 1234:2123:1111:2 yes +# example: v-add-dns-on-web-alias admin www.example.com '' 1234:2123:1111:2 no +# +# deprecated options: USER ALIAS IP [RESTART] (only for backwards compatibilty purposes) +# depricated example: v-add-dns-on-web-alias admin www.example.com 8.8.8.8 # # This function adds dns domain or dns record based on web domain alias. @@ -11,18 +15,30 @@ #----------------------------------------------------------# # Argument definition -user=$1 -alias=$2 -ip=$3 -restart=$4 +user=${1} +alias=${2} +ip=${3} +parameter4=${4} # IPV6 address or restart option (backwards compatibility mode) +restart=${5} +ipv6="" +ipv4_par=\'\' +ipv6_par=\'\' + +if [ -n "$parameter4" ]; then + if [ "$parameter4" = "yes" -o "$parameter4" = "no" ]; then + restart="$parameter4" + else + ipv6="$parameter4" + fi +fi # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -31,13 +47,21 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '3' "$#" 'USER ALIAS IP [RESTART]' -is_format_valid 'user' 'alias' 'ip' 'restart' +is_format_valid 'user' 'alias' 'restart' is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" if [ -e "$USER_DATA/dns/$alias.conf" ]; then exit fi +if [ -n "$ip" ]; then + is_ip_format_valid "$ip" 'ipv4' # check for correct ipv4 format if not empty + ipv4_par=$ip +fi +if [ -n "$ipv6" ]; then + is_ip_format_valid "$ipv6" 'ipv6' # check for correct ipv6 format if not empty + ipv6_par=$ipv6 +fi # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -55,14 +79,13 @@ top_domain=$(echo "$alias" | sed -e "s/^$sub_domain.//") domain_lvl=$(echo "$alias" | grep -o "\." | wc -l) # Adding second level domain -if [ "$domain_lvl" -eq 1 ] || [ "${#top_domain}" -le '6' ]; then - $BIN/v-add-dns-domain \ - "$user" "$alias" "$ip" '' '' '' '' '' '' '' '' "$restart" >> /dev/null +if [ "$domain_lvl" -eq 1 -o "${#top_domain}" -le 6 ]; then + "$BIN/v-add-dns-domain-ipv46" "$user" "$alias" "$ipv4_par" "$ipv6_par" '' '' '' '' '' '' '' '' "$restart" >> /dev/null exit fi # Adding top-level domain and then its sub -$BIN/v-add-dns-domain "$user" "$top_domain" "$ip" '' '' '' '' '' '' '' '' "$restart" >> /dev/null +"$BIN/v-add-dns-domain-ipv46" "$user" "$top_domain" "$ipv4_par" "$ipv6_par" '' '' '' '' '' '' '' '' "$restart" >> /dev/null # Checking top-level domain if [ ! -e "$USER_DATA/dns/$top_domain.conf" ]; then @@ -71,15 +94,19 @@ fi # Checking subdomain record if [ "$sub_domain" == '*' ]; then - check_record=$(grep -w "RECORD='\*'" $USER_DATA/dns/$top_domain.conf) + check_record=$(grep -w "RECORD='\*'" "$USER_DATA/dns/$top_domain.conf") else - check_record=$(grep -w "RECORD='$sub_domain'" $USER_DATA/dns/$top_domain.conf) + check_record=$(grep -w "RECORD=\'${sub_domain}\'" "$USER_DATA/dns/$top_domain.conf") fi # Adding subdomain record if [ -z "$check_record" ]; then - $BIN/v-add-dns-record \ - "$user" "$top_domain" "$sub_domain" A "$ip" '' '' "$restart" >> /dev/null + if [ -n "$ip" ]; then + "$BIN/v-add-dns-record" "$user" "$top_domain" "$sub_domain" A "$ip" '' '' "$restart" >> /dev/null + fi + if [ -n "$ipv6" ]; then + "$BIN/v-add-dns-record" "$user" "$top_domain" "$sub_domain" AAAA "$ipv6" '' '' "$restart" >> /dev/null + fi fi #----------------------------------------------------------# diff --git a/bin/v-add-domain b/bin/v-add-domain index be69cbe50d..dd02b87241 100755 --- a/bin/v-add-domain +++ b/bin/v-add-domain @@ -1,8 +1,13 @@ #!/bin/bash # info: add web/dns/mail domain -# options: USER DOMAIN [IP] [RESTART] +# options: USER DOMAIN [IPV4] [IPV6] [RESTART] # -# example: v-add-domain admin example.com +# example: v-add-domain admin example.com 192.168.0.1 +# example: v-add-domain admin example.com 192.168.0.1 1111:2222:3333:1 +# example: v-add-domain admin example.com '' 1111:2222:3333:1 yes +# +# depricated options: USER DOMAIN [IP] [RESTART] +# depricated example: v-add-domain admin example.com # # This function adds web/dns/mail domain to a server. @@ -11,31 +16,52 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -ip=$3 -restart=$4 +user=${1} +domain=${2} +ip=${3} +parameter4=${4} # IPV6 address or restart option (backwards compatibility mode) +restart=${5} +ipv6="" +ipv4_par=\'\' +ipv6_par=\'\' + +if [ -n "$parameter4" ]; then + if [ "$parameter4" = "yes" -o "$parameter4" = "no" ]; then + restart="$parameter4" + else + ipv6="$parameter4" + fi +fi # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# -check_args '2' "$#" 'USER DOMAIN [IP] [RESTART]' +check_args '2' "$#" 'USER DOMAIN [IPV4] [IPV6] [RESTART]' is_format_valid 'user' 'domain' 'restart' if [ -n "$ip" ]; then - is_format_valid 'ip' + is_ip_format_valid "$ip" 'ipv4' # check for correct ipv4 format if not empty +fi +if [ -n "$ipv6" ]; then + is_ip_format_valid "$ipv6" 'ipv6' # check for correct ipv6 format if not empty fi is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" +if [ -z "$ip" -a -z "$ipv6" ]; then + get_user_ip # get first available user ipv4 address as fallback + get_user_ipv6 # get first available user ipv6 address as fallback +fi +ipv4_par=$ip +ipv6_par=$ipv6 # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -44,19 +70,14 @@ check_hestia_demo_mode # Action # #----------------------------------------------------------# -# Get ip if it wasn't defined -if [ -z "$ip" ]; then - get_user_ip - if [ -z "$ip" ]; then - check_result "$E_NOTEXIST" "no available IP address" - fi +if [ -z "$ip" -a -z "$ipv6" ]; then + check_result "$E_NOTEXIST" "no available IP address" fi - # Working on web domain if [ -n "$WEB_SYSTEM" ]; then check1=$(is_package_full 'WEB_DOMAINS') if [ $? -eq 0 ]; then - $BIN/v-add-web-domain "$user" "$domain" "$ip" 'no' + "$BIN/v-add-web-domain-ipv46" "$user" "$domain" "$ipv4_par" "$ipv6_par" 'no' check_result $? "can't add web domain" fi fi @@ -65,7 +86,7 @@ fi if [ -n "$DNS_SYSTEM" ]; then check2=$(is_package_full 'DNS_DOMAINS') if [ $? -eq 0 ]; then - $BIN/v-add-dns-domain "$user" "$domain" "$ip" "" "" "" "" "" "" "" "" "no" + "$BIN/v-add-dns-domain-ipv46" "$user" "$domain" "$ipv4_par" "$ipv6_par" "" "" "" "" "" "" "" "" "no" check_result $? "can't add dns domain" fi fi @@ -74,7 +95,7 @@ fi if [ -n "$MAIL_SYSTEM" ]; then check3=$(is_package_full 'MAIL_DOMAINS') if [ $? -eq 0 ]; then - $BIN/v-add-mail-domain $user $domain 'no' + "$BIN/v-add-mail-domain" "$user" "$domain" 'no' check_result $? "can't add mail domain" fi fi @@ -84,13 +105,13 @@ if [[ "$check1" != '' && "$check2" != '' && "$check3" != '' ]]; then fi # Restarting services -$BIN/v-restart-web "$restart" +"$BIN/v-restart-web" "$restart" check_result $? "can't restart web" > /dev/null -$BIN/v-restart-proxy "$restart" +"$BIN/v-restart-proxy" "$restart" check_result $? "can't restart proxy" > /dev/null -$BIN/v-restart-dns "$restart" +"$BIN/v-restart-dns" "$restart" check_result $? "can't restart dns" > /dev/null #----------------------------------------------------------# diff --git a/bin/v-add-firewall-ban b/bin/v-add-firewall-ban index 014335b049..71f0c1ed6e 100755 --- a/bin/v-add-firewall-ban +++ b/bin/v-add-firewall-ban @@ -11,19 +11,18 @@ #----------------------------------------------------------# # Argument definition -ip=$1 +ip46="$1" chain=$(echo $2 | tr '[:lower:]' '[:upper:]') -# Defining absolute path for iptables and modprobe -iptables="/sbin/iptables" +subnetcalc_bin="$(which subnetcalc)" # subnetcalc binary to calculate subnet of an ipv6 net # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/firewall.sh -source $HESTIA/func/firewall.sh +source "$HESTIA/func/firewall.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -32,9 +31,28 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '2' "$#" 'IP CHAIN' -is_format_valid 'ip' 'chain' +is_format_valid 'ip46' 'chain' is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +ip_format=$(get_ip_format "$ip46") # ip verification and format identification +case "$ip_format" in + 4) + ipfamily="inet4" + iptables="iptables" + ip2ban="$ip46" + reject_with="icmp-port-unreachable" + ;; + 6) + ipfamily="inet6" + iptables="ip6tables" + [ -x "$subnetcalc_bin" ] && ip2ban="${ip46}/64" || ip2ban="$ip46" # subnet ban of /64 net only if subnetcalc binary is installed + reject_with="icmp6-port-unreachable" + ;; + *) + check_result 1 "Wrong IP address $ip46 !" # wrong ip address + ;; +esac + # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -46,26 +64,29 @@ check_hestia_demo_mode heal_iptables_links # Checking server ip -if [ -e "$HESTIA/data/ips/$ip" ] || [ "$ip" = '127.0.0.1' ]; then +if [ -e "$HESTIA/data/ips/$ip46" ] || [ "$ip46" = '127.0.0.1' ] || [ "$ip46" = '::1' ] || [ "$ip46" = '0:0:0:0:0:0:0:1' ]; then exit fi # Checking ip exclusions excludes="$HESTIA/data/firewall/excludes.conf" -check_excludes=$(grep "^$ip$" $excludes 2> /dev/null) +check_excludes=$(grep "^$ip46$" $excludes 2> /dev/null) if [ -n "$check_excludes" ]; then exit fi # Checking ip in banlist conf="$HESTIA/data/firewall/banlist.conf" -check_ip=$(grep "IP='$ip' CHAIN='$chain'" $conf 2> /dev/null) +check_ip=$(grep "IP='$ip46' CHAIN='$chain'" $conf 2> /dev/null) if [ -n "$check_ip" ]; then exit fi # Adding chain -$BIN/v-add-firewall-chain $chain +"$BIN/v-add-firewall-chain" "$chain" "" "" "$ipfamily" "$iptables" + +# Get iptables binary +iptables="$(get_iptables_bin "$iptables")" # Generating timestamp time_n_date=$(date +'%T %F') @@ -73,19 +94,23 @@ time=$(echo "$time_n_date" | cut -f 1 -d \ ) date=$(echo "$time_n_date" | cut -f 2 -d \ ) # Adding ip to banlist -echo "IP='$ip' CHAIN='$chain' TIME='$time' DATE='$date'" >> $conf -$iptables -I fail2ban-$chain 1 -s $ip \ - -j REJECT --reject-with icmp-port-unreachable 2> /dev/null +echo "IP='$ip46' CHAIN='$chain' TIME='$time' DATE='$date'" >> "$conf" +error_message="$(${iptables} -I "fail2ban-${chain}" 1 -s "$ip2ban" -j REJECT --reject-with "$reject_with")" +if [ $? -ne 0 ]; then + error_code=$? + check_result 1 "$error_message" + exit $error_code +fi # Changing permissions -chmod 660 $conf +chmod 660 "$conf" #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Warning" "Firewall" "Banned IP address $ip." +"$BIN/v-log-action" "system" "Warning" "Firewall" "Banned IP address $ip46." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-add-firewall-chain b/bin/v-add-firewall-chain index 77f95470d7..f09d1c46dd 100755 --- a/bin/v-add-firewall-chain +++ b/bin/v-add-firewall-chain @@ -1,6 +1,6 @@ #!/bin/bash # info: add firewall chain -# options: CHAIN [PORT] [PROTOCOL] +# options: CHAIN [PORT] [PROTOCOL] [IPFAMILY] [IPTABLES] # # example: v-add-firewall-chain CRM 5678 TCP # @@ -18,8 +18,12 @@ protocol="$3" [ -z "$protocol" ] && protocol='TCP' protocol=$(echo "$protocol" | tr '[:lower:]' '[:upper:]') -# Defining absolute path to iptables -iptables="/sbin/iptables" +# Defining ip family version +ipfamily="$4" +[ -z "$ipfamily" ] && ipfamily="inet4" + +# Defining iptables version +iptables="$5" # Includes # shellcheck source=/etc/hestiacp/hestia.conf @@ -41,8 +45,8 @@ fi # Verifications # #----------------------------------------------------------# -check_args '1' "$#" 'CHAIN [PORT] [PROTOCOL]' -is_format_valid 'chain' 'port_ext' 'protocol' +check_args '1' "$#" 'CHAIN [PORT] [PROTOCOL] [IPFAMILY] [IPTABLES]' +is_format_valid 'chain' 'port_ext' 'protocol' 'ipfamily' 'iptables' is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' # Perform verification if read-only mode is enabled @@ -55,6 +59,9 @@ check_hestia_demo_mode # Self heal iptables links heal_iptables_links +# Get iptables binary +iptables="$(get_iptables_bin "$iptables")" + # Checking known chains case $chain in SSH) # Get ssh port (or ports) using v-list-sys-sshd-port. @@ -97,9 +104,9 @@ case $chain in esac # Adding chain -$iptables -N fail2ban-$chain 2> /dev/null +${iptables} -N "fail2ban-$chain" 2> /dev/null if [ $? -eq 0 ]; then - $iptables -A fail2ban-$chain -j RETURN + ${iptables} -A "fail2ban-$chain" -j RETURN # Adding multiport module if [[ "$port" =~ ,|-|: ]]; then @@ -107,7 +114,7 @@ if [ $? -eq 0 ]; then else port_str="--dport $port" fi - $iptables -I INPUT -p $protocol $port_str -j fail2ban-$chain + ${iptables} -I INPUT -p "$protocol" $port_str -j "fail2ban-$chain" fi # Preserving chain @@ -126,7 +133,7 @@ fi #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Info" "Firewall" "Added service to firewall (Service: $chain, Port: $port, Protocol: $protocol)." +"$BIN/v-log-action" "system" "Info" "Firewall" "Added service to firewall (Service: $chain, Port: $port, Protocol: $protocol)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-add-firewall-ipv6-rule b/bin/v-add-firewall-ipv6-rule new file mode 100755 index 0000000000..1887437c30 --- /dev/null +++ b/bin/v-add-firewall-ipv6-rule @@ -0,0 +1,94 @@ +#!/bin/bash +# info: add firewall rule +# options: ACTION IPV6 PORT [PROTOCOL] [COMMENT] [RULE] +# +# The function adds new rule to system firewall + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +action=$(echo $1|tr '[:lower:]' '[:upper:]') +ipv6=$2 +port_ext=$3 +protocol=${4-TCP} +protocol=$(echo $protocol|tr '[:lower:]' '[:upper:]') +comment=$5 +rule=$6 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + +# Get next firewall rule id +get_next_fw_rule() { + if [ -z "$rule" ]; then + curr_str=$(grep "RULE=" $HESTIA/data/firewallv6/rules.conf |\ + cut -f 2 -d \' | sort -n | tail -n1) + rule="$((curr_str +1))" + fi +} + +sort_fw_rules() { + cat $HESTIA/data/firewallv6/rules.conf |\ + sort -n -k 2 -t \' > $HESTIA/data/firewallv6/rules.conf.tmp + mv -f $HESTIA/data/firewallv6/rules.conf.tmp \ + $HESTIA/data/firewallv6/rules.conf +} + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '3' "$#" 'ACTION IPV6 PORT [PROTOCOL] [COMMENT] [RULE]' +is_format_valid 'action' 'protocol' 'port_ext' 'ipv6' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +get_next_fw_rule +is_format_valid 'rule' +is_object_new '../../data/firewallv6/rules' 'RULE' "$rule" +if [ ! -z "$comment" ]; then + is_format_valid 'comment' +fi + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" |cut -f 1 -d \ ) +date=$(echo "$time_n_date" |cut -f 2 -d \ ) + +# Concatenating rule +str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'" +str="$str IP6='$ipv6' COMMENT='$comment' SUSPENDED='no'" +str="$str TIME='$time' DATE='$date'" + +# Adding to config +echo "$str" >> $HESTIA/data/firewallv6/rules.conf + +# Changing permissions +chmod 660 $HESTIA/data/firewallv6/rules.conf + +# Sorting firewall rules by id number +sort_fw_rules + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-add-letsencrypt-domain b/bin/v-add-letsencrypt-domain index e962703c53..9d226135f9 100755 --- a/bin/v-add-letsencrypt-domain +++ b/bin/v-add-letsencrypt-domain @@ -77,8 +77,10 @@ fi # Set DNS CAA record retrieval commands if [ -n "$DNS_SYSTEM" ]; then - dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1) - caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "CAA" | grep -i "letsencrypt.org" | cut -d' ' -f1) + dns_domain=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain/p") + if [ -n "$dns_domain" ]; then + caa_record=$("$BIN/v-list-dns-records" "$user" "$domain" | grep -i "CAA" | grep -i "letsencrypt.org" | cut -d' ' -f1) + fi fi if [ -z "$mail" ] || [ "$mail" = 'no' ]; then @@ -272,8 +274,10 @@ for auth in $authz; do if [[ "$status" -ne 200 ]]; then # Delete DNS CAA record if [ -n "$DNS_SYSTEM" ]; then - dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1) - caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + dns_domain=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain/p") + if [ -n "$dns_domain" ]; then + caa_record=$("$BIN/v-list-dns-records" "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + fi if [ "$dns_domain" = "$domain" ]; then if [ -n "$caa_record" ]; then @@ -288,11 +292,13 @@ for auth in $authz; do 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 + if [ -n "$dns_domain" ]; then + 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 + fi $BIN/v-add-dns-record "$user" "$domain" "_acme-challenge" "TXT" "$record" check_result $? "DNS _acme-challenge record wasn't created ($domain)" else @@ -390,8 +396,10 @@ for auth in $authz; do if [[ "$status" -ne 200 ]]; then # Delete DNS CAA record if [ -n "$DNS_SYSTEM" ]; then - dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1) - caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + dns_domain=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain/p") + if [ -n "$dns_domain" ]; then + caa_record=$("$BIN/v-list-dns-records" "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + fi if [ "$dns_domain" = "$domain" ]; then if [ -n "$caa_record" ]; then @@ -413,8 +421,10 @@ for auth in $authz; do if [ "$i" -gt 10 ]; then # Delete DNS CAA record if [ -n "$DNS_SYSTEM" ]; then - dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1) - caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + dns_domain=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain/p") + if [ -n "$dns_domain" ]; then + caa_record=$("$BIN/v-list-dns-records" "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + fi if [ "$dns_domain" = "$domain" ]; then if [ -n "$caa_record" ]; then @@ -430,8 +440,10 @@ for auth in $authz; do if [ "$validation" = 'invalid' ]; then # Delete DNS CAA record if [ -n "$DNS_SYSTEM" ]; then - dns_domain=$($BIN/v-list-dns-domains "$user" | grep "$domain" | cut -d' ' -f1) - caa_record=$($BIN/v-list-dns-records "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + dns_domain=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain/p") + if [ -n "$dns_domain" ]; then + caa_record=$("$BIN/v-list-dns-records" "$user" "$domain" | grep -i "letsencrypt" | cut -d' ' -f1) + fi if [ "$dns_domain" = "$domain" ]; then if [ -n "$caa_record" ]; then diff --git a/bin/v-add-letsencrypt-host b/bin/v-add-letsencrypt-host index e0239d4565..e0427c193d 100755 --- a/bin/v-add-letsencrypt-host +++ b/bin/v-add-letsencrypt-host @@ -50,7 +50,7 @@ is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' # Check if hostname already exists as domain if [ "$($BIN/v-list-web-domain $user $domain plain | cut -f 1)" != "$domain" ]; then # Create web domain for hostname - $BIN/v-add-web-domain "$user" "$domain" + $BIN/v-add-web-domain-ipv46 "$user" "$domain" fi # Validate web domain diff --git a/bin/v-add-mail-domain b/bin/v-add-mail-domain index ebe5a70fca..cd49b0f946 100755 --- a/bin/v-add-mail-domain +++ b/bin/v-add-mail-domain @@ -11,8 +11,8 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 +user=${1} +domain=${2} antispam=${3-yes} antivirus=${4-yes} dkim=${5-yes} @@ -24,13 +24,13 @@ reject=${8-no} # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # shellcheck source=/usr/local/hestia/func/syshealth.sh -source $HESTIA/func/syshealth.sh +source "$HESTIA/func/syshealth.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -56,21 +56,23 @@ is_system_enabled "$MAIL_SYSTEM" 'MAIL_SYSTEM' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" -if [ "$($BIN/v-list-mail-domain $user $domain_utf plain | cut -f 1) " != "$domain" ]; then +users_domain_utf=$("$BIN/v-list-mail-domains" "$user" list | sed -ne "/$domain_utf/p") +if [ "$users_domain_utf" != "$domain" ]; then is_domain_new 'mail' "$domain_utf" fi -if [ "$($BIN/v-list-mail-domain $user $domain_idn plain | cut -f 1) " != "$domain" ]; then +users_domain_idn=$("$BIN/v-list-mail-domains" "$user" list | sed -ne "/$domain_idn/p") +if [ "$users_domain_idn" != "$domain" ]; then is_domain_new 'mail' "$domain_idn" else is_domain_new 'mail' "$domain" fi -if [ -z "$(is_ip_format_valid $domain)" ]; then +if [ -n "$(get_ip_format "$domain")" ]; then echo "Error: Invalid domain format. IP address detected as input." exit 1 fi is_package_full 'MAIL_DOMAINS' -is_dir_symlink $HOMEDIR/$user/mail +is_dir_symlink "$HOMEDIR/$user/mail" is_base_domain_owner "$domain" @@ -84,11 +86,18 @@ check_hestia_demo_mode source_conf "$USER_DATA/user.conf" # Inherit web domain local ip address domain_ip=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP') -if [ ! -z "$domain_ip" ]; then +domain_ipv6=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP6') +if [ -n "$domain_ipv6" ]; then + local_ipv6=$(get_real_ip "$domain_ipv6") + is_ipv6_valid "$local_ipv6" "$user" +fi +if [ -n "$domain_ip" ]; then local_ip=$(get_real_ip "$domain_ip") is_ip_valid "$local_ip" "$user" -else - get_user_ip +fi +if [ -z "$local_ip" -a -z "$local_ipv6" ]; then + get_user_ipv6 # get first available user ipv6 address as fallback, if none ip address was defined + get_user_ip # get first available user ipv4 address as fallback, if none ipv6 user address available fi # Generating timestamp @@ -106,36 +115,38 @@ fi s="DOMAIN='$domain' ANTIVIRUS='$antivirus' ANTISPAM='$antispam' REJECT='$reject' DKIM='$dkim' WEBMAIL=''" s="$s SSL='no' LETSENCRYPT='no' CATCHALL='' ACCOUNTS='0' RATE_LIMIT='$RATE_LIMIT' U_DISK='0' SUSPENDED='no' TIME='$time'" s="$s DATE='$date'" -echo $s >> $USER_DATA/mail.conf -touch $USER_DATA/mail/$domain.conf +echo "$s" >> "$USER_DATA/mail.conf" +touch "$USER_DATA/mail/$domain.conf" syshealth_repair_mail_config # Generating DKIM keys if [ "$dkim" = 'yes' ]; then - openssl genrsa -out $USER_DATA/mail/$domain.pem $dkim_size &> /dev/null - openssl rsa -pubout -in $USER_DATA/mail/$domain.pem \ - -out $USER_DATA/mail/$domain.pub &> /dev/null + openssl genrsa -out "$USER_DATA/mail/$domain.pem" "$dkim_size" &> /dev/null + openssl rsa -pubout -in "$USER_DATA/mail/$domain.pem" \ + -out "$USER_DATA/mail/$domain.pub" &> /dev/null fi # Set permissions -chmod 660 $USER_DATA/mail/$domain.* -chmod 660 $USER_DATA/mail.conf +chmod 660 "$USER_DATA/mail/$domain".* +chmod 660 "$USER_DATA/mail.conf" # Building exim configs if [[ "$MAIL_SYSTEM" =~ exim ]]; then - mkdir $HOMEDIR/$user/conf/mail/$domain - mkdir $HOMEDIR/$user/mail/$domain_idn - touch $HOMEDIR/$user/conf/mail/$domain/aliases - touch $HOMEDIR/$user/conf/mail/$domain/passwd - touch $HOMEDIR/$user/conf/mail/$domain/fwd_only - touch $HOMEDIR/$user/conf/mail/$domain/accounts - ln -s $HOMEDIR/$user/conf/mail/$domain \ - /etc/$MAIL_SYSTEM/domains/$domain_idn + mkdir "$HOMEDIR/$user/conf/mail/$domain" + mkdir "$HOMEDIR/$user/mail/$domain_idn" + touch "$HOMEDIR/$user/conf/mail/$domain/aliases" + touch "$HOMEDIR/$user/conf/mail/$domain/passwd" + touch "$HOMEDIR/$user/conf/mail/$domain/fwd_only" + touch "$HOMEDIR/$user/conf/mail/$domain/accounts" + ln -s "$HOMEDIR/$user/conf/mail/$domain" "/etc/$MAIL_SYSTEM/domains/$domain_idn" # Seeting outgoing ip address if [ -n "$local_ip" ]; then - echo "$local_ip" > $HOMEDIR/$user/conf/mail/$domain/ip + echo "$local_ip" > "$HOMEDIR/$user/conf/mail/$domain/ip" + fi + if [ -n "$local_ipv6" ]; then + echo "$local_ipv6" > "$HOMEDIR/$user/conf/mail/$domain/ipv6" fi if [ -n "$ANTISPAM_SYSTEM" ]; then @@ -157,44 +168,31 @@ if [[ "$MAIL_SYSTEM" =~ exim ]]; then # Adding dkim support if [ "$dkim" = 'yes' ]; then - cp -f $USER_DATA/mail/$domain.pem \ - $HOMEDIR/$user/conf/mail/$domain/dkim.pem + cp -f "$USER_DATA/mail/$domain.pem" "$HOMEDIR/$user/conf/mail/$domain/dkim.pem" fi # Set permission - chmod 771 $HOMEDIR/$user/conf/mail/$domain - chmod 660 $HOMEDIR/$user/conf/mail/$domain/* - chmod 771 /etc/$MAIL_SYSTEM/domains/$domain_idn - chmod 770 $HOMEDIR/$user/mail/$domain_idn + chmod 771 "$HOMEDIR/$user/conf/mail/$domain" + chmod 660 "$HOMEDIR/$user/conf/mail/$domain"/* + chmod 771 "/etc/$MAIL_SYSTEM/domains/$domain_idn" + chmod 770 "$HOMEDIR/$user/mail/$domain_idn" # Set ownership - chown -R $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain + chown -R "$MAIL_USER:mail" "$HOMEDIR/$user/conf/mail/$domain" if [ "$IMAP_SYSTEM" = 'dovecot' ]; then - chown -R dovecot:mail $HOMEDIR/$user/conf/mail/$domain/passwd + chown -R dovecot:mail "$HOMEDIR/$user/conf/mail/$domain/passwd" fi - chown $MAIL_USER:mail $HOMEDIR/$user/conf/mail/$domain/accounts - chown $user:mail $HOMEDIR/$user/mail/$domain_idn + chown "$MAIL_USER:mail" "$HOMEDIR/$user/conf/mail/$domain/accounts" + chown "$user:mail" "$HOMEDIR/$user/mail/$domain_idn" fi # Adding dkim dns records -if [ -n "$DNS_SYSTEM" ] && [ "$dkim" = 'yes' ]; then - check_dns_domain=$(is_object_valid 'dns' 'DOMAIN' "$domain") - if [ "$?" -eq 0 ]; then - p=$(cat $USER_DATA/mail/$domain.pub | grep -v ' KEY---' | tr -d '\n') - record='_domainkey' - policy="\"t=y; o=~;\"" - $BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$policy" '' '' 'no' '' 'yes' - - record='mail._domainkey' - selector="\"v=DKIM1\; k=rsa\; p=$p\"" - $BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$selector" '' '' 'yes' '' 'yes' - fi -fi +add_dns_dkim_records # Add webmail configuration to mail domain if [ -n "$WEB_SYSTEM" ] || [ -n "$PROXY_SYSTEM" ]; then if [ -n "$IMAP_SYSTEM" ]; then - $BIN/v-add-mail-domain-webmail "$user" "$domain" '' 'no' + "$BIN/v-add-mail-domain-webmail" "$user" "$domain" '' 'no' fi fi @@ -210,15 +208,15 @@ if [ "$dkim" = 'yes' ]; then fi # Restarting web server -$BIN/v-restart-web "$restart" +"$BIN/v-restart-web" "$restart" check_result $? "Web restart failed" > /dev/null # Restarting proxy server -$BIN/v-restart-proxy "$restart" +"$BIN/v-restart-proxy" "$restart" check_result $? "Proxy restart failed" > /dev/null # Logging -$BIN/v-log-action "$user" "Info" "Mail" "Added new mail domain ($domain)." +"$BIN/v-log-action" "$user" "Info" "Mail" "Added new mail domain ($domain)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-add-mail-domain-ssl b/bin/v-add-mail-domain-ssl index 218ecd0ea3..662fc6737f 100755 --- a/bin/v-add-mail-domain-ssl +++ b/bin/v-add-mail-domain-ssl @@ -12,10 +12,10 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -ssl_dir=$3 -restart="$4" +user=${1} +domain=${2} +ssl_dir=${3} +restart=${4} # Additional argument formatting if [[ "$domain" =~ [[:upper:]] ]]; then @@ -34,11 +34,11 @@ domain_idn=$(idn2 --quiet "$domain") # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -72,11 +72,18 @@ fi # Inherit web domain local ip address domain_ip=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP') +domain_ipv6=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP6') +if [ -n "$domain_ipv6" ]; then + local_ipv6=$(get_real_ip "$domain_ipv6") + is_ipv6_valid "$local_ipv6" "$user" +fi if [ -n "$domain_ip" ]; then local_ip=$(get_real_ip "$domain_ip") is_ip_valid "$local_ip" "$user" -else - get_user_ip +fi +if [ -z "$local_ip" -a -z "$local_ipv6" ]; then + get_user_ipv6 # get first available user ipv6 address as fallback, if none ip address was defined + get_user_ip # get first available user ipv4 address as fallback, if none ipv6 user address available fi # Call routine to add SSL configuration to mail domain @@ -109,10 +116,10 @@ else fi fi -add_webmail_config "$WEB_SYSTEM" "${WEBMAIL_TEMPLATE}.stpl" +add_webmail_config "$WEB_SYSTEM" "$WEBMAIL_TEMPLATE.stpl" if [ -n "$PROXY_SYSTEM" ]; then - add_webmail_config "$PROXY_SYSTEM" "${PROXY_TEMPLATE}.stpl" + add_webmail_config "$PROXY_SYSTEM" "$PROXY_TEMPLATE.stpl" fi # Increase value for domain @@ -126,19 +133,19 @@ update_object_value 'mail' 'DOMAIN' "$domain" '$SSL' "yes" #----------------------------------------------------------# # Restarting mail server -$BIN/v-restart-mail "$restart" +"$BIN/v-restart-mail" "$restart" check_result $? "Mail restart failed" > /dev/null # Restarting web server -$BIN/v-restart-web "$restart" +"$BIN/v-restart-web" "$restart" check_result $? "Web restart failed" > /dev/null # Restarting proxy server -$BIN/v-restart-proxy "$restart" +"$BIN/v-restart-proxy" "$restart" check_result $? "Proxy restart failed" > /dev/null # Logging -$BIN/v-log-action "$user" "Info" "Mail" "SSL enabled (Domain: $domain)." +"$BIN/v-log-action" "$user" "Info" "Mail" "SSL enabled (Domain: $domain)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-add-mail-domain-webmail b/bin/v-add-mail-domain-webmail index 367ca098ab..bb45f24eec 100755 --- a/bin/v-add-mail-domain-webmail +++ b/bin/v-add-mail-domain-webmail @@ -13,15 +13,13 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -webmail=$3 +user="$1" +domain="$2" +webmail="$3" restart="$4" -quiet=$5 +quiet="$5" -if [ -z "$restart" ]; then - restart="yes" -fi +[ -z "$restart" ] && restart="yes" # Additional argument formatting if [[ "$domain" =~ [[:upper:]] ]]; then @@ -38,11 +36,11 @@ fi # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -79,48 +77,32 @@ check_hestia_demo_mode # Inherit web domain local ip address domain_ip=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP') +domain_ipv6=$(get_object_value 'web' 'DOMAIN' "$domain" '$IP6') +if [ -n "$domain_ipv6" ]; then + local_ipv6=$(get_real_ip "$domain_ipv6") + is_ipv6_valid "$local_ipv6" "$user" + ipv6="$local_ipv6" +fi if [ -n "$domain_ip" ]; then local_ip=$(get_real_ip "$domain_ip") is_ip_valid "$local_ip" "$user" - ip=$local_ip + ip="$local_ip" nat_ip=$(get_ip_value '$NAT') if [ -n "$nat_ip" ]; then - ip=$nat_ip + ip="$nat_ip" fi -else - get_user_ip +fi +if [ -z "$ip" -a -z "$ipv6" ]; then + get_user_ip # get first available user ipv4 address as fallback + get_user_ipv6 # get first available user ipv6 address as fallback fi # Verify that webmail alias variable exists and create it if it does not if [ -z "$WEBMAIL_ALIAS" ]; then - $BIN/v-change-sys-config-value 'WEBMAIL_ALIAS' "webmail" + "$BIN/v-change-sys-config-value" 'WEBMAIL_ALIAS' "webmail" else - # Ensure DNS record exists if Hestia is hosting DNS zones - if [ -n "$DNS_SYSTEM" ]; then - dns_domain=$($BIN/v-list-dns-domains $user | grep $domain | cut -d' ' -f1) - webmail_record=$($BIN/v-list-dns-records $user $domain | grep -i " $WEBMAIL_ALIAS " | cut -d' ' -f1) - if [ "$dns_domain" = "$domain" ]; then - if [ "$WEBMAIL_ALIAS" != "mail" ]; then - #Prevent mail.domain.com to be cycled - if [ -z "$webmail_record" ]; then - if [ "$quiet" = "yes" ]; then - $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes' - else - $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes' - fi - else - if [ "$quiet" = "yes" ]; then - $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes' - $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes' - else - $BIN/v-delete-dns-record "$user" "$domain" "$webmail_record" "$restart" 'yes' - $BIN/v-add-dns-record "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes' - fi - fi - fi - fi - fi + add_dns_webmail_records # Add DNS records for Webmail if [ "$webmail" == "roundcube" ]; then WEBMAIL_TEMPLATE="default" @@ -144,18 +126,18 @@ else fi fi - add_webmail_config "$WEB_SYSTEM" "${WEBMAIL_TEMPLATE}.tpl" + add_webmail_config "$WEB_SYSTEM" "$WEBMAIL_TEMPLATE.tpl" if [ -n "$PROXY_SYSTEM" ]; then - add_webmail_config "$PROXY_SYSTEM" "${PROXY_TEMPLATE}.tpl" + add_webmail_config "$PROXY_SYSTEM" "$PROXY_TEMPLATE.tpl" fi # Enable SSL for webmail if available - if [ -f $HOMEDIR/$user/conf/mail/$domain/ssl/$domain.crt ] || [ "$SSL" = 'yes' ]; then - add_webmail_config "$WEB_SYSTEM" "${WEBMAIL_TEMPLATE}.stpl" + if [ -f "$HOMEDIR/$user/conf/mail/$domain/ssl/$domain.crt" ] || [ "$SSL" = 'yes' ]; then + add_webmail_config "$WEB_SYSTEM" "$WEBMAIL_TEMPLATE.stpl" if [ -n "$PROXY_SYSTEM" ]; then - add_webmail_config "$PROXY_SYSTEM" "${PROXY_TEMPLATE}.stpl" + add_webmail_config "$PROXY_SYSTEM" "$PROXY_TEMPLATE.stpl" fi fi fi @@ -174,16 +156,16 @@ update_object_value 'mail' 'DOMAIN' "$domain" '$WEBMAIL' "$webmail" if [ "$restart" = 'yes' ]; then # Restarting web server - $BIN/v-restart-web "$restart" + "$BIN/v-restart-web" "$restart" check_result $? "Web restart failed" > /dev/null - $BIN/v-restart-proxy "$restart" + "$BIN/v-restart-proxy" "$restart" check_result $? "Proxy restart failed" > /dev/null fi # Logging if [ "$quiet" != 'yes' ]; then - $BIN/v-log-action "$user" "Info" "Mail" "Webmail access enabled (Domain: $domain)." + "$BIN/v-log-action" "$user" "Info" "Mail" "Webmail access enabled (Domain: $domain)." fi log_event "$OK" "$ARGUMENTS" diff --git a/bin/v-add-sys-ip b/bin/v-add-sys-ip index 7e87856b00..a81f1a7375 100755 --- a/bin/v-add-sys-ip +++ b/bin/v-add-sys-ip @@ -1,8 +1,12 @@ #!/bin/bash # info: add system IP address -# options: IP NETMASK [INTERFACE] [USER] [IP_STATUS] [IP_NAME] [NAT_IP] +# options: IP [NETMASK] [INTERFACE] [USER] [IP_STATUS] [IP_NAME] [NAT_IP] # -# example: v-add-sys-ip 203.0.113.1 255.255.255.0 +# example: v-add-sys-ip 216.239.32.21 255.255.255.0 +# example: v-add-sys-ip 216.239.32.21 /24 +# example: v-add-sys-ip 216.239.32.21/24 +# example: v-add-sys-ip 1234:55:66::1 /64 +# example: v-add-sys-ip 1234:55:66::1/64 # # This function adds IP address into a system. It also creates rc scripts. You # can specify IP name which will be used as root domain for temporary aliases. @@ -16,12 +20,13 @@ #----------------------------------------------------------# # Argument definition -ip="${1// /}" -netmask="$2" - +first_parameter=${1// /} # conventional delete of spaces +ip46=${1%/*} # clean ip address without cidr/prefix_length +[ -n "$ip46" ] && [ "$ip46" = "${1}" ] || ip_cidr=${1#$ip46} # extract cidr/prefix from first parameter +second_parameter="$2" # second parameter can be netmask, cidr or prefix_length # Get interface name # First try to detect which interface the IP address resides on -iface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')" +iface="$(ip -d -j addr show | jq --arg IP "$ip46" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')" # If that fails, detect the default interface as a fallback if [ -z "$iface" ]; then iface="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')" @@ -37,11 +42,11 @@ nat_ip="$7" # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # shellcheck source=/usr/local/hestia/func/syshealth.sh source "$HESTIA/func/syshealth.sh" # load config file @@ -55,15 +60,78 @@ fi # Verifications # #----------------------------------------------------------# -check_args '2' "$#" 'IP NETMASK [INTERFACE] [USER] [STATUS] [NAME] [NATED_IP]' -is_format_valid 'ip' 'netmask' 'iface' 'user' 'ip_status' -is_ip_free +check_args '1' "$#" 'IP [NETMASK] [INTERFACE] [USER] [STATUS] [NAME] [NATED_IP]' + +ip_format=$(get_ip_format "$ip46") # ip verification and format identification +if [ -n "$second_parameter" -a -n "$ip_format" ]; then + [ -n "$ip_cidr" ] && check_result 1 "cidr / prefix length double defined as IP address suffix and as separate argument!" # wrong parameters + netmask="$(echo "$second_parameter" | sed -nr ''/$REGEX_IPV4/p'')" # extract netmask from second parameter if available + cidr_prefixlen="$(echo "$second_parameter" | sed -ne '/^\/[0-9]\{1,3\}$/p')" # extract cidr/prefix_length from second parameter if available + [ -z "$netmask" -a -z "$cidr_prefixlen" ] && check_result 2 "Wrong netmask / cidr / prefix length definition!" # wrong parameters + [ -n "$netmask" -a $ip_format -ne 4 ] && check_result 3 "Netmask definition for a not IPV4 address! Define a prefix lenght instead of netmask!" # wrong parameters +fi +# is_ip_format_valid 'ip46' # check for correct ipv4 or ipv6 format +add_string_ipv6="" +add_cap_string_ipv6="" +full_ip46="" +netmask_prelen="" +closed_ip="" +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 4 ]; then + ip="$ip46" + ipv6='' + if [ -n "$netmask" ]; then + is_ip_format_valid "$netmask" 'netmask' # check for correct netmask + cidr=$(convert_netmask "$netmask") # convert netmask to cidr + fi + if [ -n "$cidr_prefixlen" ]; then + cidr="$cidr_prefixlen" + fi + if [ -n "$ip_cidr" ]; then + cidr="$ip_cidr" + else + [ -z "$cidr" ] && cidr="/32" + fi + if [ -z "$netmask" ]; then + is_ip_format_valid "$cidr" 'cidr' # check for correct cidr + netmask=$(convert_cidr "$cidr") # convert cidr to netmask + fi + is_ip_format_valid "$ip" 'ipv4' # check for correct ipv4 format + broadcast=$(get_broadcast "$ip" "$netmask") # generate broadcast + full_ip46="$ip$cidr" + netmask_prelen="$netmask" + closed_ip="$ip" + fi + if [ $ip_format -eq 6 ]; then + ip='' + ipv6="$ip46" + add_string_ipv6="6" + [ -n "$cidr_prefixlen" ] && prefix_length="$cidr_prefixlen" + if [ -n "$ip_cidr" ]; then + prefix_length="$ip_cidr" + else + [ -z "$prefix_length" ] && prefix_length="/64" + fi + is_ip_format_valid "$ipv6" 'ipv6' # check for correct ipv6 format + broadcast="" # reset broadcast + full_ip46="$ipv6$prefix_length" + netmask_prelen="$prefix_length" + closed_ip="[$ipv6]" + add_cap_string_ipv6="V6" + fi +fi + +[ -z "$iface" ] && iface=$("$BIN/v-list-sys-interfaces" plain | head -n 1) # Get first available system interface, if none defined +[ -z "$iface" ] && iface='eth0' # eth0, if still not defined + +is_format_valid 'netmask' 'cidr' 'prefix_length' 'iface' 'user' 'ip_status' +is_ip_free "$ip46" is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" if [ -n "$ip_name" ]; then is_format_valid 'ip_name' fi -if [ -n "$nat_ip" ]; then +if [ -n "$nat_ip" -a $ip_format -eq 4 ]; then is_format_valid 'nat_ip' fi if [ "$user" != "$ROOT_USER" ]; then @@ -77,24 +145,32 @@ check_hestia_demo_mode # Action # #----------------------------------------------------------# -cidr="$(convert_netmask "$netmask")" -broadcast="$(get_broadcast "$ip" "$netmask")" - -sys_ip_check="$(ip addr | grep -w "$ip")" +check_ip_par="" +[ -n "$ip_format" ] && [ $ip_format -eq 4 -o $ip_format -eq 6 ] && check_ip_par=" -${ip_format}" +sys_ip_check=$(ip$check_ip_par addr | sed -ne "/inet[6]*[ \t]${ip46}\//p") if [ -z "$sys_ip_check" ]; then # Adding system IP - ip addr add "$ip/$cidr" dev "$iface" broadcast "$broadcast" label "$iface" + if [ -n "$ip_format" ] && [ $ip_format -eq 6 ]; then + /sbin/ip addr add "$full_ip46" dev "${iface%:*}" label "$iface" + else + ip addr add "$full_ip46" dev "$iface" broadcast "$broadcast" label "$iface" + fi + sleep 2 # wait to avoid issues with apache and nginx port binding # Check if netplan is in use and generate configuration file - if [ -n "$(netplan generate --mapping "$iface" 2> /dev/null | grep networkd)" ]; then - netplan="true" + if [ -n "$(which netplan)" ]; then + if [ -n "$(netplan generate --mapping "$iface" 2> /dev/null | grep networkd)" ]; then + netplan="true" + else + netplan="false" + fi else netplan="false" fi if [ "$netplan" = "true" ]; then if [ -f "/etc/netplan/60-hestia.yaml" ]; then - sys_ip=" - $ip/$cidr" + sys_ip=" - $full_ip46" else sys_ip="# Added by Hestia, please do not edit the file manually!" sys_ip="$sys_ip\nnetwork:" @@ -103,7 +179,7 @@ if [ -z "$sys_ip_check" ]; then sys_ip="$sys_ip\n ethernets:" sys_ip="$sys_ip\n $iface:" sys_ip="$sys_ip\n addresses:" - sys_ip="$sys_ip\n - $ip/$cidr" + sys_ip="$sys_ip\n - $full_ip46" fi IFS='%' echo -e "$sys_ip" >> /etc/netplan/60-hestia.yaml @@ -111,10 +187,14 @@ if [ -z "$sys_ip_check" ]; then else sys_ip="\n# Added by Hestia Control Panel" sys_ip="$sys_ip\nauto $iface" - sys_ip="$sys_ip\niface $iface inet static" - sys_ip="$sys_ip\naddress $ip" - sys_ip="$sys_ip\nnetmask $netmask" - echo -e $sys_ip >> /etc/network/interfaces + sys_ip="$sys_ip\niface $iface inet${add_string_ipv6} static" + if [ -n "$ip_format" ] && [ $ip_format -eq 6 ]; then + sys_ip="$sys_ip\naddress $full_ip46" + else + sys_ip="$sys_ip\naddress $ip" + sys_ip="$sys_ip\nnetmask $netmask" + fi + echo -e "$sys_ip" >> /etc/network/interfaces fi fi @@ -128,73 +208,75 @@ NAME='$ip_name' U_SYS_USERS='' U_WEB_DOMAINS='0' INTERFACE='$iface' -NETMASK='$netmask' +NETMASK='$netmask_prelen' NAT='$nat_ip' TIME='$time' -DATE='$date'" > $HESTIA/data/ips/$ip -chmod 660 $HESTIA/data/ips/$ip +DATE='$date' +VERSION='$ip_format'" > "$HESTIA/data/ips/$ip46" +chmod 660 "$HESTIA/data/ips/$ip46" # WEB support if [ -n "$WEB_SYSTEM" ]; then - web_conf="/etc/$WEB_SYSTEM/conf.d/$ip.conf" + web_conf="/etc/$WEB_SYSTEM/conf.d/$ip46.conf" rm -f "$web_conf" if [ "$WEB_SYSTEM" = 'httpd' ] || [ "$WEB_SYSTEM" = 'apache2' ]; then if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then - echo "NameVirtualHost $ip:$WEB_PORT" > "$web_conf" + echo "NameVirtualHost ${closed_ip}:$WEB_PORT" > "$web_conf" fi - echo "Listen $ip:$WEB_PORT" >> "$web_conf" - cat $HESTIA_INSTALL_DIR/apache2/unassigned.conf >> "$web_conf" - sed -i 's/directIP/'$ip'/g' "$web_conf" + echo "Listen $closed_ip:$WEB_PORT" >> "$web_conf" + cat "$HESTIA_INSTALL_DIR/apache2/unassigned.conf" >> "$web_conf" + [ -n "$ip_format" ] && [ $ip_format -eq 6 ] && sed -i 's/\(VirtualHost \)directIP/\1'${closed_ip}'/g' "$web_conf" + sed -i 's/directIP/'${ip46}'/g' "$web_conf" sed -i 's/directPORT/'$WEB_PORT'/g' "$web_conf" elif [ "$WEB_SYSTEM" = 'nginx' ]; then - cp -f $HESTIA_INSTALL_DIR/nginx/unassigned.inc "$web_conf" - sed -i 's/directIP/'$ip'/g' "$web_conf" + cp -f "$HESTIA_INSTALL_DIR/nginx/unassigned.inc" "$web_conf" + sed -i 's/directIP/'${closed_ip}'/g' "$web_conf" process_http2_directive "$web_conf" fi if [ "$WEB_SSL" = 'mod_ssl' ]; then if [ -z "$(/usr/sbin/apachectl -v | grep Apache/2.4)" ]; then - sed -i "1s/^/NameVirtualHost $ip:$WEB_SSL_PORT\n/" "$web_conf" + sed -i "1s/^/NameVirtualHost ${closed_ip}:$WEB_SSL_PORT\n/" "$web_conf" fi - sed -i "1s/^/Listen $ip:$WEB_SSL_PORT\n/" "$web_conf" + sed -i "1s/^/Listen ${closed_ip}:$WEB_SSL_PORT\n/" "$web_conf" sed -i 's/directSSLPORT/'$WEB_SSL_PORT'/g' "$web_conf" fi fi # Proxy support if [ -n "$PROXY_SYSTEM" ]; then - cat $WEBTPL/$PROXY_SYSTEM/proxy_ip.tpl \ - | sed -e "s/%ip%/$ip/g" \ + cat "$WEBTPL/$PROXY_SYSTEM/proxy_ip.tpl" \ + | sed -e "s/%ip%/${closed_ip}/g" \ -e "s/%web_port%/$WEB_PORT/g" \ -e "s/%proxy_port%/$PROXY_PORT/g" \ -e "s/%proxy_ssl_port%/$PROXY_SSL_PORT/g" \ - > /etc/$PROXY_SYSTEM/conf.d/$ip.conf + > "/etc/$PROXY_SYSTEM/conf.d/$ip46.conf" - process_http2_directive "/etc/$PROXY_SYSTEM/conf.d/$ip.conf" + process_http2_directive "/etc/$PROXY_SYSTEM/conf.d/$ip46.conf" # mod_extract_forwarded fw_conf="/etc/$WEB_SYSTEM/conf.d/mod_extract_forwarded.conf" if [ -e "$fw_conf" ]; then - ips=$(grep 'MEFaccept ' "$fw_conf" | grep -v '#' | head -n1) - sed -i "s/$ips/$ips $ip/g" "$fw_conf" + ips=$(grep 'MEFaccept ' $fw_conf | grep -v '#' | head -n1) + sed -i "s/$ips/$ips $ip46/g" "$fw_conf" fi # mod_rpaf rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf" if [ -e "$rpaf_conf" ]; then - rpaf_str="$(grep RPAFproxy_ips "$rpaf_conf")" + rpaf_str=$(grep RPAFproxy_ips $rpaf_conf) [ -z "$rpaf_str" ] && sed -i 's||RPAFproxy_ips\n|' "$rpaf_conf" && rpaf_str='RPAFproxy_ips' - rpaf_str="$rpaf_str $ip" + rpaf_str="$rpaf_str $ip46" sed -i "s/.*RPAFproxy_ips.*/$rpaf_str/" "$rpaf_conf" fi - # mod_remoteip + #mod_remoteip remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf" if [ -e "$remoteip_conf" ]; then - if [ "$(grep -ic "$ip" "$remoteip_conf")" -eq "0" ]; then - sed -i "s/<\/IfModule>/RemoteIPInternalProxy $ip\n<\/IfModule>/g" "$remoteip_conf" + if [ $(grep -ic "$ip46" "$remoteip_conf") -eq 0 ]; then + sed -i "s/<\/IfModule>/RemoteIPInternalProxy $ip46\n<\/IfModule>/g" "$remoteip_conf" fi fi fi @@ -208,33 +290,33 @@ syshealth_adapt_nginx_resolver #----------------------------------------------------------# # Updating user counters -increase_user_value "$user" '$IP_OWNED' +increase_user_value "$user" '$IP'$add_cap_string_ipv6'_OWNED' if [ "$user" = $ROOT_USER ]; then if [ "$ip_status" = 'shared' ]; then for hestia_user in $("$BIN/v-list-users" list); do - increase_user_value "$hestia_user" '$IP_AVAIL' + increase_user_value "$hestia_user" '$IP'$add_cap_string_ipv6'_AVAIL' done else - increase_user_value $ROOT_USER '$IP_AVAIL' + increase_user_value $ROOT_USER '$IP'$add_cap_string_ipv6'_AVAIL' fi else - increase_user_value "$user" '$IP_AVAIL' - increase_user_value $ROOT_USER '$IP_AVAIL' + increase_user_value "$user" '$IP'$add_cap_string_ipv6'_AVAIL' + increase_user_value $ROOT_USER '$IP'$add_cap_string_ipv6'_AVAIL' fi # Restarting web server -$BIN/v-restart-web +"$BIN/v-restart-web" check_result $? "Web restart failed" > /dev/null # Restarting proxy server if [ -n "$PROXY_SYSTEM" ]; then - $BIN/v-restart-proxy + "$BIN/v-restart-proxy" check_result $? "Proxy restart failed" > /dev/null fi # Restarting firewall if [ -n "$FIREWALL_SYSTEM" ]; then - $BIN/v-update-firewall + "$BIN/v-update-firewall" fi # Restarting hestia service if nginx backend config was changed @@ -244,7 +326,7 @@ if [ "$NGINX_BCONF_CHANGED" = "yes" -a -f "/etc/init.d/hestia" ]; then fi # Logging -$BIN/v-log-action "system" "Info" "Network" "Added new IP address to the system (IP: $ip)." +"$BIN/v-log-action" "system" "Info" "Network" "Added new IP$add_cap_string_ipv6 address to the system (IP$add_cap_string_ipv6: $ip46)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-add-web-domain b/bin/v-add-web-domain index e26378a28e..1234831cc5 100755 --- a/bin/v-add-web-domain +++ b/bin/v-add-web-domain @@ -1,6 +1,9 @@ #!/bin/bash +# +# !!! WRAPPER SCRIPT FOR COMPATIBILITY PURPOSES WITH OLD IPV4 SCRIPTS AND EXTERNAL CALLS !!! +# # info: add web domain -# options: USER DOMAIN [IP] [RESTART] [ALIASES] [PROXY_EXTENSIONS] +# options: USER DOMAIN [IPV4] [RESTART] [ALIASES] [PROXY_EXTENSIONS] # # example: v-add-web-domain admin wonderland.com 192.18.22.43 yes www.wonderland.com # @@ -16,244 +19,16 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -domain_idn=$2 -ip=$3 -restart=$4 # will be moved to the end soon -aliases=$5 -proxy_ext=$6 +user="$1" +domain="$2" +ip="$3" +ipv6="" # EMPTY! Not used here, because of old IPV4 only mode +restart="$4" # will be moved to the end soon +aliases="$5" +proxy_ext="$6" # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf -# shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh -# shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh -# shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh -# shellcheck source=/usr/local/hestia/func/syshealth.sh -source $HESTIA/func/syshealth.sh -# load config file -source_conf "$HESTIA/conf/hestia.conf" - -# Additional argument formatting -format_domain -format_domain_idn -format_aliases -domain_utf=$(idn2 --quiet -d "$domain_idn") - -#----------------------------------------------------------# -# Verifications # -#----------------------------------------------------------# - -is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' -check_args '2' "$#" 'USER DOMAIN [IP] [RESTART] [ALIASES] [PROXY_EXTENSIONS]' -is_format_valid 'user' 'domain' 'aliases' 'ip' 'proxy_ext' -is_object_valid 'user' 'USER' "$user" -is_object_unsuspended 'user' 'USER' "$user" -is_package_full 'WEB_DOMAINS' - -if [ "$aliases" != "none" ]; then - ALIAS="$aliases" - is_package_full 'WEB_ALIASES' -fi - -if [ "$($BIN/v-list-web-domain $user $domain_utf plain | cut -f 1) " != "$domain" ]; then - is_domain_new 'web' "$domain_utf,$aliases" -fi -if [ "$($BIN/v-list-web-domain $user $domain_idn plain | cut -f 1) " != "$domain" ]; then - is_domain_new 'web' "$domain_idn,$aliases" -else - is_domain_new 'web' "$domain,$aliases" -fi -if [ -z "$(is_ip_format_valid $domain)" ]; then - echo "Error: Invalid domain format. IP address detected as input." - exit 1 -fi - -is_dir_symlink "$HOMEDIR/$user/web" -is_dir_symlink "$HOMEDIR/$user/web/$domain" - -is_base_domain_owner "$domain,$aliases" - -if [ -n "$ip" ]; then - is_ip_valid "$ip" "$user" -else - get_user_ip -fi - -# Perform verification if read-only mode is enabled -check_hestia_demo_mode - -#----------------------------------------------------------# -# Action # -#----------------------------------------------------------# - -# Reading user values -source_conf "$USER_DATA/user.conf" - -[[ -e "$HOMEDIR/$user/web/$domain" ]] && check_result "$E_EXISTS" "Web domain folder for $domain should not exist" - -# Creating domain directories -mkdir $HOMEDIR/$user/web/$domain -chown $user:$user $HOMEDIR/$user/web/$domain -$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/public_html" -$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/document_errors" -$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/cgi-bin" -$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/private" -$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/stats" -$BIN/v-add-fs-directory "$user" "$HOMEDIR/$user/web/$domain/logs" - -# Creating domain logs -touch /var/log/$WEB_SYSTEM/domains/$domain.bytes \ - /var/log/$WEB_SYSTEM/domains/$domain.log \ - /var/log/$WEB_SYSTEM/domains/$domain.error.log -ln -f -s /var/log/$WEB_SYSTEM/domains/$domain.*log \ - $HOMEDIR/$user/web/$domain/logs/ - -# Adding domain skeleton -user_exec cp -r $WEBTPL/skel/* "$HOMEDIR/$user/web/$domain/" > /dev/null 2>&1 -for file in $(find "$HOMEDIR/$user/web/$domain/" -type f); do - sed -i "s/%domain%/$domain/g" $file -done - -# Changing file owner & permission -chown -R $user:$user $HOMEDIR/$user/web/$domain -chown root:$user /var/log/$WEB_SYSTEM/domains/$domain.* $conf -chmod 640 /var/log/$WEB_SYSTEM/domains/$domain.* -user_exec chmod 751 $HOMEDIR/$user/web/$domain/* -user_exec chmod 551 $HOMEDIR/$user/web/$domain/stats $HOMEDIR/$user/web/$domain/logs -user_exec chmod 644 $HOMEDIR/$user/web/$domain/public_*html/* - -# domain folder permissions: DOMAINDIR_WRITABLE: default-val:no source:hestia.conf -DOMAINDIR_MODE=551 -if [ "$DOMAINDIR_WRITABLE" = 'yes' ]; then DOMAINDIR_MODE=751; fi - -user_exec chmod $DOMAINDIR_MODE $HOMEDIR/$user/web/$domain -chown --no-dereference $user:www-data $HOMEDIR/$user/web/$domain/public_*html - -# Adding PHP-FPM backend -if [ -n "$WEB_BACKEND" ]; then - if [ -z "$BACKEND_TEMPLATE" ]; then - BACKEND_TEMPLATE='default' - if [ -z "$(grep BACKEND_TEMPLATE $USER_DATA/user.conf)" ]; then - sed -i "s/^DNS_TEMPL/BACKEND_TEMPLATE='default'\nDNS_TEMPL/g" \ - $USER_DATA/user.conf - else - update_user_value "$user" '$BACKEND_TEMPLATE' "default" - fi - fi - export BACKEND="$BACKEND_TEMPLATE" - $BIN/v-add-web-domain-backend "$user" "$domain" "$BACKEND_TEMPLATE" "$restart" - check_result $? "Backend error" > /dev/null -fi - -# Preparing domain aliases -if [ "$aliases" = 'none' ]; then - ALIAS='' -else - ALIAS="www.$domain" - if [ -z "$aliases" ]; then - # Check and skip www alias for subdomains. - IFS='.' read -r -a domain_elements <<< "$domain" - if [ "${#domain_elements[@]}" -gt 2 ]; then - is_valid_2_part_extension $domain - if [ $? -ne 0 ]; then - ALIAS="" - else - ALIAS="www.$domain" - fi - else - ALIAS="www.$domain" - fi - else - ALIAS="$aliases" - fi - - ip_alias=$(get_ip_alias "$domain") - if [ -n "$ip_alias" ]; then - ALIAS="$ALIAS,$ip_alias" - fi -fi - -# Preparing domain variables -prepare_web_domain_values - -if [ -z "$WEB_TEMPLATE" ]; then - WEB_TEMPLATE='default' - update_user_value "$user" '$WEB_TEMPLATE' "default" -fi - -# Adding web server config -add_web_config "$WEB_SYSTEM" "$WEB_TEMPLATE.tpl" - -# Adding proxy config -if [ -n "$PROXY_SYSTEM" ]; then - PROXY_EXT="$proxy_ext" - if [ -z "$proxy_ext" ]; then - # Code - PROXY_EXT="css,htm,html,js,json,xml" - # Image (from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) - PROXY_EXT="$PROXY_EXT,apng,avif,bmp,cur,gif,ico,jfif,jpg,jpeg,pjp,pjpeg,png,svg,tif,tiff,webp" - # Audio from (https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Audio_codecs) - PROXY_EXT="$PROXY_EXT,aac,caf,flac,m4a,midi,mp3,ogg,opus,wav" - # Video (from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs) - PROXY_EXT="$PROXY_EXT,3gp,av1,avi,m4v,mkv,mov,mpg,mpeg,mp4,mp4v,webm" - # Fonts - PROXY_EXT="$PROXY_EXT,otf,ttf,woff,woff2" - # Productivity - PROXY_EXT="$PROXY_EXT,doc,docx,odf,odp,ods,odt,pdf,ppt,pptx,rtf,txt,xls,xlsx" - # Archive - PROXY_EXT="$PROXY_EXT,7z,bz2,gz,rar,tar,tgz,zip" - # Binaries - PROXY_EXT="$PROXY_EXT,apk,appx,bin,dmg,exe,img,iso,jar,msi" - # Other - PROXY_EXT="$PROXY_EXT,webmanifest" - - fi - if [ -z "$PROXY_TEMPLATE" ]; then - PROXY_TEMPLATE='default' - update_user_value "$user" '$PROXY_TEMPLATE' "default" - fi - - add_web_config "$PROXY_SYSTEM" "$PROXY_TEMPLATE.tpl" -fi - -#----------------------------------------------------------# -# Hestia # -#----------------------------------------------------------# - -# Increasing counters -increase_ip_value "$local_ip" -increase_user_value "$user" '$U_WEB_DOMAINS' -increase_user_value "$user" '$U_WEB_ALIASES' "$alias_number" - -# Generating timestamp -time_n_date=$(date +'%T %F') -time=$(echo "$time_n_date" | cut -f 1 -d \ ) -date=$(echo "$time_n_date" | cut -f 2 -d \ ) - -# Adding domain in web.conf -echo "DOMAIN='$domain' IP='$ip' IP6='' CUSTOM_DOCROOT='' ALIAS='$ALIAS' TPL='$WEB_TEMPLATE'\ - SSL='no' SSL_FORCE='no' SSL_HOME='same' LETSENCRYPT='no' FTP_USER='' FTP_MD5=''\ - BACKEND='$BACKEND_TEMPLATE' PROXY='$PROXY_TEMPLATE' PROXY_EXT='$PROXY_EXT'\ - STATS='' STATS_USER='' STATS_CRYPT='' U_DISK='0' U_BANDWIDTH='0'\ - SUSPENDED='no' TIME='$time' DATE='$date'" >> $USER_DATA/web.conf - -syshealth_repair_web_config - -# Restarting web server -$BIN/v-restart-web "$restart" -check_result $? "Web restart failed" > /dev/null - -# Restarting proxy server -$BIN/v-restart-proxy "$restart" -check_result $? "Proxy restart failed" > /dev/null - -# Logging -$BIN/v-log-action "$user" "Info" "Web" "Added new web domain (Name: $domain)." -log_event "$OK" "$ARGUMENTS" -exit +"$HESTIA/bin/v-add-web-domain-ipv46" "$user" "$domain" "$ip" "$ipv6" "$restart" "$aliases" "$proxy_ext" diff --git a/bin/v-add-web-domain-ipv46 b/bin/v-add-web-domain-ipv46 new file mode 100755 index 0000000000..7498665888 --- /dev/null +++ b/bin/v-add-web-domain-ipv46 @@ -0,0 +1,277 @@ +#!/bin/bash +# info: add web domain +# options: USER DOMAIN [IPV4] [IPV6] [RESTART] [ALIASES] [PROXY_EXTENSIONS] +# +# example: v-add-web-domain admin wonderland.com 192.18.22.43 1111:2222:3333::1 yes www.wonderland.com +# +# This function adds virtual host to a server. In cases when ip is +# undefined in the script, "default" template will be used. The alias of +# www.domain.tld type will be automatically assigned to the domain unless +# "none" is transmited as argument. If ip have associated dns name, this +# domain will also get the alias domain-tpl.$ipname. An alias with the ip +# name is useful during the site testing while dns isn't moved to server yet. + +#----------------------------------------------------------# +# Variables & Functions # +#----------------------------------------------------------# + +# Argument definition +user=${1} +domain=${2} +domain_idn=${2} +ip=${3} +ipv6=${4} +restart=${5} # will be moved to the end soon +aliases=${6} +proxy_ext=${7} + +# Includes +# shellcheck source=/etc/hestiacp/hestia.conf +source /etc/hestiacp/hestia.conf +# shellcheck source=/usr/local/hestia/func/main.sh +source "$HESTIA/func/main.sh" +# shellcheck source=/usr/local/hestia/func/domain.sh +source "$HESTIA/func/domain.sh" +# shellcheck source=/usr/local/hestia/func/ip.sh +source "$HESTIA/func/ip.sh" +# shellcheck source=/usr/local/hestia/func/syshealth.sh +source "$HESTIA/func/syshealth.sh" +# load config file +source_conf "$HESTIA/conf/hestia.conf" + +# Additional argument formatting +format_domain +format_domain_idn +format_aliases +domain_utf=$(idn2 --quiet -d "$domain_idn") + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' +check_args '2' "$#" 'USER DOMAIN [IPV4] [IPV6] [RESTART] [ALIASES] [PROXY_EXTENSIONS]' +is_format_valid 'user' 'domain' 'aliases' 'ip' 'ipv6' 'proxy_ext' +is_object_valid 'user' 'USER' "$user" +is_object_unsuspended 'user' 'USER' "$user" +is_package_full 'WEB_DOMAINS' + +if [ "$aliases" != "none" ]; then + ALIAS="$aliases" + is_package_full 'WEB_ALIASES' +fi + +users_domain_utf=$("$BIN/v-list-web-domains" "$user" list | sed -ne "/$domain_utf/p") +if [ "$users_domain_utf" != "$domain" ]; then + is_domain_new 'web' "$domain_utf,$aliases" +fi +users_domain_idn=$("$BIN/v-list-web-domains" "$user" list | sed -ne "/$domain_idn/p") +if [ "$users_domain_idn" != "$domain" ]; then + is_domain_new 'web' "$domain_idn,$aliases" +else + is_domain_new 'web' "$domain,$aliases" +fi +if [ -n "$(get_ip_format "$domain")" ]; then + echo "Error: Invalid domain format. IP address detected as input." + exit 1 +fi + +is_dir_symlink "$HOMEDIR/$user/web" +is_dir_symlink "$HOMEDIR/$user/web/$domain" + +is_base_domain_owner "$domain,$aliases" + +if [ -n "$ip" ]; then + is_ip_valid "$ip" "$user" +fi + +if [ -n "$ipv6" ]; then + is_ipv6_valid "$ipv6" "$user" +fi + +if [ -z "$ip" -a -z "$ipv6" ]; then + get_user_ip # get first available user ipv4 address as fallback + get_user_ipv6 # get first available user ipv6 address as fallback +fi + +# Perform verification if read-only mode is enabled +check_hestia_demo_mode + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Reading user values +source_conf "$USER_DATA/user.conf" + +[[ -e "$HOMEDIR/$user/web/$domain" ]] && check_result "$E_EXISTS" "Web domain folder for $domain should not exist" + +# Creating domain directories +mkdir "$HOMEDIR/$user/web/$domain" +chown $user:$user "$HOMEDIR/$user/web/$domain" +"$BIN/v-add-fs-directory" "$user" "$HOMEDIR/$user/web/$domain/public_html" +"$BIN/v-add-fs-directory" "$user" "$HOMEDIR/$user/web/$domain/document_errors" +"$BIN/v-add-fs-directory" "$user" "$HOMEDIR/$user/web/$domain/cgi-bin" +"$BIN/v-add-fs-directory" "$user" "$HOMEDIR/$user/web/$domain/private" +"$BIN/v-add-fs-directory" "$user" "$HOMEDIR/$user/web/$domain/stats" +"$BIN/v-add-fs-directory" "$user" "$HOMEDIR/$user/web/$domain/logs" + +# Creating domain logs +touch "/var/log/$WEB_SYSTEM/domains/$domain.bytes" \ + "/var/log/$WEB_SYSTEM/domains/$domain.log" \ + "/var/log/$WEB_SYSTEM/domains/$domain.error.log" +ln -f -s /var/log/"$WEB_SYSTEM"/domains/"$domain".*log \ + "$HOMEDIR/$user/web/$domain/logs/" + +# Adding domain skeleton +user_exec cp -r "$WEBTPL"/skel/* "$HOMEDIR/$user/web/$domain/" > /dev/null 2>&1 +for file in $(find "$HOMEDIR/$user/web/$domain/" -type f); do + sed -i "s/%domain%/${domain}/g" "$file" +done + +# Changing file owner & permission +chown -R $user:$user "$HOMEDIR/$user/web/$domain" +chown root:$user "/var/log/$WEB_SYSTEM/domains/$domain".* $conf +chmod 640 "/var/log/$WEB_SYSTEM/domains/$domain".* +user_exec chmod 751 "$HOMEDIR/$user/web/$domain/"* +user_exec chmod 551 "$HOMEDIR/$user/web/$domain/stats" "$HOMEDIR/$user/web/$domain/logs" +user_exec chmod 644 "$HOMEDIR/$user/web/$domain/"public_*html/* + +# domain folder permissions: DOMAINDIR_WRITABLE: default-val:no source:hestia.conf +DOMAINDIR_MODE=551 +if [ "$DOMAINDIR_WRITABLE" = 'yes' ]; then DOMAINDIR_MODE=751; fi + +user_exec chmod 551 "$HOMEDIR/$user/web/$domain" +chown --no-dereference $user:www-data "$HOMEDIR/$user/web/$domain/"public_*html + +# Adding PHP-FPM backend +if [ -n "$WEB_BACKEND" ]; then + if [ -z "$BACKEND_TEMPLATE" ]; then + BACKEND_TEMPLATE='default' + if [ -z "$(grep BACKEND_TEMPLATE $USER_DATA/user.conf)" ]; then + sed -i "s/^DNS_TEMPL/BACKEND_TEMPLATE='default'\nDNS_TEMPL/g" \ + "$USER_DATA/user.conf" + else + update_user_value "$user" '$BACKEND_TEMPLATE' "default" + fi + fi + export BACKEND="$BACKEND_TEMPLATE" + "$BIN/v-add-web-domain-backend" "$user" "$domain" "$BACKEND_TEMPLATE" "$restart" + check_result $? "Backend error" > /dev/null +fi + +# Preparing domain aliases +if [ "$aliases" = 'none' ]; then + ALIAS='' +else + ALIAS="www.$domain" + if [ -z "$aliases" ]; then + # Check and skip www alias for subdomains. + IFS='.' read -r -a domain_elements <<< "$domain" + if [ "${#domain_elements[@]}" -gt 2 ]; then + is_valid_2_part_extension $domain + if [ $? -ne 0 ]; then + ALIAS="" + else + ALIAS="www.$domain" + fi + else + ALIAS="www.$domain" + fi + else + ALIAS="$aliases" + fi + + if [ -n "$local_ip" ]; then + ip_alias=$(get_ip_alias "$domain" "$local_ip") + if [ -n "$ip_alias" ]; then + ALIAS="$ALIAS,$ip_alias" + fi + fi + if [ -n "$local_ipv6" ]; then + ipv6_alias=$(get_ip_alias "$domain" "$local_ipv6") + if [ -n "$ipv6_alias" ]; then + ALIAS="$ALIAS,$ipv6_alias" + fi + fi +fi + +# Preparing domain variables +prepare_web_domain_values + +if [ -z "$WEB_TEMPLATE" ]; then + WEB_TEMPLATE='default' + update_user_value "$user" '$WEB_TEMPLATE' "default" +fi + +# Adding web server config +add_web_config "$WEB_SYSTEM" "$WEB_TEMPLATE.tpl" + +# Adding proxy config +if [ -n "$PROXY_SYSTEM" ]; then + PROXY_EXT="$proxy_ext" + if [ -z "$proxy_ext" ]; then + # Code + PROXY_EXT="css,htm,html,js,json,xml" + # Image (from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Image_types) + PROXY_EXT="$PROXY_EXT,apng,avif,bmp,cur,gif,ico,jfif,jpg,jpeg,pjp,pjpeg,png,svg,tif,tiff,webp" + # Audio from (https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Audio_codecs) + PROXY_EXT="$PROXY_EXT,aac,caf,flac,m4a,midi,mp3,ogg,opus,wav" + # Video (from https://developer.mozilla.org/en-US/docs/Web/Media/Formats/Video_codecs) + PROXY_EXT="$PROXY_EXT,3gp,av1,avi,m4v,mkv,mov,mpg,mpeg,mp4,mp4v,webm" + # Fonts + PROXY_EXT="$PROXY_EXT,otf,ttf,woff,woff2" + # Productivity + PROXY_EXT="$PROXY_EXT,doc,docx,odf,odp,ods,odt,pdf,ppt,pptx,rtf,txt,xls,xlsx" + # Archive + PROXY_EXT="$PROXY_EXT,7z,bz2,gz,rar,tar,tgz,zip" + # Binaries + PROXY_EXT="$PROXY_EXT,apk,appx,bin,dmg,exe,img,iso,jar,msi" + # Other + PROXY_EXT="$PROXY_EXT,webmanifest" + fi + if [ -z "$PROXY_TEMPLATE" ]; then + PROXY_TEMPLATE='default' + update_user_value "$user" '$PROXY_TEMPLATE' "default" + fi + + add_web_config "$PROXY_SYSTEM" "$PROXY_TEMPLATE.tpl" +fi + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +# Increasing counters +[ -n "$local_ip" ] && increase_ip_value "$local_ip" +[ -n "$local_ipv6" ] && increase_ip_value "$local_ipv6" +increase_user_value "$user" '$U_WEB_DOMAINS' +increase_user_value "$user" '$U_WEB_ALIASES' "$alias_number" + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" | cut -f 1 -d \ ) +date=$(echo "$time_n_date" | cut -f 2 -d \ ) + +# Adding domain in web.conf +echo "DOMAIN='$domain' IP='$ip' IP6='$ipv6' CUSTOM_DOCROOT='' ALIAS='$ALIAS' TPL='$WEB_TEMPLATE'\ + SSL='no' SSL_FORCE='no' SSL_HOME='same' LETSENCRYPT='no' FTP_USER='' FTP_MD5=''\ + BACKEND='$BACKEND_TEMPLATE' PROXY='$PROXY_TEMPLATE' PROXY_EXT='$PROXY_EXT'\ + STATS='' STATS_USER='' STATS_CRYPT='' U_DISK='0' U_BANDWIDTH='0'\ + SUSPENDED='no' TIME='$time' DATE='$date'" >> "$USER_DATA/web.conf" + +syshealth_repair_web_config + +# Restarting web server +"$BIN/v-restart-web" "$restart" +check_result $? "Web restart failed" > /dev/null + +# Restarting proxy server +"$BIN/v-restart-proxy" "$restart" +check_result $? "Proxy restart failed" > /dev/null + +# Logging +"$BIN/v-log-action" "$user" "Info" "Web" "Added new web domain (Name: $domain)." +log_event "$OK" "$ARGUMENTS" + +exit diff --git a/bin/v-add-web-domain-ssl b/bin/v-add-web-domain-ssl index 15f49545ad..046199f731 100755 --- a/bin/v-add-web-domain-ssl +++ b/bin/v-add-web-domain-ssl @@ -94,6 +94,7 @@ fi # Parsing domain values get_domain_values 'web' local_ip=$(get_real_ip "$IP") +local_ipv6="$IP6" # Preparing domain values for the template substitution SSL_HOME="$ssl_home" diff --git a/bin/v-change-dns-domain-ip b/bin/v-change-dns-domain-ip index 745e8338f4..ed4f4b1d96 100755 --- a/bin/v-change-dns-domain-ip +++ b/bin/v-change-dns-domain-ip @@ -1,8 +1,13 @@ #!/bin/bash # info: change dns domain ip address -# options: USER DOMAIN IP [RESTART] +# options: USER DOMAIN IP [RESTART] [IP FORMAT] # # example: v-change-dns-domain-ip admin domain.com 123.212.111.222 +# example: v-change-dns-domain-ip admin domain.com 1234:55:66::1 +# example: v-change-dns-domain-ip admin domain.com 123.212.111.222 no 4 +# example: v-change-dns-domain-ip admin domain.com 1234:55:66::1 yes 6 +# example: v-change-dns-domain-ip admin domain.com '' '' 4 +# example: v-change-dns-domain-ip admin domain.com '' no 6 # # This function for changing the main ip of DNS zone. @@ -11,21 +16,24 @@ #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -domain_idn=$2 -ip=$3 -restart=$4 +user=${1} +domain=${2} +domain_idn=${2} +ip46=${3} +restart=${4} +ip_format=${5} # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" +# shellcheck source=/usr/local/hestia/func/ip.sh +source "$HESTIA/func/ip.sh" # shellcheck source=/usr/local/hestia/func/rebuild.sh -source $HESTIA/func/rebuild.sh +source "$HESTIA/func/rebuild.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -38,14 +46,39 @@ format_domain_idn # Verifications # #----------------------------------------------------------# -check_args '3' "$#" 'USER DOMAIN IP [RESTART]' -is_format_valid 'user' 'domain' 'ip' +check_args '3' "$#" 'USER DOMAIN IP [RESTART] [IP FORMAT]' +is_format_valid 'user' 'domain' 'ip46' is_system_enabled "$DNS_SYSTEM" 'DNS_SYSTEM' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" is_object_valid 'dns' 'DOMAIN' "$domain" is_object_unsuspended 'dns' 'DOMAIN' "$domain" +if [ -z "$ip_format" ]; then + ip_format="$(get_ip_format ${ip46})" # ip verification and automatic format identification +fi +if [ -n "$ip_format" ]; then + if [ $ip_format -ne 4 -a $ip_format -ne 6 ]; then + check_result "$E_INVALID" "invalid IP format :: $ip46 IPV$ip_format?? " # error in case of wrong or undefined IP format + fi +else + check_result "$E_INVALID" "invalid or undefined IP format :: $ip46" # error in case of wrong or undefined IP format +fi +if [ $ip_format -eq 4 ]; then + ip="$ip46" + ipv6='' + if [ -n "$ip" ]; then + is_ip_format_valid "$ip" 'ipv4' # check for correct ipv4 format if not empty + fi +fi +if [ $ip_format -eq 6 ]; then + ip='' + ipv6="$ip46" + if [ -n "$ipv6" ]; then + is_ip_format_valid "$ipv6" 'ipv6' # check for correct ipv6 format if not empty + fi +fi + # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -55,13 +88,48 @@ check_hestia_demo_mode # Get old ip get_domain_values 'dns' -old=$IP # Changing ip -update_object_value 'dns' 'DOMAIN' "$domain" '$IP' "$ip" +if [ $ip_format -eq 4 ]; then + old_ip46="$IP" + ipv6="$IP6" + update_object_value 'dns' 'DOMAIN' "$domain" '$IP' "$ip" +fi +if [ $ip_format -eq 6 ]; then + ip="$IP" + old_ip46="$IP6" + update_object_value 'dns' 'DOMAIN' "$domain" '$IP6' "$ipv6" +fi # Changing records -sed -i "s/$old/$ip/g" $USER_DATA/dns/$domain.conf +if [ -n "$old_ip46" -a -n "$ip46" ]; then + sed -i "s/$old_ip46/$ip46/g" "$USER_DATA/dns/$domain".conf # simple find and replace only if old and new IPs both not empty +else + # Reload DNS template if old or new IP is empty + + get_dns_values # get DNS values, nameservers + + dkim_records="$("$BIN/v-list-dns-records" "$user" "$domain" | sed -ne '/DKIM/p')" + webmail_records="$("$BIN/v-list-dns-records" "$user" "$domain" | sed -ne "/${WEBMAIL_ALIAS}/p")" + + template=$(get_user_value '$DNS_TEMPLATE') + is_dns_template_valid "$template" + + # Generating timestamp + time_n_date=$(date +'%T %F') + time=$(echo "$time_n_date" | cut -f 1 -d \ ) + date=$(echo "$time_n_date" | cut -f 2 -d \ ) + + # Create DNS domain config + create_dns_domain_config + if [ -n "$dkim_records" ]; then + dkim="yes" + add_dns_dkim_records + fi + if [ -n "$webmail_records" ]; then + add_dns_webmail_records + fi +fi # Update serial update_domain_serial @@ -76,7 +144,7 @@ if [ "$DNS_CLUSTER" = "yes" ]; then dlock=$(grep "domain $user $domain" $HESTIA/data/queue/dns-cluster.pipe) if [ -z "$dlock" ]; then cmd="$BIN/v-add-remote-dns-domain $user $domain yes" - echo "$cmd" >> $HESTIA/data/queue/dns-cluster.pipe + echo "$cmd" >> "$HESTIA"/data/queue/dns-cluster.pipe fi fi @@ -85,11 +153,11 @@ fi #----------------------------------------------------------# # Restarting named -$BIN/v-restart-dns "$restart" +"$BIN/v-restart-dns" "$restart" check_result $? "DNS restart failed" > /dev/null # Logging -$BIN/v-log-action "$user" "Info" "DNS" "IP address for DNS domain changed (IP: $ip, Domain: $domain)." +"$BIN/v-log-action" "$user" "Info" "DNS" "IP address for DNS domain changed (IP: $ip46, Domain: $domain)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-change-dns-domain-tpl b/bin/v-change-dns-domain-tpl index d7219c2694..7e39796997 100755 --- a/bin/v-change-dns-domain-tpl +++ b/bin/v-change-dns-domain-tpl @@ -56,52 +56,12 @@ check_hestia_demo_mode #----------------------------------------------------------# # Defining variables -get_domain_values 'dns' -i=1 -ns=$(get_user_value '$NS') -for nameserver in ${ns//,/ }; do - eval ns$i=$nameserver - ((++i)) -done - -# Reading template -template_data=$(cat "$DNSTPL/$template.tpl") - -# Deleting unused nameservers -if [ -z "$ns3" ]; then - template_data=$(echo "$template_data" | grep -v %ns3%) -fi -if [ -z "$ns4" ]; then - template_data=$(echo "$template_data" | grep -v %ns4%) -fi -if [ -z "$ns5" ]; then - template_data=$(echo "$template_data" | grep -v %ns5%) -fi -if [ -z "$ns6" ]; then - template_data=$(echo "$template_data" | grep -v %ns6%) -fi -if [ -z "$ns7" ]; then - template_data=$(echo "$template_data" | grep -v %ns7%) -fi -if [ -z "$ns8" ]; then - template_data=$(echo "$template_data" | grep -v %ns8%) -fi +get_dns_values # get DNS values, nameservers +time=${TIME} +date=${DATE} -# Changing tpl -echo "$template_data" \ - | sed -e "s/%ip%/$IP/g" \ - -e "s/%domain_idn%/$domain_idn/g" \ - -e "s/%domain%/$domain/g" \ - -e "s/%ns1%/$ns1/g" \ - -e "s/%ns2%/$ns2/g" \ - -e "s/%ns3%/$ns3/g" \ - -e "s/%ns4%/$ns4/g" \ - -e "s/%ns5%/$ns5/g" \ - -e "s/%ns6%/$ns6/g" \ - -e "s/%ns7%/$ns7/g" \ - -e "s/%ns8%/$ns8/g" \ - -e "s/%time%/$TIME/g" \ - -e "s/%date%/$DATE/g" > $USER_DATA/dns/$domain.conf +# Create DNS domain config +create_dns_domain_config records="$(wc -l $USER_DATA/dns/$domain.conf | cut -f 1 -d ' ')" # Refresh DKIM records in DNS if signing key exists for domain diff --git a/bin/v-change-firewall-ipv6-rule b/bin/v-change-firewall-ipv6-rule new file mode 100755 index 0000000000..15f96a62c0 --- /dev/null +++ b/bin/v-change-firewall-ipv6-rule @@ -0,0 +1,85 @@ +#!/bin/bash +# info: change firewall rule +# options: RULE ACTION IPV6 PORT [PROTOCOL] [COMMENT] +# +# The function is used for changing existing firewall rule. +# It fully replace rule with new one but keeps same id. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +rule=$1 +action=$(echo $2|tr '[:lower:]' '[:upper:]') +ipv6=$3 +port_ext=$4 +protocol=${5-TCP} +protocol=$(echo $protocol|tr '[:lower:]' '[:upper:]') +comment=$6 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + +# Sort function +sort_fw_rules() { + cat $HESTIA/data/firewallv6/rules.conf |\ + sort -n -k 2 -t \' > $HESTIA/data/firewallv6/rules.conf.tmp + mv -f $HESTIA/data/firewallv6/rules.conf.tmp \ + $HESTIA/data/firewallv6/rules.conf +} + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '5' "$#" 'RULE ACTION IPV6 PORT [PROTOCOL] [COMMENT]' +is_format_valid 'rule' 'action' 'protocol' 'port_ext' 'ipv6' +if [ ! -z "$comment" ]; then + is_format_valid 'comment' +fi +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Generating timestamp +time_n_date=$(date +'%T %F') +time=$(echo "$time_n_date" |cut -f 1 -d \ ) +date=$(echo "$time_n_date" |cut -f 2 -d \ ) + +# Concatenating firewall rule +str="RULE='$rule' ACTION='$action' PROTOCOL='$protocol' PORT='$port_ext'" +str="$str IP6='$ipv6' COMMENT='$comment' SUSPENDED='no'" +str="$str TIME='$time' DATE='$date'" + +# Deleting old rule +sed -i "/RULE='$rule' /d" $HESTIA/data/firewallv6/rules.conf + +# Adding new +echo "$str" >> $HESTIA/data/firewallv6/rules.conf + +# Sorting firewall rules by id number +sort_fw_rules + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-change-sys-ip-name b/bin/v-change-sys-ip-name index c08a45441d..4b515eff4e 100755 --- a/bin/v-change-sys-ip-name +++ b/bin/v-change-sys-ip-name @@ -2,7 +2,8 @@ # info: change IP name # options: IP NAME # -# example: v-change-sys-ip-name 203.0.113.1 acme.com +# example: v-change-sys-ip-name 80.122.52.70 acme.com +# example: v-change-sys-ip-name 1111:2222:3333::1 acme.com # # This function for changing dns domain associated with IP. @@ -11,16 +12,16 @@ #----------------------------------------------------------# # Argument definition -ip="$1" +ip46="$1" ip_name="$2" # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -29,9 +30,21 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '2' "$#" 'IP IP_NAME' -is_format_valid 'ip' +ip_format="$(get_ip_format "$ip46")" # ip verification and format identification +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 6 ]; then + ip="" + ipv6="$ip46" + is_format_valid 'ipv6' + is_ip_valid "$ipv6" + else + ip="$ip46" + ipv6="" + is_format_valid 'ip' + is_ip_valid "$ip" + fi +fi is_format_valid 'ip_name' -is_ip_valid "$ip" # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -41,14 +54,14 @@ check_hestia_demo_mode #----------------------------------------------------------# # Changing IP name -update_ip_value '$NAME' "$ip_name" +update_ip_value '$NAME' "$ip_name" "$ip46" #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Info" "System" "Changed associated DNS on $ip to $ip_name." +"$BIN/v-log-action" "system" "Info" "System" "Changed associated DNS on $ip to $ip_name." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-change-sys-ip-nat b/bin/v-change-sys-ip-nat index 924c71e82f..ca1e01944b 100755 --- a/bin/v-change-sys-ip-nat +++ b/bin/v-change-sys-ip-nat @@ -3,6 +3,7 @@ # options: IP NAT_IP [RESTART] # # example: v-change-sys-ip-nat 10.0.0.1 203.0.113.1 +# example: v-change-sys-ip-nat 1111:2222:3333::1 '' # # This function for changing NAT IP associated with IP. @@ -11,7 +12,7 @@ #----------------------------------------------------------# # Argument definition -ip="$1" +ip46="$1" nat_ip="$2" restart="$3" @@ -19,9 +20,9 @@ restart="$3" # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -30,9 +31,22 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '2' "$#" 'IP NAT_IP [RESTART]' -is_format_valid 'ip' -is_format_valid 'nat_ip' -is_ip_valid "$ip" +ip_format="$(get_ip_format "$ip46")" # ip verification and format identification +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 6 ]; then + ip="" + ipv6="$ip46" + is_format_valid 'ipv6' + is_ip_valid "$ipv6" + nat_ip="" + else + ip="$ip46" + ipv6="" + is_format_valid 'ip' + is_ip_valid "$ip" + is_format_valid 'nat_ip' + fi +fi # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -42,38 +56,38 @@ check_hestia_demo_mode #----------------------------------------------------------# # Updating IP -if [ -z "$(grep NAT= $HESTIA/data/ips/$ip)" ]; then - sed -i "s/^TIME/NAT='$nat_ip'\nTIME/g" $HESTIA/data/ips/$ip +if [ -z "$(grep NAT= $HESTIA/data/ips/${ip46})" ]; then + sed -i "s/^TIME/NAT='$nat_ip'\nTIME/g" "$HESTIA/data/ips/$ip46" old='' new="$nat_ip" else old="$(get_ip_value '$NAT')" new="$nat_ip" - sed -i "s/NAT=.*/NAT='$new'/" $HESTIA/data/ips/$ip + sed -i "s/NAT=.*/NAT='$new'/" "$HESTIA/data/ips/$ip46" if [ -z "$nat_ip" ]; then - new="$ip" + new="$ip46" fi fi # Updating WEB configs if [ -n "$old" ] && [ -n "$WEB_SYSTEM" ]; then for user in $("$BIN/v-list-users" list); do - sed -i "s/$old/$new/" $HESTIA/data/users/$user/web.conf - $BIN/v-rebuild-web-domains "$user" no + sed -i "s/$old/$new/" "$HESTIA/data/users/$user/web.conf" + "$BIN/v-rebuild-web-domains" "$user" no done - $BIN/v-restart-dns "$restart" + "$BIN/v-restart-dns" "$restart" fi # Updating DNS configs if [ -n "$old" ] && [ -n "$DNS_SYSTEM" ]; then for user in $("$BIN/v-list-users" list); do sed -i "s/$old/$new/" "$HESTIA/data/users/$user/dns.conf" - if ls $HESTIA/data/users/$user/dns/*.conf > /dev/null 2>&1; then - sed -i "s/$old/$new/" $HESTIA/data/users/$user/dns/*.conf + if ls "$HESTIA/data/users/$user/dns/"*.conf 1> /dev/null 2>&1; then + sed -i "s/$old/$new/" "$HESTIA/data/users/$user/dns/"*.conf fi - $BIN/v-rebuild-dns-domains "$user" no + "$BIN/v-rebuild-dns-domains" "$user" no done - $BIN/v-restart-dns "$restart" + "$BIN/v-restart-dns" "$restart" fi # Updating FTP @@ -95,17 +109,17 @@ if [ -n "$old" ] && [ -n "$FTP_SYSTEM" ]; then fi fi if [ "$FTP_SYSTEM" = 'proftpd' ]; then + "$BIN/v-restart-ftp" "$restart" ext_ip_conf="/etc/$FTP_SYSTEM/conf.d/external_ip.conf" - content="MasqueradeAddress ${nat_ip}" + content="MasqueradeAddress $nat_ip" echo "$content" > "$ext_ip_conf" fi - $BIN/v-restart-ftp "$restart" fi # Updating firewall if [ -n "$old" ] && [ -n "$FIREWALL_SYSTEM" ]; then - sed -i "s/$old/$new/g" $HESTIA/data/firewall/*.conf - $BIN/v-update-firewall + sed -i "s/$old/$new/g" "$HESTIA/data/firewall/"*.conf + "$BIN/v-update-firewall" fi #----------------------------------------------------------# @@ -113,7 +127,7 @@ fi #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Info" "System" "IP NAT address changed (IP: $ip, NAT IP: $nat_ip)." +"$BIN/v-log-action" "system" "Info" "System" "IP NAT address changed (IP: $ip46, NAT IP: $nat_ip)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-change-sys-ip-owner b/bin/v-change-sys-ip-owner index b915470ad0..3393a9a87e 100755 --- a/bin/v-change-sys-ip-owner +++ b/bin/v-change-sys-ip-owner @@ -2,7 +2,8 @@ # info: change IP owner # options: IP USER # -# example: v-change-sys-ip-owner 203.0.113.1 admin +# example: v-change-sys-ip-owner 91.198.136.14 admin +# example: v-change-sys-ip-owner 1111:2222:3333::1 admin # # This function of changing IP address ownership. @@ -11,15 +12,15 @@ #----------------------------------------------------------# # Argument definition -ip="$1" +ip46="$1" user="$2" # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -28,12 +29,25 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '2' "$#" 'IP USER' -is_format_valid 'ip' 'user' +ip_format="$(get_ip_format "$ip46")" # ip verification and format identification +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 6 ]; then + ip="" + ipv6="$ip46" + is_format_valid 'ipv6' + is_ip_valid "$ipv6" + else + ip="$ip46" + ipv6="" + is_format_valid 'ip' + is_ip_valid "$ip" + fi +fi +is_format_valid 'user' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" -is_ip_valid "$ip" -is_ip_key_empty '$U_WEB_DOMAINS' -is_ip_key_empty '$U_SYS_USERS' +is_ip_key_empty '$U_WEB_DOMAINS' "$ip46" +is_ip_key_empty '$U_SYS_USERS' "$ip46" ip_status="$(get_ip_value '$STATUS')" @@ -47,7 +61,7 @@ check_hestia_demo_mode # Changing IP owner ip_owner=$(get_ip_value '$OWNER') if [ "$ip_owner" != "$user" ]; then - update_ip_value '$OWNER' "$user" + update_ip_value '$OWNER' "$user" "$ip46" decrease_user_value "$ip_owner" '$IP_OWNED' if [ "$ip_owner" = "$ROOT_USER" ]; then if [ "$ip_status" = 'shared' ]; then @@ -80,7 +94,7 @@ fi # Set status to dedicated if owner is not admin ip_status="$(get_ip_value '$STATUS')" if [ "$user" != "$ROOT_USER" ] && [ "$ip_status" = 'shared' ]; then - $BIN/v-change-sys-ip-status "$ip" 'dedicated' + "$BIN/v-change-sys-ip-status" "$ip46" 'dedicated' fi #----------------------------------------------------------# @@ -88,7 +102,7 @@ fi #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Info" "System" "IP address owner changed (IP: $ip, Owner: $user)" +"$BIN/v-log-action" "system" "Info" "System" "IP address owner changed (IP: $ip46, Owner: $user)" log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-change-sys-ip-status b/bin/v-change-sys-ip-status index b90ac0d42c..621a9226b6 100755 --- a/bin/v-change-sys-ip-status +++ b/bin/v-change-sys-ip-status @@ -3,6 +3,7 @@ # options: IP IP_STATUS # # example: v-change-sys-ip-status 203.0.113.1 yourstatus +# example: v-change-sys-ip-status 1111:2222:3333::1 yourstatus # # This function of changing an IP address's status. @@ -11,16 +12,16 @@ #----------------------------------------------------------# # Argument definition -ip="$1" +ip46="$1" ip_status="$2" # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -29,8 +30,21 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '2' "$#" 'IP IP_STATUS' -is_format_valid 'ip' 'ip_status' -is_ip_valid "$ip" +ip_format="$(get_ip_format "$ip46")" # ip verification and format identification +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 6 ]; then + ip="" + ipv6="$ip46" + is_format_valid 'ipv6' + is_ip_valid "$ipv6" + else + ip="$ip46" + ipv6="" + is_format_valid 'ip' + is_ip_valid "$ip" + fi +fi +is_format_valid 'ip_status' if [ "$ip_status" = "$(get_ip_value '$STATUS')" ]; then check_result "$E_EXISTS" "status $ip_status is already set" fi @@ -38,10 +52,10 @@ web_domains=$(get_ip_value '$U_WEB_DOMAINS') sys_user=$(get_ip_value '$U_SYS_USERS') ip_owner=$(get_ip_value '$OWNER') if [ "$web_domains" -ne '0' ] && [ "$sys_user" != "$ip_owner" ]; then - check_result "$E_INUSE" "IP $ip is used" + check_result "$E_INUSE" "IP $ip46 is used" fi if [ "$ip_owner" != "$ROOT_USER" ] && [ "$ip_status" = "shared" ]; then - $BIN/v-change-sys-ip-owner "$ip" "$ROOT_USER" + "$BIN/v-change-sys-ip-owner" "$ip46" "$ROOT_USER" fi # Perform verification if read-only mode is enabled @@ -52,14 +66,14 @@ check_hestia_demo_mode #----------------------------------------------------------# # Changing IP status -update_ip_value '$STATUS' "$ip_status" +update_ip_value '$STATUS' "$ip_status" "$ip46" #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Info" "System" "IP address status changed (Status: $ip_status, IP: $ip)." +"$BIN/v-log-action" "system" "Info" "System" "IP address status changed (Status: $ip_status, IP: $ip46)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-change-web-domain-ip b/bin/v-change-web-domain-ip index 80ea029a0f..df0fa7784f 100755 --- a/bin/v-change-web-domain-ip +++ b/bin/v-change-web-domain-ip @@ -1,31 +1,37 @@ #!/bin/bash # info: change web domain ip -# options: USER DOMAIN DOMAIN [RESTART] +# options: USER DOMAIN IP [RESTART] [IP FORMAT] # -# example: v-change-web-domain-ip admin example.com 167.86.105.230 yes +# example: v-change-web-domain-ip admin domain.com 123.212.111.222 +# example: v-change-web-domain-ip admin domain.com 1234:55:66::1 +# example: v-change-web-domain-ip admin domain.com 123.212.111.222 no 4 +# example: v-change-web-domain-ip admin domain.com 1234:55:66::1 yes 6 +# example: v-change-web-domain-ip admin domain.com '' '' 4 +# example: v-change-web-domain-ip admin domain.com '' no 6 # -# This function is used for changing domain ip +# This function is used for changing web domain ip #----------------------------------------------------------# # Variables & Functions # #----------------------------------------------------------# # Argument definition -user=$1 -domain=$2 -domain_idn=$2 -ip=$3 -restart=$4 +user="$1" +domain="$2" +domain_idn="$2" +ip46="$3" +restart="$4" +ip_format="$5" # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -38,14 +44,40 @@ format_domain_idn # Verifications # #----------------------------------------------------------# -check_args '3' "$#" 'USER DOMAIN IP [RESTART]' -is_format_valid 'user' 'domain' 'ip' +check_args '3' "$#" 'USER DOMAIN IP [RESTART] [IP FORMAT]' +is_format_valid 'user' 'domain' 'ip46' is_system_enabled "$WEB_SYSTEM" 'WEB_SYSTEM' is_object_valid 'user' 'USER' "$user" is_object_unsuspended 'user' 'USER' "$user" is_object_valid 'web' 'DOMAIN' "$domain" is_object_unsuspended 'web' 'DOMAIN' "$domain" -is_ip_valid "$ip" "$user" + +if [ -z "$ip_format" ]; then + ip_format="$(get_ip_format "$ip46")" # ip verification and automatic format identification +fi +if [ -n "$ip_format" ]; then + if [ $ip_format -ne 4 -a $ip_format -ne 6 ]; then + check_result "$E_INVALID" "invalid IP format :: $ip46 IPV$ip_format?? " # error in case of wrong or undefined IP format + fi +else + check_result "$E_INVALID" "invalid or undefined IP format :: $ip46" # error in case of wrong or undefined IP format +fi +if [ $ip_format -eq 4 ]; then + ip="$ip46" + ipv6='' + if [ -n "$ip" ]; then + is_ip_format_valid "$ip" 'ipv4' # check for correct ipv4 format if not empty + is_ip_valid "$ip" "$user" + fi +fi +if [ $ip_format -eq 6 ]; then + ip='' + ipv6="$ip46" + if [ -n "$ipv6" ]; then + is_ip_format_valid "$ipv6" 'ipv6' # check for correct ipv6 format if not empty + is_ipv6_valid "$ipv6" "$user" + fi +fi # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -56,8 +88,18 @@ check_hestia_demo_mode # Preparing variables for vhost replace get_domain_values 'web' -old=$(get_real_ip "$IP") -new=$(get_real_ip "$ip") +if [ $ip_format -eq 4 ]; then + old=$(get_real_ip "$IP") + new=$(get_real_ip "$ip") + ipv6="$IP6" + local_ipv6="$ipv6" +fi +if [ $ip_format -eq 6 ]; then + old=$(get_real_ip "$IP6") + new=$(get_real_ip "$ipv6") + ip=$(get_real_ip "$IP") + local_ip="$ip" +fi # Replacing vhost replace_web_config "$WEB_SYSTEM" "$TPL.tpl" @@ -73,9 +115,15 @@ if [ -n "$PROXY_SYSTEM" ] && [ -n "$PROXY" ]; then fi fi +WEBDOMAIN="$DOMAIN" +DOMAIN='' +get_object_value 'mail' 'DOMAIN' "$domain" +MAILDOMAIN="$DOMAIN" +DOMAIN="$WEBDOMAIN" + # Check for webmail -if [ -n "$IMAP_SYSTEM" ]; then - $BIN/v-rebuild-mail-domain "$user" "$domain" +if [ -n "$IMAP_SYSTEM" -a -n "$MAILDOMAIN" ]; then + "$BIN/v-rebuild-mail-domain" "$user" "$domain" fi #----------------------------------------------------------# @@ -83,21 +131,26 @@ fi #----------------------------------------------------------# # Update config -update_object_value 'web' 'DOMAIN' "$domain" '$IP' "$3" +if [ $ip_format -eq 4 ]; then + update_object_value 'web' 'DOMAIN' "$domain" '$IP' "$3" +fi +if [ $ip_format -eq 6 ]; then + update_object_value 'web' 'DOMAIN' "$domain" '$IP6' "$3" +fi # Update counters -increase_ip_value "$new" -decrease_ip_value "$old" +[ -n "$new" ] && increase_ip_value "$new" +[ -n "$old" ] && decrease_ip_value "$old" # Restart web server -$BIN/v-restart-web "$restart" +"$BIN/v-restart-web" "$restart" check_result $? "WEB restart failed" > /dev/null -$BIN/v-restart-proxy "$restart" +"$BIN/v-restart-proxy" "$restart" check_result $? "Proxy restart failed" > /dev/null # Logging -$BIN/v-log-action "$user" "Info" "Web" "Web domain IP address changed (IP: $3, Domain: $domain)." +"$BIN/v-log-action" "$user" "Info" "Web" "Web domain IP address changed (IP: $3, Domain: $domain)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-delete-firewall-ban b/bin/v-delete-firewall-ban index 6972247312..adcba8c9bb 100755 --- a/bin/v-delete-firewall-ban +++ b/bin/v-delete-firewall-ban @@ -11,19 +11,18 @@ #----------------------------------------------------------# # Argument definition -ip=$1 -chain=$(echo $2 | tr '[:lower:]' '[:upper:]') +ip46="$1" +chain=$(echo "$2" | tr '[:lower:]' '[:upper:]') -# Defining absolute path for iptables and modprobe -iptables="/sbin/iptables" +subnetcalc_bin="$(which subnetcalc)" # subnetcalc binary to calculate subnet of an ipv6 net # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/firewall.sh -source $HESTIA/func/firewall.sh +source "$HESTIA/func/firewall.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -32,9 +31,30 @@ source_conf "$HESTIA/conf/hestia.conf" #----------------------------------------------------------# check_args '2' "$#" 'IP CHAIN' -is_format_valid 'ip' 'chain' +is_format_valid 'ip46' 'chain' is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +ip_format=$(get_ip_format "$ip46") # ip verification and format identification +case "$ip_format" in + 4) + iptables="iptables" + ip2ban="$ip46" + ;; + 6) + iptables="ip6tables" + if [ -x "$subnetcalc_bin" ]; then + # if subnetcalc binary is installed on system, then ban of whole /64 subnet + ip2ban="$("$subnetcalc_bin" "${ip46}/64" | sed -ne '/Network/s/Network[ \t]*=[ \t]*\(.*\)/\1/p' | sed -e 's/[ \t]*//g')" + else + # otherwise ban the ip only, without subnet + ip2ban="$ip46" + fi + ;; + *) + check_result 1 "Wrong IP address $ip46 !" # wrong ip address + ;; +esac + # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -45,44 +65,59 @@ check_hestia_demo_mode # Self heal iptables links heal_iptables_links +# Get iptables binary +iptables="$(get_iptables_bin "$iptables")" + conf="$HESTIA/data/firewall/banlist.conf" +error_message="Banned address $ip46 not found in the chain $chain" if [ "$chain" == "ALL" ]; then - check_ip=$(grep "IP='$ip' CHAIN='*'" $conf) + check_ip=$(grep "IP='$ip46' CHAIN='*'" "$conf") if [ -z "$check_ip" ]; then exit fi - grep "IP='$ip' CHAIN='*'" $conf | while read -r line; do - parse_object_kv_list $line - + grep "IP='$ip46' CHAIN='*'" "$conf" | while read -r line; do + parse_object_kv_list "$line" + if [ -x "$subnetcalc_bin" ]; then + # if subnetcalc binary is installed on system, then ban of whole /64 subnet + ip2ban="$("$subnetcalc_bin" "${IP}/64" | sed -ne '/Network/s/Network[ \t]*=[ \t]*\(.*\)/\1/p' | sed -e 's/[ \t]*//g')" + else + # otherwise ban the ip only, without subnet + ip2ban="$IP" + fi + del_param="$(${iptables} -S | grep -m 1 "fail2ban-$CHAIN.*$ip2ban" | sed -e 's/^-A[ \t]*//')" + [ -n "$del_param" ] && error_message="$(${iptables} -D $del_param)" # Deleting ip from banlist sip=$(echo "$IP" | sed "s|/|\\\/|g") - sed -i "/IP='$sip' CHAIN='$CHAIN'/d" $conf - b=$($iptables -L fail2ban-$CHAIN --line-number -n | grep -w $ip | awk '{print $1}') - $iptables -D fail2ban-$CHAIN $b 2> /dev/null + sed -i "/IP='$sip' CHAIN='$CHAIN'/d" "$conf" done else # Checking ip in banlist - check_ip=$(grep "IP='$ip' CHAIN='$chain'" $conf 2> /dev/null) + check_ip=$(grep "IP='$ip46' CHAIN='$chain'" "$conf" 2> /dev/null) if [ -z "$check_ip" ]; then exit fi + del_param="$(${iptables} -S | grep -m 1 "fail2ban-$chain.*$ip2ban" | sed -e 's/^-A[ \t]*//')" + [ -n "$del_param" ] && error_message="$(${iptables} -D $del_param)" + if [ $? -ne 0 ]; then + error_code=$? + check_result 1 "$error_message" + exit $error_code + fi # Deleting ip from banlist - sip=$(echo "$ip" | sed "s|/|\\\/|g") - sed -i "/IP='$sip' CHAIN='$chain'/d" $conf - b=$($iptables -L fail2ban-$chain --line-number -n | grep -w $ip | awk '{print $1}') - $iptables -D fail2ban-$chain $b 2> /dev/null + sip=$(echo "$ip46" | sed "s|/|\\\/|g") + sed -i "/IP='$sip' CHAIN='$chain'/d" "$conf" fi # Changing permissions -chmod 660 $conf +chmod 660 "$conf" #----------------------------------------------------------# # Hestia # #----------------------------------------------------------# # Logging -$BIN/v-log-action "system" "Info" "Firewall" "Removed IP from ban list (IP: $ip, Service: $chain)." +"$BIN/v-log-action" "system" "Info" "Firewall" "Removed IP from ban list (IP: $ip46, Service: $chain)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-delete-firewall-ipv6-rule b/bin/v-delete-firewall-ipv6-rule new file mode 100755 index 0000000000..f67df1e27c --- /dev/null +++ b/bin/v-delete-firewall-ipv6-rule @@ -0,0 +1,51 @@ +#!/bin/bash +# info: delete firewall rule +# options: RULE +# +# The function deletes firewall rule. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +rule=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE' +is_format_valid 'rule' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Deleting rule +sed -i "/RULE='$rule' /d" $HESTIA/data/firewallv6/rules.conf + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-delete-sys-ip b/bin/v-delete-sys-ip index a0f93c9f22..7c25fcb11c 100755 --- a/bin/v-delete-sys-ip +++ b/bin/v-delete-sys-ip @@ -1,8 +1,10 @@ #!/bin/bash # info: delete system IP +# both kind of IP addresses, ipv4 and ipv6 are allowed # options: IP # # example: v-delete-sys-ip 203.0.113.1 +# example: v-delete-sys-ip 1234:5678:abcd:ef90::1 # # This function for deleting a system IP. It does not allow to delete first IP # on interface and do not allow to delete IP which is used by a web domain. @@ -12,29 +14,52 @@ #----------------------------------------------------------# # Argument definition -ip="$1" +ip46=${1%/*} # clean ip address without cidr/prefix_length # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" +# Variables +NETPLAN_FILE="/etc/netplan/60-hestia.yaml" +NETWORK_IF_FILE="/etc/network/interfaces" + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# check_args '1' "$#" 'IP' -is_format_valid 'ip' -is_ip_valid "$ip" -is_ip_key_empty '$U_WEB_DOMAINS' -is_ip_key_empty '$U_SYS_USERS' +ip_format="$(get_ip_format "$ip46")" # ip verification and format identification +retval=$? +check_ip_par="" +add_cap_string_ipv6="" +[ -n "$ip_format" ] && [ $ip_format -eq 4 -o $ip_format -eq 6 ] && check_ip_par=" -$ip_format" +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 6 ]; then + ip="" + ipv6="$ip46" + add_cap_string_ipv6="V6" + is_format_valid 'ipv6' + is_ipv6_valid "$ipv6" + else + ip="$ip46" + ipv6="" + is_format_valid 'ip' + is_ip_valid "$ip" + fi +else + check_result $retval "$ip46 is not a valid IPV4/IPV6 address!" +fi +is_ip_key_empty '$U_WEB_DOMAINS' "$ip46" +is_ip_key_empty '$U_SYS_USERS' "$ip46" # Perform verification if read-only mode is enabled check_hestia_demo_mode @@ -44,25 +69,48 @@ check_hestia_demo_mode #----------------------------------------------------------# # Import IP variables -source "$HESTIA/data/ips/$ip" -cidr="$(convert_netmask "$NETMASK")" +source "$HESTIA/data/ips/$ip46" +if [ $ip_format -eq 6 ]; then + prefix_length="$NETMASK" + is_format_valid 'prefix_length' + cidr_prefixlen="$prefix_length" +else + netmask="$NETMASK" + is_format_valid 'netmask' + cidr_prefixlen=$(convert_netmask "$netmask") +fi -# Get primary IP -default_nic="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')" -primary_ipv4="$(ip -4 -d -j addr show "$default_nic" | jq -r '.[].addr_info[] | if .scope == "global" then .local else empty end' | head -n1)" +host_ip_check=$(hostname -i | sed -ne "/$ip46/p") # check, if IP address is primary (listed in /etc/hosts) +# shellcheck disable=SC1087 +interface=$("$BIN/v-list-sys-network" plain | sed -ne "/$ip46/s/$ip46[ \t]*\/[0-9]*[ \t]*\(.*\)/\1/p") # interface for IP address +number_of_ips=$("$BIN/v-list-sys-network" list "$check_ip_par" | wc -l) # number of global system IP addresses -# Checking primary IP on the interface -interface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')" -if [ -n "$interface" ] && [ "$ip" = "$primary_ipv4" ]; then +if [[ (-n "$host_ip_check" && -n "$interface") || ($number_of_ips -eq 1) ]]; then echo "Error: can't delete primary IP address" log_event "$E_FORBIDEN" "$ARGUMENTS" exit "$E_FORBIDEN" fi +# Check if IP is managed by HESTIA and not by system +if [ -f "$NETPLAN_FILE" ]; then + NETPLAN_MATCH="$(sed -ne "/[ \t]*-[ \t]*$ip46\//p" "$NETPLAN_FILE")" +fi +if [ -f "$NETWORK_IF_FILE" ]; then + NETWORK_IF_MATCH="$(sed -ne "/address[ \t]*$ip46\//p" "$NETWORK_IF_FILE")" # first attempt for address/preflen definition + if [ -z "$NETWORK_IF_MATCH" ]; then + NETWORK_IF_MATCH="$(sed -ne "/address[ \t]*$ip46$/p" "$NETWORK_IF_FILE")" # second attempt, if address and netmask are defined in separate lines + fi +fi +if [ -z "$NETPLAN_MATCH" -a -z "$NETWORK_IF_MATCH" ]; then + echo "Error: can't delete system managed IP address" + log_event "$E_FORBIDEN" "$ARGUMENTS" + exit "$E_FORBIDEN" +fi + # Deleting system IP if [ -n "$interface" ]; then - ip addr del "$ip/$cidr" dev "$interface" 2> /dev/null - if [ "$?" -ne "0" ]; then + ip addr del "$ip46$cidr_prefixlen" dev ${INTERFACE%:*} + if [ "$?" -ne 0 ]; then echo "Error: can't delete system IP address" log_event "$E_FORBIDEN" "$ARGUMENTS" exit "$E_FORBIDEN" @@ -77,36 +125,43 @@ fi # Deleting startup conf on Debian/Ubuntu if [ -f "/etc/netplan/60-hestia.yaml" ]; then - sed -i "/$ip\//d" /etc/netplan/60-hestia.yaml + sed -i "/$ip46\//d" /etc/netplan/60-hestia.yaml if ! grep -q '-' /etc/netplan/60-hestia.yaml; then rm /etc/netplan/60-hestia.yaml fi elif [ -e "/etc/network/interfaces" ]; then - ip_str="$(grep -n "$ip$" /etc/network/interfaces | cut -f1 -d:)" + if [ $ip_format -eq 6 ]; then + filter_ip="${ip46}${cidr_prefixlen}" + lines_after=0 # ipv6 is defined using prefix length in one line + else + filter_ip="${ip46}" + lines_after=1 # ipv4 is defined using netmask in a separate line + fi + ip_str=$(grep -n $filter_ip$ /etc/network/interfaces | cut -f1 -d:) if [ -n "$ip_str" ]; then - first_str="$((ip_str - 3))" - last_str="$((ip_str + 1))" + first_str=$((ip_str - 4)) + last_str=$((ip_str + $lines_after)) sed -i "$first_str,$last_str d" /etc/network/interfaces fi fi # Deleting Hestia IP -rm -f $HESTIA/data/ips/$ip +rm -f "$HESTIA/data/ips/$ip46" # Deleting web config if [ -n "$WEB_SYSTEM" ]; then - rm -f /etc/$WEB_SYSTEM/conf.d/$ip.conf + rm -f "/etc/$WEB_SYSTEM/conf.d/$ip46.conf" fi # Deleting proxy config if [ -n "$PROXY_SYSTEM" ]; then - rm -f /etc/$PROXY_SYSTEM/conf.d/$ip.conf + rm -f "/etc/$PROXY_SYSTEM/conf.d/$ip46.conf" # mod_extract_forwarded fw_conf="/etc/$WEB_SYSTEM/conf.d/mod_extract_forwarded.conf" if [ -e "$fw_conf" ]; then ips="$(grep 'MEFaccept 127.0.0.1' "$fw_conf")" - new_ips="$(echo "$ips" | sed "s/$ip//")" + new_ips="$(echo "$ips" | sed "s/$ip46//")" sed -i "s/$ips/$new_ips/g" "$fw_conf" fi @@ -114,7 +169,7 @@ if [ -n "$PROXY_SYSTEM" ]; then rpaf_conf="/etc/$WEB_SYSTEM/mods-enabled/rpaf.conf" if [ -e "$rpaf_conf" ]; then ips="$(grep RPAFproxy_ips "$rpaf_conf")" - new_ips="$(echo "$ips" | sed "s/ $ip//")" + new_ips="$(echo "$ips" | sed "s/ $ip46//")" sed -i "s/$ips/$new_ips/g" "$rpaf_conf" # Remove RPAFproxy_ips line when ip list is empty @@ -124,7 +179,7 @@ if [ -n "$PROXY_SYSTEM" ]; then # mod_remoteip remoteip_conf="/etc/$WEB_SYSTEM/mods-enabled/remoteip.conf" if [ -e "$remoteip_conf" ]; then - sed -i "/RemoteIPInternalProxy $ip\$/d" "$remoteip_conf" + sed -i "/RemoteIPInternalProxy $ip46\$/d" "$remoteip_conf" fi fi @@ -134,38 +189,38 @@ fi # Updating user conf if [ -n "$OWNER" ]; then - decrease_user_value "$OWNER" '$IP_OWNED' + decrease_user_value "$OWNER" '$IP'$add_cap_string_ipv6'_OWNED' fi if [ "$OWNER" = "$ROOT_USER" ]; then if [ "$STATUS" = 'shared' ]; then for hestia_user in $("$BIN/v-list-users" list); do - decrease_user_value "$hestia_user" '$IP_AVAIL' + decrease_user_value "$hestia_user" '$IP'$add_cap_string_ipv6'_AVAIL' done else - decrease_user_value "$OWNER" '$IP_AVAIL' + decrease_user_value "$OWNER" '$IP'$add_cap_string_ipv6'_AVAIL' fi else - decrease_user_value "$OWNER" '$IP_AVAIL' + decrease_user_value "$OWNER" '$IP'$add_cap_string_ipv6'_AVAIL' fi # Restarting web server -$BIN/v-restart-web +"$BIN/v-restart-web" check_result $? "Web restart failed" > /dev/null # Restarting proxy server if [ -n "$PROXY_SYSTEM" ]; then - $BIN/v-restart-proxy + "$BIN/v-restart-proxy" check_result $? "Proxy restart failed" > /dev/null fi # Restarting firewall if [ -n "$FIREWALL_SYSTEM" ]; then - $BIN/v-update-firewall + "$BIN/v-update-firewall" fi # Logging -$BIN/v-log-action "system" "Info" "System" "IP address deleted (IP: $ip)." +"$BIN/v-log-action" "system" "Info" "System" "IP address deleted (IP: $ip46)." log_event "$OK" "$ARGUMENTS" exit diff --git a/bin/v-delete-web-domain b/bin/v-delete-web-domain index 90deaeba95..e00a203626 100755 --- a/bin/v-delete-web-domain +++ b/bin/v-delete-web-domain @@ -56,6 +56,7 @@ check_hestia_demo_mode # Parsing domain values get_domain_values 'web' local_ip=$(get_real_ip "$IP") +local_ipv6="$IP6" # Deleting ftp users if [ -n "$FTP_USER" ]; then @@ -120,7 +121,8 @@ rm -rf $HOMEDIR/$user/conf/web/$domain #----------------------------------------------------------# # Decreasing user counters -decrease_ip_value "$local_ip" +[ -n "$local_ip" ] && decrease_ip_value "$local_ip" +[ -n "$local_ipv6" ] && decrease_ip_value "$local_ipv6" decrease_user_value "$user" '$U_WEB_DOMAINS' if [ "$SSL" = 'yes' ]; then decrease_user_value "$user" '$U_WEB_SSL' diff --git a/bin/v-list-dns-domain b/bin/v-list-dns-domain index a8e1b11065..bf709539f0 100755 --- a/bin/v-list-dns-domain +++ b/bin/v-list-dns-domain @@ -19,7 +19,7 @@ format=${3-shell} # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -28,6 +28,7 @@ json_list() { echo '{' echo ' "'$DOMAIN'": { "IP": "'$IP'", + "IP6": "'$IP6'", "TPL": "'$TPL'", "TTL": "'$TTL'", "EXP": "'$EXP'", @@ -47,6 +48,7 @@ json_list() { shell_list() { echo "DOMAIN: $DOMAIN" echo "IP: $IP" + echo "IP6: $IP6" echo "TEMPLATE: $TPL" echo "TTL: $TTL" echo "EXP: $EXP" @@ -61,17 +63,22 @@ shell_list() { # PLAIN list function plain_list() { - echo -ne "$DOMAIN\t$IP\t$TPL\t$TTL\t$EXP\t$SOA\t$SERIAL\t$DNSSEC\t$RECORDS\t" + echo -ne "$DOMAIN\t$IP\t$IP6\t$TPL\t$TTL\t$EXP\t$SOA\t$SERIAL\t$DNSSEC\t$RECORDS\t" echo -e "$SUSPENDED\t$TIME\t$DATE" } # CSV list function csv_list() { - echo "DOMAIN,IP,TPL,TTL,EXP,SOA,SERIAL,DNSSEC,RECORDS,SUSPENDED,TIME,DATE" - echo -n "$DOMAIN,$IP,$TPL,$TTL,$EXP,$SOA,$SERIAL,$DNSSEC,$RECORDS,$SUSPENDED," + echo "DOMAIN,IP,IP6,TPL,TTL,EXP,SOA,SERIAL,DNSSEC,RECORDS,SUSPENDED,TIME,DATE" + echo -n "$DOMAIN,$IP,$IP6,$TPL,$TTL,$EXP,$SOA,$SERIAL,$DNSSEC,$RECORDS,$SUSPENDED," echo "$TIME,$DATE" } +# DOMAIN ONLY list (Simple list, only domain names) +only_domain_list() { + echo "$DOMAIN" +} + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# @@ -86,7 +93,7 @@ is_object_valid 'dns' 'DOMAIN' "$domain" #----------------------------------------------------------# # Parsing domain -parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/dns.conf) +parse_object_kv_list $(grep "DOMAIN='$domain'" "$USER_DATA/dns.conf") # Listing data case $format in @@ -94,6 +101,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list ;; + list) only_domain_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-dns-domains b/bin/v-list-dns-domains index 671da2ab48..80adc2b644 100755 --- a/bin/v-list-dns-domains +++ b/bin/v-list-dns-domains @@ -11,14 +11,14 @@ #----------------------------------------------------------# # Argument definition -user=$1 +user=${1} format=${2-shell} # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -26,12 +26,13 @@ source_conf "$HESTIA/conf/hestia.conf" json_list() { IFS=$'\n' i=1 - objects=$(grep DOMAIN $USER_DATA/dns.conf | wc -l) + objects=$(grep DOMAIN "$USER_DATA/dns.conf" | wc -l) echo "{" while read str; do parse_object_kv_list "$str" echo -n ' "'$DOMAIN'": { "IP": "'$IP'", + "IP6": "'$IP6'", "TPL": "'$TPL'", "TTL": "'$TTL'", "EXP": "'$EXP'", @@ -50,19 +51,19 @@ json_list() { echo fi ((i++)) - done < <(cat $USER_DATA/dns.conf) + done < <(cat "$USER_DATA/dns.conf") echo '}' } # SHELL list function shell_list() { IFS=$'\n' - echo "DOMAIN IP TPL TTL DNSSEC REC SPND DATE" - echo "------ -- --- --- ------ --- ---- ----" + echo "DOMAIN IP IP6 TPL TTL DNSSEC REC SPND DATE" + echo "------ -- --- --- --- ------ --- ---- ----" while read str; do parse_object_kv_list "$str" - echo "$DOMAIN $IP $TPL $TTL $DNSSEC $RECORDS $SUSPENDED $DATE" - done < <(cat $USER_DATA/dns.conf) + echo "$DOMAIN $IP $IP6 $TPL $TTL $DNSSEC $RECORDS $SUSPENDED $DATE" + done < <(cat "$USER_DATA/dns.conf") } # PLAIN list function @@ -70,20 +71,29 @@ plain_list() { IFS=$'\n' while read str; do parse_object_kv_list "$str" - echo -ne "$DOMAIN\t$IP\t$TPL\t$TTL\t$EXP\t$SOA\t$DNSSEC\t$SERIAL\t" + echo -ne "$DOMAIN\t$IP\t$IP6\t$TPL\t$TTL\t$EXP\t$SOA\t$DNSSEC\t$SERIAL\t" echo -e "$SRC\t$RECORDS\t$SUSPENDED\t$TIME\t$DATE" - done < <(cat $USER_DATA/dns.conf) + done < <(cat "$USER_DATA/dns.conf") } # CSV list function csv_list() { IFS=$'\n' - echo "DOMAIN,IP,TPL,TTL,EXP,SOA,SERIAL,SRC,DNSSEC,RECORDS,SUSPENDED,TIME,DATE" + echo "DOMAIN,IP,IP6,TPL,TTL,EXP,SOA,SERIAL,SRC,DNSSEC,RECORDS,SUSPENDED,TIME,DATE" while read str; do parse_object_kv_list "$str" - echo -n "$DOMAIN,$IP,$TPL,$TTL,$EXP,$SOA,$SERIAL," + echo -n "$DOMAIN,$IP,$IP6,$TPL,$TTL,$EXP,$SOA,$SERIAL," echo "$SRC,$DNSSEC,$RECORDS,$SUSPENDED,$TIME,$DATE" - done < <(cat $USER_DATA/dns.conf) + done < <(cat "$USER_DATA/dns.conf") +} + +# DOMAIN ONLY list (Simple list, only domain names) +only_domain_list() { + IFS=$'\n' + while read str; do + parse_object_kv_list "$str" + echo -e "$DOMAIN" + done < <(cat "$USER_DATA/dns.conf") } #----------------------------------------------------------# @@ -104,6 +114,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list | column -t ;; + list) only_domain_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-firewall-ipv6 b/bin/v-list-firewall-ipv6 new file mode 100755 index 0000000000..f67df1e27c --- /dev/null +++ b/bin/v-list-firewall-ipv6 @@ -0,0 +1,51 @@ +#!/bin/bash +# info: delete firewall rule +# options: RULE +# +# The function deletes firewall rule. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Importing system variables +source /etc/profile + +# Argument definition +rule=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE' +is_format_valid 'rule' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Deleting rule +sed -i "/RULE='$rule' /d" $HESTIA/data/firewallv6/rules.conf + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-list-firewall-ipv6-rule b/bin/v-list-firewall-ipv6-rule new file mode 100755 index 0000000000..2bb1ca00c9 --- /dev/null +++ b/bin/v-list-firewall-ipv6-rule @@ -0,0 +1,88 @@ +#!/bin/bash +# info: list firewall rule +# options: RULE [FORMAT] +# +# The function of obtaining firewall rule parameters. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +rule=$1 +format=${2-shell} + +# Includes +source $HESTIA/func/main.sh + +json_list() { + echo '{' + echo ' "'$RULE'": { + "ACTION": "'$ACTION'", + "PROTOCOL": "'$PROTOCOL'", + "PORT": "'$PORT'", + "IP6": "'$IP6'", + "COMMENT": "'$COMMENT'", + "SUSPENDED": "'$SUSPENDED'", + "TIME": "'$TIME'", + "DATE": "'$DATE'" + }' + echo '}' +} + +# SHELL list function +shell_list() { + echo "ACTION: $ACTION" + echo "PROTOCOL: $PROTOCOL" + echo "PORT: $PORT" + echo "IP6: $IP6" + echo "COMMENT: $COMMENT" + echo "SUSPENDED: $SUSPENDED" + echo "TIME: $TIME" + echo "DATE: $DATE" +} + +# PLAIN list function +plain_list() { + echo -ne "$RULE\t$ACTION\t$PROTOCOL\t$PORT\t$IP6\t$COMMENT\t" + echo -e "$SUSPENDED\t$TIME\t$DATE" +} + +# CSV list function +csv_list() { + echo "RULE,ACTION,PROTOCOL,PORT,IP6,COMMENT,SUSPENDED,TIME,DATE" + echo "$RULE,$ACTION,$PROTOCOL,$PORT,$IP6,$COMMENT,$SUSPENDED,$TIME,$DATE" +} + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE [FORMAT]' +is_number_format_valid "$rule" "rule id" +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Parsing rules +eval $(grep "RULE='$rule'" $HESTIA/data/firewallv6/rules.conf) + +# Listing data +case $format in + json) json_list ;; + plain) plain_list ;; + csv) csv_list ;; + shell) shell_list ;; +esac + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +exit \ No newline at end of file diff --git a/bin/v-list-mail-domain b/bin/v-list-mail-domain index a724a5f4c7..f694576a69 100755 --- a/bin/v-list-mail-domain +++ b/bin/v-list-mail-domain @@ -19,13 +19,12 @@ format=${3-shell} # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" # JSON list function json_list() { - parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/mail.conf) echo '{' echo ' "'$DOMAIN'": { "ANTIVIRUS": "'$ANTIVIRUS'", @@ -70,9 +69,9 @@ shell_list() { echo "WEBMAIL_ALIAS: $WEBMAIL_ALIAS.$domain" echo "WEBMAIL: $WEBMAIL" echo "U_SMTP_RELAY: $U_SMTP_RELAY" - echo "U_SMTP_RELAY_HOST $U_SMTP_RELAY_HOST" - echo "U_SMTP_RELAY_PORT $U_SMTP_RELAY_PORT" - echo "U_SMTP_RELAY_USERNAME $U_SMTP_RELAY_USERNAME" + echo "U_SMTP_RELAY_HOST: $U_SMTP_RELAY_HOST" + echo "U_SMTP_RELAY_PORT: $U_SMTP_RELAY_PORT" + echo "U_SMTP_RELAY_USERNAME: $U_SMTP_RELAY_USERNAME" } # PLAIN list function @@ -92,6 +91,11 @@ csv_list() { echo "$U_SMTP_RELAY,$U_SMTP_RELAY_HOST,$U_SMTP_RELAY_PORT,$U_SMTP_RELAY_USERNAME" } +# ONLY DOMAIN list function +only_domain_list() { + echo "$DOMAIN" +} + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# @@ -106,7 +110,7 @@ is_object_valid 'mail' 'DOMAIN' "$domain" #----------------------------------------------------------# # Parsing mail domain -parse_object_kv_list $(grep "DOMAIN='$domain'" $USER_DATA/mail.conf) +parse_object_kv_list $(grep "DOMAIN='$domain'" "$USER_DATA/mail.conf") # Listing data case $format in @@ -114,6 +118,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list ;; + list) only_domain_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-mail-domains b/bin/v-list-mail-domains index 21aab0872d..3bfe6bd920 100755 --- a/bin/v-list-mail-domains +++ b/bin/v-list-mail-domains @@ -18,7 +18,7 @@ format=${2-shell} # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -26,7 +26,7 @@ source_conf "$HESTIA/conf/hestia.conf" json_list() { IFS=$'\n' i=1 - objects=$(grep DOMAIN $USER_DATA/mail.conf | wc -l) + objects=$(grep DOMAIN "$USER_DATA/mail.conf" | wc -l) echo "{" while read str; do parse_object_kv_list "$str" @@ -52,7 +52,7 @@ json_list() { echo fi ((i++)) - done < <(cat $USER_DATA/mail.conf) + done < <(cat "$USER_DATA/mail.conf") echo '}' } @@ -65,7 +65,7 @@ shell_list() { parse_object_kv_list "$str" echo -n "$DOMAIN $ANTIVIRUS $ANTISPAM $DKIM $SSL $ACCOUNTS $U_DISK " echo "$SUSPENDED $DATE" - done < <(cat $USER_DATA/mail.conf) + done < <(cat "$USER_DATA/mail.conf") } # PLAIN list function @@ -75,7 +75,7 @@ plain_list() { parse_object_kv_list "$str" echo -ne "$DOMAIN\t$ANTIVIRUS\t$ANTISPAM\t$DKIM\t$SSL\$CATCHALL\t" echo -e "$ACCOUNTS\t$U_DISK\t$SUSPENDED\t$TIME\t$DATE\t$WEBMAIL_ALIAS\t$WEBMAIL" - done < <(cat $USER_DATA/mail.conf) + done < <(cat "$USER_DATA/mail.conf") } # CSV list function @@ -88,7 +88,16 @@ csv_list() { echo -n "$DOMAIN,$ANTIVIRUS,$ANTISPAM,$DKIM,$SSL,$CATCHALL,$ACCOUNTS," echo "'$U_DISK,$SUSPENDED,$TIME,$DATE,$WEBMAIL_ALIAS,$WEBMAIL" echo - done < <(cat $USER_DATA/mail.conf) + done < <(cat "$USER_DATA/mail.conf") +} + +# ONLY DOMAIN list function +only_domain_list() { + IFS=$'\n' + while read str; do + parse_object_kv_list "$str" + echo "$DOMAIN" + done < <(cat "$USER_DATA/mail.conf") } #----------------------------------------------------------# @@ -109,6 +118,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list | column -t ;; + list) only_domain_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-sys-ip b/bin/v-list-sys-ip index 2ca8c3c771..680fb0ddf9 100755 --- a/bin/v-list-sys-ip +++ b/bin/v-list-sys-ip @@ -3,6 +3,7 @@ # options: IP [FORMAT] # # example: v-list-sys-ip 203.0.113.1 +# example: v-list-sys-ip 1234:55:66::1 # # This function for getting the list of system IP parameters. @@ -11,23 +12,23 @@ #----------------------------------------------------------# # Argument definition -ip="$1" -format="${2-shell}" +ip46=${1} +format=${2-shell} # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" # JSON list function json_list() { echo '{' - echo ' "'$ip'": { + echo ' "'$ip46'": { "OWNER": "'$OWNER'", "STATUS": "'$STATUS'", "NAME": "'$NAME'", @@ -44,7 +45,7 @@ json_list() { # SHELL list function shell_list() { - echo "IP: $ip" + echo "IP: $ip46" echo "NETMASK: $NETMASK" echo "INTERFACE: $INTERFACE" echo "NAT: $NAT" @@ -59,7 +60,7 @@ shell_list() { # PLAIN list function plain_list() { - echo -ne "$ip\t$OWNER\t$STATUS\t$NAME\t$U_SYS_USERS\t$U_WEB_DOMAINS\t" + echo -ne "$ip46\t$OWNER\t$STATUS\t$NAME\t$U_SYS_USERS\t$U_WEB_DOMAINS\t" echo -e "$INTERFACE\t$NETMASK\t$NAT\t$TIME\t$DATE" } @@ -67,7 +68,7 @@ plain_list() { csv_list() { echo -n "IP,OWNER,STATUS,NAME,U_SYS_USERS,U_WEB_DOMAINS,INTERFACE" echo "NETMASK,NAT,TIME,DATE" - echo -n "$ip,$OWNER,$STATUS,$NAME,\"$U_SYS_USERS\",$U_WEB_DOMAINS," + echo -n "$ip46,$OWNER,$STATUS,$NAME,\"$U_SYS_USERS\",$U_WEB_DOMAINS," echo "$INTERFACE, $NETMASK,$NAT,$TIME,$DATE" } @@ -76,9 +77,20 @@ csv_list() { #----------------------------------------------------------# check_args '1' "$#" 'IP [FORMAT]' -is_format_valid 'ip' -if [ ! -e "$HESTIA/data/ips/$ip" ]; then - check_result $E_NOTEXIST "IP $ip doesn't exist" +ip_format="$(get_ip_format $ip46)" # ip verification and format identification +if [ -n "$ip_format" ]; then + if [ $ip_format -eq 6 ]; then + ip="" + ipv6="$ip46" + is_format_valid 'ipv6' + else + ip="$ip46" + ipv6="" + is_format_valid 'ip' + fi +fi +if [ ! -e "$HESTIA/data/ips/$ip46" ]; then + check_result $E_NOTEXIST "IP $ip46 doesn't exist" fi #----------------------------------------------------------# @@ -86,7 +98,7 @@ fi #----------------------------------------------------------# # Parsing IP -source_conf "$HESTIA/data/ips/$ip" +source_conf "$HESTIA/data/ips/$ip46" # Listing data case $format in diff --git a/bin/v-list-sys-ips b/bin/v-list-sys-ips index a024907081..8e90b30f54 100755 --- a/bin/v-list-sys-ips +++ b/bin/v-list-sys-ips @@ -1,8 +1,9 @@ #!/bin/bash # info: list system IPs -# options: [FORMAT] +# options: [FORMAT] [FILTER] # # example: v-list-sys-ips +# example: v-list-sys-ips list 6 # # This function for obtaining the list of system IP addresses. @@ -11,44 +12,62 @@ #----------------------------------------------------------# # Argument definition -format="${1-shell}" +format=$1 +filter=$2 + +[ -z "$format" ] && format="shell" # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" +# ONLY IP list function with filtering +only_ip_list() { + ls "$HESTIA/data/ips/" | while read IP; do + ip_format="$(get_ip_format $IP)" + if [ -n "$ip_format" ]; then + if [ -n "$filter" ]; then + if [ "$filter" = "$ip_format" ]; then + echo "$IP" + fi + else + echo "$IP" + fi + fi + done +} + # JSON list function json_list() { echo '{' - ip_count="$(ls $HESTIA/data/ips/ | wc -l)" i=1 - while read IP; do + echo "$IPS" | while read IP; do source_conf "$HESTIA/data/ips/$IP" echo -n ' "'$IP'": { - "OWNER": "'$OWNER'", - "STATUS": "'$STATUS'", - "NAME": "'$NAME'", - "U_SYS_USERS": "'$U_SYS_USERS'", - "U_WEB_DOMAINS": "'$U_WEB_DOMAINS'", - "INTERFACE": "'$INTERFACE'", - "NETMASK": "'$NETMASK'", - "NAT": "'$NAT'", - "TIME": "'$TIME'", - "DATE": "'$DATE'" - }' - if [ "$i" -lt "$ip_count" ]; then + "OWNER": "'$OWNER'", + "STATUS": "'$STATUS'", + "NAME": "'$NAME'", + "U_SYS_USERS": "'$U_SYS_USERS'", + "U_WEB_DOMAINS": "'$U_WEB_DOMAINS'", + "INTERFACE": "'$INTERFACE'", + "NETMASK": "'$NETMASK'", + "NAT": "'$NAT'", + "TIME": "'$TIME'", + "DATE": "'$DATE'" + }' + if [ "$i" -lt "$IPN" ]; then echo ',' else echo fi ((i++)) - done < <(ls $HESTIA/data/ips/) + done echo '}' } @@ -56,45 +75,50 @@ json_list() { shell_list() { echo "IP MASK NAT STATUS WEB DATE" echo "-- ---- --- ------ --- ----" - while read IP; do + echo "$IPS" | while read IP; do source_conf "$HESTIA/data/ips/$IP" if [ -z "$NAT" ]; then NAT='no' fi echo "$IP $NETMASK $NAT $STATUS $U_WEB_DOMAINS $DATE" - done < <(ls $HESTIA/data/ips/) + done } # PLAIN list function plain_list() { - while read IP; do + echo "$IPS" | while read IP; do source_conf "$HESTIA/data/ips/$IP" echo -ne "$IP\t$OWNER\t$STATUS\t$NAME\t$U_SYS_USERS\t$U_WEB_DOMAINS\t" echo -e "$INTERFACE\t$NETMASK\t$NAT\t$TIME\t$DATE" - done < <(ls $HESTIA/data/ips/) + done } # CSV list function csv_list() { echo -n "IP,OWNER,STATUS,NAME,U_SYS_USERS,U_WEB_DOMAINS,INTERFACE" echo "NETMASK,NAT,TIME,DATE" - while read IP; do + echo "$IPS" | while read IP; do source_conf "$HESTIA/data/ips/$IP" echo -n "$IP,$OWNER,$STATUS,$NAME,\"$U_SYS_USERS\",$U_WEB_DOMAINS," echo "$INTERFACE, $NETMASK,$NAT,$TIME,$DATE" - done < <(ls $HESTIA/data/ips/) + done } #----------------------------------------------------------# # Action # #----------------------------------------------------------# +# Read IPs from IP configuration directory +IPS="$(only_ip_list)" # IP Listing +IPN=$(echo "$IPS" | wc -l) # Number of IPs + # Listing data case $format in json) json_list ;; plain) plain_list ;; csv) csv_list ;; shell) shell_list | column -t ;; + list) echo "$IPS" ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-sys-network b/bin/v-list-sys-network new file mode 100755 index 0000000000..c01c004ee7 --- /dev/null +++ b/bin/v-list-sys-network @@ -0,0 +1,81 @@ +#!/bin/bash +# info: list system network +# options: [FORMAT] [FILTER] +# +# example: v-list-sys-network list 6 +# +# This function for obtaining the list of system ip addresses. + +#----------------------------------------------------------# +# Variables & Functions # +#----------------------------------------------------------# + +function list_network() { + ip_view=$1 + ip_type=$2 + ip_filter="" + ii=1 + [ -z "$ip_view" ] && ip_view="shell" + [ -n "$ip_type" ] && [ $ip_type -ne 4 -a $ip_type -ne 6 ] && ip_type="" + [ -n "$ip_type" ] && ip_filter=" -$ip_type" + ip_raw_string="$(/sbin/ip$ip_filter addr show scope global | sed -ne '/^[0-9]*\:/{h;d};G;s/^\(.*\)\n[0-9]*:\(.*\)/\1 \2/;/inet/s/[ \t]*\(inet[6]*\) \([0-9A-Fa-f.:]*\)\/\([0-9]*\).* \([0-9a-z@]*\):[ \t]<.*>.*/\2 \/\3 \4 \1/;s/inet$/4/p;s/inet6$/6/p')" + ip_count=$(echo "$ip_raw_string" | wc -l) + case $ip_view in + shell) + printf '%-25s%-5s%-10s%-4s\n' 'IP address' 'Mask' 'Interface' 'IPV' + echo "------------------------ ---- --------- ---" + ;; + csv) + echo 'IP,MASK,INTERFACE,IPV' + ;; + json) + echo '{' + ;; + esac + echo "$ip_raw_string" | while read ip_addr ip_mask ip_iface ip_family; do + case $ip_view in + list) + echo "$ip_addr" + ;; + plain) + echo "$ip_addr $ip_mask $ip_iface $ip_family" + ;; + shell) + printf '%-25s%-5s%-10s%-4s\n' $ip_addr $ip_mask $ip_iface $ip_family + ;; + csv) + echo "$ip_addr,$ip_mask,$ip_iface,$ip_family" + ;; + json) + echo -n ' "'$ip_addr'": { + "MASK": "'$ip_mask'", + "INTERFACE": "'$ip_iface'", + "IPV": "'$ip_family'" + }' + if [ "$ii" -lt "$ip_count" ]; then + echo ',' + else + echo + fi + ;; + esac + ((ii++)) + done + case $ip_view in + json) + echo '}' + ;; + esac +} + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +list_network "$1" "$2" + +#----------------------------------------------------------# +# Hestia # +#----------------------------------------------------------# + +exit diff --git a/bin/v-list-user b/bin/v-list-user index f02aa328ce..493d44d270 100755 --- a/bin/v-list-user +++ b/bin/v-list-user @@ -163,6 +163,11 @@ csv_list() { echo "$U_BACKUPS,$LANGUAGE,$THEME,$NOTIFICATIONS,$TIME,$DATE" } +# USER ONLY list function +only_user_list() { + echo "$USER" +} + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# @@ -188,6 +193,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list ;; + list) only_user_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-user-ips b/bin/v-list-user-ips index aa0087d6a6..3392dab3bc 100755 --- a/bin/v-list-user-ips +++ b/bin/v-list-user-ips @@ -1,8 +1,8 @@ #!/bin/bash # info: list user IPs -# options: USER [FORMAT] +# options: USER [FORMAT] [FILTER] # -# example: v-list-user-ips admin +# example: v-list-user-ips admin 6 # # This function for obtaining the list of available IP addresses. @@ -11,70 +11,115 @@ #----------------------------------------------------------# # Argument definition -user="$1" -format="${2-shell}" +user=$1 +format=$2 +filter=$3 +filter_match="" + +[ -z "$format" ] && format="shell" # Includes # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" +# Read and filter IP config file +read_ip_config() { + VERSION="" + source_conf "${HESTIA}/data/ips/$1" + if [ -z "$VERSION" ]; then + VERSION=4 + fi + if [ -n "$filter" ]; then + if [ "$filter" = "$VERSION" ]; then + filter_match="y" + else + filter_match="" + fi + else + filter_match="y" + fi +} + # JSON list function json_list() { echo '{' - ip_count="$(echo "$ips" | wc -l)" + ip_count=$(echo "$ips" | wc -l) i=1 for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" - echo -n ' "'$IP'": { - "OWNER": "'$OWNER'", - "STATUS": "'$STATUS'", - "NAME": "'$NAME'", - "NAT": "'$NAT'" - }' - if [ "$i" -lt "$ip_count" ]; then - echo ',' - else - echo + read_ip_config "$IP" + if [ -n "$filter_match" ]; then + if [ "$i" -ne 1 ]; then + echo ',' + fi + echo -n ' "'$IP'": { + "OWNER": "'$OWNER'", + "STATUS": "'$STATUS'", + "NAME": "'$NAME'", + "NAT": "'$NAT'", + "VERSION": "'$VERSION'" + }' + ((i++)) fi - ((i++)) done - echo '}' + if [ "$i" -gt 1 ]; then + echo -e '\n}' + else + echo '}' + fi } # SHELL list function shell_list() { - echo "IP NAT OWNER STATUS NAME" - echo "-- --- ----- ------ ---" + echo "IP NAT OWNER STATUS NAME VER" + echo "-- --- ----- ------ ---- ---" for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" + read_ip_config "$IP" if [ -z "$NAT" ]; then NAT='no' fi if [ -z "$NAME" ]; then NAME='no' fi - echo "$IP $NAT $OWNER $STATUS $NAME" + if [ -n "$filter_match" ]; then + echo "$IP $NAT $OWNER $STATUS $NAME $VERSION" + fi done } # PLAIN list function plain_list() { for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" - echo -e "$IP\t$OWNER\t$STATUS\t$NAME\t$NAT" + VERSION="" + read_ip_config "$IP" + if [ -n "$filter_match" ]; then + echo -e "$IP\t$OWNER\t$STATUS\t$NAME\t$NAT\t$VERSION" + fi done } # CSV list function csv_list() { - echo "IP,OWNER,STATUS,NAME,NAT" + echo "IP,OWNER,STATUS,NAME,NAT,VERSION" for IP in $ips; do - source_conf "$HESTIA/data/ips/$IP" - echo "$IP,$OWNER,$STATUS,$NAME,$NAT" + read_ip_config "$IP" + if [ -n "$filter_match" ]; then + echo "$IP,$OWNER,$STATUS,$NAME,$NAT,$VERSION" + fi + done +} + +# ONLY IP list function +plain_list() { + for IP in $ips; do + VERSION="" + read_ip_config "$IP" + if [ -n "$filter_match" ]; then + echo "$IP" + fi done } @@ -82,7 +127,7 @@ csv_list() { # Verifications # #----------------------------------------------------------# -check_args '1' "$#" 'USER [FORMAT]' +check_args '1' "$#" 'USER [FORMAT] [FILTER]' is_format_valid 'user' is_object_valid 'user' 'USER' "$user" @@ -92,13 +137,10 @@ is_object_valid 'user' 'USER' "$user" # Defining fileds to select owner="$ROOT_USER" -owner_ips="$(grep -A 1 -H "OWNER='$owner'" $HESTIA/data/ips/*)" -owner_ips="$(echo "$owner_ips" | grep "STATUS='shared'")" -owner_ips="$(echo "$owner_ips" | cut -f 7 -d / | cut -f 1 -d -)" -user_ips="$(grep -H "OWNER='$user'" $HESTIA/data/ips/*)" -user_ips="$(echo "$user_ips" | cut -f 7 -d / | cut -f 1 -d :)" +owner_ips="$(grep -A 1 -H "OWNER='$owner'" $HESTIA/data/ips/* | grep "STATUS='shared'" | sed 's/.*\/\([^\/*]\)/\1/;s/-STATUS.*//')" +user_ips="$(grep -l "OWNER='$user'" $HESTIA/data/ips/* | sed 's/.*\/\([^\/*]\)/\1/')" ips="$(echo -e "$user_ips\n$owner_ips" | sort -u | sed "/^$/d")" -fields='$IP $OWNER $STATUS $NAME $NAT' +fields='$IP $OWNER $STATUS $NAME $NAT $VERSION' # Listing data case $format in @@ -106,6 +148,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list | column -t ;; + list) only_ip_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-web-domain b/bin/v-list-web-domain index dbcc42e2af..990c0cbf1f 100755 --- a/bin/v-list-web-domain +++ b/bin/v-list-web-domain @@ -19,7 +19,7 @@ format=${3-shell} # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -140,6 +140,11 @@ csv_list() { echo "$DATE" } +# ONLY DOMAIN list function +only_domain_list() { + echo "$DOMAIN" +} + #----------------------------------------------------------# # Verifications # #----------------------------------------------------------# @@ -168,6 +173,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list ;; + list) only_domain_list ;; esac #----------------------------------------------------------# diff --git a/bin/v-list-web-domains b/bin/v-list-web-domains index 0cb683eefd..e74ca1eed9 100755 --- a/bin/v-list-web-domains +++ b/bin/v-list-web-domains @@ -18,7 +18,7 @@ format=${2-shell} # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -26,7 +26,7 @@ source_conf "$HESTIA/conf/hestia.conf" json_list() { IFS=$'\n' i=1 - objects=$(grep DOMAIN $USER_DATA/web.conf | wc -l) + objects=$(grep DOMAIN "$USER_DATA/web.conf" | wc -l) echo "{" while read str; do parse_object_kv_list "$str" @@ -65,7 +65,7 @@ json_list() { echo fi ((i++)) - done < <(cat $USER_DATA/web.conf) + done < <(cat "$USER_DATA/web.conf") echo '}' } @@ -77,7 +77,7 @@ shell_list() { while read str; do parse_object_kv_list "$str" echo "$DOMAIN $IP $TPL $SSL $U_DISK $U_BANDWIDTH $SUSPENDED $DATE" - done < <(cat $USER_DATA/web.conf) + done < <(cat "$USER_DATA/web.conf") } # PLAIN list function @@ -95,7 +95,7 @@ plain_list() { echo -ne "$ALIAS\t$STATS\t$STATS_USER\t$SSL\t$SSL_HOME\t$LETSENCRYPT\t" echo -ne "$FTP_USER\t$FTP_PATH\t$AUTH_USER\t$BACKEND\t$PROXY\t" echo -e "$PROXY_EXT\t$SUSPENDED\t$TIME\t$DATE" - done < <(cat $USER_DATA/web.conf) + done < <(cat "$USER_DATA/web.conf") } # CSV list function @@ -116,7 +116,22 @@ csv_list() { echo -n "\"$ALIAS\",$STATS,\"$STATS_USER\",$SSL,$SSL_HOME,$LETSENCRYPT," echo -n "\"$FTP_USER\",\"$FTP_PATH\",\"$AUTH_USER\",$BACKEND,$PROXY," echo "\"$PROXY_EXT\",$SUSPENDED,$TIME,$DATE" - done < <(cat $USER_DATA/web.conf) + done < <(cat "$USER_DATA/web.conf") +} + +# ONLY DOMAIN list function +only_domain_list() { + IFS=$'\n' + while read str; do + parse_object_kv_list "$str" + # Set correct document root path + if [ ! -z "$CUSTOM_DOCROOT" ]; then + DOCROOT="$CUSTOM_DOCROOT" + else + DOCROOT="$HOMEDIR/$user/web/$DOMAIN/public_html/" + fi + echo "$DOMAIN" + done < <(cat "$USER_DATA/web.conf") } #----------------------------------------------------------# @@ -137,6 +152,7 @@ case $format in plain) plain_list ;; csv) csv_list ;; shell) shell_list ;; + list) only_domain_list ;; esac unset docroot diff --git a/bin/v-suspend-firewall-ipv6-rule b/bin/v-suspend-firewall-ipv6-rule new file mode 100755 index 0000000000..102407f103 --- /dev/null +++ b/bin/v-suspend-firewall-ipv6-rule @@ -0,0 +1,49 @@ +#!/bin/bash +# info: suspend firewall rule +# options: RULE +# +# The function suspends a certain firewall rule. + + +#----------------------------------------------------------# +# Variable&Function # +#----------------------------------------------------------# + +# Argument definition +rule=$1 + +# Includes +source $HESTIA/func/main.sh +source $HESTIA/conf/HESTIA.conf + + +#----------------------------------------------------------# +# Verifications # +#----------------------------------------------------------# + +check_args '1' "$#" 'RULE' +is_format_valid 'rule' +is_system_enabled "$FIREWALL_SYSTEM" 'FIREWALL_SYSTEM' +is_object_valid '../../data/firewallv6/rules' 'RULE' "$rule" +is_object_unsuspended '../../data/firewallv6/rules' 'RULE' "$rule" + + +#----------------------------------------------------------# +# Action # +#----------------------------------------------------------# + +# Suspending rule +update_object_value ../../data/firewallv6/rules RULE $rule '$SUSPENDED' yes + +# Updating system firewall +$BIN/v-update-firewall-ipv6 + + +#----------------------------------------------------------# +# HESTIA # +#----------------------------------------------------------# + +# Logging +log_event "$OK" "$ARGUMENTS" + +exit \ No newline at end of file diff --git a/bin/v-update-firewall b/bin/v-update-firewall index 917124c429..40dd8cfe89 100755 --- a/bin/v-update-firewall +++ b/bin/v-update-firewall @@ -12,6 +12,7 @@ # Defining absolute path for iptables and modprobe iptables="/sbin/iptables" +ip6tables="/sbin/ip6tables" modprobe="/sbin/modprobe" sysctl="/sbin/sysctl" @@ -91,7 +92,7 @@ if [ "$conntrack" != 'no' ] || grep --quiet container=lxc /proc/1/environ; then echo "$str" >> "$tmp" fi -ips="$(ls $HESTIA/data/ips)" +ips="$("$BIN/v-list-sys-ips" list)" # Handling local traffic for ip in $ips; do echo "$iptables -A INPUT -s $ip -j ACCEPT" >> "$tmp" diff --git a/bin/v-update-sys-ip b/bin/v-update-sys-ip index aebe29aeda..650d2104ed 100755 --- a/bin/v-update-sys-ip +++ b/bin/v-update-sys-ip @@ -17,11 +17,11 @@ # shellcheck source=/etc/hestiacp/hestia.conf source /etc/hestiacp/hestia.conf # shellcheck source=/usr/local/hestia/func/main.sh -source $HESTIA/func/main.sh +source "$HESTIA/func/main.sh" # shellcheck source=/usr/local/hestia/func/ip.sh -source $HESTIA/func/ip.sh +source "$HESTIA/func/ip.sh" # shellcheck source=/usr/local/hestia/func/domain.sh -source $HESTIA/func/domain.sh +source "$HESTIA/func/domain.sh" # load config file source_conf "$HESTIA/conf/hestia.conf" @@ -37,36 +37,31 @@ check_hestia_demo_mode #----------------------------------------------------------# # Listing system IP addresses -nics="$(ip -d -j link show | jq -r '.[] | if .link_type == "loopback" then empty else .ifname end')" - -for nic in $nics; do - nic_type="$(ip -d -j link show "$nic" | jq -r '.[].linkinfo.info_kind')" - if [ "$nic_type" = "bridge" ]; then - break - fi - nic_ipv4s="$(ip -4 -d -j addr show "$nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end')" - if [ -z "$ips" ]; then - ips="$nic_ipv4s" - else - ips="$ips $nic_ipv4s" - fi -done - -v_ips="$(ls $HESTIA/data/ips/)" -ip_num="$(echo "$ips" | wc -w)" -v_ip_num="$(echo "$v_ips" | wc -w)" +ips="$($BIN/v-list-sys-network list 4)" +ipv6s="$($BIN/v-list-sys-network list 6)" +v_ips="$($BIN/v-list-sys-ips list)" +ip_num=$(echo "$ips" | wc -l) +ipv6_num=$(echo "$ipv6s" | wc -l) +v_ip_num=$(echo "$v_ips" | wc -l) # Checking primary IP change -if [ "$ip_num" -eq "1" ] && [ "$v_ip_num" -eq "1" ]; then - if [ -n "$v_ips" ] && [ "$ips" != "$v_ips" ]; then +if [ "$ip_num" -eq 1 -a "$ipv6_num" -eq 0 -a "$v_ip_num" -eq 1 ]; then + if [ "$ips" != "$v_ips" ]; then new_ip="$ips" old_ip="$v_ips" fi fi +if [ "$ipv6_num" -eq 1 -a "$ip_num" -eq 0 -a "$v_ip_num" -eq 1 ]; then + if [ "$ipv6s" != "$v_ips" ]; then + new_ip="$ipv6s" + old_ip="$v_ips" + fi +fi + # Updating configs if [ -n "$old_ip" ]; then - mv $HESTIA/data/ips/$old_ip $HESTIA/data/ips/$new_ip + mv "$HESTIA/data/ips/$old_ip" "$HESTIA/data/ips/$new_ip" # Generating timestamp new_timestamp @@ -83,25 +78,25 @@ if [ -n "$old_ip" ]; then # Updating PROXY if [ -n "$PROXY_SYSTEM" ]; then - cd /etc/$PROXY_SYSTEM/conf.d + cd "/etc/$PROXY_SYSTEM/conf.d" if [ -e "$old_ip.conf" ]; then - mv $old_ip.conf $new_ip.conf - sed -i "s/$old_ip/$new_ip/g" $new_ip.conf + mv "$old_ip.conf" "$new_ip.conf" + sed -i "s/$old_ip/$new_ip/g" "$new_ip.conf" fi fi # Updating WEB if [ -n "$WEB_SYSTEM" ]; then - cd /etc/$WEB_SYSTEM/conf.d + cd "/etc/$WEB_SYSTEM/conf.d" if [ -e "$old_ip.conf" ]; then - mv $old_ip.conf $new_ip.conf - sed -i "s/$old_ip/$new_ip/g" $new_ip.conf + mv "$old_ip.conf" "$new_ip.conf" + sed -i "s/$old_ip/$new_ip/g" "$new_ip.conf" fi for user in $("$BIN/v-list-users" list); do - sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/users/$user/web.conf - $BIN/v-rebuild-web-domains "$user" no + sed -i "s/$old_ip/$new_ip/g" "$HESTIA/data/users/$user/web.conf" + "$BIN/v-rebuild-web-domains" "$user" no done if [ -e "/etc/apache2/mods-available/remoteip.conf" ]; then @@ -112,26 +107,26 @@ if [ -n "$old_ip" ]; then sed -i "s/$old_ip/$new_ip/g" /etc/apache2/mods-enabled/rpaf.conf fi - $BIN/v-restart-proxy - $BIN/v-restart-web + "$BIN/v-restart-proxy" + "$BIN/v-restart-web" fi # Updating MAIL if [ -n "$IMAP_SYSTEM" ]; then for user in $("$BIN/v-list-users" list); do - $BIN/v-rebuild-mail-domains "$user" no + "$BIN/v-rebuild-mail-domains" "$user" no done - $BIN/v-restart-mail + "$BIN/v-restart-mail" fi # Updating DNS if [ -n "$DNS_SYSTEM" ]; then for user in $("$BIN/v-list-users" list); do - sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/users/$user/dns.conf - sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/users/$user/dns/*.conf - $BIN/v-rebuild-dns-domains "$user" no + sed -i "s/$old_ip/$new_ip/g" "$HESTIA/data/users/$user/dns.conf" + sed -i "s/$old_ip/$new_ip/g" "$HESTIA/data/users/$user/dns/"*.conf + "$BIN/v-rebuild-dns-domains" "$user" no done - $BIN/v-restart-dns + "$BIN/v-restart-dns" fi # Updating FTP @@ -139,41 +134,37 @@ if [ -n "$old_ip" ]; then ftp_conf="$(find /etc/ -maxdepth 2 -name $FTP_SYSTEM.conf)" if [ -n "$ftp_conf" ]; then sed -i "s/$old_ip/$new_ip/g" "$ftp_conf" - $BIN/v-restart-ftp + "$BIN/v-restart-ftp" fi fi # Updating firewall if [ -n "$FIREWALL_SYSTEM" ]; then - sed -i "s/$old_ip/$new_ip/g" $HESTIA/data/firewall/*.conf - $BIN/v-update-firewall + sed -i "s/$old_ip/$new_ip/g" "$HESTIA/data/firewall/"*.conf + "$BIN/v-update-firewall" fi fi # Adding system IP -for ip in $ips; do - check_ip="$(ip addr list | grep -w "$ip")" - if [ ! -e "$HESTIA/data/ips/$ip" ] && [ -n "$check_ip" ]; then - interface="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[] | if .addr_info[].local == $IP then .ifname else empty end')" - prefixlen="$(ip -d -j addr show | jq --arg IP "$ip" -r '.[].addr_info[] | if .local == $IP then .prefixlen else empty end')" - netmask="$(convert_cidr "$prefixlen")" - $BIN/v-add-sys-ip "$ip" "$netmask" "$interface" - elif [ -e "/etc/nginx/conf.d/$ip.conf" ]; then - process_http2_directive "/etc/nginx/conf.d/$ip.conf" +"$BIN/v-list-sys-network" plain | while read listed_ip listed_mask listed_iface listed_family; do + if [ ! -e "$HESTIA/data/ips/$listed_ip" ]; then + "$BIN/v-add-sys-ip" "$listed_ip" "$listed_mask" "$listed_iface" + elif [ -e "/etc/nginx/conf.d/$listed_ip.conf" ]; then + process_http2_directive "/etc/nginx/conf.d/$listed_ip.conf" fi done # Updating NAT pub_ipv4="$(curl -fsLm5 --retry 2 --ipv4 https://ip.hestiacp.com/)" -if [ ! -e "$HESTIA/data/ips/$pub_ipv4" ]; then - if [ -z "$(grep -R "$pub_ipv4" $HESTIA/data/ips/)" ]; then - ip="$(ls -t $HESTIA/data/ips/ | head -n1)" - $BIN/v-change-sys-ip-nat "$ip" "$pub_ipv4" +if [ ! -e "$HESTIA/data/ips/$pub_ip" ]; then + if [ -z "$(grep -R "$pub_ip" "$HESTIA/data/ips/")" ]; then + ip="$(ls -t "$HESTIA/data/ips/" | head -n1)" + "$BIN/v-change-sys-ip-nat" "$ip" "$pub_ipv4" fi fi # Updating IP usage counters -$BIN/v-update-sys-ip-counters +"$BIN/v-update-sys-ip-counters" #----------------------------------------------------------# # Hestia # diff --git a/docs/docs/reference/cli.md b/docs/docs/reference/cli.md index 84233b3373..976700d3ec 100644 --- a/docs/docs/reference/cli.md +++ b/docs/docs/reference/cli.md @@ -169,7 +169,7 @@ This function creates an temporary database user mysql_sso_db_XXXXXXXX and a ran The user has an limited validity and only granted access to the specific database Returns json to be read SSO Script -## v-add-dns-domain +## v-add-dns-domain-ipv46 [Source](https://github.com/hestiacp/hestiacp/blob/release/bin/v-add-dns-domain) @@ -180,7 +180,25 @@ add dns domain **Examples**: ```bash -v-add-dns-domain admin example.com ns1.example.com ns2.example.com '' '' '' '' '' '' yes +v-add-dns-domain admin example.com 192.168.0.1 ns1.example.com ns2.example.com '' '' '' '' '' '' yes +``` + +!!! WRAPPER SCRIPT FOR COMPATIBILITY PURPOSES WITH OLD IPV4 SCRIPTS AND EXTERNAL CALLS !!! +This function adds DNS zone with records defined in the template. If the exp +argument isn't stated, the expiration date value will be set to next year. +The soa argument is responsible for the relevant record. By default the first +user's NS server is used. TTL is set as common for the zone and for all of +its records with a default value of 14400 seconds. + +## v-add-dns-domain-ipv46 + +add dns domain + +**Options**: `USER` `DOMAIN` `IPV4` `[IPV6]` `[NS1]` `[NS2]` `[NS3]` `[NS4]` `[NS5]` `[NS6]` `[NS7]` `[NS8]` `[RESTART]` `[DNSSEC]` +**Examples**: + +```bash +v-add-dns-domain-ipv46 admin example.com 192.168.0.1 1111:2222:3333:111 ns1.example.com ns2.example.com '' '' '' '' '' '' yes ``` This function adds DNS zone with records defined in the template. If the exp @@ -195,12 +213,14 @@ its records with a default value of 14400 seconds. add dns domain or dns record after web domain alias -**Options**: `USER` `ALIAS` `IP` `[RESTART]` +**Options**: `USER` `ALIAS` `IPV4` `[IPV6]` `[RESTART]` **Examples**: ```bash -v-add-dns-on-web-alias admin www.example.com 8.8.8.8 +v-add-dns-on-web-alias admin www.example.com 8.8.8.8 1234:2123:1111:2 yes +v-add-dns-on-web-alias admin www.example.com '' 1234:2123:1111:2 no +v-add-dns-on-web-alias admin www.example.com 8.8.8.8 yes # DEPRICATED but possible call option ``` This function adds dns domain or dns record based on web domain alias. @@ -738,15 +758,20 @@ This function enables the system firewall. add system IP address -**Options**: `IP` `NETMASK` `[INTERFACE]` `[USER]` `[IP_STATUS]` `[IP_NAME]` `[NAT_IP]` +**Options**: `IP` `[NETMASK]` `[INTERFACE]` `[USER]` `[IP_STATUS]` `[IP_NAME]` `[NAT_IP]` **Examples**: ```bash -v-add-sys-ip 203.0.113.1 255.255.255.0 +v-add-sys-ip 216.239.32.21 255.255.255.0 +v-add-sys-ip 216.239.32.21 /24 +v-add-sys-ip 216.239.32.21/24 +v-add-sys-ip 1234:55:66::1 /64 +v-add-sys-ip 1234:55:66::1/64 ``` -This function adds IP address into a system. It also creates rc scripts. You +This function adds IP address into a system. It also creates rc scripts. Both +IPV4 and IPV6 addresses with CIDR, prefix length or netmask are allowed. You can specify IP name which will be used as root domain for temporary aliases. For example, if you set a1.myhosting.com as name, each new domain created on this IP will automatically receive alias $domain.a1.myhosting.com. Of course @@ -995,6 +1020,7 @@ add web domain v-add-web-domain admin wonderland.com 192.18.22.43 yes www.wonderland.com ``` +!!! WRAPPER SCRIPT FOR COMPATIBILITY PURPOSES WITH OLD IPV4 SCRIPTS AND EXTERNAL CALLS !!! This function adds virtual host to a server. In cases when ip is undefined in the script, "default" template will be used. The alias of type will be automatically assigned to the domain unless @@ -1086,6 +1112,25 @@ v-add-web-domain-httpauth admin acme.com user02 super_pass This function is used for securing web domain with http auth +## v-add-web-domain-ipv46 + +add web domain + +**Options**: `USER` `DOMAIN` `[IPV4]` `[IPV6]` `[RESTART]` `[ALIASES]` `[PROXY_EXTENSIONS]` + +**Examples**: + +```bash +v-add-web-domain-ipv46 admin wonderland.com 192.18.22.43 1111:222:333:111:2 yes www.wonderland.com +``` + +This function adds virtual host to a server. In cases when ip is +undefined in the script, "default" template will be used. The alias of + type will be automatically assigned to the domain unless +"none" is transmited as argument. If ip have associated dns name, this +domain will also get the alias domain-tpl.$ipname. An alias with the ip +name is useful during the site testing while dns isn't moved to server yet. + ## v-add-web-domain-proxy [Source](https://github.com/hestiacp/hestiacp/blob/release/bin/v-add-web-domain-proxy) @@ -1378,12 +1423,17 @@ serial number will be refreshed automatically during update. change dns domain ip address -**Options**: `USER` `DOMAIN` `IP` `[RESTART]` +**Options**: `USER` `DOMAIN` `IP` `[RESTART]` `[IP FORMAT]` **Examples**: ```bash v-change-dns-domain-ip admin domain.com 123.212.111.222 +v-change-dns-domain-ip admin domain.com 1234:55:66::1 +v-change-dns-domain-ip admin domain.com 123.212.111.222 no 4 +v-change-dns-domain-ip admin domain.com 1234:55:66::1 yes 6 +v-change-dns-domain-ip admin domain.com '' '' 4 +v-change-dns-domain-ip admin domain.com '' no 6 ``` This function for changing the main ip of DNS zone. @@ -1769,6 +1819,7 @@ change IP name ```bash v-change-sys-ip-name 203.0.113.1 acme.com +v-change-sys-ip-name 1111:2222:3333::1 acme.com ``` This function for changing dns domain associated with IP. @@ -1785,6 +1836,7 @@ change NAT IP address ```bash v-change-sys-ip-nat 10.0.0.1 203.0.113.1 +v-change-sys-ip-nat 1111:2222:3333::1 '' ``` This function for changing NAT IP associated with IP. @@ -1801,6 +1853,7 @@ change IP owner ```bash v-change-sys-ip-owner 203.0.113.1 admin +v-change-sys-ip-owner 1111:2222:3333::1 admin ``` This function of changing IP address ownership. @@ -1817,6 +1870,7 @@ change IP status ```bash v-change-sys-ip-status 203.0.113.1 yourstatus +v-change-sys-ip-status 1111:2222:3333::1 yourstatus ``` This function of changing an IP address's status. @@ -2273,15 +2327,20 @@ This function is used for changing http auth user password change web domain ip -**Options**: `USER` `DOMAIN` `DOMAIN` `[RESTART]` +**Options**: `USER` `DOMAIN` `IP` `[RESTART]` `[IP FORMAT]` **Examples**: ```bash -v-change-web-domain-ip admin example.com 167.86.105.230 yes +v-change-web-domain-ip admin domain.com 123.212.111.222 +v-change-web-domain-ip admin domain.com 1234:55:66::1 +v-change-web-domain-ip admin domain.com 123.212.111.222 no 4 +v-change-web-domain-ip admin domain.com 1234:55:66::1 yes 6 +v-change-web-domain-ip admin domain.com '' '' 4 +v-change-web-domain-ip admin domain.com '' no 6 ``` -This function is used for changing domain ip +This function is used for changing web domain ip ## v-change-web-domain-name @@ -3270,9 +3329,11 @@ delete system IP ```bash v-delete-sys-ip 203.0.113.1 +v-delete-sys-ip 1234:5678:abcd:ef90::1 ``` -This function for deleting a system IP. It does not allow to delete first IP +This function for deleting a system IP. Both kind of IP addresses, IPV4 +and IPV6 are allowed. It does not allow to delete system IP, first IP on interface and do not allow to delete IP which is used by a web domain. ## v-delete-sys-mail-queue @@ -4816,6 +4877,25 @@ list mysql config parameters This function for obtaining the list of mysql config parameters. +## v-list-sys-network + +list system network + +**Options**: `[FORMAT]` `[FILTER]` + +**Examples**: + +```bash +v-list-sys-network +v-list-sys-network list 6 +v-list-sys-network plain 4 +v-list-sys-network shell +v-list-sys-network csv +v-list-sys-network json +``` + +This function for obtaining the list of system ip addresses. + ## v-list-sys-network-status [Source](https://github.com/hestiacp/hestiacp/blob/release/bin/v-list-sys-network-status) @@ -5065,12 +5145,13 @@ This function for obtaining the list of available user backups. list user IPs -**Options**: `USER` `[FORMAT]` +**Options**: `USER` `[FORMAT]` `[FILTER]` **Examples**: ```bash v-list-user-ips admin +v-list-user-ips admin 6 ``` This function for obtaining the list of available IP addresses. diff --git a/func/domain.sh b/func/domain.sh index 7eec918b49..525b8c3c63 100644 --- a/func/domain.sh +++ b/func/domain.sh @@ -6,6 +6,44 @@ # # #===========================================================================# +#----------------------------------------------------------# +# COMMON FUNCTIONS # +#----------------------------------------------------------# + +# Prepare IPV4 and IPV6 addresses for using in templates +prepare_ips_for_template() { + if [ -z "$local_ip" ]; then + i4mark="" + ipv4="" + web_ipv4="" + web_ip="[$local_ipv6]" + proxy_ipv4="" + proxy_ip="[$local_ipv6]" + legacy_ip="[$local_ipv6]" + else + i4mark="\1" + ipv4="$local_ip" + web_ipv4="$local_ip" + web_ip="$local_ip" + proxy_ipv4="$local_ip" + proxy_ip="$local_ip" + legacy_ip="$local_ip" + fi + if [ -z "$local_ipv6" ]; then + i6mark="" + web_ipv6="" + web_ip="$local_ip" + proxy_ipv6="" + proxy_ip="$local_ip" + else + i6mark="\1" + web_ipv6="[$local_ipv6]" + web_ip="[$local_ipv6]" + proxy_ipv6="[$local_ipv6]" + proxy_ip="[$local_ipv6]" + fi +} + #----------------------------------------------------------# # WEB # #----------------------------------------------------------# @@ -255,12 +293,22 @@ add_web_config() { fi fi + prepare_ips_for_template # prepare IPV4 and IPV6 variables for template substitution + # Note: Removing or renaming template variables will lead to broken custom templates. # -If possible custom templates should be automatically upgraded to use the new format # -Alternatively a depreciation period with proper notifications should be considered cat "${WEBTPL_LOCATION}/$2" \ - | sed -e "s|%ip%|$local_ip|g" \ + | sed -e "s|%%|$i4mark|g" \ + -e "s|%%|$i6mark|g" \ + -e "s|%web_ipv4%|$web_ipv4|g" \ + -e "s|%web_ipv6%|$web_ipv6|g" \ + -e "s|%web_ip%|$web_ip|g" \ + -e "s|%proxy_ipv4%|$proxy_ipv4|g" \ + -e "s|%proxy_ipv6%|$proxy_ipv6|g" \ + -e "s|%proxy_ip%|$proxy_ip|g" \ + -e "s|%ip%|$legacy_ip|g" \ -e "s|%domain%|$domain|g" \ -e "s|%domain_idn%|$domain_idn|g" \ -e "s|%alias%|${aliases//,/ }|g" \ @@ -293,7 +341,6 @@ add_web_config() { chown root:$user $conf chmod 640 $conf - if [[ "$2" =~ stpl$ ]]; then rm -f /etc/$1/conf.d/domains/$domain.ssl.conf ln -s $conf /etc/$1/conf.d/domains/$domain.ssl.conf @@ -368,13 +415,17 @@ get_web_config_lines() { # Replace web config replace_web_config() { - conf="$HOMEDIR/$user/conf/web/$domain/$1.conf" - if [[ "$2" =~ stpl$ ]]; then - conf="$HOMEDIR/$user/conf/web/$domain/$1.ssl.conf" - fi - - if [ -e "$conf" ]; then - sed -i "s|$old|$new|g" $conf + if [ -z "$old" -o -z "$new" ]; then + prepare_web_domain_values # if one of both parameters is empty, prepare values for the web domain + add_web_config "$@" # web configs must be new generated from the templates + else + conf="$HOMEDIR/$user/conf/web/$domain/$1.conf" + if [[ "$2" =~ stpl$ ]]; then + conf="$HOMEDIR/$user/conf/web/$domain/$1.ssl.conf" + fi + if [ -e "$conf" ]; then + sed -i "s|$old|$new|g" $conf # simple search and replace only possible, if both (old and new) parameters are defined + fi fi } @@ -466,6 +517,124 @@ is_web_domain_cert_valid() { # DNS # #----------------------------------------------------------# +# Get DNS values +get_dns_values() { + get_domain_values 'dns' + i_ns=1 + ns=$(get_user_value '$NS') + for nameserver in ${ns//,/ }; do + eval ns$i_ns=$nameserver + ((++i_ns)) + done +} + +# Create DNS domain config +create_dns_domain_config() { + # Reading template + template_data=$(cat "$DNSTPL/$template.tpl") + + # Deleting unused nameservers + if [ -z "$ns3" ]; then + template_data=$(echo "$template_data" | grep -v %ns3%) + fi + if [ -z "$ns4" ]; then + template_data=$(echo "$template_data" | grep -v %ns4%) + fi + if [ -z "$ns5" ]; then + template_data=$(echo "$template_data" | grep -v %ns5%) + fi + if [ -z "$ns6" ]; then + template_data=$(echo "$template_data" | grep -v %ns6%) + fi + if [ -z "$ns7" ]; then + template_data=$(echo "$template_data" | grep -v %ns7%) + fi + if [ -z "$ns8" ]; then + template_data=$(echo "$template_data" | grep -v %ns8%) + fi + if [ -z "$dnssec" ]; then + dnssec="no" + fi + + # Marker deinition for IPV4 and IPV6 configuration + if [ -z "$ip" ]; then + i4mark="" + else + i4mark="\1" + fi + if [ -z "$ipv6" ]; then + i6mark="" + else + i6mark="\1" + fi + + # Adding dns zone to the user config + echo "$template_data" \ + | sed -e "s|%%|$i4mark|g" \ + -e "s|%%|$i6mark|g" \ + -e "s|%ipv4%|$ip|g" \ + -e "s|%ipv6%|$ipv6|g" \ + -e "s|%ip%|$ip|g" \ + -e "s|%domain_idn%|$domain_idn|g" \ + -e "s|%domain%|$domain|g" \ + -e "s|%ns1%|$ns1|g" \ + -e "s|%ns2%|$ns2|g" \ + -e "s|%ns3%|$ns3|g" \ + -e "s|%ns4%|$ns4|g" \ + -e "s|%ns5%|$ns5|g" \ + -e "s|%ns6%|$ns6|g" \ + -e "s|%ns7%|$ns7|g" \ + -e "s|%ns8%|$ns8|g" \ + -e "s|%time%|$time|g" \ + -e "s|%date%|$date|g" \ + -e "/^[ \t]*$/d" > ${USER_DATA}/dns/${domain}.conf + + chmod 660 ${USER_DATA}/dns/${domain}.conf +} + +# Add DNS DKIM records +add_dns_dkim_records() { + if [ -n "$DNS_SYSTEM" ] && [ "$dkim" = 'yes' ]; then + check_dns_domain=$(is_object_valid 'dns' 'DOMAIN' "$domain") + if [ "$?" -eq 0 ]; then + p=$(cat $USER_DATA/mail/$domain.pub | grep -v ' KEY---' | tr -d '\n') + record='_domainkey' + policy="\"t=y; o=~;\"" + $BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$policy" '' '' 'no' '' 'yes' + + record='mail._domainkey' + selector="\"v=DKIM1\; k=rsa\; p=$p\"" + $BIN/v-add-dns-record "$user" "$domain" "$record" TXT "$selector" '' '' 'yes' '' 'yes' + fi + fi +} + +# Add DNS webmail records +add_dns_webmail_records() { + # Ensure DNS record exists if Hestia is hosting DNS zones + if [ -n "$DNS_SYSTEM" ]; then + dns_domain=$("$BIN/v-list-dns-domains" "$user" list | sed -ne "/$domain/p") + # shellcheck disable=SC1087 + webmail_records="$("$BIN/v-list-dns-records" "$user" "$domain" plain | sed -ne "/$WEBMAIL_ALIAS/s/^\([0-9]*\)[ \t]*$WEBMAIL_ALIAS[ \t]*.*/\1/gp")" + if [ "$dns_domain" = "$domain" ]; then + if [ "$WEBMAIL_ALIAS" != "mail" ]; then + #Prevent mail.domain.com to be cycled + if [ -n "$webmail_records" ]; then + echo "$webmail_records" | while read webmail_record; do + "$BIN/v-delete-dns-record" "$user" "$domain" "$webmail_record" "$restart" 'yes' + done + fi + if [ -n "$ip" ]; then + "$BIN/v-add-dns-record" "$user" "$domain" "$WEBMAIL_ALIAS" A "$ip" '' '' "$restart" '' 'yes' + fi + if [ -n "$ipv6" ]; then + "$BIN/v-add-dns-record" "$user" "$domain" "$WEBMAIL_ALIAS" AAAA "$ipv6" '' '' "$restart" '' 'yes' + fi + fi + fi + fi +} + # DNS template check is_dns_template_valid() { if [ ! -e "$DNSTPL/$1.tpl" ]; then @@ -827,12 +996,22 @@ add_webmail_config() { override_alias_idn="mail.$domain_idn" fi + prepare_ips_for_template # prepare IPV4 and IPV6 variables for template substitution + # Note: Removing or renaming template variables will lead to broken custom templates. # -If possible custom templates should be automatically upgraded to use the new format # -Alternatively a depreciation period with proper notifications should be considered cat $MAILTPL/$1/$2 \ - | sed -e "s|%ip%|$local_ip|g" \ + | sed -e "s|%%|$i4mark|g" \ + -e "s|%%|$i6mark|g" \ + -e "s|%web_ipv4%|$web_ipv4|g" \ + -e "s|%web_ipv6%|$web_ipv6|g" \ + -e "s|%web_ip%|$web_ip|g" \ + -e "s|%proxy_ipv4%|$proxy_ipv4|g" \ + -e "s|%proxy_ipv6%|$proxy_ipv6|g" \ + -e "s|%proxy_ip%|$proxy_ip|g" \ + -e "s|%ip%|$legacy_ip|g" \ -e "s|%domain%|$WEBMAIL_ALIAS.$domain|g" \ -e "s|%domain_idn%|$WEBMAIL_ALIAS.$domain_idn|g" \ -e "s|%root_domain%|$domain|g" \ diff --git a/func/firewall.sh b/func/firewall.sh index 9e0575b795..d04f5b07ad 100644 --- a/func/firewall.sh +++ b/func/firewall.sh @@ -7,7 +7,7 @@ #===========================================================================# heal_iptables_links() { - packages="iptables iptables-save iptables-restore" + packages="iptables iptables-save iptables-restore ip6tables ip6tables-save ip6tables-restore" for package in $packages; do if [ ! -e "/sbin/${package}" ]; then if which ${package}; then @@ -23,3 +23,12 @@ heal_iptables_links() { fi done } +get_iptables_bin() { + # get iptables binary + iptables_par="$1" # input parameter + fw_lockingopt="-w" # Option was introduced to iptables since version 1.4.20 to prevent multiple instances from running concurrently and causing irratic behavior + [ -z "$iptables_par" ] && iptables_par="iptables" # IPV4 version, if empty or not defined + [ "$iptables_par" = "iptables" -o "$iptables_par" = "ip6tables" ] && iptables_found_binary="$(which "$iptables_par")" # find iptables binary in system using which + [ -n "$iptables_found_binary" -a -n "$fw_lockingopt" ] && iptables_found_binary="$iptables_found_binary $fw_lockingopt" # add locking option as first call parameter to iptables, if not empty + echo "$iptables_found_binary" # output binary call string to stdout +} diff --git a/func/ip.sh b/func/ip.sh index f57820af0c..ad9eb124f6 100644 --- a/func/ip.sh +++ b/func/ip.sh @@ -6,33 +6,43 @@ # # #===========================================================================# -# Global definitions -REGEX_IPV4="^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}$" +# === Global definitions === +REGEX_IPV4="^((1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])\.){3}(1?[0-9][0-9]?|2[0-4][0-9]|25[0-5])$" + +# === IPV4 specific functions === # Check ip ownership is_ip_owner() { - owner=$(grep 'OWNER=' $HESTIA/data/ips/$ip | cut -f 2 -d \') + # ip address (ipv4/ipv6) as first parameter, otherwise $ip (ipv4) + ip_for_test="${1-$ip}" + owner=$(grep 'OWNER=' $HESTIA/data/ips/$ip_for_test | cut -f 2 -d \') if [ "$owner" != "$user" ]; then - check_result "$E_FORBIDEN" "$ip is not owned by $user" + check_result "$E_FORBIDEN" "$ip_for_test is not owned by $user" fi } # Check if ip address is free is_ip_free() { - if [ -e "$HESTIA/data/ips/$ip" ]; then - check_result "$E_EXISTS" "$ip is already exists" + ip_for_test="${1-$ip}" # ip address (ipv4/ipv6) as first parameter, otherwise $ip (ipv4) + if [ -e "$HESTIA/data/ips/$ip_for_test" ]; then + check_result "$E_EXISTS" "$ip_for_test is already exists" fi } # Check ip address specific value is_ip_key_empty() { key="$1" - string=$(cat $HESTIA/data/ips/$ip) - eval $string - eval value="$key" - if [ -n "$value" ] && [ "$value" != '0' ]; then - key="$(echo $key | sed -e "s/\$U_//")" - check_result "$E_EXISTS" "IP is in use / $key = $value" + ip_for_test="${2-$ip}" # ip address (ipv4/ipv6) as second parameter, otherwise $ip (ipv4) + if [ -n "$ip_for_test" ]; then + string=$(cat $HESTIA/data/ips/$ip_for_test) + eval $string + eval value="$key" + if [ -n "$value" ] && [ "$value" != '0' ]; then + key="$(echo $key | sed -e "s/\$U_//")" + check_result "$E_EXISTS" "IP is in use / $key = $value" + fi + else + check_result 1 "is_ip_key_empty(): IP address is empty!" fi } @@ -58,15 +68,20 @@ is_ip_rdns_valid() { update_ip_value() { key="$1" value="$2" - conf="$HESTIA/data/ips/$ip" - str=$(cat $conf) - eval $str - c_key=$(echo "${key//$/}") - eval old="${key}" - old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') - new=$(echo "$value" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') - sed -i "$str_number s/$c_key='${old//\*/\\*}'/$c_key='${new//\*/\\*}'/g" \ - $conf + ip_for_update="${3-$ip}" # ip address (ipv4/ipv6) as third parameter, otherwise $ip (ipv4) + if [ -n "$ip_for_update" ]; then + conf="$HESTIA/data/ips/$ip_for_update" + str=$(cat $conf) + eval $str + c_key=$(echo "${key//$/}") + eval old="${key}" + old=$(echo "$old" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + new=$(echo "$value" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/\//\\\//g') + sed -i "$str_number s/$c_key='${old//\*/\\*}'/$c_key='${new//\*/\\*}'/g" \ + $conf + else + check_result 1 "is_ip_key_empty(): IP address is empty!" + fi } # New method that is improved on a later date we need to check if we can improve it for other locations @@ -84,9 +99,15 @@ update_ip_value_new() { # Get ip name get_ip_alias() { - ip_name=$(grep "NAME=" $HESTIA/data/ips/$local_ip | cut -f 2 -d \') - if [ -n "$ip_name" ]; then - echo "${1//./-}.$ip_name" + # ip address (ipv4/ipv6) as second parameter, otherwise $local_ip (ipv4) + ip_for_test="${2-$local_ip}" + if [ -n "$ip_for_test" ]; then + ip_name=$(grep "NAME=" $HESTIA/data/ips/${ip_for_test} | cut -f 2 -d \') + if [ -n "$ip_name" ]; then + echo "${1//./-}.$ip_name" + fi + else + ip_name="" fi } @@ -103,7 +124,11 @@ increase_ip_value() { log_event "$E_PARSING" "$ARGUMENTS" exit "$E_PARSING" fi - new_web=$((current_web + 1)) + if (($current_web <= 0)); then + new_web=1 + else + new_web=$((current_web + 1)) + fi if [ -z "$current_usr" ]; then new_usr="$USER" else @@ -141,7 +166,11 @@ decrease_ip_value() { check_result $E_PARSING "Parsing error" fi - new_web=$((current_web - 1)) + if (($current_web <= 0)); then + new_web=0 + else + new_web=$((current_web - 1)) + fi check_ip=$(grep $sip $USER_DATA/web.conf | wc -l) if [[ $check_ip = 0 ]]; then new_usr=$(echo "$current_usr" \ @@ -183,6 +212,9 @@ get_real_ip() { # Convert CIDR to netmask convert_cidr() { + # CIDR can be defined as /32 (with leading /) or as 32 (number without leading /) + # please check the value range of cidr before converting! + set ${1#/} # allow to use cidr format with leading / set -- $((5 - ($1 / 8))) 255 255 255 255 \ $(((255 << (8 - ($1 % 8))) & 255)) 0 0 0 if [[ $1 -gt 1 ]]; then @@ -210,7 +242,7 @@ convert_netmask() { 0) ;; esac done - echo "$nbits" + echo "/$nbits" } # Calculate broadcast address @@ -278,3 +310,58 @@ is_ip_valid() { fi fi } + +# === IPV6 specific functions === + +# Get full interface name +get_ipv6_iface() { + i=$(/sbin/ip addr | grep -w $interface \ + | awk '{print $NF}' | tail -n 1 | cut -f 2 -d :) + if [ "$i" = "$interface" ]; then + n=0 + else + n=$((i + 1)) + fi + echo "$interface:$n" +} + +# Get user ip6s +get_user_ip6s() { + dedicated=$(grep -H -A10 "OWNER='$user'" $HESTIA/data/ips/* | grep "VERSION='6'") + dedicated=$(echo "$dedicated" | cut -f 1 -d '-' | sed 's=.*/==') + shared=$(grep -H -A10 "OWNER='$ROOT_USER'" $HESTIA/data/ips/* | grep -A10 shared | grep "VERSION='6'") + shared=$(echo "$shared" | cut -f 1 -d '-' | sed 's=.*/==' | cut -f 1 -d \-) + for dedicated_ip in $dedicated; do + shared=$(echo "$shared" | grep -v $dedicated_ip) + done + echo -e "$dedicated\n$shared" | sed "/^$/d" +} + +# Get user ipv6 +get_user_ipv6() { + ipv6=$(get_user_ip6s | head -n1) + local_ipv6="$ipv6" +} + +# Validate ipv6 address +is_ipv6_valid() { + local_ipv6="$1" + if [ -z "$local_ipv6" ]; then + check_result $E_NOTEXIST "IPV6 address is empty" + fi + if [ ! -e "$HESTIA/data/ips/$1" ]; then + check_result $E_NOTEXIST "IPV6 $1 doesn't exist" + fi + if [ ! -z $2 ]; then + ip_data=$(cat $HESTIA/data/ips/$1) + ip_owner=$(echo "$ip_data" | grep OWNER= | cut -f2 -d \') + ip_status=$(echo "$ip_data" | grep STATUS= | cut -f2 -d \') + if [ "$ip_owner" != "$user" ] && [ "$ip_status" = 'dedicated' ]; then + check_result $E_FORBIDEN "$user user can't use IPV6 $1" + fi + get_user_owner + if [ "$ip_owner" != "$user" ] && [ "$ip_owner" != "$owner" ]; then + check_result $E_FORBIDEN "$user user can't use IPV6 $1" + fi + fi +} diff --git a/func/main.sh b/func/main.sh index fb43e15781..5124485c60 100644 --- a/func/main.sh +++ b/func/main.sh @@ -737,7 +737,7 @@ is_domain_format_valid() { is_no_new_line_format "$1" } -# Alias forman validator +# Alias format validator is_alias_format_valid() { for object in ${1//,/ }; do exclude="[!|@|#|$|^|&|(|)|+|=|{|}|:|<|>|?|_|/|\|\"|'|;|%|\`| ]" @@ -750,118 +750,136 @@ is_alias_format_valid() { done } -# IP format validator -is_ip_format_valid() { - object_name=${2-ip} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' +# Get IP format +get_ip_format() { + object_name=${2-ipv4} + local ret_code=0 + local ret_string="" + ip_clean=$(echo "${1%/*}") - if ! [[ $ip_clean =~ ^$ip_regex\.$ip_regex\.$ip_regex\.$ip_regex$ ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - if [ $1 != "$ip_clean" ]; then - ip_cidr="$ip_clean/" - ip_cidr=$(echo "${1#$ip_cidr}") - if [[ "$ip_cidr" -gt 32 ]] || [[ "$ip_cidr" =~ [:alnum:] ]]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" + cidr_prefixlen=$(echo "$1" | sed -ne '/\//s/[^\/]*\/\([0-9][0-9]*$\)/\1/p') + + # First check of "clean" ip address (without /cidr and without /prefix_length), if ip address not empty + if [[ "$object_name" != "prefix_length" ]] && [[ "$object_name" != "cidr" ]] || [[ -n "$ip_clean" ]]; then + # Check for IPV4 address format excluding "cidr only" mode + ipv4_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' + if [[ $ip_clean =~ ^$ipv4_regex\.$ipv4_regex\.$ipv4_regex\.$ipv4_regex$ ]]; then + ret_string="4" # Valid IPV4 address detected + else + ret_code=1 # BIT 0: invalid IPV4 format + + # Check for IPV6 address format excluding "prefix length only" mode + WORD="[0-9A-Fa-f]\{1,4\}" + # flat address, no compressed words + FLAT="^${WORD}\(:${WORD}\)\{7\}$" + + COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" + COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" + COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" + COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" + COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" + COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" + # trailing :: edge case, includes case of only :: (all 0's) + EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" + # leading :: edge case + EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" + echo $ip_clean | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" + if [ $? -ne 0 ]; then + ret_code=$(($ret_code | 4)) # BIT 2: invalid IPV6 format + echo "" + return $ret_code # Return with merged error code + fi + ret_string="6" # Valid IPV6 address detected + ret_code=0 fi fi -} -# IPv6 format validator -is_ipv6_format_valid() { - object_name=${2-ipv6} - ip_regex='([1-9]?[0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])' - t_ip=$(echo $1 | awk -F / '{print $1}') - t_cidr=$(echo $1 | awk -F / '{print $2}') - valid_cidr=1 - - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi - - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 + # Check for IPV4 cidr + if [[ "$ret_string" != "6" ]] && [[ -n "$cidr_prefixlen" ]] || [[ "$object_name" = "cidr" ]]; then + [ "$object_name" = "cidr" ] && ret_code=0 + if [[ "$cidr_prefixlen" =~ ^[0-9]+$ ]]; then + if [ $cidr_prefixlen -le 32 ]; then + ret_string="4" + else + [ "$object_name" != "prefix_length" ] && ret_code=$(($ret_code | 2)) # BIT 1: invalid cidr for IPV4 format + fi + else + [ "$object_name" != "prefix_length" ] && ret_code=$(($ret_code | 2)) # BIT 1: invalid cidr for IPV4 format fi fi - if [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid $object_name format :: $1" - fi -} -is_ip46_format_valid() { - t_ip=$(echo $1 | awk -F / '{print $1}') - t_cidr=$(echo $1 | awk -F / '{print $2}') - valid_octets=0 - valid_cidr=1 - for octet in ${t_ip//./ }; do - if [[ $octet =~ ^[0-9]{1,3}$ ]] && [[ $octet -le 255 ]]; then - ((++valid_octets)) + # Check for IPV6 prefix_length + if [[ "$ret_string" != "4" ]] && [[ -n "$cidr_prefixlen" ]] || [[ "$object_name" = "prefix_length" ]]; then + if [[ "$cidr_prefixlen" -lt 0 ]] || [[ "$cidr_prefixlen" -gt 128 ]]; then + ret_code=$(($ret_code | 8)) # BIT 3: invalid prefix lenght for IPV6 format fi - done - - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 32 ]]; then - valid_cidr=0 - fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 + if ! [[ "$cidr_prefixlen" =~ ^[0-9]+$ ]]; then + ret_code=$(($ret_code | 8)) # BIT 3: invalid prefix lenght for IPV6 format fi + [ $ret_code -eq 0 ] && ret_string="6" fi - if [ "$valid_octets" -lt 4 ] || [ "$valid_cidr" -eq 0 ]; then - #Check IPV6 - ipv6_valid="" - WORD="[0-9A-Fa-f]\{1,4\}" - # flat address, no compressed words - FLAT="^${WORD}\(:${WORD}\)\{7\}$" - - COMP2="^\(${WORD}:\)\{1,1\}\(:${WORD}\)\{1,6\}$" - COMP3="^\(${WORD}:\)\{1,2\}\(:${WORD}\)\{1,5\}$" - COMP4="^\(${WORD}:\)\{1,3\}\(:${WORD}\)\{1,4\}$" - COMP5="^\(${WORD}:\)\{1,4\}\(:${WORD}\)\{1,3\}$" - COMP6="^\(${WORD}:\)\{1,5\}\(:${WORD}\)\{1,2\}$" - COMP7="^\(${WORD}:\)\{1,6\}\(:${WORD}\)\{1,1\}$" - # trailing :: edge case, includes case of only :: (all 0's) - EDGE_TAIL="^\(\(${WORD}:\)\{1,7\}\|:\):$" - # leading :: edge case - EDGE_LEAD="^:\(:${WORD}\)\{1,7\}$" - - echo $t_ip | grep --silent "\(${FLAT}\)\|\(${COMP2}\)\|\(${COMP3}\)\|\(${COMP4}\)\|\(${COMP5}\)\|\(${COMP6}\)\|\(${COMP7}\)\|\(${EDGE_TAIL}\)\|\(${EDGE_LEAD}\)" - if [ $? -ne 0 ]; then - ipv6_valid="INVALID" - fi + echo "$ret_string" + return $ret_code +} - if [ -n "$(echo $1 | grep '/')" ]; then - if [[ "$t_cidr" -lt 0 ]] || [[ "$t_cidr" -gt 128 ]]; then - valid_cidr=0 +# IP format validator +is_ip_format_valid() { + # $1: IPV4/IPV6 address with/without cidr/prefix_length or only cidr/prefix_length with slash as key char before + # $2: FORMAT {ip or ip4 or ipv4}/{ip6 or ipv6}/{ip46 or ipv46} netmask/cidr/prefix_length + object_name=${2-ipv4} + local ip_format="" + local ret_code=0 + local err_message="invalid" + ip_format="$(get_ip_format "$1" "$object_name")" + ret_code=$? + case "$object_name" in + ip46 | ipv46) + if [[ "$ip_format" == "4" ]] || [[ "$ip_format" == "6" ]] && [[ $ret_code -eq 0 ]]; then + return $ret_code + else + [ $ret_code -eq 2 ] && err_message="$err_message cidr of" + [ $ret_code -eq 8 ] && err_message="$err_message prefix length of" + check_result "$E_INVALID" "$err_message $object_name format :: $1" + return $ret_code fi - if ! [[ "$t_cidr" =~ ^[0-9]+$ ]]; then - valid_cidr=0 + ;; + ip6 | ipv6 | prefix_length) + ret_code=$(($ret_code & 12)) # Filter BIT 2 and 3 from error codes for IPV6 format + if [ "$ip_format" = "6" -a $ret_code -eq 0 ]; then + return $ret_code + else + if [ "$ip_format" = "4" ]; then + check_result "$E_INVALID" "ipv4 but not ipv6 format :: $1" + return 12 + else + [ $ret_code -eq 8 -a "$object_name" != "prefix_length" ] && err_message="$err_message prefix length of" + check_result "$E_INVALID" "$err_message $object_name format :: $1" + return $ret_code + fi fi - fi + ;; + ip | ip4 | ipv4 | netmask | cidr | *) + ret_code=$(($ret_code & 3)) # Filter BIT 0 and 1 from error codes for IPV4 format + if [ "$ip_format" = "4" -a $ret_code -eq 0 ]; then + return $ret_code + else + if [ $ret_code -ne 0 ]; then + [ $ret_code -eq 2 -a "$object_name" != "cidr" ] && err_message="$err_message cidr of" + check_result "$E_INVALID" "$err_message $object_name format :: $1" + return $ret_code + else + check_result "$E_INVALID" "ipv6 but not ipv4 format :: $1" + return 3 + fi + fi + ;; + esac +} - if [ -n "$ipv6_valid" ] || [ "$valid_cidr" -eq 0 ]; then - check_result "$E_INVALID" "invalid IP format :: $1" - fi +# IP family validator +is_ipfamily_format_valid() { + if [ "$1" != "inet" ] && [ "$1" != "inet4" ] && [ "$1" != 'inet6' ] && [ -n "$1" ]; then + check_result "$E_INVALID" "invalid ipfamily format :: $1" fi } @@ -1039,11 +1057,19 @@ is_fw_port_format_valid() { fi else if ! [[ "$1" =~ ^[0-9][-|,|:|0-9]{0,76}[0-9]$ ]]; then - check_result "$E_INVALID" "invalid port format and/or more than 78 chars used :: $1" + [ -f "/etc/services" ] && service_matched="$(grep "^$1" /etc/services)" # port definition by name, listed in /etc/services + [ -z "$service_matched" ] && check_result "$E_INVALID" "port not found, invalid port format and/or more than 78 chars used :: $1" fi fi } +# Firewall iptables validator +is_fw_iptables_format_valid() { + if [ "$1" != "iptables" ] && [ "$1" != 'ip6tables' ] && [ -n "$1" ]; then + check_result "$E_INVALID" "invalid iptables format :: $1" + fi +} + # DNS record id validator is_id_format_valid() { if ! echo "$1" | grep -qE '^[1-9][0-9]{0,}$'; then @@ -1197,6 +1223,7 @@ is_format_valid() { charset) is_object_format_valid "$arg" "$arg_name" ;; charsets) is_common_format_valid "$arg" 'charsets' ;; chain) is_object_format_valid "$arg" 'chain' ;; + cidr) is_ip_format_valid "$arg" 'cidr' ;; comment) is_object_format_valid "$arg" 'comment' ;; database) is_database_format_valid "$arg" 'database' ;; day) is_cron_format_valid "$arg" $arg_name ;; @@ -1219,11 +1246,13 @@ is_format_valid() { hour) is_cron_format_valid "$arg" $arg_name ;; id) is_id_format_valid "$arg" 'id' ;; iface) is_interface_format_valid "$arg" ;; - ip) is_ip_format_valid "$arg" ;; - ipv6) is_ipv6_format_valid "$arg" ;; - ip46) is_ip46_format_valid "$arg" ;; + ip | ip4 | ipv4) is_ip_format_valid "$arg" 'ipv4' ;; + ip6 | ipv6) is_ip_format_valid "$arg" 'ipv6' ;; + ip46 | ipv46) is_ip_format_valid "$arg" 'ipv46' ;; ip_name) is_domain_format_valid "$arg" 'IP name' ;; ip_status) is_ip_status_format_valid "$arg" ;; + ipfamily) is_ipfamily_format_valid "$arg" ;; + iptables) is_fw_iptables_format_valid "$arg" ;; job) is_int_format_valid "$arg" 'job' ;; key) is_common_format_valid "$arg" "$arg_name" ;; malias) is_user_format_valid "$arg" "$arg_name" '64' ;; @@ -1231,7 +1260,7 @@ is_format_valid() { min) is_cron_format_valid "$arg" $arg_name ;; month) is_cron_format_valid "$arg" $arg_name ;; name) is_name_format_valid "$arg" "name" ;; - nat_ip) is_ip_format_valid "$arg" ;; + nat_ip) is_ip_format_valid "$arg" 'ipv4' ;; netmask) is_ip_format_valid "$arg" 'netmask' ;; newid) is_int_format_valid "$arg" 'id' ;; ns1) is_domain_format_valid "$arg" 'ns1' ;; @@ -1248,6 +1277,7 @@ is_format_valid() { priority) is_int_format_valid $arg ;; port) is_int_format_valid "$arg" 'port' ;; port_ext) is_fw_port_format_valid "$arg" ;; + prefix_length) is_ip_format_valid "$arg" 'prefix_length' ;; protocol) is_fw_protocol_format_valid "$arg" ;; proxy_ext) is_extention_format_valid "$arg" ;; quota) is_int_format_valid "$arg" 'quota' ;; diff --git a/func/rebuild.sh b/func/rebuild.sh index 0a9ddd9410..574fb9eb57 100644 --- a/func/rebuild.sh +++ b/func/rebuild.sh @@ -232,7 +232,12 @@ rebuild_web_domain_conf() { syshealth_repair_web_config get_domain_values 'web' - is_ip_valid $IP + if [ -n "$IP" ]; then + is_ip_valid ${IP} + fi + if [ -n "$IP6" ]; then + is_ipv6_valid ${IP6} + fi prepare_web_domain_values # Remove old web configuration files @@ -485,16 +490,11 @@ rebuild_dns_domain_conf() { if [ "$SLAVE" != "yes" ]; then # Checking zone file if [ ! -e "$USER_DATA/dns/$domain.conf" ]; then - cat $DNSTPL/$TPL.tpl \ - | sed -e "s/%ip%/$IP/g" \ - -e "s/%domain_idn%/$domain_idn/g" \ - -e "s/%domain%/$domain/g" \ - -e "s/%ns1%/$ns1/g" \ - -e "s/%ns2%/$ns2/g" \ - -e "s/%ns3%/$ns3/g" \ - -e "s/%ns4%/$ns4/g" \ - -e "s/%time%/$TIME/g" \ - -e "s/%date%/$DATE/g" > $USER_DATA/dns/$domain.conf + ip=${IP} + ipv6=${IP6} + time=${TIME} + date=${DATE} + create_dns_domain_config fi # Sorting records diff --git a/func/syshealth.sh b/func/syshealth.sh index cb12bca9da..c0b706ddbe 100644 --- a/func/syshealth.sh +++ b/func/syshealth.sh @@ -136,7 +136,7 @@ function syshealth_update_ip_config_format() { # IP ADDRESS # Create array of known keys in configuration file system="ip" - known_keys="OWNER STATUS NAME U_SYS_USERS U_WEB_DOMAINS INTERFACE NETMASK NAT TIME DATE" + known_keys="OWNER STATUS NAME U_SYS_USERS U_WEB_DOMAINS INTERFACE NETMASK NAT TIME DATE VERSION" write_kv_config_file unset system unset known_keys @@ -198,7 +198,7 @@ function syshealth_update_system_config_format() { # SYSTEM CONFIGURATION # Create array of known keys in configuration file system="system" - known_keys="ANTISPAM_SYSTEM ANTIVIRUS_SYSTEM API_ALLOWED_IP API BACKEND_PORT BACKUP_GZIP BACKUP_MODE BACKUP_SYSTEM CRON_SYSTEM DB_PMA_ALIAS DB_SYSTEM DISK_QUOTA DNS_SYSTEM ENFORCE_SUBDOMAIN_OWNERSHIP FILE_MANAGER FIREWALL_EXTENSION FIREWALL_SYSTEM FTP_SYSTEM IMAP_SYSTEM INACTIVE_SESSION_TIMEOUT LANGUAGE LOGIN_STYLE MAIL_SYSTEM PROXY_PORT PROXY_SSL_PORT PROXY_SYSTEM RELEASE_BRANCH STATS_SYSTEM THEME UPDATE_HOSTNAME_SSL UPGRADE_SEND_EMAIL UPGRADE_SEND_EMAIL_LOG WEB_BACKEND WEBMAIL_ALIAS WEBMAIL_SYSTEM WEB_PORT WEB_RGROUPS WEB_SSL WEB_SSL_PORT WEB_SYSTEM WEB_TERMINAL WEB_TERMINAL_PORT VERSION DISABLE_IP_CHECK" + known_keys="ANTISPAM_SYSTEM ANTIVIRUS_SYSTEM API_ALLOWED_IP API BACKEND_PORT BACKUP_GZIP BACKUP_MODE BACKUP_SYSTEM CRON_SYSTEM DB_PMA_ALIAS DB_SYSTEM DISK_QUOTA DNS_SYSTEM ENFORCE_SUBDOMAIN_OWNERSHIP FILE_MANAGER FIREWALL_EXTENSION FIREWALL_SYSTEM FTP_SYSTEM IMAP_SYSTEM INACTIVE_SESSION_TIMEOUT IPV6_SUPPORT LANGUAGE LOGIN_STYLE MAIL_SYSTEM PROXY_PORT PROXY_SSL_PORT PROXY_SYSTEM RELEASE_BRANCH STATS_SYSTEM THEME UPDATE_HOSTNAME_SSL UPGRADE_SEND_EMAIL UPGRADE_SEND_EMAIL_LOG WEB_BACKEND WEBMAIL_ALIAS WEBMAIL_SYSTEM WEB_PORT WEB_RGROUPS WEB_SSL WEB_SSL_PORT WEB_SYSTEM WEB_TERMINAL WEB_TERMINAL_PORT VERSION DISABLE_IP_CHECK" write_kv_config_file unset system unset known_keys @@ -543,6 +543,12 @@ function syshealth_repair_system_config() { $BIN/v-change-sys-config-value "DOMAINDIR_WRITABLE" "no" fi + # IPV6 Support + if [[ -z $(check_key_exists 'IPV6_SUPPORT') ]]; then + echo "[ ! ] Adding missing variable to hestia.conf: IPV6_SUPPORT ('no')" + $BIN/v-change-sys-config-value "IPV6_SUPPORT" "no" + fi + touch $HESTIA/conf/hestia.conf.new while IFS='= ' read -r lhs rhs; do if [[ ! $lhs =~ ^\ *# && -n $lhs ]]; then diff --git a/install/common/templates/dns/default.tpl b/install/common/templates/dns/default.tpl index b12a6d7b40..a429047ad1 100755 --- a/install/common/templates/dns/default.tpl +++ b/install/common/templates/dns/default.tpl @@ -6,16 +6,18 @@ ID='5' RECORD='@' TYPE='NS' PRIORITY='' VALUE='%ns5%.' SUSPENDED='no' TIME='%tim ID='6' RECORD='@' TYPE='NS' PRIORITY='' VALUE='%ns6%.' SUSPENDED='no' TIME='%time%' DATE='%date%' ID='7' RECORD='@' TYPE='NS' PRIORITY='' VALUE='%ns7%.' SUSPENDED='no' TIME='%time%' DATE='%date%' ID='8' RECORD='@' TYPE='NS' PRIORITY='' VALUE='%ns8%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='9' RECORD='@' TYPE='A' PRIORITY='' VALUE='%ip%' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='10' RECORD='www' TYPE='CNAME' PRIORITY='' VALUE='%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='11' RECORD='ftp' TYPE='CNAME' PRIORITY='' VALUE='%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='12' RECORD='mail' TYPE='A' PRIORITY='' VALUE='%ip%' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='13' RECORD='webmail' TYPE='CNAME' PRIORITY='' VALUE='mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='14' RECORD='@' TYPE='MX' PRIORITY='0' VALUE='mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='15' RECORD='@' TYPE='TXT' PRIORITY='' VALUE='"v=spf1 a mx ip4:%ip% -all"' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='16' RECORD='_dmarc' TYPE='TXT' PRIORITY='' VALUE='"v=DMARC1; p=quarantine; pct=100"' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='17' RECORD='_submission._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 587 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='18' RECORD='_imap._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 143 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='19' RECORD='_imaps._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 993 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='20' RECORD='_pop3._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 110 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' -ID='21' RECORD='_pop3s._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 995 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +%% +%% +ID='11' RECORD='www' TYPE='CNAME' PRIORITY='' VALUE='%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='12' RECORD='ftp' TYPE='CNAME' PRIORITY='' VALUE='%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +%% +%% +ID='15' RECORD='webmail' TYPE='CNAME' PRIORITY='' VALUE='mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='16' RECORD='@' TYPE='MX' PRIORITY='0' VALUE='mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='17' RECORD='@' TYPE='TXT' PRIORITY='' VALUE='"v=spf1 a mx %%%%-all"' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='18' RECORD='_dmarc' TYPE='TXT' PRIORITY='' VALUE='"v=DMARC1; p=quarantine; pct=100"' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='19' RECORD='_submission._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 587 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='20' RECORD='_imap._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 143 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='21' RECORD='_imaps._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 993 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='22' RECORD='_pop3._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 110 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' +ID='23' RECORD='_pop3s._tcp' TYPE='SRV' PRIORITY='1' VALUE='0 995 mail.%domain%.' SUSPENDED='no' TIME='%time%' DATE='%date%' diff --git a/install/deb/fail2ban/action.d/hestia.conf b/install/deb/fail2ban/action.d/hestia.conf index e6baff5055..bbac65b7fb 100644 --- a/install/deb/fail2ban/action.d/hestia.conf +++ b/install/deb/fail2ban/action.d/hestia.conf @@ -1,8 +1,13 @@ # Fail2Ban configuration file for hestia +[INCLUDES] + +before = iptables.conf + + [Definition] -actionstart = /usr/local/hestia/bin/v-add-firewall-chain +actionstart = /usr/local/hestia/bin/v-add-firewall-chain actionstop = /usr/local/hestia/bin/v-delete-firewall-chain actioncheck = iptables -n -L INPUT | grep -q 'fail2ban-[ \t]' actionban = /usr/local/hestia/bin/v-add-firewall-ban diff --git a/install/deb/templates/mail/apache2/default.stpl b/install/deb/templates/mail/apache2/default.stpl index 31b8834621..6acf385c6e 100644 --- a/install/deb/templates/mail/apache2/default.stpl +++ b/install/deb/templates/mail/apache2/default.stpl @@ -1,4 +1,4 @@ - +%%%> ServerName %domain_idn% ServerAlias %alias% Alias / /var/lib/roundcube/ diff --git a/install/deb/templates/mail/apache2/default.tpl b/install/deb/templates/mail/apache2/default.tpl index acb3ee014d..0aa389ee43 100644 --- a/install/deb/templates/mail/apache2/default.tpl +++ b/install/deb/templates/mail/apache2/default.tpl @@ -1,4 +1,4 @@ - +%%%> ServerName %domain_idn% ServerAlias %alias_idn% Alias / /var/lib/roundcube/ diff --git a/install/deb/templates/mail/apache2/disabled.stpl b/install/deb/templates/mail/apache2/disabled.stpl index 785a831fd5..e64b9a1447 100644 --- a/install/deb/templates/mail/apache2/disabled.stpl +++ b/install/deb/templates/mail/apache2/disabled.stpl @@ -1,4 +1,4 @@ - +%%%> ServerName %domain_idn% ServerAlias %alias_idn% DocumentRoot /var/www/html/ diff --git a/install/deb/templates/mail/apache2/disabled.tpl b/install/deb/templates/mail/apache2/disabled.tpl index aa65b0a7b9..44bffd3f5f 100644 --- a/install/deb/templates/mail/apache2/disabled.tpl +++ b/install/deb/templates/mail/apache2/disabled.tpl @@ -1,4 +1,4 @@ - +%%%> ServerName %domain_idn% ServerAlias %alias_idn% DocumentRoot /var/www/html/ diff --git a/install/deb/templates/mail/apache2/rainloop.stpl b/install/deb/templates/mail/apache2/rainloop.stpl index 84b8244b29..f7198b4204 100644 --- a/install/deb/templates/mail/apache2/rainloop.stpl +++ b/install/deb/templates/mail/apache2/rainloop.stpl @@ -1,4 +1,4 @@ - +%%%> ServerName %domain_idn% ServerAlias %alias_idn% Alias / /var/lib/rainloop/ diff --git a/install/deb/templates/mail/apache2/rainloop.tpl b/install/deb/templates/mail/apache2/rainloop.tpl index 83afa1f280..e232db5670 100644 --- a/install/deb/templates/mail/apache2/rainloop.tpl +++ b/install/deb/templates/mail/apache2/rainloop.tpl @@ -1,4 +1,4 @@ - +%%%> ServerName %domain_idn% ServerAlias %alias_idn% Alias / /var/lib/rainloop/ diff --git a/install/deb/templates/mail/nginx/default.stpl b/install/deb/templates/mail/nginx/default.stpl index c86fb59add..766a2bf5a0 100644 --- a/install/deb/templates/mail/nginx/default.stpl +++ b/install/deb/templates/mail/nginx/default.stpl @@ -1,5 +1,6 @@ server { - listen %ip%:%proxy_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/roundcube; index index.php index.html index.htm; @@ -30,7 +31,7 @@ server { try_files $uri $uri/ =404; - proxy_pass https://%ip%:%web_ssl_port%; + proxy_pass https://%web_ip%:%web_ssl_port%; location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ { expires 7d; @@ -39,7 +40,7 @@ server { } location @fallback { - proxy_pass https://%ip%:%web_ssl_port%; + proxy_pass https://%web_ip%:%web_ssl_port%; } location /error/ { diff --git a/install/deb/templates/mail/nginx/default.tpl b/install/deb/templates/mail/nginx/default.tpl index 91a456f41f..b7d21d7b1d 100644 --- a/install/deb/templates/mail/nginx/default.tpl +++ b/install/deb/templates/mail/nginx/default.tpl @@ -1,5 +1,6 @@ server { - listen %ip%:%proxy_port%; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/roundcube; index index.php index.html index.htm; @@ -23,7 +24,7 @@ server { try_files $uri $uri/ =404; - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ { expires 7d; @@ -32,7 +33,7 @@ server { } location @fallback { - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; } location /error/ { diff --git a/install/deb/templates/mail/nginx/default_disabled.stpl b/install/deb/templates/mail/nginx/default_disabled.stpl index 3ff225387a..437fcbc241 100644 --- a/install/deb/templates/mail/nginx/default_disabled.stpl +++ b/install/deb/templates/mail/nginx/default_disabled.stpl @@ -1,5 +1,6 @@ server { - listen %ip%:%proxy_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; index index.php index.html index.htm; access_log /var/log/nginx/domains/%domain%.log combined; @@ -20,7 +21,7 @@ server { } location / { - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; } proxy_hide_header Upgrade; diff --git a/install/deb/templates/mail/nginx/default_disabled.tpl b/install/deb/templates/mail/nginx/default_disabled.tpl index 89b2bd38ff..26eb3ef21a 100644 --- a/install/deb/templates/mail/nginx/default_disabled.tpl +++ b/install/deb/templates/mail/nginx/default_disabled.tpl @@ -1,5 +1,6 @@ server { - listen %ip%:%proxy_port%; +%% +%% server_name %domain_idn% %alias_idn%; index index.php index.html index.htm; access_log /var/log/nginx/domains/%domain%.log combined; @@ -13,7 +14,7 @@ server { } location / { - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; } include %home%/%user%/conf/mail/%root_domain%/%proxy_system%.conf_*; diff --git a/install/deb/templates/mail/nginx/default_snappymail.stpl b/install/deb/templates/mail/nginx/default_snappymail.stpl index 5e1062c717..d37d526a88 100644 --- a/install/deb/templates/mail/nginx/default_snappymail.stpl +++ b/install/deb/templates/mail/nginx/default_snappymail.stpl @@ -1,5 +1,6 @@ server { - listen %ip%:%proxy_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/snappymail; index index.php index.html index.htm; @@ -30,7 +31,7 @@ server { try_files $uri $uri/ =404; - proxy_pass https://%ip%:%web_ssl_port%; + proxy_pass https://%web_ip%:%web_ssl_port%; location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ { expires 7d; @@ -39,7 +40,7 @@ server { } location @fallback { - proxy_pass https://%ip%:%web_ssl_port%; + proxy_pass https://%web_ip%:%web_ssl_port%; } location /error/ { diff --git a/install/deb/templates/mail/nginx/default_snappymail.tpl b/install/deb/templates/mail/nginx/default_snappymail.tpl index 87a9ff11b8..65f7d53eee 100644 --- a/install/deb/templates/mail/nginx/default_snappymail.tpl +++ b/install/deb/templates/mail/nginx/default_snappymail.tpl @@ -1,5 +1,6 @@ server { - listen %ip%:%proxy_port%; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/snappymail; index index.php index.html index.htm; @@ -28,7 +29,7 @@ server { try_files $uri $uri/ =404; - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; location ~* ^.+\.(ogg|ogv|svg|svgz|swf|eot|otf|woff|woff2|mov|mp3|mp4|webm|flv|ttf|rss|atom|jpg|jpeg|gif|png|webp|ico|bmp|mid|midi|wav|rtf|css|js|jar)$ { expires 7d; @@ -37,7 +38,7 @@ server { } location @fallback { - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; } location /error/ { diff --git a/install/deb/templates/mail/nginx/disabled.stpl b/install/deb/templates/mail/nginx/disabled.stpl index 8458ebeda7..6d7ade81ee 100644 --- a/install/deb/templates/mail/nginx/disabled.stpl +++ b/install/deb/templates/mail/nginx/disabled.stpl @@ -1,5 +1,6 @@ server { - listen %ip%:%web_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; root /var/www/html; index index.php index.html index.htm; diff --git a/install/deb/templates/mail/nginx/disabled.tpl b/install/deb/templates/mail/nginx/disabled.tpl index c094b88f71..311cc4fe88 100644 --- a/install/deb/templates/mail/nginx/disabled.tpl +++ b/install/deb/templates/mail/nginx/disabled.tpl @@ -1,5 +1,6 @@ server { - listen %ip%:%web_port%; +%% +%% server_name %domain_idn% %alias_idn%; root /var/www/html; index index.php index.html index.htm; diff --git a/install/deb/templates/mail/nginx/snappymail.stpl b/install/deb/templates/mail/nginx/snappymail.stpl index df0786cab6..87d64b2f8d 100644 --- a/install/deb/templates/mail/nginx/snappymail.stpl +++ b/install/deb/templates/mail/nginx/snappymail.stpl @@ -1,5 +1,6 @@ server { - listen %ip%:%web_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/snappymail; index index.php index.html index.htm; diff --git a/install/deb/templates/mail/nginx/snappymail.tpl b/install/deb/templates/mail/nginx/snappymail.tpl index ccd38da953..14991db8d8 100644 --- a/install/deb/templates/mail/nginx/snappymail.tpl +++ b/install/deb/templates/mail/nginx/snappymail.tpl @@ -1,5 +1,6 @@ server { - listen %ip%:%web_port%; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/snappymail; index index.php index.html index.htm; diff --git a/install/deb/templates/mail/nginx/web_system.stpl b/install/deb/templates/mail/nginx/web_system.stpl index 74fc36619b..6817c0cdb5 100644 --- a/install/deb/templates/mail/nginx/web_system.stpl +++ b/install/deb/templates/mail/nginx/web_system.stpl @@ -1,5 +1,6 @@ server { - listen %ip%:%web_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/roundcube; index index.php index.html index.htm; diff --git a/install/deb/templates/mail/nginx/web_system.tpl b/install/deb/templates/mail/nginx/web_system.tpl index 554854e555..7f7891f537 100644 --- a/install/deb/templates/mail/nginx/web_system.tpl +++ b/install/deb/templates/mail/nginx/web_system.tpl @@ -1,5 +1,6 @@ server { - listen %ip%:%web_port%; +%% +%% server_name %domain_idn% %alias_idn%; root /var/lib/roundcube; index index.php index.html index.htm; diff --git a/install/deb/templates/web/apache2/default.stpl b/install/deb/templates/web/apache2/default.stpl index 213de8576b..7a748246d6 100644 --- a/install/deb/templates/web/apache2/default.stpl +++ b/install/deb/templates/web/apache2/default.stpl @@ -4,7 +4,7 @@ # https://hestiacp.com/docs/server-administration/web-templates.html # #=========================================================================# - +%%%> ServerName %domain_idn% %alias_string% diff --git a/install/deb/templates/web/apache2/default.tpl b/install/deb/templates/web/apache2/default.tpl index 2851acb42f..7ffac8ca31 100644 --- a/install/deb/templates/web/apache2/default.tpl +++ b/install/deb/templates/web/apache2/default.tpl @@ -4,7 +4,7 @@ # https://hestiacp.com/docs/server-administration/web-templates.html # #=========================================================================# - +%%%> ServerName %domain_idn% %alias_string% diff --git a/install/deb/templates/web/apache2/php-fpm/default.stpl b/install/deb/templates/web/apache2/php-fpm/default.stpl index ca7a019563..a7703e5930 100644 --- a/install/deb/templates/web/apache2/php-fpm/default.stpl +++ b/install/deb/templates/web/apache2/php-fpm/default.stpl @@ -4,7 +4,7 @@ # https://hestiacp.com/docs/server-administration/web-templates.html # #=========================================================================# - +%%%> ServerName %domain_idn% %alias_string% diff --git a/install/deb/templates/web/apache2/php-fpm/default.tpl b/install/deb/templates/web/apache2/php-fpm/default.tpl index 0cc22c6550..64a246a320 100644 --- a/install/deb/templates/web/apache2/php-fpm/default.tpl +++ b/install/deb/templates/web/apache2/php-fpm/default.tpl @@ -4,7 +4,7 @@ # https://hestiacp.com/docs/server-administration/web-templates.html # #=========================================================================# - +%%%> ServerName %domain_idn% %alias_string% diff --git a/install/deb/templates/web/nginx/default.stpl b/install/deb/templates/web/nginx/default.stpl index 13f652ca68..b85eb78938 100755 --- a/install/deb/templates/web/nginx/default.stpl +++ b/install/deb/templates/web/nginx/default.stpl @@ -5,7 +5,8 @@ #=========================================================================# server { - listen %ip%:%proxy_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; error_log /var/log/%web_system%/domains/%domain%.error.log error; @@ -26,7 +27,7 @@ server { } location / { - proxy_pass https://%ip%:%web_ssl_port%; + proxy_pass https://%web_ip%:%web_ssl_port%; location ~* ^.+\.(%proxy_extensions%)$ { try_files $uri @fallback; @@ -40,7 +41,7 @@ server { } location @fallback { - proxy_pass https://%ip%:%web_ssl_port%; + proxy_pass https://%web_ip%:%web_ssl_port%; } location /error/ { diff --git a/install/deb/templates/web/nginx/default.tpl b/install/deb/templates/web/nginx/default.tpl index 2f310fd959..6e2d5291c7 100755 --- a/install/deb/templates/web/nginx/default.tpl +++ b/install/deb/templates/web/nginx/default.tpl @@ -5,7 +5,8 @@ #=========================================================================# server { - listen %ip%:%proxy_port%; +%% +%% server_name %domain_idn% %alias_idn%; error_log /var/log/%web_system%/domains/%domain%.error.log error; @@ -17,7 +18,7 @@ server { } location / { - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; location ~* ^.+\.(%proxy_extensions%)$ { try_files $uri @fallback; @@ -31,7 +32,7 @@ server { } location @fallback { - proxy_pass http://%ip%:%web_port%; + proxy_pass http://%web_ip%:%web_port%; } location /error/ { diff --git a/install/deb/templates/web/nginx/php-fpm/default.stpl b/install/deb/templates/web/nginx/php-fpm/default.stpl index 579e5e129e..a571fa93de 100644 --- a/install/deb/templates/web/nginx/php-fpm/default.stpl +++ b/install/deb/templates/web/nginx/php-fpm/default.stpl @@ -5,7 +5,8 @@ #=========================================================================# server { - listen %ip%:%web_ssl_port% ssl; +%% +%% server_name %domain_idn% %alias_idn%; root %sdocroot%; index index.php index.html index.htm; diff --git a/install/deb/templates/web/nginx/php-fpm/default.tpl b/install/deb/templates/web/nginx/php-fpm/default.tpl index 287ee39f14..4bbc6e043c 100644 --- a/install/deb/templates/web/nginx/php-fpm/default.tpl +++ b/install/deb/templates/web/nginx/php-fpm/default.tpl @@ -5,7 +5,8 @@ #=========================================================================# server { - listen %ip%:%web_port%; +%% +%% server_name %domain_idn% %alias_idn%; root %docroot%; index index.php index.html index.htm; diff --git a/install/hst-install-debian.sh b/install/hst-install-debian.sh index b7b8c67fc3..b537341d91 100755 --- a/install/hst-install-debian.sh +++ b/install/hst-install-debian.sh @@ -50,7 +50,7 @@ software="acl apache2 apache2-suexec-custom apache2-suexec-pristine apache2-util php$fpm_v php$fpm_v-apcu php$fpm_v-bz2 php$fpm_v-cgi php$fpm_v-cli php$fpm_v-common php$fpm_v-curl php$fpm_v-gd php$fpm_v-imagick php$fpm_v-imap php$fpm_v-intl php$fpm_v-ldap php$fpm_v-mbstring php$fpm_v-mysql php$fpm_v-opcache php$fpm_v-pgsql php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml php$fpm_v-zip postgresql postgresql-contrib - proftpd-basic quota rrdtool rsyslog spamd sudo sysstat unrar-free unzip util-linux vim-common vsftpd xxd whois zip zstd" + proftpd-basic quota rrdtool rsyslog spamd subnetcalc sudo sysstat unrar-free unzip util-linux vim-common vsftpd xxd whois zip zstd" installer_dependencies="apt-transport-https ca-certificates curl dirmngr gnupg openssl wget" @@ -79,6 +79,7 @@ help() { -r, --port Change Backend Port default: 8083 -l, --lang Default language default: en -y, --interactive Interactive install [yes|no] default: yes + -6, --ipv6 Enable IPv6 Support [yes|no] default: no -s, --hostname Set hostname -e, --email Set admin email -u, --username Set admin user @@ -262,6 +263,7 @@ for arg; do --port) args="${args}-r " ;; --lang) args="${args}-l " ;; --interactive) args="${args}-y " ;; + --ipv6) args="${args}-6 " ;; --api) args="${args}-d " ;; --hostname) args="${args}-s " ;; --email) args="${args}-e " ;; @@ -279,38 +281,39 @@ done eval set -- "$args" # Parsing arguments -while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:u:e:p:W:D:fh" Option; do +while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:6:s:u:e:p:W:D:fh" Option; do case $Option in - a) apache=$OPTARG ;; # Apache - w) phpfpm=$OPTARG ;; # PHP-FPM - o) multiphp=$OPTARG ;; # Multi-PHP - v) vsftpd=$OPTARG ;; # Vsftpd - j) proftpd=$OPTARG ;; # Proftpd - k) named=$OPTARG ;; # Named - m) mysql=$OPTARG ;; # MariaDB - M) mysql8=$OPTARG ;; # MySQL - g) postgresql=$OPTARG ;; # PostgreSQL - x) exim=$OPTARG ;; # Exim - z) dovecot=$OPTARG ;; # Dovecot - Z) sieve=$OPTARG ;; # Sieve - c) clamd=$OPTARG ;; # ClamAV - t) spamd=$OPTARG ;; # SpamAssassin - i) iptables=$OPTARG ;; # Iptables - b) fail2ban=$OPTARG ;; # Fail2ban - q) quota=$OPTARG ;; # FS Quota - W) webterminal=$OPTARG ;; # Web Terminal - r) port=$OPTARG ;; # Backend Port - l) lang=$OPTARG ;; # Language - d) api=$OPTARG ;; # Activate API - y) interactive=$OPTARG ;; # Interactive install - s) servername=$OPTARG ;; # Hostname - e) email=$OPTARG ;; # Admin email - u) username=$OPTARG ;; # Admin username - p) vpass=$OPTARG ;; # Admin password - D) withdebs=$OPTARG ;; # Hestia debs path - f) force='yes' ;; # Force install - h) help ;; # Help - *) help ;; # Print help (default) + a) apache=$OPTARG ;; # Apache + w) phpfpm=$OPTARG ;; # PHP-FPM + o) multiphp=$OPTARG ;; # Multi-PHP + v) vsftpd=$OPTARG ;; # Vsftpd + j) proftpd=$OPTARG ;; # Proftpd + k) named=$OPTARG ;; # Named + m) mysql=$OPTARG ;; # MariaDB + M) mysql8=$OPTARG ;; # MySQL + g) postgresql=$OPTARG ;; # PostgreSQL + x) exim=$OPTARG ;; # Exim + z) dovecot=$OPTARG ;; # Dovecot + Z) sieve=$OPTARG ;; # Sieve + c) clamd=$OPTARG ;; # ClamAV + t) spamd=$OPTARG ;; # SpamAssassin + i) iptables=$OPTARG ;; # Iptables + b) fail2ban=$OPTARG ;; # Fail2ban + q) quota=$OPTARG ;; # FS Quota + W) webterminal=$OPTARG ;; # Web Terminal + r) port=$OPTARG ;; # Backend Port + l) lang=$OPTARG ;; # Language + d) api=$OPTARG ;; # Activate API + y) interactive=$OPTARG ;; # Interactive install + 6) ipv6_support=$OPTARG ;; # IPv6 + s) servername=$OPTARG ;; # Hostname + e) email=$OPTARG ;; # Admin email + u) username=$OPTARG ;; # Admin username + p) vpass=$OPTARG ;; # Admin password + D) withdebs=$OPTARG ;; # Hestia debs path + f) force='yes' ;; # Force install + h) help ;; # Help + *) help ;; # Print help (default) esac done @@ -380,6 +383,7 @@ set_default_value 'fail2ban' 'yes' set_default_value 'quota' 'no' set_default_value 'webterminal' 'no' set_default_value 'interactive' 'yes' +set_default_value 'ipv6_support' 'no' set_default_value 'api' 'yes' set_default_port '8083' set_default_lang 'en' @@ -763,10 +767,16 @@ if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then servername="example.com" fi echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6_support" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6_support" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi # Set email if it wasn't set @@ -1518,23 +1528,42 @@ cp -f $HESTIA_INSTALL_DIR/nginx/cloudflare.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ +if [ "$ipv6_support" = 'yes' ]; then + resolver_line_ipv6="1.0.0.1 8.8.4.4 1.1.1.1 8.8.8.8 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=300s ipv6=on;" + sed -i -e "s/\(resolver[ \t]*\)[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*.*/\1$resolver_line_ipv6/" /etc/nginx/nginx.conf + listen_nginx_ipv6="listen [::1]:8084 default;" + sed -i -e "/listen[ \t]*[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*.*/a\\\t$listen_nginx_ipv6" /etc/nginx/conf.d/status.conf +fi mkdir -p /etc/nginx/conf.d/domains mkdir -p /etc/nginx/conf.d/main mkdir -p /etc/nginx/modules-enabled mkdir -p /var/log/nginx/domains # Update dns servers in nginx.conf -for nameserver in $(grep -is '^nameserver' /etc/resolv.conf | cut -d' ' -f2 | tr '\r\n' ' ' | xargs); do +dns_resolver="$(sed -ne '/^nameserver/s/^nameserver[ \t]*\(.*\)/\1/p' /etc/resolv.conf)" +for nameserver in $dns_resolver; do if [[ "$nameserver" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - if [ -z "$resolver" ]; then - resolver="$nameserver" + if [ -z "$resolver_ipv4" ]; then + resolver_ipv4="$nameserver" else - resolver="$resolver $nameserver" + resolver_ipv4="$resolver_ipv4 $nameserver" + fi + fi + if [ "$ipv6_support" = 'yes' ]; then + if [[ $nameserver =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + if [ -z "$resolver_ipv6" ]; then + resolver_ipv6="[$nameserver]" + else + resolver_ipv6="$resolver_ipv6 [$nameserver]" + fi fi fi done -if [ -n "$resolver" ]; then - sed -i "s/1.0.0.1 8.8.4.4 1.1.1.1 8.8.8.8/$resolver/g" /etc/nginx/nginx.conf +if [ -n "$resolver_ipv4" ]; then + sed -i "s/1.0.0.1 8.8.4.4 1.1.1.1 8.8.8.8/$resolver_ipv4/g" /etc/nginx/nginx.conf +fi +if [ "$ipv6_support" = 'yes' -a -n "$resolver_ipv6" ]; then + sed -i "s/\[2606:4700:4700::1111\] \[2606:4700:4700::1001\]/$resolver_ipv6/g" /etc/nginx/nginx.conf fi # https://github.com/ergin/nginx-cloudflare-real-ip/ @@ -1578,6 +1607,12 @@ if [ "$apache" = 'yes' ]; then cp -f $HESTIA_INSTALL_DIR/apache2/status.conf /etc/apache2/mods-available/hestia-status.conf cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ + if [ "$ipv6_support" = 'yes' ]; then + listen_apache_ipv6="Listen [::1]:8081" + sed -i -e "/Listen 127\.0\.0\.1:8081.*/a$listen_apache_ipv6" /etc/apache2/mods-available/hestia-status.conf + allow_from_apache_ipv6="Allow from ::1" + sed -i -e "/Allow from 127\.0\.0\.1*/a\\\t$allow_from_apache_ipv6" /etc/apache2/mods-available/hestia-status.conf + fi # Enable needed modules a2enmod rewrite > /dev/null 2>&1 @@ -1671,6 +1706,9 @@ chmod 755 /etc/cron.daily/php-session-cleanup if [ "$vsftpd" = 'yes' ]; then echo "[ * ] Configuring Vsftpd server..." cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ + if [ "$ipv6_support" = 'yes' ]; then + sed -i -e "s/\(listen\)\(=YES\)/\1_ipv6\2/" /etc/vsftpd.conf + fi touch /var/log/vsftpd.log chown root:adm /var/log/vsftpd.log chmod 640 /var/log/vsftpd.log @@ -2241,10 +2279,19 @@ $HESTIA/bin/v-update-sys-ip > /dev/null 2>&1 default_nic="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')" # IPv4 primary_ipv4="$(ip -4 -d -j addr show "$default_nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end' | head -n1)" -# IPv6 -#primary_ipv6="$(ip -6 -d -j addr show "$default_nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end' | head -n1)" + ip="$primary_ipv4" local_ip="$primary_ipv4" +# IPv6 +if [ "$ipv6_support" = 'yes' ]; then + primary_ipv6="$(ip -6 -d -j addr show "$default_nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end' | tail -1)" + ipv6="$primary_ipv6" + local_ipv6="$primary_ipv6" +else + primary_ipv6="" + ipv6="" + local_ipv6="" +fi # Configuring firewall if [ "$iptables" = 'yes' ]; then @@ -2253,6 +2300,12 @@ fi # Get public IP pub_ipv4="$(curl -fsLm5 --retry 2 --ipv4 https://ip.hestiacp.com/)" +if [ "$ipv6_support" = 'yes' ]; then + pub_ipv6="$(curl -fsLm5 --retry 2 --ipv6 https://ip.hestiacp.com/)" +else + pub_ipv6="" +fi + if [ -n "$pub_ipv4" ] && [ "$pub_ipv4" != "$ip" ]; then if [ -e /etc/rc.local ]; then sed -i '/exit 0/d' /etc/rc.local @@ -2300,7 +2353,17 @@ if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then fi # Adding default domain -$HESTIA/bin/v-add-web-domain "$username" "$servername" "$ip" +if [ -n "$ip" ]; then + if [ -n "$ipv6" ]; then + $HESTIA/bin/v-add-web-domain-ipv46 "$username" "$servername" "$ip" "$ipv6" + else + $HESTIA/bin/v-add-web-domain-ipv46 "$username" "$servername" "$ip" + fi +else + if [ -n "$ipv6" ]; then + $HESTIA/bin/v-add-web-domain-ipv46 "$username" "$servername" "" "$ipv6" + fi +fi check_result $? "can't create $servername domain" # Adding cron jobs @@ -2382,9 +2445,21 @@ fi' >> /root/.bashrc #----------------------------------------------------------# # Comparing hostname and IP -host_ip=$(host $servername | head -n 1 | awk '{print $NF}') -if [ "$host_ip" = "$ip" ]; then - ip="$servername" +host_ipv4=$(host -t A "$servername") +if [ $? -eq 0 ]; then + host_ipv4=$(echo "$host_ipv4" | sed -e 's/[^ ]* .* \([^ ]*\)/\1/') +else + host_ipv4="" +fi +if [ "$ipv6_support" = 'yes' ]; then + host_ipv6=$(host -t AAAA "$servername") + if [ $? -eq 0 ]; then + host_ipv6=$(echo "$host_ipv6" | sed -e 's/[^ ]* .* \([^ ]*\)/\1/') + else + host_ipv6="" + fi +else + host_ipv6="" fi echo -e "\n" @@ -2397,11 +2472,29 @@ echo -e "Congratulations! You have successfully installed Hestia Control Panel on your server. Ready to get started? Log in using the following credentials: - - Admin URL: https://$servername:$port" > $tmpfile -if [ "$host_ip" != "$ip" ]; then +" > $tmpfile +if [ -n "$ip" -a "$host_ipv4" = "$ip" ]; then + ipv4_accessible=1 +else + ipv4_accessible=0 +fi +if [ -n "$ipv6" -a "$host_ipv6" = "$ipv6" ]; then + ipv6_accessible=1 +else + ipv6_accessible=0 +fi +if [ $ipv4_accessible -eq 1 -o $ipv6_accessible -eq 1 ]; then + echo -e " Admin URL: https://$servername:$port" >> $tmpfile +else + echo -e " ${servername} is not accessible from internet!" >> $tmpfile + echo -e " Use Backup URL for Admin login:" >> $tmpfile +fi +if [ -n "$ip" ]; then echo " Backup URL: https://$ip:$port" >> $tmpfile fi +if [ -n "$ipv6" ]; then + echo " Backup URL: https://[$ipv6]:$port" >> $tmpfile +fi echo -e -n " Username: $username Password: $displaypass diff --git a/install/hst-install-ubuntu.sh b/install/hst-install-ubuntu.sh index 238857bc0a..de8bd39798 100755 --- a/install/hst-install-ubuntu.sh +++ b/install/hst-install-ubuntu.sh @@ -51,7 +51,7 @@ software="acl apache2 apache2.2-common apache2-suexec-custom apache2-utils appar php$fpm_v php$fpm_v-apcu php$fpm_v-bz2 php$fpm_v-cgi php$fpm_v-cli php$fpm_v-common php$fpm_v-curl php$fpm_v-gd php$fpm_v-imagick php$fpm_v-imap php$fpm_v-intl php$fpm_v-ldap php$fpm_v-mbstring php$fpm_v-mysql php$fpm_v-opcache php$fpm_v-pgsql php$fpm_v-pspell php$fpm_v-readline php$fpm_v-xml php$fpm_v-zip postgresql postgresql-contrib - proftpd-basic quota rrdtool rsyslog setpriv spamassassin sudo sysstat unzip vim-common vsftpd whois zip zstd" + proftpd-basic quota rrdtool rsyslog setpriv spamassassin subnetcalc sudo sysstat unzip vim-common vsftpd whois zip zstd" installer_dependencies="apt-transport-https ca-certificates curl dirmngr gnupg openssl software-properties-common wget" @@ -80,6 +80,7 @@ help() { -r, --port Change Backend Port default: 8083 -l, --lang Default language default: en -y, --interactive Interactive install [yes|no] default: yes + -6, --ipv6 Enable IPv6 Support [yes|no] default: no -s, --hostname Set hostname -e, --email Set admin email -u, --username Set admin user @@ -263,6 +264,7 @@ for arg; do --port) args="${args}-r " ;; --lang) args="${args}-l " ;; --interactive) args="${args}-y " ;; + --ipv6) args="${args}-6 " ;; --api) args="${args}-d " ;; --hostname) args="${args}-s " ;; --email) args="${args}-e " ;; @@ -280,38 +282,39 @@ done eval set -- "$args" # Parsing arguments -while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:s:u:e:p:W:D:fh" Option; do +while getopts "a:w:v:j:k:m:M:g:d:x:z:Z:c:t:i:b:r:o:q:l:y:6:s:u:e:p:W:D:fh" Option; do case $Option in - a) apache=$OPTARG ;; # Apache - w) phpfpm=$OPTARG ;; # PHP-FPM - o) multiphp=$OPTARG ;; # Multi-PHP - v) vsftpd=$OPTARG ;; # Vsftpd - j) proftpd=$OPTARG ;; # Proftpd - k) named=$OPTARG ;; # Named - m) mysql=$OPTARG ;; # MariaDB - M) mysql8=$OPTARG ;; # MySQL - g) postgresql=$OPTARG ;; # PostgreSQL - x) exim=$OPTARG ;; # Exim - z) dovecot=$OPTARG ;; # Dovecot - Z) sieve=$OPTARG ;; # Sieve - c) clamd=$OPTARG ;; # ClamAV - t) spamd=$OPTARG ;; # SpamAssassin - i) iptables=$OPTARG ;; # Iptables - b) fail2ban=$OPTARG ;; # Fail2ban - q) quota=$OPTARG ;; # FS Quota - W) webterminal=$OPTARG ;; # Web Terminal - r) port=$OPTARG ;; # Backend Port - l) lang=$OPTARG ;; # Language - d) api=$OPTARG ;; # Activate API - y) interactive=$OPTARG ;; # Interactive install - s) servername=$OPTARG ;; # Hostname - e) email=$OPTARG ;; # Admin email - u) username=$OPTARG ;; # Admin username - p) vpass=$OPTARG ;; # Admin password - D) withdebs=$OPTARG ;; # Hestia debs path - f) force='yes' ;; # Force install - h) help ;; # Help - *) help ;; # Print help (default) + a) apache=$OPTARG ;; # Apache + w) phpfpm=$OPTARG ;; # PHP-FPM + o) multiphp=$OPTARG ;; # Multi-PHP + v) vsftpd=$OPTARG ;; # Vsftpd + j) proftpd=$OPTARG ;; # Proftpd + k) named=$OPTARG ;; # Named + m) mysql=$OPTARG ;; # MariaDB + M) mysql8=$OPTARG ;; # MySQL + g) postgresql=$OPTARG ;; # PostgreSQL + x) exim=$OPTARG ;; # Exim + z) dovecot=$OPTARG ;; # Dovecot + Z) sieve=$OPTARG ;; # Sieve + c) clamd=$OPTARG ;; # ClamAV + t) spamd=$OPTARG ;; # SpamAssassin + i) iptables=$OPTARG ;; # Iptables + b) fail2ban=$OPTARG ;; # Fail2ban + q) quota=$OPTARG ;; # FS Quota + W) webterminal=$OPTARG ;; # Web Terminal + r) port=$OPTARG ;; # Backend Port + l) lang=$OPTARG ;; # Language + d) api=$OPTARG ;; # Activate API + y) interactive=$OPTARG ;; # Interactive install + 6) ipv6_support=$OPTARG ;; # IPv6 + s) servername=$OPTARG ;; # Hostname + e) email=$OPTARG ;; # Admin email + u) username=$OPTARG ;; # Admin username + p) vpass=$OPTARG ;; # Admin password + D) withdebs=$OPTARG ;; # Hestia debs path + f) force='yes' ;; # Force install + h) help ;; # Help + *) help ;; # Print help (default) esac done @@ -381,6 +384,7 @@ set_default_value 'fail2ban' 'yes' set_default_value 'quota' 'no' set_default_value 'webterminal' 'no' set_default_value 'interactive' 'yes' +set_default_value 'ipv6_support' 'no' set_default_value 'api' 'yes' set_default_port '8083' set_default_lang 'en' @@ -751,10 +755,16 @@ if ! [[ "$servername" =~ ^${mask1}${mask2}$ ]]; then servername="example.com" fi echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6_support" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi if [[ -z $(grep -i "$servername" /etc/hosts) ]]; then echo "127.0.0.1 $servername" >> /etc/hosts + if [ "$ipv6_support" = 'yes' ]; then + echo "::1 $servername" >> /etc/hosts + fi fi # Set email if it wasn't set @@ -1203,6 +1213,18 @@ if [ ! -e "/sbin/iptables" ]; then ln -s "$autoiptables" /sbin/iptables fi fi + if [ "$ipv6_support" = 'yes' ]; then + if which ip6tables; then + ln -s "$(which ip6tables)" /sbin/ip6tables + elif [ -e "/usr/sbin/ip6tables" ]; then + ln -s /usr/sbin/ip6tables /sbin/ip6tables + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables; then + autoip6tables=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables | cut -d '' -f 2) + if [ -x "$autoip6tables" ]; then + ln -s "$autoip6tables" /sbin/ip6tables + fi + fi + fi fi if [ ! -e "/sbin/iptables-save" ]; then @@ -1216,6 +1238,18 @@ if [ ! -e "/sbin/iptables-save" ]; then ln -s "$autoiptables_save" /sbin/iptables-save fi fi + if [ "$ipv6_support" = 'yes' ]; then + if which ip6tables-save; then + ln -s "$(which ip6tables-save)" /sbin/ip6tables-save + elif [ -e "/usr/sbin/ip6tables-save" ]; then + ln -s /usr/sbin/ip6tables-save /sbin/ip6tables-save + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-save; then + autoip6tables_save=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-save | cut -d '' -f 2) + if [ -x "$autoip6tables_save" ]; then + ln -s "$autoip6tables_save" /sbin/ip6tables-save + fi + fi + fi fi if [ ! -e "/sbin/iptables-restore" ]; then @@ -1229,6 +1263,18 @@ if [ ! -e "/sbin/iptables-restore" ]; then ln -s "$autoiptables_restore" /sbin/iptables-restore fi fi + if [ "$ipv6_support" = 'yes' ]; then + if which ip6tables-restore; then + ln -s "$(which ip6tables-restore)" /sbin/ip6tables-restore + elif [ -e "/usr/sbin/ip6tables-restore" ]; then + ln -s /usr/sbin/ip6tables-restore /sbin/ip6tables-restore + elif whereis -B /bin /sbin /usr/bin /usr/sbin -f -b ip6tables-restore; then + autoip6tables_restore=$(whereis -B /bin /sbin /usr/bin /usr/sbin -f -b iptables-restore | cut -d '' -f 2) + if [ -x "$autoip6tables_restore" ]; then + ln -s "$autoip6tables_restore" /sbin/ip6tables-restore + fi + fi + fi fi # Restrict access to /proc fs @@ -1533,23 +1579,42 @@ cp -f $HESTIA_INSTALL_DIR/nginx/cloudflare.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phpmyadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/nginx/phppgadmin.inc /etc/nginx/conf.d/ cp -f $HESTIA_INSTALL_DIR/logrotate/nginx /etc/logrotate.d/ +if [ "$ipv6_support" = 'yes' ]; then + resolver_line_ipv6="1.0.0.1 8.8.4.4 1.1.1.1 8.8.8.8 [2606:4700:4700::1111] [2606:4700:4700::1001] valid=300s ipv6=on;" + sed -i -e "s/\(resolver[ \t]*\)[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*.*/\1$resolver_line_ipv6/" /etc/nginx/nginx.conf + listen_nginx_ipv6="listen [::1]:8084 default;" + sed -i -e "/listen[ \t]*[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*.*/a\\\t$listen_nginx_ipv6" /etc/nginx/conf.d/status.conf +fi mkdir -p /etc/nginx/conf.d/domains mkdir -p /etc/nginx/conf.d/main mkdir -p /etc/nginx/modules-enabled mkdir -p /var/log/nginx/domains # Update dns servers in nginx.conf -for nameserver in $(grep -is '^nameserver' /etc/resolv.conf | cut -d' ' -f2 | tr '\r\n' ' ' | xargs); do +dns_resolver="$(sed -ne '/^nameserver/s/^nameserver[ \t]*\(.*\)/\1/p' /etc/resolv.conf)" +for nameserver in $dns_resolver; do if [[ "$nameserver" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then - if [ -z "$resolver" ]; then - resolver="$nameserver" + if [ -z "$resolver_ipv4" ]; then + resolver_ipv4="$nameserver" else - resolver="$resolver $nameserver" + resolver_ipv4="$resolver_ipv4 $nameserver" + fi + fi + if [ "$ipv6_support" = 'yes' ]; then + if [[ $nameserver =~ ^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$ ]]; then + if [ -z "$resolver_ipv6" ]; then + resolver_ipv6="[$nameserver]" + else + resolver_ipv6="$resolver_ipv6 [$nameserver]" + fi fi fi done -if [ -n "$resolver" ]; then - sed -i "s/1.0.0.1 8.8.4.4 1.1.1.1 8.8.8.8/$resolver/g" /etc/nginx/nginx.conf +if [ -n "$resolver_ipv4" ]; then + sed -i "s/1.0.0.1 8.8.4.4 1.1.1.1 8.8.8.8/$resolver_ipv4/g" /etc/nginx/nginx.conf +fi +if [ "$ipv6_support" = 'yes' -a -n "$resolver_ipv6" ]; then + sed -i "s/\[2606:4700:4700::1111\] \[2606:4700:4700::1001\]/$resolver_ipv6/g" /etc/nginx/nginx.conf fi # https://github.com/ergin/nginx-cloudflare-real-ip/ @@ -1593,6 +1658,12 @@ if [ "$apache" = 'yes' ]; then cp -f $HESTIA_INSTALL_DIR/apache2/status.conf /etc/apache2/mods-available/hestia-status.conf cp -f /etc/apache2/mods-available/status.load /etc/apache2/mods-available/hestia-status.load cp -f $HESTIA_INSTALL_DIR/logrotate/apache2 /etc/logrotate.d/ + if [ "$ipv6_support" = 'yes' ]; then + listen_apache_ipv6="Listen [::1]:8081" + sed -i -e "/Listen 127\.0\.0\.1:8081.*/a$listen_apache_ipv6" /etc/apache2/mods-available/hestia-status.conf + allow_from_apache_ipv6="Allow from ::1" + sed -i -e "/Allow from 127\.0\.0\.1*/a\\\t$allow_from_apache_ipv6" /etc/apache2/mods-available/hestia-status.conf + fi # Enable needed modules a2enmod rewrite > /dev/null 2>&1 @@ -1686,6 +1757,9 @@ chmod 755 /etc/cron.daily/php-session-cleanup if [ "$vsftpd" = 'yes' ]; then echo "[ * ] Configuring Vsftpd server..." cp -f $HESTIA_INSTALL_DIR/vsftpd/vsftpd.conf /etc/ + if [ "$ipv6_support" = 'yes' ]; then + sed -i -e "s/\(listen\)\(=YES\)/\1_ipv6\2/" /etc/vsftpd.conf + fi touch /var/log/vsftpd.log chown root:adm /var/log/vsftpd.log chmod 640 /var/log/vsftpd.log @@ -2215,10 +2289,18 @@ $HESTIA/bin/v-update-sys-ip > /dev/null 2>&1 default_nic="$(ip -d -j route show | jq -r '.[] | if .dst == "default" then .dev else empty end')" # IPv4 primary_ipv4="$(ip -4 -d -j addr show "$default_nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end' | head -n1)" -# IPv6 -#primary_ipv6="$(ip -6 -d -j addr show "$default_nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end' | head -n1)" ip="$primary_ipv4" local_ip="$primary_ipv4" +# IPv6 +if [ "$ipv6_support" = 'yes' ]; then + primary_ipv6="$(ip -6 -d -j addr show "$default_nic" | jq -r '.[] | select(length > 0) | .addr_info[] | if .scope == "global" then .local else empty end' | tail -1)" + ipv6="$primary_ipv6" + local_ipv6="$primary_ipv6" +else + primary_ipv6="" + ipv6="" + local_ipv6="" +fi # Configuring firewall if [ "$iptables" = 'yes' ]; then @@ -2227,6 +2309,12 @@ fi # Get public IP pub_ipv4="$(curl -fsLm5 --retry 2 --ipv4 https://ip.hestiacp.com/)" +if [ "$ipv6_support" = 'yes' ]; then + pub_ipv6="$(curl -fsLm5 --retry 2 --ipv6 https://ip.hestiacp.com/)" +else + pub_ipv6="" +fi + if [ -n "$pub_ipv4" ] && [ "$pub_ipv4" != "$ip" ]; then if [ -e /etc/rc.local ]; then sed -i '/exit 0/d' /etc/rc.local @@ -2274,7 +2362,17 @@ if [ "$apache" = 'yes' ] && [ "$nginx" = 'yes' ]; then fi # Adding default domain -$HESTIA/bin/v-add-web-domain "$username" "$servername" "$ip" +if [ -n "$ip" ]; then + if [ -n "$ipv6" ]; then + $HESTIA/bin/v-add-web-domain-ipv46 "$username" "$servername" "$ip" "$ipv6" + else + $HESTIA/bin/v-add-web-domain-ipv46 "$username" "$servername" "$ip" + fi +else + if [ -n "$ipv6" ]; then + $HESTIA/bin/v-add-web-domain-ipv46 "$username" "$servername" "" "$ipv6" + fi +fi check_result $? "can't create $servername domain" # Adding cron jobs @@ -2355,9 +2453,21 @@ fi' >> /root/.bashrc #----------------------------------------------------------# # Comparing hostname and IP -host_ip=$(host $servername | head -n 1 | awk '{print $NF}') -if [ "$host_ip" = "$ip" ]; then - ip="$servername" +host_ipv4=$(host -t A "$servername") +if [ $? -eq 0 ]; then + host_ipv4=$(echo "$host_ipv4" | sed -e 's/[^ ]* .* \([^ ]*\)/\1/') +else + host_ipv4="" +fi +if [ "$ipv6_support" = 'yes' ]; then + host_ipv6=$(host -t AAAA "$servername") + if [ $? -eq 0 ]; then + host_ipv6=$(echo "$host_ipv6" | sed -e 's/[^ ]* .* \([^ ]*\)/\1/') + else + host_ipv6="" + fi +else + host_ipv6="" fi echo -e "\n" @@ -2370,11 +2480,29 @@ echo -e "Congratulations! You have successfully installed Hestia Control Panel on your server. Ready to get started? Log in using the following credentials: - - Admin URL: https://$servername:$port" > $tmpfile -if [ "$host_ip" != "$ip" ]; then +" > $tmpfile +if [ -n "$ip" -a "$host_ipv4" = "$ip" ]; then + ipv4_accessible=1 +else + ipv4_accessible=0 +fi +if [ -n "$ipv6" -a "$host_ipv6" = "$ipv6" ]; then + ipv6_accessible=1 +else + ipv6_accessible=0 +fi +if [ $ipv4_accessible -eq 1 -o $ipv6_accessible -eq 1 ]; then + echo -e " Admin URL: https://$servername:$port" >> $tmpfile +else + echo -e " ${servername} is not accessible from internet!" >> $tmpfile + echo -e " Use Backup URL for Admin login:" >> $tmpfile +fi +if [ -n "$ip" ]; then echo " Backup URL: https://$ip:$port" >> $tmpfile fi +if [ -n "$ipv6" ]; then + echo " Backup URL: https://[$ipv6]:$port" >> $tmpfile +fi echo -e -n " Username: $username Password: $displaypass diff --git a/install/upgrade/manual/add_ipv6.sh b/install/upgrade/manual/add_ipv6.sh new file mode 100755 index 0000000000..2c132ca4d1 --- /dev/null +++ b/install/upgrade/manual/add_ipv6.sh @@ -0,0 +1,69 @@ +#!/bin/bash +source /etc/profile.d/hestia.sh +source /usr/local/hestia/func/main.sh + +#Download firewallv6 templates +if [ ! -e "$HESTIA/data/firewallv6" ]; then + mkdir -p $HESTIA/data/firewallv6 + chmod 770 $HESTIA/data/firewallv6 + + cp $HESTIA/install/rhel/6/firewallv6/* \ + $HESTIA/data/firewallv6/ + chmod 660 $HESTIA/data/firewallv6/* + +fi + +#download new templates +if [ -z $0 ]; then + $BIN/v-update-web-templates + $BIN/v-update-dns-templates +fi +#testing +#rm -rf /usr/local/hestia/data/templates/* +# cp -rf /usr/local/hestia/install/rhel/7/templates/* /usr/local/hestia/data/templates/es/ + +#set IPv4 version +iplist=$(ls --sort=time $HESTIA/data/ips/) +for ip in $iplist; do + echo "VERSION='4'" >> $HESTIA/data/ips/$ip +done + +#Add IP6 field +ipv6=$(ip addr show | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | grep -ve "^fe80" | tail -1) +ipv6use="" +if [ ! -z "$ipv6" ] && [ "::1" != "$ipv6" ]; then + netmask="ip addr show | grep '$ipv6' | awk -F '/' '{print \$2}' | awk '{print \$1}'" + netmask=$(eval $netmask) + $HESTIA/bin/v-add-sys-ip $ipv6 $netmask + $BIN/v-update-firewall-ipv6 + ipv6use=$ipv6 +fi + +#set IPv6 +userlist=$(ls --sort=time $HESTIA/data/users/) +for user in $userlist; do + USER_DATA="$HESTIA/data/users/$user" + + #UPDATE WEB + conf="$USER_DATA/web.conf" + while read line; do + eval $line + update_object_value 'web' 'DOMAIN' "$DOMAIN" '$IP6' "$ipv6use" + done < $conf + + #UPDATE DNS + conf="$USER_DATA/dns.conf" + while read line; do + eval $line + if [ "$(echo $line | grep 'IP6=')" == "" ]; then + sed -i "s/DOMAIN='$DOMAIN' IP='$IP'/DOMAIN='$DOMAIN' IP='$IP' IP6='$ipv6use'/g" "$conf" + else + update_object_value 'dns' 'DOMAIN' "$DOMAIN" '$IP6' "$ipv6use" + fi + done < $conf + $BIN/v-rebuild-user $user +done + +$BIN/v-update-sys-ip-counters + +$BIN/v-add-user-notification admin "IPv6 support" "Your hestia installation supports IPv6!" diff --git a/test/config-tests.bats b/test/config-tests.bats index e1bf8891f7..664fe68f21 100644 --- a/test/config-tests.bats +++ b/test/config-tests.bats @@ -27,6 +27,7 @@ function setup() { echo 'subdomain=cdn.testhestiacp.com' >> /tmp/hestia-test-env.sh echo 'database=test-5285_database' >> /tmp/hestia-test-env.sh echo 'dbuser=test-5285_dbuser' >> /tmp/hestia-test-env.sh + echo 'ipv6="fd66:69b9:9537:6ce3:11:22:33:44"' >> /tmp/hestia-test-env.sh fi source /tmp/hestia-test-env.sh @@ -35,12 +36,19 @@ function setup() { source $HESTIA/func/ip.sh } +@test "[ IPV6 ] Add IPV6 address" { + # Remove IPV6 Address to be removed when merged with main + run v-add-sys-ip "$ipv6" "/64" + assert_success + refute_output +} + @test "Setup Test domain" { run v-add-user $user $user $user@hestiacp.com default "Super Test" assert_success refute_output - run v-add-web-domain $user 'testhestiacp.com' + run v-add-web-domain-ipv46 $user 'testhestiacp.com' assert_success refute_output @@ -79,3 +87,10 @@ function setup() { assert_success refute_output } + +@test "[ IPV6 ] Delete IPV6 address" { + # Remove IPV6 Address to be removed when merged with main + run v-delete-sys-ip "$ipv6" + assert_success + refute_output +} diff --git a/test/letsencrypt.bats b/test/letsencrypt.bats index 0602e50f85..6eb185e69f 100644 --- a/test/letsencrypt.bats +++ b/test/letsencrypt.bats @@ -14,12 +14,22 @@ function random() { } function setup() { + if [ $BATS_TEST_NUMBER = 1 ]; then + echo 'ipv6="fd66:69b9:9537:6ce3:11:22:33:44"' >> /tmp/hestia-le-env.sh + fi source /tmp/hestia-le-env.sh source $HESTIA/func/main.sh source $HESTIA/conf/hestia.conf source $HESTIA/func/ip.sh } +@test "[ IPV6 ] Add IPV6 address" { + # Add IPV6 Address to be removed when merged with main + run v-add-sys-ip "$ipv6" "/64" + assert_success + refute_output +} + @test "[ User ] Create new user" { run v-add-user $user $user $user@hestiacp.com default "Super Test" assert_success @@ -33,13 +43,13 @@ function setup() { } @test "[ Web ] Create web domain" { - run v-add-web-domain $user $domain $ip yes "www.$domain,renewal.$domain,foobar.$domain,bar.$domain" + run v-add-web-domain-ipv46 $user $domain $ip $ipv6 yes "www.$domain,renewal.$domain,foobar.$domain,bar.$domain" assert_success refute_output } @test "[ Web ] Create 2nd web domain" { - run v-add-web-domain $user "hestia.$domain" $ip yes + run v-add-web-domain-ipv46 $user "hestia.$domain" $ip $ipv6 yes assert_success refute_output } @@ -105,7 +115,7 @@ function setup() { } @test "[ Redirect ] Create web domain" { - run v-add-web-domain $user "redirect.$domain" $ip yes + run v-add-web-domain-ipv46 $user "redirect.$domain" $ip $ipv6 yes assert_success refute_output } @@ -128,10 +138,15 @@ function setup() { refute_output } - - @test "Delete user" { run v-delete-user $user assert_success refute_output } + +@test "[ IPV6 ] Delete IPV6 address" { + # Remove IPV6 Address to be removed when merged with main + run v-delete-sys-ip "$ipv6" + assert_success + refute_output +} diff --git a/test/test.bats b/test/test.bats index 948d5280ad..947679574b 100755 --- a/test/test.bats +++ b/test/test.bats @@ -774,7 +774,7 @@ function check_ip_not_banned(){ # WEB # #----------------------------------------------------------# -@test "WEB: Add web domain" { +@test "WEB: Add web domain. Wrapper Script for compatibility purposes" { run v-add-web-domain $user $domain 198.18.0.125 assert_success refute_output @@ -785,7 +785,7 @@ function check_ip_not_banned(){ } @test "WEB: Add web domain (duplicate)" { - run v-add-web-domain $user $domain 198.18.0.125 + run v-add-web-domain-ipv46 $user $domain 198.18.0.125 assert_failure $E_EXISTS } @@ -910,7 +910,7 @@ function check_ip_not_banned(){ #----------------------------------------------------------# @test "WEB: Add IDN domain UTF idn-tést.eu" { - run v-add-web-domain $user idn-tést.eu 198.18.0.125 + run v-add-web-domain-ipv46 $user idn-tést.eu 198.18.0.125 assert_success refute_output @@ -921,7 +921,7 @@ function check_ip_not_banned(){ @test "WEB: Add IDN domain ASCII idn-tést.eu" { # Expected to fail due to utf exists - run v-add-web-domain $user "xn--idn-tst-fya.eu" 198.18.0.125 + run v-add-web-domain-ipv46 $user "xn--idn-tst-fya.eu" 198.18.0.125 assert_failure $E_EXISTS } @@ -939,7 +939,7 @@ function check_ip_not_banned(){ } @test "WEB: Add IDN domain UTF bløst.рф" { - run v-add-web-domain $user bløst.рф 198.18.0.125 + run v-add-web-domain-ipv46 $user bløst.рф 198.18.0.125 assert_success refute_output } @@ -963,7 +963,7 @@ function check_ip_not_banned(){ def_phpver=$(multiphp_default_version) multi_domain="multiphp.${domain}" - run v-add-web-domain $user $multi_domain 198.18.0.125 + run v-add-web-domain-ipv46 $user $multi_domain 198.18.0.125 assert_success refute_output @@ -1209,7 +1209,7 @@ function check_ip_not_banned(){ @test "Docroot: Self Subfolder" { docroot1_domain="docroot1.${domain}" - run v-add-web-domain $user $docroot1_domain 198.18.0.125 + run v-add-web-domain-ipv46 $user $docroot1_domain 198.18.0.125 assert_success refute_output @@ -1230,7 +1230,7 @@ function check_ip_not_banned(){ docroot1_domain="docroot1.${domain}" docroot2_domain="docroot2.${domain}" - run v-add-web-domain $user $docroot2_domain 198.18.0.125 + run v-add-web-domain-ipv46 $user $docroot2_domain 198.18.0.125 assert_success refute_output @@ -1741,11 +1741,11 @@ function check_ip_not_banned(){ assert_success refute_output - run v-add-web-domain $user2 $rootdomain + run v-add-web-domain-ipv46 $user2 $rootdomain assert_success refute_output - run v-add-web-domain $user $subdomain + run v-add-web-domain-ipv46 $user $subdomain assert_failure $E_EXISTS } @@ -1774,7 +1774,7 @@ function check_ip_not_banned(){ } @test "Allow Users: User can add user.user2.com" { - run v-add-web-domain $user $subdomain + run v-add-web-domain-ipv46 $user $subdomain assert_success refute_output } @@ -1823,12 +1823,12 @@ function check_ip_not_banned(){ } @test "Allow Users: User can't add user.user2.com again" { - run v-add-web-domain $user $subdomain + run v-add-web-domain-ipv46 $user $subdomain assert_failure $E_EXISTS } @test "Allow Users: user2 can add user.user2.com again" { - run v-add-web-domain $user2 $subdomain + run v-add-web-domain-ipv46 $user2 $subdomain assert_success refute_output } diff --git a/test/wildcard.bats b/test/wildcard.bats index 09bdcd86af..761a5d8edf 100644 --- a/test/wildcard.bats +++ b/test/wildcard.bats @@ -23,7 +23,7 @@ function setup() { # User and domain needs to already exists as dns domain due to DNS @test "[ Web ] Create web domain" { - run v-add-web-domain $user $domain $ip yes "*.$domain" + run v-add-web-domain-ipv46 $user $domain $ip $ipv6 yes "*.$domain" assert_success refute_output } diff --git a/web/add/dns/index.php b/web/add/dns/index.php index e728e27af0..bcd6bc2ba6 100644 --- a/web/add/dns/index.php +++ b/web/add/dns/index.php @@ -8,10 +8,14 @@ include $_SERVER["DOCUMENT_ROOT"] . "/inc/main.php"; // List ip addresses -exec(HESTIA_CMD . "v-list-user-ips " . $user . " json", $output, $return_var); +exec(HESTIA_CMD . "v-list-user-ips " . $user . " json 4", $output, $return_var); $v_ips = json_decode(implode("", $output), true); unset($output); +exec(HESTIA_CMD . "v-list-user-ips " . $user . " json 6", $output, $return_var); +$v_ipv6s = json_decode(implode("", $output), true); +unset($output); + // Check POST request for dns domain if (!empty($_POST["ok"])) { // Check token @@ -21,8 +25,8 @@ if (empty($_POST["v_domain"])) { $errors[] = _("Domain"); } - if (empty($_POST["v_ip"])) { - $errors[] = _("IP Address"); + if (empty($_POST["v_ip"]) && empty($_POST["v_ipv6"])) { + $errors[] = _("IPV4 and IPV6 Address"); } if (!empty($errors[0])) { foreach ($errors as $i => $error) { @@ -39,7 +43,15 @@ $v_domain = preg_replace("/^www./i", "", $_POST["v_domain"]); $v_domain = quoteshellarg($v_domain); $v_domain = strtolower($v_domain); + // Change IPV4 and IPV6 + if (empty($_POST["v_ip"])) { + $_POST["v_ip"] = ""; + } + if (empty($_POST["v_ipv6"])) { + $_POST["v_ipv6"] = ""; + } $v_ip = $_POST["v_ip"]; + $v_ipv6 = $_POST["v_ipv6"]; // Change NameServers if (empty($_POST["v_ns1"])) { $_POST["v_ns1"] = ""; @@ -82,13 +94,15 @@ if (empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . - "v-add-dns-domain " . + "v-add-dns-domain-ipv46 " . $user . " " . $v_domain . " " . quoteshellarg($v_ip) . " " . + quoteshellarg($v_ipv6) . + " " . $v_ns1 . " " . $v_ns2 . @@ -331,6 +345,11 @@ $v_ip = empty($v_ips[$ip]["NAT"]) ? $ip : $v_ips[$ip]["NAT"]; } +if (empty($v_ipv6) && count($v_ipv6s) > 0) { + $ipv6 = array_key_first($v_ipv6s); + $v_ipv6 = $ipv6; +} + // List dns templates exec(HESTIA_CMD . "v-list-dns-templates json", $output, $return_var); $templates = json_decode(implode("", $output), true); diff --git a/web/add/web/index.php b/web/add/web/index.php index 3b7c367294..ce7a3c0890 100644 --- a/web/add/web/index.php +++ b/web/add/web/index.php @@ -16,8 +16,8 @@ if (empty($_POST["v_domain"])) { $errors[] = _("Domain"); } - if (empty($_POST["v_ip"])) { - $errors[] = _("IP Address"); + if (empty($_POST["v_ip"]) && empty($_POST["v_ipv6"])) { + $errors[] = _("IPV4 and IPV6 Address"); } if (!empty($errors[0])) { @@ -37,6 +37,8 @@ // Define domain ip address $v_ip = quoteshellarg($_POST["v_ip"]); + // Define domain ipv6 address + $v_ipv6 = quoteshellarg($_POST["v_ipv6"]); // Using public IP instead of internal IP when creating DNS // Gets public IP from 'v-list-user-ips' command (that reads /hestia/data/ips/ip), precisely from 'NAT' field @@ -72,12 +74,14 @@ if (empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . - "v-add-web-domain " . + "v-add-web-domain-ipv46 " . $user . " " . quoteshellarg($v_domain) . " " . $v_ip . + " " . + $v_ipv6 . " 'yes'", $output, $return_var, @@ -97,12 +101,14 @@ if ($_POST["v_dns"] == "on" && empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . - "v-add-dns-domain " . + "v-add-dns-domain-ipv46 " . $user . " " . quoteshellarg($v_domain) . " " . $v_public_ip . + " " . + $v_ipv6 . " '' '' '' '' '' '' '' '' 'no'", $output, $return_var, @@ -178,7 +184,9 @@ $user_domains = array_keys($user_domains); unset($output); -$accept = $_GET["accept"] ?? ""; +if (empty($_GET["accept"])) { + $_GET["accept"] = false; +} $v_domain = $_POST["domain"] ?? ""; diff --git a/web/edit/dns/index.php b/web/edit/dns/index.php index 92e3c739a8..7761c882ba 100644 --- a/web/edit/dns/index.php +++ b/web/edit/dns/index.php @@ -40,6 +40,7 @@ $v_username = $user; $v_domain = $_GET["domain"]; $v_ip = $data[$v_domain]["IP"]; + $v_ipv6 = $data[$v_domain]["IP6"]; $v_template = $data[$v_domain]["TPL"]; $v_ttl = $data[$v_domain]["TTL"]; $v_dnssec = $data[$v_domain]["DNSSEC"]; @@ -98,7 +99,7 @@ // Check token verify_csrf($_POST); - // Change domain IP + // Change domain IPV4 if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { $v_ip = quoteshellarg($_POST["v_ip"]); exec( @@ -109,7 +110,33 @@ $v_domain . " " . $v_ip . - " 'no'", + " " . + "'no'" . + " " . + "4", + $output, + $return_var, + ); + check_return_code($return_var, $output); + $restart_dns = "yes"; + unset($output); + } + + // Change domain IPV6 + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + $v_ipv6 = quoteshellarg($_POST["v_ipv6"]); + exec( + HESTIA_CMD . + "v-change-dns-domain-ip " . + $user . + " " . + $v_domain . + " " . + $v_ipv6 . + " " . + "'no'" . + " " . + "6", $output, $return_var, ); diff --git a/web/edit/web/index.php b/web/edit/web/index.php index b76ffca4dd..6d88da262e 100644 --- a/web/edit/web/index.php +++ b/web/edit/web/index.php @@ -39,6 +39,7 @@ // Parse domain $v_ip = $data[$v_domain]["IP"]; +$v_ipv6 = $data[$v_domain]["IP6"]; $v_template = $data[$v_domain]["TPL"]; $v_aliases = str_replace(",", "\n", $data[$v_domain]["ALIAS"]); $valiases = explode(",", $data[$v_domain]["ALIAS"]); @@ -193,6 +194,7 @@ // Change web domain IP $v_newip = ""; + $v_newipv6 = ""; $v_newip_public = ""; if (!empty($_POST["v_ip"])) { @@ -200,6 +202,10 @@ $v_newip_public = empty($ips[$v_newip]["NAT"]) ? $v_newip : $ips[$v_newip]["NAT"]; } + if (!empty($_POST["v_ipv6"])) { + $v_newipv6 = $_POST["v_ipv6"]; + } + if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { exec( HESTIA_CMD . @@ -209,7 +215,10 @@ quoteshellarg($v_domain) . " " . quoteshellarg($_POST["v_ip"]) . - " 'no'", + " " . + "'no'" . + " " . + "4", $output, $return_var, ); @@ -219,38 +228,78 @@ unset($output); } - // Change dns domain IP - if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { exec( - HESTIA_CMD . "v-list-dns-domain " . $user . " " . quoteshellarg($v_domain) . " json", + HESTIA_CMD . + "v-change-web-domain-ip " . + $user . + " " . + quoteshellarg($v_domain) . + " " . + quoteshellarg($_POST["v_ipv6"]) . + " " . + "'no'" . + " " . + "6", $output, $return_var, ); + check_return_code($return_var, $output); + $restart_web = "yes"; + $restart_proxy = "yes"; unset($output); - if ($return_var == 0) { + } + + // Change dns domain IP + if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { + exec(HESTIA_CMD . "v-list-dns-domains " . $user . " " . " plain", $output, $return_var); + if (!empty($output)) { + unset($output); exec( HESTIA_CMD . - "v-change-dns-domain-ip " . + "v-list-dns-domain " . $user . " " . quoteshellarg($v_domain) . - " " . - quoteshellarg($v_newip_public) . - " 'no'", + " json", $output, $return_var, ); - check_return_code($return_var, $output); unset($output); - $restart_dns = "yes"; + if ($return_var == 0) { + exec( + HESTIA_CMD . + "v-change-dns-domain-ip " . + $user . + " " . + quoteshellarg($v_domain) . + " " . + quoteshellarg($v_newip_public) . + " " . + "'no'" . + " " . + "4", + $output, + $return_var, + ); + check_return_code($return_var, $output); + unset($output); + $restart_dns = "yes"; + } } } - // Change dns ip for each alias - if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { - foreach ($valiases as $v_alias) { + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + exec(HESTIA_CMD . "v-list-dns-domains " . $user . " " . " plain", $output, $return_var); + if (!empty($output)) { + unset($output); exec( - HESTIA_CMD . "v-list-dns-domain " . $user . " " . quoteshellarg($v_alias) . " json", + HESTIA_CMD . + "v-list-dns-domain " . + $user . + " " . + quoteshellarg($v_domain) . + " json", $output, $return_var, ); @@ -261,9 +310,13 @@ "v-change-dns-domain-ip " . $user . " " . - quoteshellarg($v_alias) . + quoteshellarg($v_domain) . + " " . + quoteshellarg($v_newipv6) . + " " . + "'no'" . " " . - quoteshellarg($v_newip_public), + "6", $output, $return_var, ); @@ -274,23 +327,113 @@ } } - // Change mail domain IP + // Change dns ip for each alias if ($v_ip != $_POST["v_ip"] && empty($_SESSION["error_msg"])) { - exec( - HESTIA_CMD . "v-list-mail-domain " . $user . " " . quoteshellarg($v_domain) . " json", - $output, - $return_var, - ); - unset($output); - if ($return_var == 0) { + foreach ($valiases as $v_alias) { + exec(HESTIA_CMD . "v-list-dns-domains " . $user . " " . " plain", $output, $return_var); + if (!empty($output)) { + unset($output); + exec( + HESTIA_CMD . + "v-list-dns-domain " . + $user . + " " . + quoteshellarg($v_alias) . + " json", + $output, + $return_var, + ); + unset($output); + if ($return_var == 0) { + exec( + HESTIA_CMD . + "v-change-dns-domain-ip " . + $user . + " " . + quoteshellarg($v_alias) . + " " . + quoteshellarg($v_newip_public) . + " " . + "'no'" . + " " . + "4", + $output, + $return_var, + ); + check_return_code($return_var, $output); + unset($output); + $restart_dns = "yes"; + } + } + } + } + + if ($v_ipv6 != $_POST["v_ipv6"] && empty($_SESSION["error_msg"])) { + foreach ($valiases as $v_alias) { + exec(HESTIA_CMD . "v-list-dns-domains " . $user . " " . " plain", $output, $return_var); + if (!empty($output)) { + unset($output); + exec( + HESTIA_CMD . + "v-list-dns-domain " . + $user . + " " . + quoteshellarg($v_alias) . + " json", + $output, + $return_var, + ); + unset($output); + if ($return_var == 0) { + exec( + HESTIA_CMD . + "v-change-dns-domain-ip " . + $user . + " " . + quoteshellarg($v_alias) . + " " . + quoteshellarg($v_newipv6) . + " " . + "'no'" . + " " . + "4", + $output, + $return_var, + ); + check_return_code($return_var, $output); + unset($output); + $restart_dns = "yes"; + } + } + } + } + + // Change mail domain IP + if (($v_ip != $_POST["v_ip"] || $v_ipv6 != $_POST["v_ipv6"]) && empty($_SESSION["error_msg"])) { + exec(HESTIA_CMD . "v-list-mail-domains " . $user . " " . " plain", $output, $return_var); + if (!empty($output)) { + unset($output); exec( - HESTIA_CMD . "v-rebuild-mail-domain " . $user . " " . quoteshellarg($v_domain), + HESTIA_CMD . + "v-list-mail-domain " . + $user . + " " . + quoteshellarg($v_domain) . + " json", $output, $return_var, ); - check_return_code($return_var, $output); unset($output); - $restart_email = "yes"; + if ($return_var == 0) { + exec( + HESTIA_CMD . "v-rebuild-mail-domain " . $user . " " . quoteshellarg($v_domain), + $output, + $return_var, + ); + check_return_code($return_var, $output); + unset($output); + $restart_email = "yes"; + } } } @@ -569,6 +712,8 @@ quoteshellarg($alias) . " " . quoteshellarg($v_newip_public ?: $v_ip_public) . + " " . + quoteshellarg($v_newipv6 ?: $v_ipv6) . " no", $output, $return_var, diff --git a/web/templates/pages/add_dns.php b/web/templates/pages/add_dns.php index 5eb8b3d44e..8c408c4170 100644 --- a/web/templates/pages/add_dns.php +++ b/web/templates/pages/add_dns.php @@ -51,7 +51,7 @@ " required>
- +
">
+
+ +
+ + "> +
+
- + +
+
+ + diff --git a/web/templates/pages/edit_dns.php b/web/templates/pages/edit_dns.php index 73d834e729..9744a9207b 100644 --- a/web/templates/pages/edit_dns.php +++ b/web/templates/pages/edit_dns.php @@ -30,21 +30,41 @@ ">
- +
">
+
+ +
+ + "> +
+
- + +
+
+ + diff --git a/web/templates/pages/list_web.php b/web/templates/pages/list_web.php index 2a0c620822..f31d276c16 100644 --- a/web/templates/pages/list_web.php +++ b/web/templates/pages/list_web.php @@ -30,8 +30,11 @@
  • -
  • - +
  • + +
  • +
  • +
  • @@ -80,7 +83,8 @@
    -
    +
    +
    @@ -187,7 +191,8 @@ } ?>
    - : + :
    +
    + : + +
    :