From b8a1687f88f4abd159c0845559c6352739ef0ca0 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Fri, 3 Apr 2020 20:30:00 +0200 Subject: [PATCH 01/91] Use a dedicated php service for each app --- data/helpers.d/php | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 41af467c5..5167afa2a 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -18,7 +18,7 @@ ynh_add_fpm_config () { phpversion="${phpversion:-7.0}" local fpm_config_dir="/etc/php/$phpversion/fpm" - local fpm_service="php${phpversion}-fpm" + local fpm_service="php${phpversion}-fpm-$app" # Configure PHP-FPM 5 on Debian Jessie if [ "$(ynh_get_debian_release)" == "jessie" ]; then fpm_config_dir="/etc/php5/fpm" @@ -45,7 +45,27 @@ ynh_add_fpm_config () { chown root: "$finalphpini" ynh_store_file_checksum "$finalphpini" fi - ynh_systemd_action --service_name=$fpm_service --action=reload + + # Create a config for a dedicated php-fpm service for the app + echo " +[Unit] +Description=PHP $phpversion FastCGI Process Manager for $app +After=network.target + +[Service] +Type=notify +PIDFile=/run/php/php${phpversion}-fpm-$app.pid +ExecStart=/usr/sbin/php-fpm${phpversion} --nodaemonize --fpm-config $finalphpconf --pid /run/php/php${phpversion}-fpm-$app.pid +ExecReload=/bin/kill -USR2 \$MAINPID + +[Install] +WantedBy=multi-user.target +" > ../conf/$fpm_service + + # Create this dedicated php-fpm service + ynh_add_systemd_config --service=$fpm_service --template=$fpm_service + + ynh_systemd_action --service_name=$fpm_service --action=restart } # Remove the dedicated php-fpm config @@ -61,7 +81,10 @@ ynh_remove_fpm_config () { fpm_config_dir="/etc/php/7.0/fpm" fpm_service="php7.0-fpm" fi + + # Remove the dedicated service php-fpm service + ynh_remove_systemd_config --service=$fpm_service + ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf" ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" 2>&1 - ynh_systemd_action --service_name=$fpm_service --action=reload } From 147b2490074d4d306fd68e43a8502afd597d2737 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sat, 4 Apr 2020 01:19:49 +0200 Subject: [PATCH 02/91] Proper migration to new directory --- data/helpers.d/php | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 5167afa2a..3c5390f56 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -17,17 +17,36 @@ ynh_add_fpm_config () { # Configure PHP-FPM 7.0 by default phpversion="${phpversion:-7.0}" - local fpm_config_dir="/etc/php/$phpversion/fpm" + local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm" + local old_fpm_config_dir="/etc/php/$phpversion/fpm" local fpm_service="php${phpversion}-fpm-$app" # Configure PHP-FPM 5 on Debian Jessie if [ "$(ynh_get_debian_release)" == "jessie" ]; then fpm_config_dir="/etc/php5/fpm" fpm_service="php5-fpm" fi + + # Create the directory for fpm pools + mkdir -p "$fpm_config_dir/pool.d" + ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir" ynh_app_setting_set --app=$app --key=fpm_service --value="$fpm_service" finalphpconf="$fpm_config_dir/pool.d/$app.conf" - ynh_backup_if_checksum_is_different --file="$finalphpconf" + + # Migrate from mutual php service to dedicated one. + if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ] + then + ynh_print_info --message="Migrate to a dedicated php-fpm service for $app." + # Create a backup of the old file before migration + ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf" + # Remove the old php config file + ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf" + # Reload php to release the socket and allow the dedicated service to use it + systemctl reload php${phpversion}-fpm + else + ynh_backup_if_checksum_is_different --file="$finalphpconf" + fi + cp ../conf/php-fpm.conf "$finalphpconf" ynh_replace_string --match_string="__NAMETOCHANGE__" --replace_string="$app" --target_file="$finalphpconf" ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalphpconf" From 195214bdbfcab61979fe2bb83c05e6d2483765b8 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Sun, 5 Apr 2020 23:18:58 +0200 Subject: [PATCH 03/91] Update data/helpers.d/php Co-Authored-By: Kayou --- data/helpers.d/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 3c5390f56..13036684b 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -103,7 +103,7 @@ ynh_remove_fpm_config () { # Remove the dedicated service php-fpm service ynh_remove_systemd_config --service=$fpm_service - + yunohost service remove $fpm_service ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf" ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" 2>&1 } From 4968f1aa7df184c9c731a2fb3c2d4f4aa04a719e Mon Sep 17 00:00:00 2001 From: Kayou Date: Thu, 9 Apr 2020 18:08:51 +0200 Subject: [PATCH 04/91] [fix] custom_portal and custom_overlay redirect --- data/templates/nginx/plain/yunohost_panel.conf.inc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/templates/nginx/plain/yunohost_panel.conf.inc b/data/templates/nginx/plain/yunohost_panel.conf.inc index 1c5a2d656..53a69d705 100644 --- a/data/templates/nginx/plain/yunohost_panel.conf.inc +++ b/data/templates/nginx/plain/yunohost_panel.conf.inc @@ -4,5 +4,5 @@ sub_filter_once on; # Apply to other mime types than text/html sub_filter_types application/xhtml+xml; # Prevent YunoHost panel files from being blocked by specific app rules -location ~ (ynh_portal.js|ynh_overlay.css|ynh_userinfo.json) { +location ~ (ynh_portal.js|ynh_overlay.css|ynh_userinfo.json|ynhtheme/custom_portal.js|ynhtheme/custom_overlay.css) { } From 7f48631c3f4831f7f293f345865bee6e33d5dc11 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Mon, 13 Apr 2020 13:36:33 +0200 Subject: [PATCH 05/91] Optionnal dedicated service --- data/helpers.d/php | 80 ++++++++++++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 13036684b..14b107582 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -2,24 +2,35 @@ # Create a dedicated php-fpm config # -# usage: ynh_add_fpm_config [--phpversion=7.X] +# usage: ynh_add_fpm_config [--phpversion=7.X] [--dedicated_service] # | arg: -v, --phpversion - Version of php to use. +# | arg: -d, --dedicated_service - Use a dedicated php-fpm service instead of the common one. # # Requires YunoHost version 2.7.2 or higher. ynh_add_fpm_config () { # Declare an array to define the options of this helper. - local legacy_args=v - declare -Ar args_array=( [v]=phpversion= ) + local legacy_args=vd + declare -Ar args_array=( [v]=phpversion= [d]=dedicated_service ) local phpversion + local dedicated_service # Manage arguments with getopts ynh_handle_getopts_args "$@" # Configure PHP-FPM 7.0 by default phpversion="${phpversion:-7.0}" - local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm" - local old_fpm_config_dir="/etc/php/$phpversion/fpm" - local fpm_service="php${phpversion}-fpm-$app" + # Do not use a dedicated service by default + dedicated_service=${dedicated_service:-0} + + if [ $dedicated_service -eq 1 ] + then + local fpm_service="php${phpversion}-fpm-$app" + local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm" + local old_fpm_config_dir="/etc/php/$phpversion/fpm" + else + local fpm_service="php${phpversion}-fpm" + local fpm_config_dir="/etc/php/$phpversion/fpm" + fi # Configure PHP-FPM 5 on Debian Jessie if [ "$(ynh_get_debian_release)" == "jessie" ]; then fpm_config_dir="/etc/php5/fpm" @@ -31,22 +42,27 @@ ynh_add_fpm_config () { ynh_app_setting_set --app=$app --key=fpm_config_dir --value="$fpm_config_dir" ynh_app_setting_set --app=$app --key=fpm_service --value="$fpm_service" + ynh_app_setting_set --app=$app --key=fpm_dedicated_service --value="$dedicated_service" finalphpconf="$fpm_config_dir/pool.d/$app.conf" # Migrate from mutual php service to dedicated one. - if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ] + if [ $dedicated_service -eq 1 ] then - ynh_print_info --message="Migrate to a dedicated php-fpm service for $app." - # Create a backup of the old file before migration - ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf" - # Remove the old php config file - ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf" - # Reload php to release the socket and allow the dedicated service to use it - systemctl reload php${phpversion}-fpm - else - ynh_backup_if_checksum_is_different --file="$finalphpconf" + # If a config file exist in the common pool, move it. + if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ] + then + ynh_print_info --message="Migrate to a dedicated php-fpm service for $app." + # Create a backup of the old file before migration + ynh_backup_if_checksum_is_different --file="$old_fpm_config_dir/pool.d/$app.conf" + # Remove the old php config file + ynh_secure_remove --file="$old_fpm_config_dir/pool.d/$app.conf" + # Reload php to release the socket and allow the dedicated service to use it + ynh_systemd_action --service_name=php${phpversion}-fpm --action=reload + fi fi + ynh_backup_if_checksum_is_different --file="$finalphpconf" + cp ../conf/php-fpm.conf "$finalphpconf" ynh_replace_string --match_string="__NAMETOCHANGE__" --replace_string="$app" --target_file="$finalphpconf" ynh_replace_string --match_string="__FINALPATH__" --replace_string="$final_path" --target_file="$finalphpconf" @@ -65,9 +81,10 @@ ynh_add_fpm_config () { ynh_store_file_checksum "$finalphpini" fi - # Create a config for a dedicated php-fpm service for the app - echo " -[Unit] + if [ $dedicated_service -eq 1 ] + then + # Create a config for a dedicated php-fpm service for the app + echo "[Unit] Description=PHP $phpversion FastCGI Process Manager for $app After=network.target @@ -81,10 +98,14 @@ ExecReload=/bin/kill -USR2 \$MAINPID WantedBy=multi-user.target " > ../conf/$fpm_service - # Create this dedicated php-fpm service - ynh_add_systemd_config --service=$fpm_service --template=$fpm_service - - ynh_systemd_action --service_name=$fpm_service --action=restart + # Create this dedicated php-fpm service + ynh_add_systemd_config --service=$fpm_service --template=$fpm_service + # Restart the service, as this service is either stopped or only for this app + ynh_systemd_action --service_name=$fpm_service --action=restart + else + # Reload php, to not impact other parts of the system using php + ynh_systemd_action --service_name=$fpm_service --action=reload + fi } # Remove the dedicated php-fpm config @@ -95,15 +116,20 @@ WantedBy=multi-user.target ynh_remove_fpm_config () { local fpm_config_dir=$(ynh_app_setting_get --app=$app --key=fpm_config_dir) local fpm_service=$(ynh_app_setting_get --app=$app --key=fpm_service) - # Assume php version 7 if not set + local dedicated_service=$(ynh_app_setting_get --app=$app --key=fpm_dedicated_service) + dedicated_service=${dedicated_service:-0} + # Assume php version 7.0 if not set if [ -z "$fpm_config_dir" ]; then fpm_config_dir="/etc/php/7.0/fpm" fpm_service="php7.0-fpm" fi - # Remove the dedicated service php-fpm service - ynh_remove_systemd_config --service=$fpm_service - yunohost service remove $fpm_service + if [ $dedicated_service -eq 1 ] + then + # Remove the dedicated service php-fpm service for the app + ynh_remove_systemd_config --service=$fpm_service + fi + ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf" ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" 2>&1 } From 613142c34290b422e8d130432bc251e1a258503b Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Mon, 13 Apr 2020 13:37:00 +0200 Subject: [PATCH 06/91] Dedicate log for each php service --- data/helpers.d/php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 14b107582..7aefc697e 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -83,6 +83,15 @@ ynh_add_fpm_config () { if [ $dedicated_service -eq 1 ] then + # Create a dedicated php-fpm.conf for the service + local globalphpconf=$fpm_config_dir/php-fpm-$app.conf + cp /etc/php/${phpversion}/fpm/php-fpm.conf $globalphpconf + + ynh_replace_string --match_string="^[; ]*pid *=.*" --replace_string="pid = /run/php/php${phpversion}-fpm-$app.pid" --target_file="$globalphpconf" + ynh_replace_string --match_string="^[; ]*error_log *=.*" --replace_string="error_log = /var/log/php/fpm-php.$app.log" --target_file="$globalphpconf" + ynh_replace_string --match_string="^[; ]*syslog.ident *=.*" --replace_string="syslog.ident = php-fpm-$app" --target_file="$globalphpconf" + ynh_replace_string --match_string="^[; ]*include *=.*" --replace_string="include = $finalphpconf" --target_file="$globalphpconf" + # Create a config for a dedicated php-fpm service for the app echo "[Unit] Description=PHP $phpversion FastCGI Process Manager for $app @@ -91,7 +100,7 @@ After=network.target [Service] Type=notify PIDFile=/run/php/php${phpversion}-fpm-$app.pid -ExecStart=/usr/sbin/php-fpm${phpversion} --nodaemonize --fpm-config $finalphpconf --pid /run/php/php${phpversion}-fpm-$app.pid +ExecStart=/usr/sbin/php-fpm$phpversion --nodaemonize --fpm-config $globalphpconf ExecReload=/bin/kill -USR2 \$MAINPID [Install] @@ -100,6 +109,10 @@ WantedBy=multi-user.target # Create this dedicated php-fpm service ynh_add_systemd_config --service=$fpm_service --template=$fpm_service + # Integrate the service in YunoHost admin panel + yunohost service add $fpm_service --log /var/log/php/fpm-php.$app.log --log_type file --description "Php-fpm dedicated to $app" + # Configure log rotate + ynh_use_logrotate --logfile=/var/log/php # Restart the service, as this service is either stopped or only for this app ynh_systemd_action --service_name=$fpm_service --action=restart else @@ -128,6 +141,10 @@ ynh_remove_fpm_config () { then # Remove the dedicated service php-fpm service for the app ynh_remove_systemd_config --service=$fpm_service + # Remove the global php-fpm conf + ynh_secure_remove --file="$fpm_config_dir/php-fpm-$app.conf" + # Remove the service from the list of services known by Yunohost + yunohost service remove $fpm_service fi ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf" From a62513b0b0bb24d83031a614f34d687b42a835f1 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Mon, 13 Apr 2020 16:21:45 +0200 Subject: [PATCH 07/91] Clean after conflict --- data/helpers.d/php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index b479747c6..7a26824a4 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -77,6 +77,8 @@ ynh_add_fpm_config () { if [ -n "$usage" ] || [ -n "$footprint" ]; then use_template=0 fi + # Do not use a dedicated service by default + dedicated_service=${dedicated_service:-0} # Set the default PHP-FPM version by default phpversion="${phpversion:-$YNH_PHP_VERSION}" @@ -98,14 +100,10 @@ ynh_add_fpm_config () { ynh_add_app_dependencies --package="$package" fi - # Do not use a dedicated service by default - dedicated_service=${dedicated_service:-0} - if [ $dedicated_service -eq 1 ] then local fpm_service="php${phpversion}-fpm-$app" local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm" - local old_fpm_config_dir="/etc/php/$phpversion/fpm" else local fpm_service="php${phpversion}-fpm" local fpm_config_dir="/etc/php/$phpversion/fpm" @@ -128,6 +126,7 @@ ynh_add_fpm_config () { # Migrate from mutual php service to dedicated one. if [ $dedicated_service -eq 1 ] then + local old_fpm_config_dir="/etc/php/$phpversion/fpm" # If a config file exist in the common pool, move it. if [ -e "$old_fpm_config_dir/pool.d/$app.conf" ] then @@ -163,7 +162,7 @@ ynh_add_fpm_config () { ynh_get_scalable_phpfpm --usage=$usage --footprint=$footprint # Copy the default file - cp "$fpm_config_dir/pool.d/www.conf" "$finalphpconf" + cp "/etc/php/$phpversion/fpm/pool.d/www.conf" "$finalphpconf" # Replace standard variables into the default file ynh_replace_string --match_string="^\[www\]" --replace_string="[$app]" --target_file="$finalphpconf" From 8005429dc489f856f579b3406629b43ffc45af86 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Mon, 13 Apr 2020 20:50:58 +0200 Subject: [PATCH 08/91] Update php --- data/helpers.d/php | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 118477a0b..19e586b70 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -355,6 +355,18 @@ ynh_get_scalable_phpfpm () { footprint=50 fi + # Define the factor to determine min_spare_servers + # To not have not enough children ready to start for heavy apps. + if [ $footprint -le 20 ] + then + min_spare_servers_factor=8 + elif [ $footprint -le 35 ] + then + min_spare_servers_factor=5 + else + min_spare_servers_factor=3 + fi + # Define the way the process manager handle child processes. if [ "$usage" = "low" ] then @@ -405,7 +417,7 @@ ynh_get_scalable_phpfpm () { if [ "$php_pm" = "dynamic" ] then # Define pm.start_servers, pm.min_spare_servers and pm.max_spare_servers for a dynamic process manager - php_min_spare_servers=$(( $php_max_children / 8 )) + php_min_spare_servers=$(( $php_max_children / $min_spare_servers_factor )) php_min_spare_servers=$(at_least_one $php_min_spare_servers) php_max_spare_servers=$(( $php_max_children / 2 )) From df47040462983b65b0978f17960a6920279a712b Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Tue, 14 Apr 2020 12:47:29 +0200 Subject: [PATCH 09/91] Allow to overwrite pm.max_children --- data/helpers.d/php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/data/helpers.d/php b/data/helpers.d/php index 118477a0b..401c262b3 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -402,6 +402,12 @@ ynh_get_scalable_phpfpm () { php_max_children=$max_proc fi + # Get an potential forced value for php_max_children + local php_forced_max_children=$(ynh_app_setting_get --app=$app --key=php_forced_max_children) + if [ -n "$php_forced_max_children" ]; then + php_max_children=$php_forced_max_children + fi + if [ "$php_pm" = "dynamic" ] then # Define pm.start_servers, pm.min_spare_servers and pm.max_spare_servers for a dynamic process manager From f57b302299f9ea4b90d09db248329ce374de6980 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Fri, 17 Apr 2020 00:40:00 +0200 Subject: [PATCH 10/91] Update data/helpers.d/php Co-Authored-By: Kayou --- data/helpers.d/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 7a26824a4..3509bdc3d 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -292,7 +292,7 @@ ynh_remove_fpm_config () { fi ynh_secure_remove --file="$fpm_config_dir/pool.d/$app.conf" - ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" 2>&1 + ynh_exec_warn_less ynh_secure_remove --file="$fpm_config_dir/conf.d/20-$app.ini" # If the php version used is not the default version for YunoHost if [ "$phpversion" != "$YNH_DEFAULT_PHP_VERSION" ] From 05503d2f8ea5623831b809dd16a8ad6189ecdc21 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Fri, 17 Apr 2020 00:48:32 +0200 Subject: [PATCH 11/91] fpm_service name --- data/helpers.d/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 3509bdc3d..4d3cdc480 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -102,7 +102,7 @@ ynh_add_fpm_config () { if [ $dedicated_service -eq 1 ] then - local fpm_service="php${phpversion}-fpm-$app" + local fpm_service="${app}-phpfpm" local fpm_config_dir="/etc/php/$phpversion/dedicated-fpm" else local fpm_service="php${phpversion}-fpm" From 8e83f8aa2904bae4a253b40d572b64ede5a326af Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Apr 2020 00:16:18 +0200 Subject: [PATCH 12/91] Add a 'yunohost diagnosis get' to get one specific raw info --- data/actionsmap/yunohost.yml | 13 +++++- data/hooks/diagnosis/10-ip.py | 4 +- data/hooks/diagnosis/14-ports.py | 4 +- src/yunohost/diagnosis.py | 76 +++++++++++++++++++++++--------- 4 files changed, 70 insertions(+), 27 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 44419a342..48b1687d4 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1676,7 +1676,7 @@ diagnosis: action: store_true run: - action_help: Show most recents diagnosis results + action_help: Run diagnosis api: POST /diagnosis/run arguments: categories: @@ -1701,3 +1701,14 @@ diagnosis: --list: help: List active ignore filters action: store_true + + get: + action_help: Low-level command to fetch raw data and status about a specific diagnosis test + api: GET /diagnosis/item/ + arguments: + category: + help: Diagnosis category to fetch results from + item: + help: "List of criteria describing the test. Must correspond exactly to the 'meta' infos in 'yunohost diagnosis show'" + metavar: CRITERIA + nargs: "*" diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 552092fe3..32232457e 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -72,13 +72,13 @@ class IPDiagnoser(Diagnoser): ipv4 = self.get_public_ip(4) if can_ping_ipv4 else None ipv6 = self.get_public_ip(6) if can_ping_ipv6 else None - yield dict(meta={"test": "ip", "version": 4}, + yield dict(meta={"test": "ip", "version": '4'}, data=ipv4, status="SUCCESS" if ipv4 else "ERROR", summary=("diagnosis_ip_connected_ipv4", {}) if ipv4 else ("diagnosis_ip_no_ipv4", {})) - yield dict(meta={"test": "ip", "version": 6}, + yield dict(meta={"test": "ip", "version": '6'}, data=ipv6, status="SUCCESS" if ipv6 else "WARNING", summary=("diagnosis_ip_connected_ipv6", {}) if ipv6 diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 7730ddb57..712d0007b 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -46,12 +46,12 @@ class PortsDiagnoser(Diagnoser): for port, service in sorted(ports.items()): category = services[service].get("category", "[?]") if r["ports"].get(str(port), None) is not True: - yield dict(meta={"port": port, "needed_by": service}, + yield dict(meta={"port": str(port)}, status="ERROR", summary=("diagnosis_ports_unreachable", {"port": port}), details=[("diagnosis_ports_needed_by", (service, category)), ("diagnosis_ports_forwarding_tip", ())]) else: - yield dict(meta={"port": port, "needed_by": service}, + yield dict(meta={"port": str(port)}, status="SUCCESS", summary=("diagnosis_ports_ok", {"port": port}), details=[("diagnosis_ports_needed_by", (service, category))]) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index db791fcdf..7f488b6aa 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -44,6 +44,25 @@ def diagnosis_list(): return {"categories": all_categories_names} +def diagnosis_get(category, item): + + # Get all the categories + all_categories = _list_diagnosis_categories() + all_categories_names = [c for c, _ in all_categories] + + if category not in all_categories_names: + raise YunohostError('diagnosis_unknown_categories', categories=category) + + if isinstance(item, list): + if any("=" not in criteria for criteria in item): + raise YunohostError("Criterias should be of the form key=value (e.g. domain=yolo.test)") + + # Convert the provided criteria into a nice dict + item = {c.split("=")[0]: c.split("=")[1] for c in item} + + return Diagnoser.get_cached_report(category, item=item) + + def diagnosis_show(categories=[], issues=False, full=False, share=False): # Get all the categories @@ -56,7 +75,7 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): else: unknown_categories = [c for c in categories if c not in all_categories_names] if unknown_categories: - raise YunohostError('diagnosis_unknown_categories', categories=", ".join(categories)) + raise YunohostError('diagnosis_unknown_categories', categories=", ".join(unknown_categories)) if not os.path.exists(DIAGNOSIS_CACHE): logger.warning(m18n.n("diagnosis_never_ran_yet")) @@ -65,19 +84,14 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): # Fetch all reports all_reports = [] for category in categories: - if not os.path.exists(Diagnoser.cache_file(category)): - logger.warning(m18n.n("diagnosis_no_cache", category=category)) - report = {"id": category, - "cached_for": -1, - "timestamp": -1, - "items": []} - Diagnoser.i18n(report) - else: - try: - report = Diagnoser.get_cached_report(category) - except Exception as e: - logger.error(m18n.n("diagnosis_failed", category=category, error=str(e))) - continue + + try: + report = Diagnoser.get_cached_report(category) + except Exception as e: + logger.error(m18n.n("diagnosis_failed", category=category, error=str(e))) + continue + + Diagnoser.i18n(report) add_ignore_flag_to_issues(report) if not full: @@ -221,7 +235,7 @@ def diagnosis_ignore(add_filter=None, remove_filter=None, list=False): if category not in all_categories_names: raise YunohostError("%s is not a diagnosis category" % category) if any("=" not in criteria for criteria in filter_[1:]): - raise YunohostError("Extra criterias should be of the form key=value (e.g. domain=yolo.test)") + raise YunohostError("Criterias should be of the form key=value (e.g. domain=yolo.test)") # Convert the provided criteria into a nice dict criterias = {c.split("=")[0]: c.split("=")[1] for c in filter_[1:]} @@ -356,7 +370,12 @@ class Diagnoser(): for dependency in self.dependencies: dep_report = Diagnoser.get_cached_report(dependency) - dep_errors = [item for item in dep_report["items"] if item["status"] == "ERROR"] + + if dep_report["timestamp"] == -1: # No cache yet for this dep + dep_errors = True + else: + dep_errors = [item for item in dep_report["items"] if item["status"] == "ERROR"] + if dep_errors: logger.error(m18n.n("diagnosis_cant_run_because_of_dep", category=self.description, dep=Diagnoser.get_description(dependency))) return 1, {} @@ -396,12 +415,25 @@ class Diagnoser(): return os.path.join(DIAGNOSIS_CACHE, "%s.json" % id_) @staticmethod - def get_cached_report(id_): - filename = Diagnoser.cache_file(id_) - report = read_json(filename) - report["timestamp"] = int(os.path.getmtime(filename)) - Diagnoser.i18n(report) - return report + def get_cached_report(id_, item=None): + cache_file = Diagnoser.cache_file(id_) + if not os.path.exists(cache_file): + logger.warning(m18n.n("diagnosis_no_cache", category=id_)) + report = {"id": category, + "cached_for": -1, + "timestamp": -1, + "items": []} + else: + report = read_json(cache_file) + report["timestamp"] = int(os.path.getmtime(cache_file)) + + if item: + for report_item in report["items"]: + if report_item.get("meta") == item: + return report_item + return {} + else: + return report @staticmethod def get_description(id_): From f0c0f63bb4da66e5e052dbe6efea433586fa8525 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 7 Apr 2020 02:21:33 +0200 Subject: [PATCH 13/91] Let's use dict for details data, much better for semantic when defining strings etc... --- data/hooks/diagnosis/00-basesystem.py | 10 +++++++--- data/hooks/diagnosis/10-ip.py | 2 +- data/hooks/diagnosis/12-dnsrecords.py | 14 ++++++++------ data/hooks/diagnosis/14-ports.py | 5 +++-- data/hooks/diagnosis/21-web.py | 2 +- data/hooks/diagnosis/30-services.py | 4 ++-- data/hooks/diagnosis/90-security.py | 2 +- locales/en.json | 11 ++++++----- src/yunohost/diagnosis.py | 2 +- 9 files changed, 30 insertions(+), 22 deletions(-) diff --git a/data/hooks/diagnosis/00-basesystem.py b/data/hooks/diagnosis/00-basesystem.py index bf7a27047..3c932b488 100644 --- a/data/hooks/diagnosis/00-basesystem.py +++ b/data/hooks/diagnosis/00-basesystem.py @@ -27,7 +27,7 @@ class BaseSystemDiagnoser(Diagnoser): if os.path.exists("/proc/device-tree/model"): model = read_file('/proc/device-tree/model').strip() hardware["data"]["board"] = model - hardware["details"] = [("diagnosis_basesystem_hardware_board", (model,))] + hardware["details"] = [("diagnosis_basesystem_hardware_board", {"model": model})] yield hardware @@ -51,8 +51,12 @@ class BaseSystemDiagnoser(Diagnoser): # Here, ynh_core_version is for example "3.5.4.12", so [:3] is "3.5" and we check it's the same for all packages ynh_core_version = ynh_packages["yunohost"]["version"] consistent_versions = all(infos["version"][:3] == ynh_core_version[:3] for infos in ynh_packages.values()) - ynh_version_details = [("diagnosis_basesystem_ynh_single_version", (package, infos["version"], infos["repo"])) - for package, infos in ynh_packages.items()] + ynh_version_details = [("diagnosis_basesystem_ynh_single_version", + {"package":package, + "version": infos["version"], + "repo": infos["repo"]} + ) + for package, infos in ynh_packages.items()] if consistent_versions: yield dict(meta={"test": "ynh_versions"}, diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 32232457e..7e96a7b56 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -58,7 +58,7 @@ class IPDiagnoser(Diagnoser): yield dict(meta={"test": "dnsresolv"}, status="WARNING", summary=("diagnosis_ip_weird_resolvconf", {}), - details=[("diagnosis_ip_weird_resolvconf_details", ())]) + details=[("diagnosis_ip_weird_resolvconf_details", {})]) else: yield dict(meta={"test": "dnsresolv"}, status="SUCCESS", diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index a889201b9..5d8a12ebb 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -52,15 +52,17 @@ class DNSRecordsDiagnoser(Diagnoser): discrepancies = [] for r in records: - current_value = self.get_current_record(domain, r["name"], r["type"]) or "None" - expected_value = r["value"] if r["value"] != "@" else domain + "." + r["current"] = self.get_current_record(domain, r["name"], r["type"]) or "None" + if r["value"] == "@": + r["value"] = domain + "." - if current_value == "None": - discrepancies.append(("diagnosis_dns_missing_record", (r["type"], r["name"], expected_value))) - elif current_value != expected_value: - discrepancies.append(("diagnosis_dns_discrepancy", (r["type"], r["name"], expected_value, current_value))) + if r["current"] == "None": + discrepancies.append(("diagnosis_dns_missing_record", r)) + elif r["current"] != r["value"]: + discrepancies.append(("diagnosis_dns_discrepancy", r)) if discrepancies: + discrepancies = [("diagnosis_dns_point_to_doc", {})] + discrepancies status = "ERROR" if (category == "basic" or (is_main_domain and category != "extra")) else "WARNING" summary = ("diagnosis_dns_bad_conf", {"domain": domain, "category": category}) else: diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 712d0007b..fe7c9003d 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -49,12 +49,13 @@ class PortsDiagnoser(Diagnoser): yield dict(meta={"port": str(port)}, status="ERROR", summary=("diagnosis_ports_unreachable", {"port": port}), - details=[("diagnosis_ports_needed_by", (service, category)), ("diagnosis_ports_forwarding_tip", ())]) + details=[("diagnosis_ports_needed_by", {"service": service, "category": category}), + ("diagnosis_ports_forwarding_tip", {})]) else: yield dict(meta={"port": str(port)}, status="SUCCESS", summary=("diagnosis_ports_ok", {"port": port}), - details=[("diagnosis_ports_needed_by", (service, category))]) + details=[("diagnosis_ports_needed_by", {"service": service, "category": category})]) def main(args, env, loggers): diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 2a3afba88..6b65b8da3 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -51,7 +51,7 @@ class WebDiagnoser(Diagnoser): yield dict(meta={"domain": domain}, status="ERROR", summary=("diagnosis_http_unreachable", {"domain": domain}), - details=[(detail,())]) + details=[(detail,{})]) # In there or idk where else ... # try to diagnose hairpinning situation by crafting a request for the diff --git a/data/hooks/diagnosis/30-services.py b/data/hooks/diagnosis/30-services.py index a46fa735d..9d6879933 100644 --- a/data/hooks/diagnosis/30-services.py +++ b/data/hooks/diagnosis/30-services.py @@ -22,12 +22,12 @@ class ServicesDiagnoser(Diagnoser): if result["status"] != "running": item["status"] = "ERROR" item["summary"] = ("diagnosis_services_bad_status", {"service": service, "status": result["status"]}) - item["details"] = [("diagnosis_services_bad_status_tip", (service,))] + item["details"] = [("diagnosis_services_bad_status_tip", {"service":service})] elif result["configuration"] == "broken": item["status"] = "WARNING" item["summary"] = ("diagnosis_services_conf_broken", {"service": service}) - item["details"] = [(d, tuple()) for d in result["configuration-details"]] + item["details"] = [(d, {}) for d in result["configuration-details"]] else: item["status"] = "SUCCESS" diff --git a/data/hooks/diagnosis/90-security.py b/data/hooks/diagnosis/90-security.py index 0b1b61226..1eedcc8ca 100644 --- a/data/hooks/diagnosis/90-security.py +++ b/data/hooks/diagnosis/90-security.py @@ -22,7 +22,7 @@ class SecurityDiagnoser(Diagnoser): yield dict(meta={"test": "meltdown"}, status="ERROR", summary=("diagnosis_security_vulnerable_to_meltdown", {}), - details=[("diagnosis_security_vulnerable_to_meltdown_details", ())] + details=[("diagnosis_security_vulnerable_to_meltdown_details", {})] ) else: yield dict(meta={}, diff --git a/locales/en.json b/locales/en.json index 64cca8713..3318e762a 100644 --- a/locales/en.json +++ b/locales/en.json @@ -140,7 +140,7 @@ "diagnosis_basesystem_hardware_board": "Server board model is {model}", "diagnosis_basesystem_host": "Server is running Debian {debian_version}", "diagnosis_basesystem_kernel": "Server is running Linux kernel {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} version: {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} version: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Server is running YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "You are running inconsistent versions of the YunoHost packages... most probably because of a failed or partial upgrade.", "diagnosis_display_tip_web": "You can go to the Diagnosis section (in the home screen) to see the issues found.", @@ -167,12 +167,13 @@ "diagnosis_ip_weird_resolvconf_details": "Instead, this file should be a symlink to /etc/resolvconf/run/resolv.conf itself pointing to 127.0.0.1 (dnsmasq). The actual resolvers should be configured in /etc/resolv.dnsmasq.conf.", "diagnosis_dns_good_conf": "Good DNS configuration for domain {domain} (category {category})", "diagnosis_dns_bad_conf": "Bad or missing DNS configuration for domain {domain} (category {category})", - "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with type {0}, name {1} and value {2}. You can check https://yunohost.org/dns_config for more info.", - "diagnosis_dns_discrepancy": "The DNS record with type {0} and name {1} does not match the recommended configuration. Current value: {2}. Excepted value: {3}. You can check https://yunohost.org/dns_config for more info.", + "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with type: {type}, name: {name}, and value: {value}", + "diagnosis_dns_discrepancy": "The DNS record with type {type} and name {name} does not match the recommended configuration. Current value: {current}. Excepted value: {value}", + "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help about configuring DNS records", "diagnosis_services_running": "Service {service} is running!", "diagnosis_services_conf_broken": "Configuration is broken for service {service}!", "diagnosis_services_bad_status": "Service {service} is {status} :(", - "diagnosis_services_bad_status_tip": "You can try to restart the service, and if it doesn't work, have a look at the service logs using 'yunohost service log {0}' or through the 'Services' section of the webadmin.", + "diagnosis_services_bad_status_tip": "You can try to restart the service, and if it doesn't work, have a look at the service logs using 'yunohost service log {service}' or through the 'Services' section of the webadmin.", "diagnosis_diskusage_verylow": "Storage {mountpoint} (on device {device}) has only {free_abs_GB} GB ({free_percent}%) space remaining. You should really consider cleaning up some space.", "diagnosis_diskusage_low": "Storage {mountpoint} (on device {device}) has only {free_abs_GB} GB ({free_percent}%) space remaining. Be careful.", "diagnosis_diskusage_ok": "Storage {mountpoint} (on device {device}) still has {free_abs_GB} GB ({free_percent}%) space left!", @@ -205,7 +206,7 @@ "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside. Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", "diagnosis_ports_ok": "Port {port} is reachable from outside.", - "diagnosis_ports_needed_by": "Exposing this port is needed for {1} features (service {0})", + "diagnosis_ports_needed_by": "Exposing this port is needed for {category} features (service {service})", "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 7f488b6aa..7f93f7c0d 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -458,7 +458,7 @@ class Diagnoser(): item["summary"] = m18n.n(summary_key, **summary_args) if "details" in item: - item["details"] = [m18n.n(key, *values) for key, values in item["details"]] + item["details"] = [m18n.n(key, **values) for key, values in item["details"]] def _list_diagnosis_categories(): From 587a07a6e6da1ee12c0f8cf013126c74cd1a5272 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 9 Apr 2020 03:00:10 +0200 Subject: [PATCH 14/91] Propagate change in string format to other locales --- locales/ar.json | 2 +- locales/ca.json | 12 ++++++------ locales/de.json | 2 +- locales/en.json | 4 ++-- locales/eo.json | 10 +++++----- locales/es.json | 10 +++++----- locales/fr.json | 10 +++++----- locales/oc.json | 10 +++++----- 8 files changed, 30 insertions(+), 30 deletions(-) diff --git a/locales/ar.json b/locales/ar.json index a1349fde7..9c1e67fe0 100644 --- a/locales/ar.json +++ b/locales/ar.json @@ -162,7 +162,7 @@ "app_action_broke_system": "يبدو أنّ هذا الإجراء أدّى إلى تحطيم هذه الخدمات المهمة: {services}", "diagnosis_basesystem_host": "هذا الخادم يُشغّل ديبيان {debian_version}", "diagnosis_basesystem_kernel": "هذا الخادم يُشغّل نواة لينكس {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} الإصدار: {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} الإصدار: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "هذا الخادم يُشغّل YunoHost {main_version} ({repo})", "diagnosis_everything_ok": "كل شيء على ما يرام في {category}!", "diagnosis_ip_connected_ipv4": "الخادم مُتّصل بالإنترنت عبر IPv4!", diff --git a/locales/ca.json b/locales/ca.json index 175543a13..4c31e4a6c 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -510,7 +510,7 @@ "domain_cannot_remove_main_add_new_one": "No es pot eliminar «{domain:s}» ja que és el domini principal i únic domini, primer s'ha d'afegir un altre domini utilitzant «yunohost domain add », i després fer-lo el domini principal amb «yunohost domain main-domain -n » i després es pot eliminar el domini «{domain:s}» utilitzant «yunohost domain remove {domain:s}».", "diagnosis_basesystem_host": "El servidor funciona amb Debian {debian_version}", "diagnosis_basesystem_kernel": "El servidor funciona amb el nucli de Linux {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} versió: {1}({2})", + "diagnosis_basesystem_ynh_single_version": "{package} versió: {version}({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "Esteu utilitzant versions inconsistents dels paquets de YunoHost… probablement a causa d'una actualització fallida o parcial.", "diagnosis_display_tip_web": "Podeu anar a la secció de Diagnòstics (en la pantalla principal) per veure els errors que s'han trobat.", "diagnosis_failed_for_category": "Ha fallat el diagnòstic per la categoria «{category}»: {error}", @@ -535,8 +535,8 @@ "diagnosis_ip_weird_resolvconf_details": "En canvi, aquest fitxer hauria de ser un enllaç simbòlic cap a /etc/resolvconf/run/resolv.conf i que aquest apunti cap a 127.0.0.1 (dnsmasq). La configuració del «resolver» real s'hauria de fer a /etc/resolv.dnsmaq.conf.", "diagnosis_dns_good_conf": "Bona configuració DNS pel domini {domain} (categoria {category})", "diagnosis_dns_bad_conf": "Configuració DNS incorrecta o inexistent pel domini {domain} (categoria {category})", - "diagnosis_dns_missing_record": "Segons la configuració DNS recomanada, hauríeu d'afegir un registre DNS de tipus {0}, nom {1} i valor {2}. Hi ha més informació a https://yunohost.org/dns_config.", - "diagnosis_dns_discrepancy": "El registre DNS de tipus {0} i nom {1} no concorda amb la configuració recomanada. Valor actual: {2}. Valor esperat: {3}. Més informació a https://yunohost.org/dns_config.", + "diagnosis_dns_missing_record": "Segons la configuració DNS recomanada, hauríeu d'afegir un registre DNS\ntipus: {type}\nnom: {name}\nvalor: {value}.", + "diagnosis_dns_discrepancy": "El registre DNS de tipus {type} i nom {name} no concorda amb la configuració recomanada.\nValor actual: {current}\nValor esperat: {value}", "diagnosis_services_bad_status": "El servei {service} està {status} :(", "diagnosis_diskusage_verylow": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) només té disponibles {free_abs_GB} GB ({free_percent}%). Hauríeu de considerar alliberar una mica d'espai.", "diagnosis_diskusage_low": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) només té disponibles {free_abs_GB} GB ({free_percent}%). Aneu amb compte.", @@ -575,7 +575,7 @@ "diagnosis_description_mail": "Correu electrònic", "migration_description_0013_futureproof_apps_catalog_system": "Migrar al nou sistema de catàleg d'aplicacions resistent al pas del temps", "app_upgrade_script_failed": "Hi ha hagut un error en el script d'actualització de l'aplicació", - "diagnosis_services_bad_status_tip": "Podeu intentar reiniciar el servei, i si no funciona, podeu mirar els registres del servei utilitzant «yunohost service log {0}» o a través de «Serveis» a la secció de la pàgina web d'administració.", + "diagnosis_services_bad_status_tip": "Podeu intentar reiniciar el servei, i si no funciona, podeu mirar els registres del servei utilitzant «yunohost service log {service}» o a través de «Serveis» a la secció de la pàgina web d'administració.", "diagnosis_ports_forwarding_tip": "Per arreglar aquest problema, segurament s'ha de configurar el reenviament de ports en el router tal i com s'explica a https://yunohost.org/isp_box_config", "diagnosis_http_bad_status_code": "El sistema de diagnòstic no ha pogut connectar amb el servidor. Podria ser que una altra màquina hagi contestat en lloc del servidor. S'hauria de comprovar que el reenviament del port 80 sigui correcte, que la configuració NGINX està actualitzada i que el reverse-proxy no està interferint.", "diagnosis_no_cache": "Encara no hi ha memòria cau pel diagnòstic de la categoria «{category}»", @@ -586,7 +586,7 @@ "migration_description_0014_remove_app_status_json": "Eliminar els fitxers d'aplicació status.json heretats", "diagnosis_services_running": "El servei {service} s'està executant!", "diagnosis_services_conf_broken": "La configuració pel servei {service} està trencada!", - "diagnosis_ports_needed_by": "És necessari exposar aquest port per a les funcions {1} (servei {0})", + "diagnosis_ports_needed_by": "És necessari exposar aquest port per a les funcions {category} (servei {service})", "global_settings_setting_pop3_enabled": "Activa el protocol POP3 per al servidor de correu", "log_app_action_run": "Executa l'acció de l'aplicació «{}»", "log_app_config_show_panel": "Mostra el taulell de configuració de l'aplicació «{}»", @@ -596,4 +596,4 @@ "diagnosis_basesystem_hardware_board": "El model de la targeta del servidor és {model}", "diagnosis_basesystem_hardware": "L'arquitectura del maquinari del servidor és {virt} {arch}", "group_already_exist_on_system_but_removing_it": "El grup {group} ja existeix en els grups del sistema, però YunoHost l'eliminarà…" -} \ No newline at end of file +} diff --git a/locales/de.json b/locales/de.json index d250a22fd..2369e3bdc 100644 --- a/locales/de.json +++ b/locales/de.json @@ -304,7 +304,7 @@ "app_upgrade_script_failed": "Es ist ein Fehler im App-Upgrade-Skript aufgetreten", "diagnosis_basesystem_host": "Server läuft unter Debian {debian_version}.", "diagnosis_basesystem_kernel": "Server läuft unter Linux-Kernel {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} Version: {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} Version: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Server läuft YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "Sie verwenden inkonsistente Versionen der YunoHost-Pakete... wahrscheinlich wegen eines fehlgeschlagenen oder teilweisen Upgrades.", "diagnosis_display_tip_web": "Sie können den Abschnitt Diagnose (im Startbildschirm) aufrufen, um die gefundenen Probleme anzuzeigen.", diff --git a/locales/en.json b/locales/en.json index 3318e762a..cec219ee6 100644 --- a/locales/en.json +++ b/locales/en.json @@ -167,8 +167,8 @@ "diagnosis_ip_weird_resolvconf_details": "Instead, this file should be a symlink to /etc/resolvconf/run/resolv.conf itself pointing to 127.0.0.1 (dnsmasq). The actual resolvers should be configured in /etc/resolv.dnsmasq.conf.", "diagnosis_dns_good_conf": "Good DNS configuration for domain {domain} (category {category})", "diagnosis_dns_bad_conf": "Bad or missing DNS configuration for domain {domain} (category {category})", - "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with type: {type}, name: {name}, and value: {value}", - "diagnosis_dns_discrepancy": "The DNS record with type {type} and name {name} does not match the recommended configuration. Current value: {current}. Excepted value: {value}", + "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.\nType: {type}\nName: {name}\nValue: {value}", + "diagnosis_dns_discrepancy": "The DNS record with type {type} and name {name} does not match the recommended configuration.\nCurrent value: {current}\nExcepted value: {value}", "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help about configuring DNS records", "diagnosis_services_running": "Service {service} is running!", "diagnosis_services_conf_broken": "Configuration is broken for service {service}!", diff --git a/locales/eo.json b/locales/eo.json index 7142d9f72..127e7df39 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -504,7 +504,7 @@ "apps_catalog_obsolete_cache": "La kaŝmemoro de la katalogo de programoj estas malplena aŭ malaktuala.", "apps_catalog_update_success": "La aplika katalogo estis ĝisdatigita!", "diagnosis_basesystem_kernel": "Servilo funkcias Linuksan kernon {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} versio: {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} versio: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Servilo funkcias YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "Vi prizorgas malkonsekvencajn versiojn de la YunoHost-pakoj... plej probable pro malsukcesa aŭ parta ĝisdatigo.", "diagnosis_display_tip_web": "Vi povas iri al la sekcio Diagnozo (en la hejmekrano) por vidi la trovitajn problemojn.", @@ -541,8 +541,8 @@ "diagnosis_no_cache": "Neniu diagnoza kaŝmemoro por kategorio '{category}'", "diagnosis_ip_broken_dnsresolution": "Rezolucio pri domajna nomo rompiĝas pro iu kialo ... Ĉu fajroŝirmilo blokas DNS-petojn ?", "diagnosis_ip_broken_resolvconf": "Rezolucio pri domajna nomo ŝajnas esti rompita en via servilo, kiu ŝajnas rilata al /etc/resolv.conf ne notante 127.0.0.1.", - "diagnosis_dns_missing_record": "Laŭ la rekomendita DNS-agordo, vi devas aldoni DNS-registron kun tipo {0}, nomo {1} kaj valoro {2}. Vi povas kontroli https://yunohost.org/dns_config por pliaj informoj.", - "diagnosis_dns_discrepancy": "La DNS-registro kun tipo {0} kaj nomo {1} ne kongruas kun la rekomendita agordo. Nuna valoro: {2}. Esceptita valoro: {3}. Vi povas kontroli https://yunohost.org/dns_config por pliaj informoj.", + "diagnosis_dns_missing_record": "Laŭ la rekomendita DNS-agordo, vi devas aldoni DNS-registron kun\ntipo: {type}\nnomo: {name}\nvaloro: {value}", + "diagnosis_dns_discrepancy": "La DNS-registro kun tipo {type} kaj nomo {name} ne kongruas kun la rekomendita agordo.\nNuna valoro: {current}\nEsceptita valoro: {value}", "diagnosis_services_conf_broken": "Agordo estas rompita por servo {service} !", "diagnosis_services_bad_status": "Servo {service} estas {status} :(", "diagnosis_ram_low": "La sistemo havas {available_abs_MB} MB ({available_percent}%) RAM forlasita de {total_abs_MB} MB. Estu zorgema.", @@ -556,7 +556,7 @@ "diagnosis_description_systemresources": "Rimedaj sistemoj", "diagnosis_description_security": "Sekurecaj kontroloj", "diagnosis_ports_could_not_diagnose": "Ne povis diagnozi, ĉu haveblaj havenoj de ekstere. Eraro: {error}", - "diagnosis_services_bad_status_tip": "Vi povas provi rekomenci la servon, kaj se ĝi ne funkcias, trarigardu la servajn protokolojn uzante 'yunohost service log {0}' aŭ tra la sekcio 'Servoj' de la retadreso.", + "diagnosis_services_bad_status_tip": "Vi povas provi rekomenci la servon, kaj se ĝi ne funkcias, trarigardu la servajn protokolojn uzante 'yunohost service log {service}' aŭ tra la sekcio 'Servoj' de la retadreso.", "diagnosis_security_vulnerable_to_meltdown_details": "Por ripari tion, vi devas ĝisdatigi vian sistemon kaj rekomenci por ŝarĝi la novan linux-kernon (aŭ kontaktu vian servilan provizanton se ĉi tio ne funkcias). Vidu https://meltdownattack.com/ por pliaj informoj.", "diagnosis_description_basesystem": "Baza sistemo", "diagnosis_description_regenconf": "Sistemaj agordoj", @@ -576,7 +576,7 @@ "diagnosis_services_running": "Servo {service} funkcias!", "diagnosis_ports_unreachable": "Haveno {port} ne atingeblas de ekstere.", "diagnosis_ports_ok": "Haveno {port} atingeblas de ekstere.", - "diagnosis_ports_needed_by": "Eksponi ĉi tiun havenon necesas por servo {0}", + "diagnosis_ports_needed_by": "Eksponi ĉi tiun havenon necesas por servo {service}", "diagnosis_ports_forwarding_tip": "Por solvi ĉi tiun problemon, plej probable vi devas agordi la plusendon de haveno en via interreta enkursigilo kiel priskribite en https://yunohost.org/isp_box_config", "diagnosis_http_could_not_diagnose": "Ne povis diagnozi, ĉu atingeblas domajno de ekstere. Eraro: {error}", "diagnosis_http_ok": "Domajno {domain} atingeblas de ekstere.", diff --git a/locales/es.json b/locales/es.json index 5a00ab6dc..b72665066 100644 --- a/locales/es.json +++ b/locales/es.json @@ -505,7 +505,7 @@ "app_remove_after_failed_install": "Eliminando la aplicación tras el fallo de instalación…", "diagnosis_basesystem_host": "El servidor está ejecutando Debian {debian_version}.", "diagnosis_basesystem_kernel": "El servidor está ejecutando el núcleo de Linux {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} versión: {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} versión: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "El servidor está ejecutando YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "Está ejecutando versiones incoherentes de los paquetes de YunoHost... probablemente por una actualización errónea o parcial.", "diagnosis_failed_for_category": "Diagnóstico fallido para la categoría «{category}» : {error}", @@ -528,9 +528,9 @@ "diagnosis_ip_no_ipv4": "El servidor no cuenta con ipv4 funcional.", "diagnosis_ip_not_connected_at_all": "¿¡Está conectado el servidor a internet!?", "diagnosis_ip_broken_resolvconf": "DNS parece no funcionar en tu servidor, lo que parece estar relacionado con /etc/resolv.conf no apuntando a 127.0.0.1.", - "diagnosis_dns_missing_record": "Según la configuración DNS recomendada, deberías añadir un registro DNS de tipo {0}, nombre {1} y valor {2}. Puedes consultar https://yunohost.org/dns_config para más información.", + "diagnosis_dns_missing_record": "Según la configuración DNS recomendada, deberías añadir un registro DNS\ntipo: {type}\nnombre: {name}\nvalor: {value}", "diagnosis_diskusage_low": "El almacenamiento {mountpoint} (en dispositivo {device}) solo tiene {free_abs_GB} GB ({free_percent}%) de espacio disponible. Ten cuidado.", - "diagnosis_services_bad_status_tip": "Puedes intentar reiniciar el servicio, y si no funciona, echar un vistazo a los logs del servicio usando 'yunohost service log {0}' o a través de la sección 'Servicios' en webadmin.", + "diagnosis_services_bad_status_tip": "Puedes intentar reiniciar el servicio, y si no funciona, echar un vistazo a los logs del servicio usando 'yunohost service log {service}' o a través de la sección 'Servicios' en webadmin.", "diagnosis_ip_connected_ipv6": "¡El servidor está conectado a internet a través de IPv6!", "diagnosis_ip_no_ipv6": "El servidor no cuenta con IPv6 funcional.", "diagnosis_ip_dnsresolution_working": "¡DNS no está funcionando!", @@ -539,7 +539,7 @@ "diagnosis_ip_weird_resolvconf_details": "En su lugar, este fichero debería ser un enlace simbólico a /etc/resolvconf/run/resolv.conf apuntando a 127.0.0.1 (dnsmasq). Los servidores de nombre de domino deben configurarse a través de /etc/resolv.dnsmasq.conf.", "diagnosis_dns_good_conf": "Buena configuración DNS para el dominio {domain} (categoría {category})", "diagnosis_dns_bad_conf": "Configuración mala o faltante de los DNS para el dominio {domain} (categoría {category})", - "diagnosis_dns_discrepancy": "El registro DNS con tipo {0} y nombre {1} no se corresponde a la configuración recomendada. Valor actual: {2}. Valor esperado: {3}. Puedes consultar https://yunohost.org/dns_config para más información.", + "diagnosis_dns_discrepancy": "El registro DNS con tipo {type} y nombre {name} no se corresponde a la configuración recomendada.\nValor actual: {current}\nValor esperado: {value}", "diagnosis_services_bad_status": "El servicio {service} está {status} :(", "diagnosis_diskusage_verylow": "El almacenamiento {mountpoint} (en el dispositivo {device}) sólo tiene {free_abs_GB} GB ({free_percent}%) de espacio disponible. Deberías considerar la posibilidad de limpiar algo de espacio.", "diagnosis_diskusage_ok": "¡El almacenamiento {mountpoint} (en el dispositivo {device}) todavía tiene {free_abs_GB} GB ({free_percent}%) de espacio libre!", @@ -569,7 +569,7 @@ "diagnosis_description_ports": "Exposición de puertos", "diagnosis_description_systemresources": "Recursos del sistema", "diagnosis_swap_ok": "El sistema tiene {total_MB} MB de espacio de intercambio!", - "diagnosis_ports_needed_by": "La apertura de este puerto es requerida para la funcionalidad {1} (service {0})", + "diagnosis_ports_needed_by": "La apertura de este puerto es requerida para la funcionalidad {category} (service {service})", "diagnosis_ports_ok": "El puerto {port} es accesible desde internet.", "diagnosis_ports_unreachable": "El puerto {port} no es accesible desde internet.", "diagnosis_ports_could_not_diagnose": "No se puede comprobar si los puertos están accesibles desde el exterior. Error: {error}", diff --git a/locales/fr.json b/locales/fr.json index 9f7dd445b..c8dfd12a9 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -509,14 +509,14 @@ "diagnosis_ip_not_connected_at_all": "Le serveur ne semble pas du tout connecté à Internet !?", "diagnosis_ip_weird_resolvconf": "La résolution DNS semble fonctionner, mais soyez prudent en utilisant un fichier /etc/resolv.conf personnalisé.", "diagnosis_ip_weird_resolvconf_details": "Au lieu de cela, ce fichier devrait être un lien symbolique vers /etc/resolvconf/run/resolv.conf lui-même pointant vers 127.0.0.1 (dnsmasq). Les résolveurs réels doivent être configurés dans /etc/resolv.dnsmasq.conf.", - "diagnosis_dns_missing_record": "Selon la configuration DNS recommandée, vous devez ajouter un enregistrement DNS de type {0}, nom {1} et valeur {2}. Vous pouvez consulter https://yunohost.org/dns_config pour plus d'informations.", + "diagnosis_dns_missing_record": "Selon la configuration DNS recommandée, vous devez ajouter un enregistrement DNS\nType: {type}\nNom: {name}\nValeur {value}", "diagnosis_diskusage_ok": "Le stockage {mountpoint} (sur le périphérique {device}) a encore {free_abs_GB} Go ({free_percent}%) d'espace libre !", "diagnosis_ram_ok": "Le système dispose encore de {available_abs_MB} MB ({available_percent}%) de RAM sur {total_abs_MB} MB.", "diagnosis_regenconf_allgood": "Tous les fichiers de configuration sont conformes à la configuration recommandée !", "diagnosis_security_vulnerable_to_meltdown": "Vous semblez vulnérable à la vulnérabilité de sécurité critique de Meltdown", "diagnosis_basesystem_host": "Le serveur utilise Debian {debian_version}", "diagnosis_basesystem_kernel": "Le serveur utilise le noyau Linux {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} version: {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} version: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Le serveur utilise YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "Vous exécutez des versions incohérentes des packages YunoHost ... probablement à cause d'une mise à niveau partielle ou échouée.", "diagnosis_display_tip_cli": "Vous pouvez exécuter 'yunohost diagnosis show --issues' pour afficher les problèmes détectés.", @@ -535,7 +535,7 @@ "diagnosis_ip_broken_resolvconf": "La résolution du nom de domaine semble cassée sur votre serveur, ce qui semble lié au fait que /etc/resolv.conf ne pointe pas vers 127.0.0.1.", "diagnosis_dns_good_conf": "Bonne configuration DNS pour le domaine {domain} (catégorie {category})", "diagnosis_dns_bad_conf": "Configuration DNS incorrecte ou manquante pour le domaine {domain} (catégorie {category})", - "diagnosis_dns_discrepancy": "L'enregistrement DNS de type {0} et nom {1} ne correspond pas à la configuration recommandée. Valeur actuelle: {2}. Valeur exceptée: {3}. Vous pouvez consulter https://yunohost.org/dns_config pour plus d'informations.", + "diagnosis_dns_discrepancy": "L'enregistrement DNS de type {type} et nom {name} ne correspond pas à la configuration recommandée.\nValeur actuelle: {current}\nValeur attendue: {value}", "diagnosis_services_bad_status": "Le service {service} est {status} :-(", "diagnosis_diskusage_verylow": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free_abs_GB} Go ({free_percent}%). Vous devriez vraiment envisager de nettoyer un peu d'espace.", "diagnosis_diskusage_low": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free_abs_GB} Go ({free_percent}%). Faites attention.", @@ -579,13 +579,13 @@ "migration_description_0014_remove_app_status_json": "Supprimer les fichiers d'application status.json hérités", "diagnosis_services_running": "Le service {service} s'exécute correctement !", "diagnosis_services_conf_broken": "La configuration est cassée pour le service {service} !", - "diagnosis_ports_needed_by": "Rendre ce port accessible est nécessaire pour les fonctionnalités de type {1} (service {0})", + "diagnosis_ports_needed_by": "Rendre ce port accessible est nécessaire pour les fonctionnalités de type {category} (service {service})", "diagnosis_ports_forwarding_tip": "Pour résoudre ce problème, vous devez probablement configurer la redirection de port sur votre routeur Internet comme décrit sur https://yunohost.org/isp_box_config", "diagnosis_http_connection_error": "Erreur de connexion : impossible de se connecter au domaine demandé, il est probablement injoignable.", "diagnosis_no_cache": "Pas encore de cache de diagnostique pour la catégorie « {category} »", "diagnosis_http_unknown_error": "Une erreur est survenue en essayant de joindre votre domaine, il est probablement injoignable.", "yunohost_postinstall_end_tip": "La post-installation terminée! Pour finaliser votre configuration, il est recommendé de :\n - ajouter un premier utilisateur depuis la section \"Utilisateurs\" de l'interface web (ou \"yunohost user create \" en ligne de commande);\n - diagnostiquer les potentiels problèmes dans la section \"Diagnostic\" de l'interface web (ou \"yunohost diagnosis run\" en ligne de commande);\n - lire les parties \"Finalisation de votre configuration\" et \"Découverte de Yunohost\" dans le guide de l'administrateur: https://yunohost.org/admindoc.", - "diagnosis_services_bad_status_tip": "Vous pouvez essayer de redémarrer le service. Si cela ne fonctionne pas, consultez les journaux de service à l'aide de 'yunohost service log {0}' ou de la section 'Services' de l'administrateur Web.", + "diagnosis_services_bad_status_tip": "Vous pouvez essayer de redémarrer le service. Si cela ne fonctionne pas, consultez les journaux de service à l'aide de 'yunohost service log {service}' ou de la section 'Services' de l'administrateur Web.", "diagnosis_http_bad_status_code": "Le système de diagnostique n'a pas réussi à contacter votre serveur. Il se peut qu'une autre machine réponde à la place de votre serveur. Vérifiez que le port 80 est correctement redirigé, que votre configuration nginx est à jour et qu’un reverse-proxy n’interfère pas.", "diagnosis_http_timeout": "Expiration du délai en essayant de contacter votre serveur de l'extérieur. Il semble être inaccessible. Vérifiez que vous transférez correctement le port 80, que nginx est en cours d’exécution et qu’un pare-feu n’interfère pas.", "global_settings_setting_pop3_enabled": "Activer le protocole POP3 pour le serveur de messagerie", diff --git a/locales/oc.json b/locales/oc.json index 5472c97e8..a452b72bb 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -497,7 +497,7 @@ "user_already_exists": "L’utilizaire {user} existís ja", "diagnosis_basesystem_host": "Lo servidor fonciona amb Debian {debian_version}.", "diagnosis_basesystem_kernel": "Lo servidor fonciona amb lo nuclèu Linuxl {kernel_version}", - "diagnosis_basesystem_ynh_single_version": "{0} version : {1} ({2})", + "diagnosis_basesystem_ynh_single_version": "{package} version : {version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "Utilizatz de versions inconsistentas dels paquets de YunoHost… probablament a causa d'una actualizacion fracassada o parciala.", "diagnosis_display_tip_cli": "Podètz executar « yunohost diagnosis show --issues » per mostrar las errors trobadas.", "diagnosis_ignored_issues": "(+ {nb_ignored} problèma(es) ignorat(s))", @@ -536,8 +536,8 @@ "operation_interrupted": "L’operacion es estada interrompuda manualament ?", "group_cannot_be_deleted": "Lo grop « {group} » pòt pas èsser suprimit manualament.", "diagnosis_found_warnings": "Trobat {warnings} element(s) que se poirián melhorar per {category}.", - "diagnosis_dns_missing_record": "Segon la configuracion DNS recomandada, vos calriá ajustar un enregistrament DNS de tipe {0}, nom {1} e valor {2}. Podètz consultar https://yunohost.org/dns_config per mai d’informacions.", - "diagnosis_dns_discrepancy": "Segon la configuracion DNS recomandada, la valor per l’enregistrament DNS de tipe {0} e nom {1} deuriá èsser {2} allòc de {3}.", + "diagnosis_dns_missing_record": "Segon la configuracion DNS recomandada, vos calriá ajustar un enregistrament DNS\ntipe: {type}\nnom: {name}\nvalor: {value}", + "diagnosis_dns_discrepancy": "Segon la configuracion DNS recomandada, la valor per l’enregistrament DNS\ntipe: {type}\nnom: {name}\ndeuriá èsser: {current}\nallòc de: {value}", "diagnosis_regenconf_manually_modified_debian_details": "Es pas problematic, mas car téner d’agacher...", "diagnosis_ports_could_not_diagnose": "Impossible de diagnosticar se los pòrts son accessibles de l’exterior. Error : {error}", "diagnosis_http_could_not_diagnose": "Impossible de diagnosticar se lo domeni es accessible de l’exterior. Error : {error}", @@ -556,11 +556,11 @@ "apps_catalog_init_success": "Sistèma de catalòg d’aplicacion iniciat !", "diagnosis_services_running": "Lo servici {service} es lançat !", "diagnosis_services_conf_broken": "La configuracion es copada pel servici {service} !", - "diagnosis_ports_needed_by": "Es necessari qu’aqueste pòrt siá accessible pel servici {0}", + "diagnosis_ports_needed_by": "Es necessari qu’aqueste pòrt siá accessible pel servici {service}", "diagnosis_diskusage_low": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a solament {free_abs_GB} Go ({free_percent}%). Siatz prudent.", "migration_description_0014_remove_app_status_json": "Suprimir los fichièrs d’aplicacion status.json eretats", "dyndns_provider_unreachable": "Impossible d’atenher lo provesidor Dyndns : siá vòstre YunoHost es pas corrèctament connectat a Internet siá lo servidor dynette es copat.", - "diagnosis_services_bad_status_tip": "Podètz ensajar de reaviar lo servici, e se non fonciona pas, podètz agachar los jornals en utilizant « yunohost service log {0} » o via la seccion « Servicis » de pas la pagina web d’administracion.", + "diagnosis_services_bad_status_tip": "Podètz ensajar de reaviar lo servici, e se non fonciona pas, podètz agachar los jornals en utilizant « yunohost service log {service} » o via la seccion « Servicis » de pas la pagina web d’administracion.", "diagnosis_http_connection_error": "Error de connexion : connexion impossibla al domeni demandat, benlèu qu’es pas accessible.", "diagnosis_http_unknown_error": "Una error s’es producha en ensajar de se connectar a vòstre domeni, es benlèu pas accessible.", "group_user_already_in_group": "L’utilizaire {user} es ja dins lo grop « {group} »", From 3cff370c62f2150b0a306871b2258c42f01b29d1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 9 Apr 2020 01:55:25 +0200 Subject: [PATCH 15/91] Add some bits of magic to simplify the way we yield test items --- data/hooks/diagnosis/00-basesystem.py | 37 +++++++++------------- data/hooks/diagnosis/10-ip.py | 26 +++++++-------- data/hooks/diagnosis/12-dnsrecords.py | 7 ++-- data/hooks/diagnosis/14-ports.py | 11 ++++--- data/hooks/diagnosis/21-web.py | 6 ++-- data/hooks/diagnosis/24-mail.py | 4 +-- data/hooks/diagnosis/30-services.py | 13 ++++---- data/hooks/diagnosis/50-systemresources.py | 34 +++++++++++--------- data/hooks/diagnosis/70-regenconf.py | 6 ++-- data/hooks/diagnosis/90-security.py | 6 ++-- src/yunohost/diagnosis.py | 21 ++++++++++++ 11 files changed, 94 insertions(+), 77 deletions(-) diff --git a/data/hooks/diagnosis/00-basesystem.py b/data/hooks/diagnosis/00-basesystem.py index 3c932b488..97f77cc1d 100644 --- a/data/hooks/diagnosis/00-basesystem.py +++ b/data/hooks/diagnosis/00-basesystem.py @@ -23,55 +23,48 @@ class BaseSystemDiagnoser(Diagnoser): hardware = dict(meta={"test": "hardware"}, status="INFO", data={"virt": virt, "arch": arch}, - summary=("diagnosis_basesystem_hardware", {"virt": virt, "arch": arch})) + summary="diagnosis_basesystem_hardware") if os.path.exists("/proc/device-tree/model"): model = read_file('/proc/device-tree/model').strip() - hardware["data"]["board"] = model - hardware["details"] = [("diagnosis_basesystem_hardware_board", {"model": model})] + hardware["data"]["model"] = model + hardware["details"] = ["diagnosis_basesystem_hardware_board"] yield hardware # Kernel version kernel_version = read_file('/proc/sys/kernel/osrelease').strip() yield dict(meta={"test": "kernel"}, + data={"kernel_version": kernel_version}, status="INFO", - summary=("diagnosis_basesystem_kernel", {"kernel_version": kernel_version})) + summary="diagnosis_basesystem_kernel") # Debian release debian_version = read_file("/etc/debian_version").strip() yield dict(meta={"test": "host"}, + data={"debian_version": debian_version}, status="INFO", - summary=("diagnosis_basesystem_host", {"debian_version": debian_version})) + summary="diagnosis_basesystem_host") # Yunohost packages versions - ynh_packages = ynh_packages_version() # We check if versions are consistent (e.g. all 3.6 and not 3 packages with 3.6 and the other with 3.5) # This is a classical issue for upgrades that failed in the middle # (or people upgrading half of the package because they did 'apt upgrade' instead of 'dist-upgrade') # Here, ynh_core_version is for example "3.5.4.12", so [:3] is "3.5" and we check it's the same for all packages + ynh_packages = ynh_packages_version() ynh_core_version = ynh_packages["yunohost"]["version"] consistent_versions = all(infos["version"][:3] == ynh_core_version[:3] for infos in ynh_packages.values()) ynh_version_details = [("diagnosis_basesystem_ynh_single_version", {"package":package, "version": infos["version"], "repo": infos["repo"]} - ) - for package, infos in ynh_packages.items()] + ) + for package, infos in ynh_packages.items()] - if consistent_versions: - yield dict(meta={"test": "ynh_versions"}, - data={"main_version": ynh_core_version, "repo": ynh_packages["yunohost"]["repo"]}, - status="INFO", - summary=("diagnosis_basesystem_ynh_main_version", - {"main_version": ynh_core_version, - "repo": ynh_packages["yunohost"]["repo"]}), - details=ynh_version_details) - else: - yield dict(meta={"test": "ynh_versions"}, - data={"main_version": ynh_core_version, "repo": ynh_packages["yunohost"]["repo"]}, - status="ERROR", - summary=("diagnosis_basesystem_ynh_inconsistent_versions", {}), - details=ynh_version_details) + yield dict(meta={"test": "ynh_versions"}, + data={"main_version": ynh_core_version, "repo": ynh_packages["yunohost"]["repo"]}, + status="INFO" if consistent_versions else "ERROR", + summary="diagnosis_basesystem_ynh_main_version" if consistent_versions else "diagnosis_basesystem_ynh_inconsistent_versions", + details=ynh_version_details) def main(args, env, loggers): diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 7e96a7b56..3f197a7bc 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -28,7 +28,7 @@ class IPDiagnoser(Diagnoser): if not can_ping_ipv4 and not can_ping_ipv6: yield dict(meta={"test": "ping"}, status="ERROR", - summary=("diagnosis_ip_not_connected_at_all", {})) + summary="diagnosis_ip_not_connected_at_all") # Not much else we can do if there's no internet at all return @@ -49,20 +49,19 @@ class IPDiagnoser(Diagnoser): if not can_resolve_dns: yield dict(meta={"test": "dnsresolv"}, status="ERROR", - summary=("diagnosis_ip_broken_dnsresolution", {}) if good_resolvconf - else ("diagnosis_ip_broken_resolvconf", {})) + summary="diagnosis_ip_broken_dnsresolution" if good_resolvconf else "diagnosis_ip_broken_resolvconf") return # Otherwise, if the resolv conf is bad but we were able to resolve domain name, # still warn that we're using a weird resolv conf ... elif not good_resolvconf: yield dict(meta={"test": "dnsresolv"}, status="WARNING", - summary=("diagnosis_ip_weird_resolvconf", {}), - details=[("diagnosis_ip_weird_resolvconf_details", {})]) + summary="diagnosis_ip_weird_resolvconf", + details=["diagnosis_ip_weird_resolvconf_details"]) else: yield dict(meta={"test": "dnsresolv"}, status="SUCCESS", - summary=("diagnosis_ip_dnsresolution_working", {})) + summary="diagnosis_ip_dnsresolution_working") # ##################################################### # # IP DIAGNOSIS : Check that we're actually able to talk # @@ -72,17 +71,16 @@ class IPDiagnoser(Diagnoser): ipv4 = self.get_public_ip(4) if can_ping_ipv4 else None ipv6 = self.get_public_ip(6) if can_ping_ipv6 else None - yield dict(meta={"test": "ip", "version": '4'}, - data=ipv4, + yield dict(meta={"test": "ipv4"}, + data={"global": ipv4}, status="SUCCESS" if ipv4 else "ERROR", - summary=("diagnosis_ip_connected_ipv4", {}) if ipv4 - else ("diagnosis_ip_no_ipv4", {})) + summary="diagnosis_ip_connected_ipv4" if ipv4 else "diagnosis_ip_no_ipv4") - yield dict(meta={"test": "ip", "version": '6'}, - data=ipv6, + yield dict(meta={"test": "ipv6"}, + data={"global": ipv6}, status="SUCCESS" if ipv6 else "WARNING", - summary=("diagnosis_ip_connected_ipv6", {}) if ipv6 - else ("diagnosis_ip_no_ipv6", {})) + summary="diagnosis_ip_connected_ipv6" if ipv6 else "diagnosis_ip_no_ipv6") + # TODO / FIXME : add some attempt to detect ISP (using whois ?) ? diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 5d8a12ebb..d653b044c 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -62,19 +62,18 @@ class DNSRecordsDiagnoser(Diagnoser): discrepancies.append(("diagnosis_dns_discrepancy", r)) if discrepancies: - discrepancies = [("diagnosis_dns_point_to_doc", {})] + discrepancies status = "ERROR" if (category == "basic" or (is_main_domain and category != "extra")) else "WARNING" - summary = ("diagnosis_dns_bad_conf", {"domain": domain, "category": category}) + summary = "diagnosis_dns_bad_conf" else: status = "SUCCESS" - summary = ("diagnosis_dns_good_conf", {"domain": domain, "category": category}) + summary = "diagnosis_dns_good_conf" output = dict(meta={"domain": domain, "category": category}, status=status, summary=summary) if discrepancies: - output["details"] = discrepancies + output["details"] = ["diagnosis_dns_point_to_doc"] + discrepancies yield output diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index fe7c9003d..f973a3275 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -47,15 +47,16 @@ class PortsDiagnoser(Diagnoser): category = services[service].get("category", "[?]") if r["ports"].get(str(port), None) is not True: yield dict(meta={"port": str(port)}, + data={"service": service, "category": category}, status="ERROR", - summary=("diagnosis_ports_unreachable", {"port": port}), - details=[("diagnosis_ports_needed_by", {"service": service, "category": category}), - ("diagnosis_ports_forwarding_tip", {})]) + summary="diagnosis_ports_unreachable", + details=["diagnosis_ports_needed_by", "diagnosis_ports_forwarding_tip"]) else: yield dict(meta={"port": str(port)}, + data={"service": service, "category": category}, status="SUCCESS", - summary=("diagnosis_ports_ok", {"port": port}), - details=[("diagnosis_ports_needed_by", {"service": service, "category": category})]) + summary="diagnosis_ports_ok", + details=["diagnosis_ports_needed_by"]) def main(args, env, loggers): diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 6b65b8da3..5008f0360 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -45,13 +45,13 @@ class WebDiagnoser(Diagnoser): if r["status"] == "ok": yield dict(meta={"domain": domain}, status="SUCCESS", - summary=("diagnosis_http_ok", {"domain": domain})) + summary="diagnosis_http_ok") else: detail = r["code"].replace("error_http_check", "diagnosis_http") if "code" in r else "diagnosis_http_unknown_error" yield dict(meta={"domain": domain}, status="ERROR", - summary=("diagnosis_http_unreachable", {"domain": domain}), - details=[(detail,{})]) + summary="diagnosis_http_unreachable", + details=[detail]) # In there or idk where else ... # try to diagnose hairpinning situation by crafting a request for the diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index f0060df52..0a3a97102 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -17,11 +17,11 @@ class MailDiagnoser(Diagnoser): if os.system('/bin/nc -z -w2 yunohost.org 25') == 0: yield dict(meta={"test": "ougoing_port_25"}, status="SUCCESS", - summary=("diagnosis_mail_ougoing_port_25_ok",{})) + summary="diagnosis_mail_ougoing_port_25_ok") else: yield dict(meta={"test": "outgoing_port_25"}, status="ERROR", - summary=("diagnosis_mail_ougoing_port_25_blocked",{})) + summary="diagnosis_mail_ougoing_port_25_blocked") diff --git a/data/hooks/diagnosis/30-services.py b/data/hooks/diagnosis/30-services.py index 9d6879933..6217d89d3 100644 --- a/data/hooks/diagnosis/30-services.py +++ b/data/hooks/diagnosis/30-services.py @@ -17,21 +17,22 @@ class ServicesDiagnoser(Diagnoser): for service, result in sorted(all_result.items()): - item = dict(meta={"service": service}) + item = dict(meta={"service": service}, + data={"status": result["status"], "configuration": result["configuration"]}) if result["status"] != "running": item["status"] = "ERROR" - item["summary"] = ("diagnosis_services_bad_status", {"service": service, "status": result["status"]}) - item["details"] = [("diagnosis_services_bad_status_tip", {"service":service})] + item["summary"] = "diagnosis_services_bad_status" + item["details"] = ["diagnosis_services_bad_status_tip"] elif result["configuration"] == "broken": item["status"] = "WARNING" - item["summary"] = ("diagnosis_services_conf_broken", {"service": service}) - item["details"] = [(d, {}) for d in result["configuration-details"]] + item["summary"] = "diagnosis_services_conf_broken" + item["details"] = result["configuration-details"] else: item["status"] = "SUCCESS" - item["summary"] = ("diagnosis_services_running", {"service": service, "status": result["status"]}) + item["summary"] = "diagnosis_services_running" yield item diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index 95f58ddb7..1f0c07f47 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -20,17 +20,19 @@ class SystemResourcesDiagnoser(Diagnoser): ram_total_abs_MB = ram.total / (1024**2) ram_available_abs_MB = ram.available / (1024**2) ram_available_percent = round(100 * ram.available / ram.total) - item = dict(meta={"test": "ram"}) - infos = {"total_abs_MB": ram_total_abs_MB, "available_abs_MB": ram_available_abs_MB, "available_percent": ram_available_percent} + item = dict(meta={"test": "ram"}, + data={"total_abs_MB": ram_total_abs_MB, + "available_abs_MB": ram_available_abs_MB, + "available_percent": ram_available_percent}) if ram_available_abs_MB < 100 or ram_available_percent < 5: item["status"] = "ERROR" - item["summary"] = ("diagnosis_ram_verylow", infos) + item["summary"] = "diagnosis_ram_verylow" elif ram_available_abs_MB < 200 or ram_available_percent < 10: item["status"] = "WARNING" - item["summary"] = ("diagnosis_ram_low", infos) + item["summary"] = "diagnosis_ram_low" else: item["status"] = "SUCCESS" - item["summary"] = ("diagnosis_ram_ok", infos) + item["summary"] = "diagnosis_ram_ok" yield item # @@ -39,19 +41,21 @@ class SystemResourcesDiagnoser(Diagnoser): swap = psutil.swap_memory() swap_total_abs_MB = swap.total / (1024*1024) - item = dict(meta={"test": "swap"}) - infos = {"total_MB": swap_total_abs_MB} + item = dict(meta={"test": "swap"}, + data={"total_MB": swap_total_abs_MB}) if swap_total_abs_MB <= 0: item["status"] = "ERROR" - item["summary"] = ("diagnosis_swap_none", infos) + item["summary"] = "diagnosis_swap_none" elif swap_total_abs_MB <= 256: item["status"] = "WARNING" - item["summary"] = ("diagnosis_swap_notsomuch", infos) + item["summary"] = "diagnosis_swap_notsomuch" else: item["status"] = "SUCCESS" - item["summary"] = ("diagnosis_swap_ok", infos) + item["summary"] = "diagnosis_swap_ok" yield item + # FIXME : add a check that swapiness is low if swap is on a sdcard... + # # Disks usage # @@ -66,17 +70,17 @@ class SystemResourcesDiagnoser(Diagnoser): free_abs_GB = usage.free / (1024 ** 3) free_percent = 100 - usage.percent - item = dict(meta={"test": "diskusage", "mountpoint": mountpoint}) - infos = {"mountpoint": mountpoint, "device": device, "free_abs_GB": free_abs_GB, "free_percent": free_percent} + item = dict(meta={"test": "diskusage", "mountpoint": mountpoint}, + data={"device": device, "free_abs_GB": free_abs_GB, "free_percent": free_percent}) if free_abs_GB < 1 or free_percent < 5: item["status"] = "ERROR" - item["summary"] = ("diagnosis_diskusage_verylow", infos) + item["summary"] = "diagnosis_diskusage_verylow" elif free_abs_GB < 2 or free_percent < 10: item["status"] = "WARNING" - item["summary"] = ("diagnosis_diskusage_low", infos) + item["summary"] = "diagnosis_diskusage_low" else: item["status"] = "SUCCESS" - item["summary"] = ("diagnosis_diskusage_ok", infos) + item["summary"] = "diagnosis_diskusage_ok" yield item diff --git a/data/hooks/diagnosis/70-regenconf.py b/data/hooks/diagnosis/70-regenconf.py index a3e284f90..75db146ab 100644 --- a/data/hooks/diagnosis/70-regenconf.py +++ b/data/hooks/diagnosis/70-regenconf.py @@ -22,14 +22,14 @@ class RegenconfDiagnoser(Diagnoser): if regenconf_modified_files == []: yield dict(meta={"test": "regenconf"}, status="SUCCESS", - summary=("diagnosis_regenconf_allgood", {}) + summary="diagnosis_regenconf_allgood" ) else: for f in regenconf_modified_files: yield dict(meta={"test": "regenconf", "file": f}, status="WARNING", - summary=("diagnosis_regenconf_manually_modified", {"file": f}), - details=[("diagnosis_regenconf_manually_modified_details", {})] + summary="diagnosis_regenconf_manually_modified", + details=["diagnosis_regenconf_manually_modified_details"] ) #for f in debian_modified_files: diff --git a/data/hooks/diagnosis/90-security.py b/data/hooks/diagnosis/90-security.py index 1eedcc8ca..d281042b0 100644 --- a/data/hooks/diagnosis/90-security.py +++ b/data/hooks/diagnosis/90-security.py @@ -21,13 +21,13 @@ class SecurityDiagnoser(Diagnoser): if self.is_vulnerable_to_meltdown(): yield dict(meta={"test": "meltdown"}, status="ERROR", - summary=("diagnosis_security_vulnerable_to_meltdown", {}), - details=[("diagnosis_security_vulnerable_to_meltdown_details", {})] + summary="diagnosis_security_vulnerable_to_meltdown", + details=["diagnosis_security_vulnerable_to_meltdown_details"] ) else: yield dict(meta={}, status="SUCCESS", - summary=("diagnosis_security_all_good", {}) + summary="diagnosis_security_all_good" ) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 7f93f7c0d..effd610cc 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -453,11 +453,32 @@ class Diagnoser(): report["description"] = Diagnoser.get_description(report["id"]) + def is_tuple_or_list(stuff): + return isinstance(stuff, tuple) or isinstance(stuff, list) + for item in report["items"]: + + # For the summary and each details, we want to call + # m18n() on the string, with the appropriate data for string + # formatting which can come from : + # - infos super-specific to the summary/details (if it's a tuple(key,dict_with_info) and not just a string) + # - 'meta' info = parameters of the test (e.g. which domain/category for DNS conf record) + # - actual 'data' retrieved from the test (e.g. actual global IP, ...) + + meta_data = item.get("meta", {}).copy() + meta_data.update(item.get("data", {})) + + if not is_tuple_or_list(item["summary"]): + item["summary"] = (item["summary"], {}) summary_key, summary_args = item["summary"] + summary_args.update(meta_data) + item["summary"] = m18n.n(summary_key, **summary_args) if "details" in item: + item["details"] = [(d[0], d[1]) if is_tuple_or_list(d) else (d, {}) for d in item["details"]] + for d in item["details"]: + d[1].update(meta_data) item["details"] = [m18n.n(key, **values) for key, values in item["details"]] From 7c3cce6bf97937aeb09282c229fcf3e10d63d120 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 9 Apr 2020 02:26:47 +0200 Subject: [PATCH 16/91] Try to diagnose and add details about global and local IPs --- data/hooks/diagnosis/10-ip.py | 24 ++++++++++++++++++------ locales/en.json | 2 ++ 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 3f197a7bc..70a5c9594 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -8,7 +8,7 @@ from moulinette.utils.process import check_output from moulinette.utils.filesystem import read_file from yunohost.diagnosis import Diagnoser - +from yunohost.utils.network import get_network_interfaces class IPDiagnoser(Diagnoser): @@ -71,16 +71,28 @@ class IPDiagnoser(Diagnoser): ipv4 = self.get_public_ip(4) if can_ping_ipv4 else None ipv6 = self.get_public_ip(6) if can_ping_ipv6 else None + network_interfaces = get_network_interfaces() + def get_local_ip(version): + local_ip = {iface:addr[version].split("/")[0] + for iface, addr in network_interfaces.items() if version in addr} + if not local_ip: + return None + elif len(local_ip): + return next(iter(local_ip.values())) + else: + return local_ip + yield dict(meta={"test": "ipv4"}, - data={"global": ipv4}, + data={"global": ipv4, "local": get_local_ip("ipv4")}, status="SUCCESS" if ipv4 else "ERROR", - summary="diagnosis_ip_connected_ipv4" if ipv4 else "diagnosis_ip_no_ipv4") + summary="diagnosis_ip_connected_ipv4" if ipv4 else "diagnosis_ip_no_ipv4", + details=["diagnosis_ip_global", "diagnosis_ip_local"] if ipv4 else None) yield dict(meta={"test": "ipv6"}, - data={"global": ipv6}, + data={"global": ipv6, "local": get_local_ip("ipv6")}, status="SUCCESS" if ipv6 else "WARNING", - summary="diagnosis_ip_connected_ipv6" if ipv6 else "diagnosis_ip_no_ipv6") - + summary="diagnosis_ip_connected_ipv6" if ipv6 else "diagnosis_ip_no_ipv6", + details=["diagnosis_ip_global", "diagnosis_ip_local"] if ipv6 else None) # TODO / FIXME : add some attempt to detect ISP (using whois ?) ? diff --git a/locales/en.json b/locales/en.json index cec219ee6..c02c6890e 100644 --- a/locales/en.json +++ b/locales/en.json @@ -159,6 +159,8 @@ "diagnosis_ip_no_ipv4": "The server does not have working IPv4.", "diagnosis_ip_connected_ipv6": "The server is connected to the Internet through IPv6 !", "diagnosis_ip_no_ipv6": "The server does not have working IPv6.", + "diagnosis_ip_global": "Global IP: {global}", + "diagnosis_ip_local": "Local IP: {local}", "diagnosis_ip_not_connected_at_all": "The server does not seem to be connected to the Internet at all!?", "diagnosis_ip_dnsresolution_working": "Domain name resolution is working!", "diagnosis_ip_broken_dnsresolution": "Domain name resolution seems to be broken for some reason... Is a firewall blocking DNS requests ?", From 9ebb3102cdc0a1d4d2f259f7495663a7025acfbf Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 9 Apr 2020 02:27:09 +0200 Subject: [PATCH 17/91] Remove details key if it's empty --- src/yunohost/diagnosis.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index effd610cc..31518c257 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -384,6 +384,10 @@ class Diagnoser(): items = list(self.run()) + for item in items: + if "details" in item and not item["details"]: + del item["details"] + new_report = {"id": self.id_, "cached_for": self.cache_duration, "items": items} From 8cb2640872a49d780d01029758e601caaaa03338 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 10 Apr 2020 20:43:11 +0200 Subject: [PATCH 18/91] Fix usage of systemd-detect-virt on baremetal --- data/hooks/diagnosis/00-basesystem.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/data/hooks/diagnosis/00-basesystem.py b/data/hooks/diagnosis/00-basesystem.py index 97f77cc1d..68a9570ce 100644 --- a/data/hooks/diagnosis/00-basesystem.py +++ b/data/hooks/diagnosis/00-basesystem.py @@ -17,13 +17,20 @@ class BaseSystemDiagnoser(Diagnoser): def run(self): # Detect virt technology (if not bare metal) and arch - # Also possibly the board name - virt = check_output("systemd-detect-virt").strip() or "bare-metal" + # Gotta have this "|| true" because it systemd-detect-virt return 'none' + # with an error code on bare metal ~.~ + virt = check_output("systemd-detect-virt || true", shell=True).strip() + if virt.lower() == "none": + virt = "bare-metal" + + # Detect arch arch = check_output("dpkg --print-architecture").strip() hardware = dict(meta={"test": "hardware"}, status="INFO", data={"virt": virt, "arch": arch}, summary="diagnosis_basesystem_hardware") + + # Also possibly the board name if os.path.exists("/proc/device-tree/model"): model = read_file('/proc/device-tree/model').strip() hardware["data"]["model"] = model From f9dd634ebeace1983d3dce9ce9a9048269369391 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 00:18:45 +0200 Subject: [PATCH 19/91] Detect if nginx conf does not include well-known diagnosis location --- data/hooks/diagnosis/21-web.py | 9 +++++++++ locales/en.json | 2 ++ 2 files changed, 11 insertions(+) diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 5008f0360..add192685 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -22,6 +22,15 @@ class WebDiagnoser(Diagnoser): all_domains = domain_list()["domains"] for domain in all_domains: + # If the diagnosis location ain't defined, can't do diagnosis, + # probably because nginx conf manually modified... + nginx_conf = "/etc/nginx/conf.d/%s.conf" % domain + if os.system("grep -q '^.*location .*/.well-known/ynh-diagnosis/' %s" % nginx_conf) != 0: + yield dict(meta={"domain": domain}, + status="WARNING", + summary="diagnosis_http_nginx_conf_not_up_to_date", + details=["diagnosis_http_nginx_conf_not_up_to_date_details"]) + nonce = ''.join(random.choice(nonce_digits) for i in range(16)) os.system("rm -rf /tmp/.well-known/ynh-diagnosis/") os.system("mkdir -p /tmp/.well-known/ynh-diagnosis/") diff --git a/locales/en.json b/locales/en.json index c02c6890e..5032bb4f3 100644 --- a/locales/en.json +++ b/locales/en.json @@ -217,6 +217,8 @@ "diagnosis_http_unknown_error": "An error happened while trying to reach your domain, it's very likely unreachable.", "diagnosis_http_bad_status_code": "The diagnosis system could not reach your server. It might be that another machine answered instead of your server. You should check that you're correctly forwarding port 80, that your nginx configuration is up to date, and that a reverse-proxy is not interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", + "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", + "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the different with the command line using 'yunohost tools regen-conf nginx --dry-run --with-diff' and if you're ok, apply the changes with 'yunohost tools regen-conf nginx --force'.", "diagnosis_unknown_categories": "The following categories are unknown: {categories}", "diagnosis_never_ran_yet": "It looks like this server was setup recently and there's no diagnosis report to show yet. You should start by running a full diagnosis, either from the webadmin or using 'yunohost diagnosis run' from the command line.", "domain_cannot_remove_main": "You cannot remove '{domain:s}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n '; here is the list of candidate domains: {other_domains:s}", From 3869c2f68e02f1fe170484213a46c312575764b9 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 02:28:40 +0200 Subject: [PATCH 20/91] Add html tags to improve readability of some results (in particular DNS records stuff) on webadmin --- locales/en.json | 20 +++++++++++--------- src/yunohost/diagnosis.py | 25 +++++++++++++------------ 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/locales/en.json b/locales/en.json index 5032bb4f3..d3261a2cf 100644 --- a/locales/en.json +++ b/locales/en.json @@ -159,19 +159,19 @@ "diagnosis_ip_no_ipv4": "The server does not have working IPv4.", "diagnosis_ip_connected_ipv6": "The server is connected to the Internet through IPv6 !", "diagnosis_ip_no_ipv6": "The server does not have working IPv6.", - "diagnosis_ip_global": "Global IP: {global}", - "diagnosis_ip_local": "Local IP: {local}", + "diagnosis_ip_global": "Global IP: {global}", + "diagnosis_ip_local": "Local IP: {local}", "diagnosis_ip_not_connected_at_all": "The server does not seem to be connected to the Internet at all!?", "diagnosis_ip_dnsresolution_working": "Domain name resolution is working!", "diagnosis_ip_broken_dnsresolution": "Domain name resolution seems to be broken for some reason... Is a firewall blocking DNS requests ?", - "diagnosis_ip_broken_resolvconf": "Domain name resolution seems to be broken on your server, which seems related to /etc/resolv.conf not pointing to 127.0.0.1.", - "diagnosis_ip_weird_resolvconf": "DNS resolution seems to be working, but be careful that you seem to be using a custom /etc/resolv.conf.", - "diagnosis_ip_weird_resolvconf_details": "Instead, this file should be a symlink to /etc/resolvconf/run/resolv.conf itself pointing to 127.0.0.1 (dnsmasq). The actual resolvers should be configured in /etc/resolv.dnsmasq.conf.", + "diagnosis_ip_broken_resolvconf": "Domain name resolution seems to be broken on your server, which seems related to /etc/resolv.conf not pointing to 127.0.0.1.", + "diagnosis_ip_weird_resolvconf": "DNS resolution seems to be working, but it looks like you're using a custom /etc/resolv.conf.", + "diagnosis_ip_weird_resolvconf_details": "The file /etc/resolv.conf should be a symlink to /etc/resolvconf/run/resolv.conf itself pointing to 127.0.0.1 (dnsmasq). If you want to manually configure DNS resolvers, please edit /etc/resolv.dnsmasq.conf.", "diagnosis_dns_good_conf": "Good DNS configuration for domain {domain} (category {category})", "diagnosis_dns_bad_conf": "Bad or missing DNS configuration for domain {domain} (category {category})", - "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.\nType: {type}\nName: {name}\nValue: {value}", - "diagnosis_dns_discrepancy": "The DNS record with type {type} and name {name} does not match the recommended configuration.\nCurrent value: {current}\nExcepted value: {value}", - "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help about configuring DNS records", + "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.
Type: {type}
Name: {name}
Value: {value}", + "diagnosis_dns_discrepancy": "The following DNS record does not seem to follow the recommended configuration:
Type: {type}
Name: {name}
Current value: {current}
Excepted value: {value}", + "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help about configuring DNS records.", "diagnosis_services_running": "Service {service} is running!", "diagnosis_services_conf_broken": "Configuration is broken for service {service}!", "diagnosis_services_bad_status": "Service {service} is {status} :(", @@ -209,7 +209,9 @@ "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", "diagnosis_ports_ok": "Port {port} is reachable from outside.", "diagnosis_ports_needed_by": "Exposing this port is needed for {category} features (service {service})", - "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", + "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", + "diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.", + "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable. You should check that you're correctly forwarding port 80, that nginx is running, and that a firewall is not interfering.", diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 31518c257..369554bd4 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -24,6 +24,7 @@ Look for possible issues on the server """ +import re import os import time @@ -457,9 +458,6 @@ class Diagnoser(): report["description"] = Diagnoser.get_description(report["id"]) - def is_tuple_or_list(stuff): - return isinstance(stuff, tuple) or isinstance(stuff, list) - for item in report["items"]: # For the summary and each details, we want to call @@ -472,18 +470,21 @@ class Diagnoser(): meta_data = item.get("meta", {}).copy() meta_data.update(item.get("data", {})) - if not is_tuple_or_list(item["summary"]): - item["summary"] = (item["summary"], {}) - summary_key, summary_args = item["summary"] - summary_args.update(meta_data) + html_tags = re.compile(r'<[^>]+>') + def m18n_(info): + if not isinstance(info, tuple) and not isinstance(info, list): + info = (info, {}) + info[1].update(meta_data) + s = m18n.n(info[0], **(info[1])) + # In cli, we remove the html tags + if msettings.get("interface") != "api": + s = html_tags.sub('', s.replace("
","\n")) + return s - item["summary"] = m18n.n(summary_key, **summary_args) + item["summary"] = m18n_(item["summary"]) if "details" in item: - item["details"] = [(d[0], d[1]) if is_tuple_or_list(d) else (d, {}) for d in item["details"]] - for d in item["details"]: - d[1].update(meta_data) - item["details"] = [m18n.n(key, **values) for key, values in item["details"]] + item["details"] = [m18n_(info) for info in item["details"]] def _list_diagnosis_categories(): From 2f0a95645ae58f273e55667cb266d16e4d329f11 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 03:25:03 +0200 Subject: [PATCH 21/91] Hmpf boring resolvconf shit --- data/hooks/conf_regen/43-dnsmasq | 15 +++++++++++++++ data/hooks/diagnosis/10-ip.py | 15 +++++++-------- 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index 90e96a04c..d6ab8648c 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -50,6 +50,21 @@ do_pre_regen() { do_post_regen() { regen_conf_files=$1 + # Fuck it, those domain/search entries from dhclient are usually annoying + # lying shit from the ISP trying to MiTM + if grep -q -E "^ *(domain|search)" /run/resolvconf/resolv.conf + then + if grep -q -E "^ *(domain|search)" /run/resolvconf/interface/*.dhclient 2>/dev/null + then + sed -E "s/^(domain|search)/#\1/g" -i /run/resolvconf/interface/*.dhclient + fi + + grep -q '^supersede domain-name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo '^supersede domain-name "";' >> /etc/dhcp/dhclient.conf + grep -q '^supersede domain-search "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo '^supersede domain-search "";' >> /etc/dhcp/dhclient.conf + grep -q '^supersede name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo '^supersede name "";' >> /etc/dhcp/dhclient.conf + systemctl restart resolvconf + fi + [[ -z "$regen_conf_files" ]] \ || service dnsmasq restart } diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 70a5c9594..7d0aa8da2 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -41,7 +41,7 @@ class IPDiagnoser(Diagnoser): # In every case, we can check that resolvconf seems to be okay # (symlink managed by resolvconf service + pointing to dnsmasq) - good_resolvconf = self.resolvconf_is_symlink() and self.resolvconf_points_to_localhost() + good_resolvconf = self.good_resolvconf() # If we can't resolve domain names at all, that's a pretty big issue ... # If it turns out that at the same time, resolvconf is bad, that's probably @@ -131,13 +131,12 @@ class IPDiagnoser(Diagnoser): def can_resolve_dns(self): return os.system("dig +short ip.yunohost.org >/dev/null 2>/dev/null") == 0 - def resolvconf_is_symlink(self): - return os.path.realpath("/etc/resolv.conf") == "/run/resolvconf/resolv.conf" - - def resolvconf_points_to_localhost(self): - file_ = "/etc/resolv.conf" - resolvers = [r.split(" ")[1] for r in read_file(file_).split("\n") if r.startswith("nameserver")] - return resolvers == ["127.0.0.1"] + def good_resolvconf(self): + content = read_file(file_).strip().split("\n") + # Ignore comments and empty lines + content = [l.strip() for l in content if l.strip() and not l.strip().startswith("#")] + # We should only find a "nameserver 127.0.0.1" + return len(content) == 1 and content.split() == ["nameserver", "127.0.0.1"] def get_public_ip(self, protocol=4): From 42293fcce38792e10623777542011dd10224cde0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 9 Apr 2020 13:53:31 +0200 Subject: [PATCH 22/91] Attempt to detect hairpinning --- data/hooks/diagnosis/21-web.py | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index add192685..56b054e53 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -19,6 +19,7 @@ class WebDiagnoser(Diagnoser): nonce_digits = "0123456789abcedf" + at_least_one_domain_ok = False all_domains = domain_list()["domains"] for domain in all_domains: @@ -52,6 +53,7 @@ class WebDiagnoser(Diagnoser): raise YunohostError("diagnosis_http_could_not_diagnose", error=e) if r["status"] == "ok": + at_least_one_domain_ok = True yield dict(meta={"domain": domain}, status="SUCCESS", summary="diagnosis_http_ok") @@ -62,9 +64,28 @@ class WebDiagnoser(Diagnoser): summary="diagnosis_http_unreachable", details=[detail]) - # In there or idk where else ... - # try to diagnose hairpinning situation by crafting a request for the - # global ip (from within local network) and seeing if we're getting the right page ? + # If at least one domain is correctly exposed to the outside, + # attempt to diagnose hairpinning situations. On network with + # hairpinning issues, the server may be correctly exposed on the + # outside, but from the outside, it will be as if the port forwarding + # was not configured... Hence, calling for example + # "curl --head the.global.ip" will simply timeout... + if at_least_one_domain_ok: + ipv4 = Diagnoser.get_cached_report_item("ip", {"test": "ipv4"}) + global_ipv4 = ipv4.get("data", {}).get("global", {}) + if global_ipv4: + try: + requests.head("http://" + ipv4, timeout=5) + except requests.exceptions.Timeout as e: + yield dict(meta={"test": "hairpinning"}, + status="WARNING", + summary="diagnosis_http_hairpinning_issue", + details=["diagnosis_http_hairpinning_issue_details"]) + except: + # Well I dunno what to do if that's another exception + # type... That'll most probably *not* be an hairpinning + # issue but something else super weird ... + pass def main(args, env, loggers): From ad4c13887862fbaec774619f34d41cf6de5ef4d0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 17:20:24 +0200 Subject: [PATCH 23/91] Better debugging info when miserably failing to run diagnosis --- src/yunohost/diagnosis.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 369554bd4..d8c6b5f57 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -167,7 +167,8 @@ def diagnosis_run(categories=[], force=False): try: code, report = hook_exec(path, args={"force": force}, env=None) except Exception as e: - logger.error(m18n.n("diagnosis_failed_for_category", category=category, error=str(e)), exc_info=True) + import traceback + logger.error(m18n.n("diagnosis_failed_for_category", category=category, error='\n'+traceback.format_exc())) else: diagnosed_categories.append(category) if report != {}: @@ -424,7 +425,7 @@ class Diagnoser(): cache_file = Diagnoser.cache_file(id_) if not os.path.exists(cache_file): logger.warning(m18n.n("diagnosis_no_cache", category=id_)) - report = {"id": category, + report = {"id": id_, "cached_for": -1, "timestamp": -1, "items": []} From f47352df8896ec39a26338048eb552491cd32528 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 18:10:21 +0200 Subject: [PATCH 24/91] Improve message about server unreachable on http --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index d3261a2cf..be479efca 100644 --- a/locales/en.json +++ b/locales/en.json @@ -214,10 +214,10 @@ "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", - "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable. You should check that you're correctly forwarding port 80, that nginx is running, and that a firewall is not interfering.", + "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", "diagnosis_http_unknown_error": "An error happened while trying to reach your domain, it's very likely unreachable.", - "diagnosis_http_bad_status_code": "The diagnosis system could not reach your server. It might be that another machine answered instead of your server. You should check that you're correctly forwarding port 80, that your nginx configuration is up to date, and that a reverse-proxy is not interfering.", + "diagnosis_http_bad_status_code": "Timed-out while trying to contact your server from outside. It might be that another machine answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the different with the command line using 'yunohost tools regen-conf nginx --dry-run --with-diff' and if you're ok, apply the changes with 'yunohost tools regen-conf nginx --force'.", From b443caf63a0877d63fca26f34010e47cd4a58452 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 18:10:46 +0200 Subject: [PATCH 25/91] Open links in new tab in the webadmin --- src/yunohost/diagnosis.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index d8c6b5f57..9a8962ac4 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -480,6 +480,9 @@ class Diagnoser(): # In cli, we remove the html tags if msettings.get("interface") != "api": s = html_tags.sub('', s.replace("
","\n")) + else: + # Make it so that links open in new tabs + s = s.replace(" Date: Sat, 11 Apr 2020 19:21:29 +0200 Subject: [PATCH 26/91] Uhoh typo --- data/hooks/diagnosis/10-ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 7d0aa8da2..42b52eb07 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -132,7 +132,7 @@ class IPDiagnoser(Diagnoser): return os.system("dig +short ip.yunohost.org >/dev/null 2>/dev/null") == 0 def good_resolvconf(self): - content = read_file(file_).strip().split("\n") + content = read_file("/etc/resolv.conf").strip().split("\n") # Ignore comments and empty lines content = [l.strip() for l in content if l.strip() and not l.strip().startswith("#")] # We should only find a "nameserver 127.0.0.1" From ae82fe3693cb51d0c19b0817a7093ed8980e5129 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 19:52:57 +0200 Subject: [PATCH 27/91] Improve the way we check DNS records to avoid false negative on TXT or MX --- data/hooks/diagnosis/12-dnsrecords.py | 45 +++++++++++++++++++++------ 1 file changed, 36 insertions(+), 9 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index d653b044c..f5d779118 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -52,14 +52,15 @@ class DNSRecordsDiagnoser(Diagnoser): discrepancies = [] for r in records: - r["current"] = self.get_current_record(domain, r["name"], r["type"]) or "None" + r["current"] = self.get_current_record(domain, r["name"], r["type"]) if r["value"] == "@": r["value"] = domain + "." - if r["current"] == "None": - discrepancies.append(("diagnosis_dns_missing_record", r)) - elif r["current"] != r["value"]: - discrepancies.append(("diagnosis_dns_discrepancy", r)) + if not self.current_record_match_expected(r): + if r["current"] is None: + discrepancies.append(("diagnosis_dns_missing_record", r)) + else: + discrepancies.append(("diagnosis_dns_discrepancy", r)) if discrepancies: status = "ERROR" if (category == "basic" or (is_main_domain and category != "extra")) else "WARNING" @@ -85,10 +86,36 @@ class DNSRecordsDiagnoser(Diagnoser): # FIXME : gotta handle case where this command fails ... # e.g. no internet connectivity (dependency mechanism to good result from 'ip' diagosis ?) # or the resolver is unavailable for some reason - output = check_output(command).strip() - if output.startswith('"') and output.endswith('"'): - output = '"' + ' '.join(output.replace('"', ' ').split()) + '"' - return output + output = check_output(command).strip().split("\n") + if len(output) == 0 or not output[0]: + return None + elif len(output) == 1: + return output[0] + else: + return output + + def current_record_match_expected(self, r): + if r["value"] is not None and r["current"] is None: + return False + if r["value"] is None and r["current"] is not None: + return False + elif isinstance(r["current"], list): + return False + + if r["type"] == "TXT": + # Split expected/current + # from "v=DKIM1; k=rsa; p=hugekey;" + # to a set like {'v=DKIM1', 'k=rsa', 'p=...'} + expected = set(r["value"].strip(' "').strip(";").replace(" ", "").split()) + current = set(r["current"].strip(' "').strip(";").replace(" ", "").split()) + return expected == current + elif r["type"] == "MX": + # For MX, we want to ignore the priority + expected = r["value"].split()[-1] + current = r["current"].split()[-1] + return expected == current + else: + return r["current"] == r["value"] def main(args, env, loggers): From 093ccd8020f509845a81df31c43f9843914defd7 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 20:02:47 +0200 Subject: [PATCH 28/91] Make sure that there's no AAAA records when no ipv6 --- data/hooks/diagnosis/12-dnsrecords.py | 2 +- src/yunohost/domain.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index f5d779118..7ea92e3f7 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -38,7 +38,7 @@ class DNSRecordsDiagnoser(Diagnoser): def check_domain(self, domain, is_main_domain, is_subdomain): - expected_configuration = _build_dns_conf(domain) + expected_configuration = _build_dns_conf(domain, include_empty_AAAA_if_no_ipv6=True) # FIXME: Here if there are no AAAA record, we should add something to expect "no" AAAA record # to properly diagnose situations where people have a AAAA record but no IPv6 diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 23b5a4179..7910147a3 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -395,7 +395,7 @@ def _normalize_domain_path(domain, path): return domain, path -def _build_dns_conf(domain, ttl=3600): +def _build_dns_conf(domain, ttl=3600, include_empty_AAAA_if_no_ipv6=False): """ Internal function that will returns a data structure containing the needed information to generate/adapt the dns configuration @@ -448,6 +448,8 @@ def _build_dns_conf(domain, ttl=3600): if ipv6: basic.append(["@", ttl, "AAAA", ipv6]) + elif include_empty_AAAA_if_no_ipv6: + basic.append(["@", ttl, "AAAA", None]) ######### # Email # @@ -495,8 +497,11 @@ def _build_dns_conf(domain, ttl=3600): if ipv4: extra.append(["*", ttl, "A", ipv4]) + if ipv6: extra.append(["*", ttl, "AAAA", ipv6]) + elif include_empty_AAAA_if_no_ipv6: + extra.append(["*", ttl, "AAAA", None]) extra.append(["@", ttl, "CAA", '128 issue "letsencrypt.org"']) From 16b234044137bbf24a757482a8e8f0e2820beed0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 20:04:03 +0200 Subject: [PATCH 29/91] Uhoh typo again --- data/hooks/diagnosis/21-web.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 56b054e53..2d0344abe 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -75,7 +75,7 @@ class WebDiagnoser(Diagnoser): global_ipv4 = ipv4.get("data", {}).get("global", {}) if global_ipv4: try: - requests.head("http://" + ipv4, timeout=5) + requests.head("http://" + global_ipv4, timeout=5) except requests.exceptions.Timeout as e: yield dict(meta={"test": "hairpinning"}, status="WARNING", From bfe3f415cacbea9fff7746c456128911d4d6a98a Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 11 Apr 2020 20:06:14 +0200 Subject: [PATCH 30/91] Report bad XMPP DNS records as warning for now --- data/hooks/diagnosis/12-dnsrecords.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 7ea92e3f7..3132cf45f 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -63,7 +63,7 @@ class DNSRecordsDiagnoser(Diagnoser): discrepancies.append(("diagnosis_dns_discrepancy", r)) if discrepancies: - status = "ERROR" if (category == "basic" or (is_main_domain and category != "extra")) else "WARNING" + status = "ERROR" if (category == "basic" or (is_main_domain and category == "mail")) else "WARNING" summary = "diagnosis_dns_bad_conf" else: status = "SUCCESS" From 92d9d49a05aa2d68cf78d4e7d4bfd8f4a55087d3 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Mon, 13 Apr 2020 18:50:37 +0200 Subject: [PATCH 31/91] Fix resolvconf check --- data/hooks/diagnosis/10-ip.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 42b52eb07..36e04b5c1 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -136,7 +136,7 @@ class IPDiagnoser(Diagnoser): # Ignore comments and empty lines content = [l.strip() for l in content if l.strip() and not l.strip().startswith("#")] # We should only find a "nameserver 127.0.0.1" - return len(content) == 1 and content.split() == ["nameserver", "127.0.0.1"] + return len(content) == 1 and content[0].split() == ["nameserver", "127.0.0.1"] def get_public_ip(self, protocol=4): From efb45d4ece1c870736882eebf09470198e0fa4af Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 14 Apr 2020 16:01:59 +0200 Subject: [PATCH 32/91] Add special behavior for tags for diagnosis messages... --- src/yunohost/diagnosis.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index 9a8962ac4..c11cde566 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -479,8 +479,10 @@ class Diagnoser(): s = m18n.n(info[0], **(info[1])) # In cli, we remove the html tags if msettings.get("interface") != "api": + s = s.replace("", "'").replace("", "'") s = html_tags.sub('', s.replace("
","\n")) else: + s = s.replace("", "").replace("", "") # Make it so that links open in new tabs s = s.replace("
yunohost service log {service} or through the 'Services' section of the webadmin.", "diagnosis_diskusage_verylow": "Storage {mountpoint} (on device {device}) has only {free_abs_GB} GB ({free_percent}%) space remaining. You should really consider cleaning up some space.", "diagnosis_diskusage_low": "Storage {mountpoint} (on device {device}) has only {free_abs_GB} GB ({free_percent}%) space remaining. Be careful.", "diagnosis_diskusage_ok": "Storage {mountpoint} (on device {device}) still has {free_abs_GB} GB ({free_percent}%) space left!", @@ -188,10 +188,8 @@ "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", - "diagnosis_regenconf_manually_modified": "Configuration file {file} was manually modified.", - "diagnosis_regenconf_manually_modified_details": "This is probably OK as long as you know what you're doing ;) !", - "diagnosis_regenconf_manually_modified_debian": "Configuration file {file} was manually modified compared to Debian's default.", - "diagnosis_regenconf_manually_modified_debian_details": "This may probably be OK, but gotta keep an eye on it...", + "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", + "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! Though YunoHost will stop updating this file automatically, beware that YunoHost upgrades may contain important recommended changes. You can inspect the difference with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", "diagnosis_security_all_good": "No critical security vulnerability was found.", "diagnosis_security_vulnerable_to_meltdown": "You appear vulnerable to the Meltdown criticial security vulnerability", "diagnosis_security_vulnerable_to_meltdown_details": "To fix this, you should upgrade your system and reboot to load the new linux kernel (or contact your server provider if this doesn't work). See https://meltdownattack.com/ for more infos.", @@ -220,7 +218,7 @@ "diagnosis_http_bad_status_code": "Timed-out while trying to contact your server from outside. It might be that another machine answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", - "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the different with the command line using 'yunohost tools regen-conf nginx --dry-run --with-diff' and if you're ok, apply the changes with 'yunohost tools regen-conf nginx --force'.", + "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using yunohost tools regen-conf nginx --dry-run --with-diff and if you're ok, apply the changes with yunohost tools regen-conf nginx --force.", "diagnosis_unknown_categories": "The following categories are unknown: {categories}", "diagnosis_never_ran_yet": "It looks like this server was setup recently and there's no diagnosis report to show yet. You should start by running a full diagnosis, either from the webadmin or using 'yunohost diagnosis run' from the command line.", "domain_cannot_remove_main": "You cannot remove '{domain:s}' since it's the main domain, you first need to set another domain as the main domain using 'yunohost domain main-domain -n '; here is the list of candidate domains: {other_domains:s}", From a03ee5b912dd6aaf3e6c514ee266e01cee8d3402 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 14 Apr 2020 18:47:58 +0200 Subject: [PATCH 34/91] Be able to restart services from the webadmin --- data/actionsmap/yunohost.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index 48b1687d4..ded56a7c1 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1041,6 +1041,7 @@ service: ### service_restart() restart: action_help: Restart one or more services. If the services are not running yet, they will be started. + api: PUT /services//restart arguments: names: help: Service name to restart From 4787f0ce042f8eaace1440c95e1d3cf9dabe73dc Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Tue, 14 Apr 2020 23:48:59 +0200 Subject: [PATCH 35/91] Rework diagnosis of system resources --- data/hooks/diagnosis/50-systemresources.py | 78 +++++++++++++++------- locales/en.json | 20 +++--- 2 files changed, 65 insertions(+), 33 deletions(-) diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index 1f0c07f47..491c5b665 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -12,22 +12,24 @@ class SystemResourcesDiagnoser(Diagnoser): def run(self): + MB = 1024**2 + GB = 1024**2 + # # RAM # ram = psutil.virtual_memory() - ram_total_abs_MB = ram.total / (1024**2) - ram_available_abs_MB = ram.available / (1024**2) - ram_available_percent = round(100 * ram.available / ram.total) + ram_available_percent = 100 * ram.available / ram.total item = dict(meta={"test": "ram"}, - data={"total_abs_MB": ram_total_abs_MB, - "available_abs_MB": ram_available_abs_MB, - "available_percent": ram_available_percent}) - if ram_available_abs_MB < 100 or ram_available_percent < 5: + data={"total": human_size(ram.total), + "available": human_size(ram.available), + "available_percent": round_(ram_available_percent)}) + + if ram.available < 100 * MB or ram_available_percent < 5: item["status"] = "ERROR" item["summary"] = "diagnosis_ram_verylow" - elif ram_available_abs_MB < 200 or ram_available_percent < 10: + elif ram.available < 200 * MB or ram_available_percent < 10: item["status"] = "WARNING" item["summary"] = "diagnosis_ram_low" else: @@ -40,13 +42,12 @@ class SystemResourcesDiagnoser(Diagnoser): # swap = psutil.swap_memory() - swap_total_abs_MB = swap.total / (1024*1024) item = dict(meta={"test": "swap"}, - data={"total_MB": swap_total_abs_MB}) - if swap_total_abs_MB <= 0: + data={"total": human_size(swap.total)}) + if swap.total <= 1 * MB: item["status"] = "ERROR" item["summary"] = "diagnosis_swap_none" - elif swap_total_abs_MB <= 256: + elif swap.total <= 256 * MB: item["status"] = "WARNING" item["summary"] = "diagnosis_swap_notsomuch" else: @@ -67,23 +68,54 @@ class SystemResourcesDiagnoser(Diagnoser): mountpoint = disk_partition.mountpoint usage = psutil.disk_usage(mountpoint) - free_abs_GB = usage.free / (1024 ** 3) - free_percent = 100 - usage.percent + free_percent = round_(100 - usage.percent) item = dict(meta={"test": "diskusage", "mountpoint": mountpoint}, - data={"device": device, "free_abs_GB": free_abs_GB, "free_percent": free_percent}) - if free_abs_GB < 1 or free_percent < 5: - item["status"] = "ERROR" - item["summary"] = "diagnosis_diskusage_verylow" - elif free_abs_GB < 2 or free_percent < 10: - item["status"] = "WARNING" - item["summary"] = "diagnosis_diskusage_low" + data={"device": device, "total": human_size(usage.total), "free": human_size(usage.free), "free_percent": free_percent}) + + # Special checks for /boot partition because they sometimes are + # pretty small and that's kind of okay... (for example on RPi) + if mountpoint.startswith("/boot"): + if usage.free < 10 * MB or free_percent < 10: + item["status"] = "ERROR" + item["summary"] = "diagnosis_diskusage_verylow" + elif usage.free < 20 * MB or free_percent < 20: + item["status"] = "WARNING" + item["summary"] = "diagnosis_diskusage_low" + else: + item["status"] = "SUCCESS" + item["summary"] = "diagnosis_diskusage_ok" else: - item["status"] = "SUCCESS" - item["summary"] = "diagnosis_diskusage_ok" + if usage.free < 1 * GB or free_percent < 5: + item["status"] = "ERROR" + item["summary"] = "diagnosis_diskusage_verylow" + elif usage.free < 2 * GB or free_percent < 10: + item["status"] = "WARNING" + item["summary"] = "diagnosis_diskusage_low" + else: + item["status"] = "SUCCESS" + item["summary"] = "diagnosis_diskusage_ok" + yield item +def human_size(bytes_): + # Adapted from https://stackoverflow.com/a/1094933 + for unit in ['','ki','Mi','Gi','Ti','Pi','Ei','Zi']: + if abs(bytes_) < 1024.0: + return "%s %sB" % (round_(bytes_), unit) + bytes_ /= 1024.0 + return "%s %sB" % (round_(bytes_), 'Yi') + + +def round_(n): + # round_(22.124) -> 22 + # round_(9.45) -> 9.4 + n = round(n, 1) + if n > 10: + n = int(round(n)) + return n + def main(args, env, loggers): return SystemResourcesDiagnoser(args, env, loggers).diagnose() diff --git a/locales/en.json b/locales/en.json index 4b1fdaa05..71804af97 100644 --- a/locales/en.json +++ b/locales/en.json @@ -175,21 +175,21 @@ "diagnosis_services_running": "Service {service} is running!", "diagnosis_services_conf_broken": "Configuration is broken for service {service}!", "diagnosis_services_bad_status": "Service {service} is {status} :(", - "diagnosis_services_bad_status_tip": "You can try to restart the service, and if it doesn't work, have a look at the service logs using yunohost service log {service} or through the 'Services' section of the webadmin.", - "diagnosis_diskusage_verylow": "Storage {mountpoint} (on device {device}) has only {free_abs_GB} GB ({free_percent}%) space remaining. You should really consider cleaning up some space.", - "diagnosis_diskusage_low": "Storage {mountpoint} (on device {device}) has only {free_abs_GB} GB ({free_percent}%) space remaining. Be careful.", - "diagnosis_diskusage_ok": "Storage {mountpoint} (on device {device}) still has {free_abs_GB} GB ({free_percent}%) space left!", - "diagnosis_ram_verylow": "The system has only {available_abs_MB} MB ({available_percent}%) RAM left! (out of {total_abs_MB} MB)", - "diagnosis_ram_low": "The system has {available_abs_MB} MB ({available_percent}%) RAM left out of {total_abs_MB} MB. Be careful.", - "diagnosis_ram_ok": "The system still has {available_abs_MB} MB ({available_percent}%) RAM left out of {total_abs_MB} MB.", + "diagnosis_services_bad_status_tip": "You can try to restart the service, and if it doesn't work, have a look at the service logs in the webadmin (from the command line, you can do this with yunohost service restart {service} and yunohost service log {service}).", + "diagnosis_diskusage_verylow": "Storage {mountpoint} (on device {device}) has only {free} ({free_percent}%) space remaining (out of {total}). You should really consider cleaning up some space!", + "diagnosis_diskusage_low": "Storage {mountpoint} (on device {device}) has only {free} ({free_percent}%) space remaining (out of {total}). Be careful.", + "diagnosis_diskusage_ok": "Storage {mountpoint} (on device {device}) still has {free} ({free_percent}%) space left (out of {total})!", + "diagnosis_ram_verylow": "The system has only {available} ({available_percent}%) RAM available! (out of {total})", + "diagnosis_ram_low": "The system has {available} ({available_percent}%) RAM available (out of {total}). Be careful.", + "diagnosis_ram_ok": "The system still has {available} ({available_percent}%) RAM available out of {total}.", "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least 256 MB of swap to avoid situations where the system runs out of memory.", - "diagnosis_swap_notsomuch": "The system has only {total_MB} MB swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", - "diagnosis_swap_ok": "The system has {total_MB} MB of swap!", + "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", + "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", - "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! Though YunoHost will stop updating this file automatically, beware that YunoHost upgrades may contain important recommended changes. You can inspect the difference with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", + "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", "diagnosis_security_all_good": "No critical security vulnerability was found.", "diagnosis_security_vulnerable_to_meltdown": "You appear vulnerable to the Meltdown criticial security vulnerability", "diagnosis_security_vulnerable_to_meltdown_details": "To fix this, you should upgrade your system and reboot to load the new linux kernel (or contact your server provider if this doesn't work). See https://meltdownattack.com/ for more infos.", From a85c15dd0bf58d8bc1a75fb2adac61db7a6a9cca Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 15 Apr 2020 01:07:40 +0200 Subject: [PATCH 36/91] Update data/hooks/diagnosis/50-systemresources.py Co-Authored-By: Kayou --- data/hooks/diagnosis/50-systemresources.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index 491c5b665..b4e50ccf1 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -13,7 +13,7 @@ class SystemResourcesDiagnoser(Diagnoser): def run(self): MB = 1024**2 - GB = 1024**2 + GB = MB*1024 # # RAM From 8e46b536dc9089cd2db934354dacca497036c926 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Wed, 15 Apr 2020 03:48:14 +0200 Subject: [PATCH 37/91] Somewhat cleaner hack to check the status of those damn services that aren't the real services... --- data/templates/yunohost/services.yml | 4 +- src/yunohost/service.py | 55 +++++++++++++++------------- 2 files changed, 32 insertions(+), 27 deletions(-) diff --git a/data/templates/yunohost/services.yml b/data/templates/yunohost/services.yml index fdf278fcf..e1dd57e55 100644 --- a/data/templates/yunohost/services.yml +++ b/data/templates/yunohost/services.yml @@ -13,7 +13,7 @@ metronome: category: xmpp mysql: log: [/var/log/mysql.log,/var/log/mysql.err,/var/log/mysql/error.log] - alternates: ['mariadb'] + actual_systemd_service: mariadb category: database nginx: log: /var/log/nginx @@ -27,7 +27,7 @@ php7.0-fpm: category: web postfix: log: [/var/log/mail.log,/var/log/mail.err] - test_status: systemctl show postfix@- | grep -q "^SubState=running" + actual_systemd_service: postfix@- needs_exposed_ports: [25, 587] category: email redis-server: diff --git a/src/yunohost/service.py b/src/yunohost/service.py index 748037df6..b6c93b5ae 100644 --- a/src/yunohost/service.py +++ b/src/yunohost/service.py @@ -80,7 +80,7 @@ def service_add(name, description=None, log=None, log_type="file", test_status=N services[name]['description'] = description else: # Try to get the description from systemd service - out = subprocess.check_output("systemctl show %s | grep '^Description='" % name, shell=True) + out = subprocess.check_output("systemctl show %s | grep '^Description='" % name, shell=True).strip() out = out.replace("Description=", "") # If the service does not yet exists or if the description is empty, # systemd will anyway return foo.service as default value, so we wanna @@ -295,16 +295,11 @@ def service_status(names=[]): if services[name].get("status", "") is None: continue - status = _get_service_information_from_systemd(name) - - # try to get status using alternative version if they exists - # this is for mariadb/mysql but is generic in case of - alternates = services[name].get("alternates", []) - while status is None and alternates: - status = _get_service_information_from_systemd(alternates.pop()) + systemd_service = services[name].get("actual_systemd_service", name) + status = _get_service_information_from_systemd(systemd_service) if status is None: - logger.error("Failed to get status information via dbus for service %s, systemctl didn't recognize this service ('NoSuchUnit')." % name) + logger.error("Failed to get status information via dbus for service %s, systemctl didn't recognize this service ('NoSuchUnit')." % systemd_service) result[name] = { 'status': "unknown", 'start_on_boot': "unknown", @@ -338,6 +333,8 @@ def service_status(names=[]): # gotta do this ... cf code of /lib/systemd/systemd-sysv-install if result[name]["start_on_boot"] == "generated": result[name]["start_on_boot"] = "enabled" if glob("/etc/rc[S5].d/S??"+name) else "disabled" + elif os.path.exists("/etc/systemd/system/multi-user.target.wants/%s.service" % name): + result[name]["start_on_boot"] = "enabled" if "StateChangeTimestamp" in status: result[name]['last_state_change'] = datetime.utcfromtimestamp(status["StateChangeTimestamp"] / 1000000) @@ -408,6 +405,7 @@ def service_log(name, number=50): """ services = _get_services() + number = int(number) if name not in services.keys(): raise YunohostError('service_unknown', service=name) @@ -423,11 +421,7 @@ def service_log(name, number=50): result = {} # First we always add the logs from journalctl / systemd - result["journalctl"] = _get_journalctl_logs(name, int(number)).splitlines() - - # Mysql and journalctl are fucking annoying, we gotta explictly fetch mariadb ... - if name == "mysql": - result["journalctl"] = _get_journalctl_logs("mariadb", int(number)).splitlines() + result["journalctl"] = _get_journalctl_logs(name, number).splitlines() for index, log_path in enumerate(log_list): log_type = log_type_list[index] @@ -435,7 +429,7 @@ def service_log(name, number=50): if log_type == "file": # log is a file, read it if not os.path.isdir(log_path): - result[log_path] = _tail(log_path, int(number)) if os.path.exists(log_path) else [] + result[log_path] = _tail(log_path, number) if os.path.exists(log_path) else [] continue for log_file in os.listdir(log_path): @@ -447,10 +441,11 @@ def service_log(name, number=50): if not log_file.endswith(".log"): continue - result[log_file_path] = _tail(log_file_path, int(number)) if os.path.exists(log_file_path) else [] + result[log_file_path] = _tail(log_file_path, number) if os.path.exists(log_file_path) else [] else: + # N.B. : this is legacy code that can probably be removed ... to be confirmed # get log with journalctl - result[log_path] = _get_journalctl_logs(log_path, int(number)).splitlines() + result[log_path] = _get_journalctl_logs(log_path, number).splitlines() return result @@ -572,14 +567,22 @@ def _get_services(): services = yaml.load(f) except: return {} - else: - # some services are marked as None to remove them from YunoHost - # filter this - for key, value in services.items(): - if value is None: - del services[key] - return services + # some services are marked as None to remove them from YunoHost + # filter this + for key, value in services.items(): + if value is None: + del services[key] + + # Stupid hack for postgresql which ain't an official service ... Can't + # really inject that info otherwise. Real service we want to check for + # status and log is in fact postgresql@x.y-main (x.y being the version) + if "postgresql" in services: + if "description" in services["postgresql"]: + del services["postgresql"]["description"] + services["postgresql"]["actual_systemd_service"] = "postgresql@9.6-main" + + return services def _save_services(services): @@ -674,8 +677,10 @@ def _find_previous_log_file(file): def _get_journalctl_logs(service, number="all"): + services = _get_services() + systemd_service = services.get(service, {}).get("actual_systemd_service", service) try: - return subprocess.check_output("journalctl -xn -u {0} -n{1}".format(service, number), shell=True) + return subprocess.check_output("journalctl -xn -u {0} -n{1}".format(systemd_service, number), shell=True) except: import traceback return "error while get services logs from journalctl:\n%s" % traceback.format_exc() From 7f3cc334873d693e13667de7b6ae3d34eca0217f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 02:51:29 +0200 Subject: [PATCH 38/91] Add a static method to call remote diagnosis and supports ipv4-only or ipv6-only check --- data/hooks/diagnosis/14-ports.py | 20 ++----- data/hooks/diagnosis/21-web.py | 98 +++++++++++++++++++------------- src/yunohost/diagnosis.py | 46 +++++++++++++++ 3 files changed, 110 insertions(+), 54 deletions(-) diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index f973a3275..05c28e8dc 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -1,7 +1,6 @@ #!/usr/bin/env python import os -import requests from yunohost.diagnosis import Diagnoser from yunohost.utils.error import YunohostError @@ -27,25 +26,16 @@ class PortsDiagnoser(Diagnoser): ports[port] = service try: - r = requests.post('https://diagnosis.yunohost.org/check-ports', json={'ports': ports.keys()}, timeout=30) - if r.status_code not in [200, 400, 418]: - raise Exception("Bad response from the server https://diagnosis.yunohost.org/check-ports : %s - %s" % (str(r.status_code), r.content)) - r = r.json() - if "status" not in r.keys(): - raise Exception("Bad syntax for response ? Raw json: %s" % str(r)) - elif r["status"] == "error": - if "content" in r.keys(): - raise Exception(r["content"]) - else: - raise Exception("Bad syntax for response ? Raw json: %s" % str(r)) - elif r["status"] != "ok" or "ports" not in r.keys() or not isinstance(r["ports"], dict): - raise Exception("Bad syntax for response ? Raw json: %s" % str(r)) + r = Diagnoser.remote_diagnosis('check-ports', + data={'ports': ports.keys()}, + ipversion=4) + results = r["ports"] except Exception as e: raise YunohostError("diagnosis_ports_could_not_diagnose", error=e) for port, service in sorted(ports.items()): category = services[service].get("category", "[?]") - if r["ports"].get(str(port), None) is not True: + if results.get(str(port), None) is not True: yield dict(meta={"port": str(port)}, data={"service": service, "category": category}, status="ERROR", diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 2d0344abe..270c566cc 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -4,10 +4,14 @@ import os import random import requests +from moulinette.utils.filesystem import read_file + from yunohost.diagnosis import Diagnoser from yunohost.domain import domain_list from yunohost.utils.error import YunohostError +DIAGNOSIS_SERVER = "diagnosis.yunohost.org" + class WebDiagnoser(Diagnoser): @@ -17,52 +21,42 @@ class WebDiagnoser(Diagnoser): def run(self): - nonce_digits = "0123456789abcedf" - - at_least_one_domain_ok = False all_domains = domain_list()["domains"] + domains_to_check = [] for domain in all_domains: # If the diagnosis location ain't defined, can't do diagnosis, # probably because nginx conf manually modified... nginx_conf = "/etc/nginx/conf.d/%s.conf" % domain - if os.system("grep -q '^.*location .*/.well-known/ynh-diagnosis/' %s" % nginx_conf) != 0: + if ".well-known/ynh-diagnosis/" not in read_file(nginx_conf): yield dict(meta={"domain": domain}, status="WARNING", summary="diagnosis_http_nginx_conf_not_up_to_date", details=["diagnosis_http_nginx_conf_not_up_to_date_details"]) - - nonce = ''.join(random.choice(nonce_digits) for i in range(16)) - os.system("rm -rf /tmp/.well-known/ynh-diagnosis/") - os.system("mkdir -p /tmp/.well-known/ynh-diagnosis/") - os.system("touch /tmp/.well-known/ynh-diagnosis/%s" % nonce) - - try: - r = requests.post('https://diagnosis.yunohost.org/check-http', json={'domain': domain, "nonce": nonce}, timeout=30) - if r.status_code not in [200, 400, 418]: - raise Exception("Bad response from the server https://diagnosis.yunohost.org/check-http : %s - %s" % (str(r.status_code), r.content)) - r = r.json() - if "status" not in r.keys(): - raise Exception("Bad syntax for response ? Raw json: %s" % str(r)) - elif r["status"] == "error" and ("code" not in r.keys() or not r["code"].startswith("error_http_check_")): - if "content" in r.keys(): - raise Exception(r["content"]) - else: - raise Exception("Bad syntax for response ? Raw json: %s" % str(r)) - except Exception as e: - raise YunohostError("diagnosis_http_could_not_diagnose", error=e) - - if r["status"] == "ok": - at_least_one_domain_ok = True - yield dict(meta={"domain": domain}, - status="SUCCESS", - summary="diagnosis_http_ok") else: - detail = r["code"].replace("error_http_check", "diagnosis_http") if "code" in r else "diagnosis_http_unknown_error" - yield dict(meta={"domain": domain}, - status="ERROR", - summary="diagnosis_http_unreachable", - details=[detail]) + domains_to_check.append(domain) + + self.nonce = ''.join(random.choice("0123456789abcedf") for i in range(16)) + os.system("rm -rf /tmp/.well-known/ynh-diagnosis/") + os.system("mkdir -p /tmp/.well-known/ynh-diagnosis/") + os.system("touch /tmp/.well-known/ynh-diagnosis/%s" % self.nonce) + + if not domains_to_check: + return + + # To perform hairpinning test, we gotta make sure that port forwarding + # is working and therefore we'll do it only if at least one ipv4 domain + # works. + self.do_hairpinning_test = False + ipv4 = Diagnoser.get_cached_report("ip", item={"test": "ipv4"}) or {} + if ipv4.get("status") == "SUCCESS": + for item in self.test_http(domains_to_check, ipversion=4): + yield item + + ipv6 = Diagnoser.get_cached_report("ip", item={"test": "ipv6"}) or {} + if ipv6.get("status") == "SUCCESS": + for item in self.test_http(domains_to_check, ipversion=6): + yield item # If at least one domain is correctly exposed to the outside, # attempt to diagnose hairpinning situations. On network with @@ -70,13 +64,12 @@ class WebDiagnoser(Diagnoser): # outside, but from the outside, it will be as if the port forwarding # was not configured... Hence, calling for example # "curl --head the.global.ip" will simply timeout... - if at_least_one_domain_ok: - ipv4 = Diagnoser.get_cached_report_item("ip", {"test": "ipv4"}) - global_ipv4 = ipv4.get("data", {}).get("global", {}) + if self.do_hairpinning_test: + global_ipv4 = ipv4.get("data", {}).get("global", None) if global_ipv4: try: requests.head("http://" + global_ipv4, timeout=5) - except requests.exceptions.Timeout as e: + except requests.exceptions.Timeout: yield dict(meta={"test": "hairpinning"}, status="WARNING", summary="diagnosis_http_hairpinning_issue", @@ -87,6 +80,33 @@ class WebDiagnoser(Diagnoser): # issue but something else super weird ... pass + def test_http(self, domains, ipversion): + + try: + r = Diagnoser.remote_diagnosis('check-http', + data={'domains': domains, + "nonce": self.nonce}, + ipversion=ipversion) + results = r["http"] + except Exception as e: + raise YunohostError("diagnosis_http_could_not_diagnose", error=e) + + assert set(results.keys()) == set(domains) + + for domain, result in results.items(): + + if result["status"] == "ok": + if ipversion == 4: + self.do_hairpinning_test = True + yield dict(meta={"domain": domain}, + status="SUCCESS", + summary="diagnosis_http_ok") + else: + yield dict(meta={"domain": domain}, + status="ERROR", + summary="diagnosis_http_unreachable", + details=[result["status"].replace("error_http_check", "diagnosis_http")]) + def main(args, env, loggers): return WebDiagnoser(args, env, loggers).diagnose() diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index c11cde566..fd7a37480 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -27,6 +27,8 @@ import re import os import time +import requests +import socket from moulinette import m18n, msettings from moulinette.utils import log @@ -39,6 +41,7 @@ logger = log.getActionLogger('yunohost.diagnosis') DIAGNOSIS_CACHE = "/var/cache/yunohost/diagnosis/" DIAGNOSIS_CONFIG_FILE = '/etc/yunohost/diagnosis.yml' +DIAGNOSIS_SERVER = "diagnosis.yunohost.org" def diagnosis_list(): all_categories_names = [h for h, _ in _list_diagnosis_categories()] @@ -492,6 +495,49 @@ class Diagnoser(): if "details" in item: item["details"] = [m18n_(info) for info in item["details"]] + @staticmethod + def remote_diagnosis(uri, data, ipversion, timeout=30): + + # Monkey patch socket.getaddrinfo to force request() to happen in ipv4 + # or 6 ... + # Inspired by https://stackoverflow.com/a/50044152 + old_getaddrinfo = socket.getaddrinfo + + def getaddrinfo_ipv4_only(*args, **kwargs): + responses = old_getaddrinfo(*args, **kwargs) + return [response + for response in responses + if response[0] == socket.AF_INET] + + def getaddrinfo_ipv6_only(*args, **kwargs): + responses = old_getaddrinfo(*args, **kwargs) + return [response + for response in responses + if response[0] == socket.AF_INET6] + + if ipversion == 4: + socket.getaddrinfo = getaddrinfo_ipv4_only + elif ipversion == 6: + socket.getaddrinfo = getaddrinfo_ipv6_only + + url = 'https://%s/%s' % (DIAGNOSIS_SERVER, uri) + try: + r = requests.post(url, json=data, timeout=timeout) + finally: + socket.getaddrinfo = old_getaddrinfo + + if r.status_code not in [200, 400]: + raise Exception("Bad response from diagnosis server.\nURL: %s\nStatus code: %s\nMessage: %s" % (url, r.status_code, r.content)) + if r.status_code == 400: + raise Exception("Diagnosis request was refused: %s" % r.content) + + try: + r = r.json() + except Exception as e: + raise Exception("Failed to parse json from diagnosis server response.\nError: %s\nOriginal content: %s" % (e, r.content)) + + return r + def _list_diagnosis_categories(): hooks_raw = hook_list("diagnosis", list_by="priority", show_info=True)["hooks"] From e8730ad92b4abe2f27e22b090b1693773d3d1eec Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 03:21:01 +0200 Subject: [PATCH 39/91] Correctly handle cases where domain works in IPv4 but not IPv6 or viceversa --- data/hooks/diagnosis/21-web.py | 57 ++++++++++++++++++++++------------ locales/en.json | 1 + 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 270c566cc..c54544aa0 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -48,15 +48,20 @@ class WebDiagnoser(Diagnoser): # is working and therefore we'll do it only if at least one ipv4 domain # works. self.do_hairpinning_test = False + + ipversions = [] ipv4 = Diagnoser.get_cached_report("ip", item={"test": "ipv4"}) or {} if ipv4.get("status") == "SUCCESS": - for item in self.test_http(domains_to_check, ipversion=4): - yield item + ipversions.append(4) + # To be discussed: we could also make this check dependent on the + # existence of an AAAA record... ipv6 = Diagnoser.get_cached_report("ip", item={"test": "ipv6"}) or {} if ipv6.get("status") == "SUCCESS": - for item in self.test_http(domains_to_check, ipversion=6): - yield item + ipversions.append(6) + + for item in self.test_http(domains_to_check, ipversions): + yield item # If at least one domain is correctly exposed to the outside, # attempt to diagnose hairpinning situations. On network with @@ -80,32 +85,44 @@ class WebDiagnoser(Diagnoser): # issue but something else super weird ... pass - def test_http(self, domains, ipversion): + def test_http(self, domains, ipversions): - try: - r = Diagnoser.remote_diagnosis('check-http', - data={'domains': domains, - "nonce": self.nonce}, - ipversion=ipversion) - results = r["http"] - except Exception as e: - raise YunohostError("diagnosis_http_could_not_diagnose", error=e) + results = {} + for ipversion in ipversions: + try: + r = Diagnoser.remote_diagnosis('check-http', + data={'domains': domains, + "nonce": self.nonce}, + ipversion=ipversion) + results[ipversion] = r["http"] + except Exception as e: + raise YunohostError("diagnosis_http_could_not_diagnose", error=e) - assert set(results.keys()) == set(domains) + for domain in domains: - for domain, result in results.items(): - - if result["status"] == "ok": - if ipversion == 4: + # If both IPv4 and IPv6 (if applicable) are good + if all(results[ipversion][domain]["status"] == "ok" for ipversion in ipversions): + if 4 in ipversions: self.do_hairpinning_test = True yield dict(meta={"domain": domain}, status="SUCCESS", summary="diagnosis_http_ok") - else: + # If both IPv4 and IPv6 (if applicable) are failed + elif all(results[ipversion][domain]["status"] != "ok" for ipversion in ipversions): + detail = results[4 if 4 in ipversions else 6][domain]["status"] yield dict(meta={"domain": domain}, status="ERROR", summary="diagnosis_http_unreachable", - details=[result["status"].replace("error_http_check", "diagnosis_http")]) + details=[detail.replace("error_http_check", "diagnosis_http")]) + # If only IPv4 is failed or only IPv6 is failed (if applicable) + else: + passed, failed = (4, 6) if results[4][domain]["status"] == "ok" else (6, 4) + detail = results[failed][domain]["status"] + yield dict(meta={"domain": domain}, + data={"passed": passed, "failed": failed}, + status="ERROR", + summary="diagnosis_http_partially_unreachable", + details=[detail.replace("error_http_check", "diagnosis_http")]) def main(args, env, loggers): diff --git a/locales/en.json b/locales/en.json index 71804af97..96a1f4658 100644 --- a/locales/en.json +++ b/locales/en.json @@ -217,6 +217,7 @@ "diagnosis_http_unknown_error": "An error happened while trying to reach your domain, it's very likely unreachable.", "diagnosis_http_bad_status_code": "Timed-out while trying to contact your server from outside. It might be that another machine answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", + "diagnosis_http_partiallu_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network in IPv{failed}, though it works in IPv{passed}.", "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using yunohost tools regen-conf nginx --dry-run --with-diff and if you're ok, apply the changes with yunohost tools regen-conf nginx --force.", "diagnosis_unknown_categories": "The following categories are unknown: {categories}", From 1552c6472bbd375071edb146c4be91ecd2b0fd06 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 18:57:52 +0200 Subject: [PATCH 40/91] Try to improve wording of DNS reports --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 96a1f4658..cf1b8d552 100644 --- a/locales/en.json +++ b/locales/en.json @@ -167,8 +167,8 @@ "diagnosis_ip_broken_resolvconf": "Domain name resolution seems to be broken on your server, which seems related to /etc/resolv.conf not pointing to 127.0.0.1.", "diagnosis_ip_weird_resolvconf": "DNS resolution seems to be working, but it looks like you're using a custom /etc/resolv.conf.", "diagnosis_ip_weird_resolvconf_details": "The file /etc/resolv.conf should be a symlink to /etc/resolvconf/run/resolv.conf itself pointing to 127.0.0.1 (dnsmasq). If you want to manually configure DNS resolvers, please edit /etc/resolv.dnsmasq.conf.", - "diagnosis_dns_good_conf": "Good DNS configuration for domain {domain} (category {category})", - "diagnosis_dns_bad_conf": "Bad or missing DNS configuration for domain {domain} (category {category})", + "diagnosis_dns_good_conf": "DNS records are correctly configured for domain {domain} (category {category})", + "diagnosis_dns_bad_conf": "Some DNS records are missing or incorrect for domain {domain} (category {category})", "diagnosis_dns_missing_record": "According to the recommended DNS configuration, you should add a DNS record with the following info.
Type: {type}
Name: {name}
Value: {value}", "diagnosis_dns_discrepancy": "The following DNS record does not seem to follow the recommended configuration:
Type: {type}
Name: {name}
Current value: {current}
Excepted value: {value}", "diagnosis_dns_point_to_doc": "Please check the documentation at https://yunohost.org/dns_config if you need help about configuring DNS records.", From 4e64e2ccfda6374b6c8e3eb1412afa7855f30c2f Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 19:03:44 +0200 Subject: [PATCH 41/91] Save a per-record result for DNS diagnosis and report missing AAAA as warning only --- data/hooks/diagnosis/12-dnsrecords.py | 29 +++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 3132cf45f..c4996de38 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -40,9 +40,8 @@ class DNSRecordsDiagnoser(Diagnoser): expected_configuration = _build_dns_conf(domain, include_empty_AAAA_if_no_ipv6=True) - # FIXME: Here if there are no AAAA record, we should add something to expect "no" AAAA record - # to properly diagnose situations where people have a AAAA record but no IPv6 categories = ["basic", "mail", "xmpp", "extra"] + # For subdomains, we only diagnosis A and AAAA records if is_subdomain: categories = ["basic"] @@ -50,26 +49,48 @@ class DNSRecordsDiagnoser(Diagnoser): records = expected_configuration[category] discrepancies = [] + results = {} for r in records: + id_ = r["type"] + ":" + r["name"] r["current"] = self.get_current_record(domain, r["name"], r["type"]) if r["value"] == "@": r["value"] = domain + "." - if not self.current_record_match_expected(r): + if self.current_record_match_expected(r): + results[id_] = "OK" + else: if r["current"] is None: + results[id_] = "MISSING" discrepancies.append(("diagnosis_dns_missing_record", r)) else: + results[id_] = "WRONG" discrepancies.append(("diagnosis_dns_discrepancy", r)) + + def its_important(): + # Every mail DNS records are important for main domain + # For other domain, we only report it as a warning for now... + if is_main_domain and category == "mail": + return True + elif category == "basic": + # A bad or missing A record is critical ... + # And so is a wrong AAAA record + # (However, a missing AAAA record is acceptable) + if results["A:@"] != "OK" or results["AAAA:@"] == "WRONG": + return True + + return False + if discrepancies: - status = "ERROR" if (category == "basic" or (is_main_domain and category == "mail")) else "WARNING" + status = "ERROR" if its_important() else "WARNING" summary = "diagnosis_dns_bad_conf" else: status = "SUCCESS" summary = "diagnosis_dns_good_conf" output = dict(meta={"domain": domain, "category": category}, + data=results, status=status, summary=summary) From be0da3b9dcd10bbb528ff674d8536a787c571168 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 20:48:25 +0200 Subject: [PATCH 42/91] Only report an INFO is domain ain't accessible in IPv6 and there's in fact no AAAA record set yet --- data/hooks/diagnosis/21-web.py | 40 +++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index c54544aa0..10deea28d 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -17,7 +17,7 @@ class WebDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] cache_duration = 3600 - dependencies = ["ip"] + dependencies = ["ip", "dnsrecords"] def run(self): @@ -118,11 +118,39 @@ class WebDiagnoser(Diagnoser): else: passed, failed = (4, 6) if results[4][domain]["status"] == "ok" else (6, 4) detail = results[failed][domain]["status"] - yield dict(meta={"domain": domain}, - data={"passed": passed, "failed": failed}, - status="ERROR", - summary="diagnosis_http_partially_unreachable", - details=[detail.replace("error_http_check", "diagnosis_http")]) + + # Failing in ipv4 is critical. + # If we failed in IPv6 but there's in fact no AAAA record + # It's an acceptable situation and we shall not report an + # error + def ipv6_is_important_for_this_domain(): + dnsrecords = Diagnoser.get_cached_report("dnsrecords", item={"domain": domain, "category": "basic"}) or {} + AAAA_status = dnsrecords.get("data", {}).get("AAAA:@") + + return AAAA_status in ["OK", "WRONG"] + + if failed == 4 or ipv6_is_important_for_this_domain(): + yield dict(meta={"domain": domain}, + data={"passed": passed, "failed": failed}, + status="ERROR", + summary="diagnosis_http_partially_unreachable", + details=[detail.replace("error_http_check", "diagnosis_http")]) + # So otherwise we report a success (note that this info is + # later used to know that ACME challenge is doable) + # + # And in addition we report an info about the failure in IPv6 + # *with a different meta* (important to avoid conflicts when + # fetching the other info...) + else: + self.do_hairpinning_test = True + yield dict(meta={"domain": domain}, + status="SUCCESS", + summary="diagnosis_http_ok") + yield dict(meta={"test": "ipv6", "domain": domain}, + data={"passed": passed, "failed": failed}, + status="INFO", + summary="diagnosis_http_partially_unreachable", + details=[detail.replace("error_http_check", "diagnosis_http")]) def main(args, env, loggers): From dd7b42d3e8c2878da2bb67e6a7ba442f8d8149ff Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 23:38:05 +0200 Subject: [PATCH 43/91] Add ipv6 check for ports --- data/hooks/diagnosis/14-ports.py | 87 ++++++++++++++++++++++++++------ 1 file changed, 71 insertions(+), 16 deletions(-) diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 05c28e8dc..809407be3 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -10,10 +10,12 @@ class PortsDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] cache_duration = 3600 - dependencies = ["ip"] + dependencies = ["ip", "dnsrecords"] def run(self): + # TODO: report a warning if port 53 or 5353 is exposed to the outside world... + # This dict is something like : # { 80: "nginx", # 25: "postfix", @@ -25,28 +27,81 @@ class PortsDiagnoser(Diagnoser): for port in infos.get("needs_exposed_ports", []): ports[port] = service - try: - r = Diagnoser.remote_diagnosis('check-ports', - data={'ports': ports.keys()}, - ipversion=4) - results = r["ports"] - except Exception as e: - raise YunohostError("diagnosis_ports_could_not_diagnose", error=e) + ipversions = [] + ipv4 = Diagnoser.get_cached_report("ip", item={"test": "ipv4"}) or {} + if ipv4.get("status") == "SUCCESS": + ipversions.append(4) + + # To be discussed: we could also make this check dependent on the + # existence of an AAAA record... + ipv6 = Diagnoser.get_cached_report("ip", item={"test": "ipv6"}) or {} + if ipv6.get("status") == "SUCCESS": + ipversions.append(6) + + # Fetch test result for each relevant IP version + results = {} + for ipversion in ipversions: + try: + r = Diagnoser.remote_diagnosis('check-ports', + data={'ports': ports.keys()}, + ipversion=ipversion) + results[ipversion] = r["ports"] + except Exception as e: + raise YunohostError("diagnosis_http_could_not_diagnose", error=e) + for port, service in sorted(ports.items()): + port = str(port) category = services[service].get("category", "[?]") - if results.get(str(port), None) is not True: - yield dict(meta={"port": str(port)}, - data={"service": service, "category": category}, - status="ERROR", - summary="diagnosis_ports_unreachable", - details=["diagnosis_ports_needed_by", "diagnosis_ports_forwarding_tip"]) - else: - yield dict(meta={"port": str(port)}, + + # If both IPv4 and IPv6 (if applicable) are good + if all(results[ipversion].get(port) is True for ipversion in ipversions): + yield dict(meta={"port": port}, data={"service": service, "category": category}, status="SUCCESS", summary="diagnosis_ports_ok", details=["diagnosis_ports_needed_by"]) + # If both IPv4 and IPv6 (if applicable) are failed + elif all(results[ipversion].get(port) is not True for ipversion in ipversions): + yield dict(meta={"port": port}, + data={"service": service, "category": category}, + status="ERROR", + summary="diagnosis_ports_unreachable", + details=["diagnosis_ports_needed_by", "diagnosis_ports_forwarding_tip"]) + # If only IPv4 is failed or only IPv6 is failed (if applicable) + else: + passed, failed = (4, 6) if results[4].get(port) is True else (6, 4) + + # Failing in ipv4 is critical. + # If we failed in IPv6 but there's in fact no AAAA record + # It's an acceptable situation and we shall not report an + # error + # If any AAAA record is set, IPv6 is important... + def ipv6_is_important(): + dnsrecords = Diagnoser.get_cached_report("dnsrecords") or {} + return any(record["data"]["AAAA:@"] in ["OK", "WRONG"] for record in dnsrecords.get("items", [])) + + if failed == 4 or ipv6_is_important(): + yield dict(meta={"port": port}, + data={"service": service, "category": category, "passed": passed, "failed": failed}, + status="ERROR", + summary="diagnosis_ports_partially_unreachable", + details=["diagnosis_ports_needed_by", "diagnosis_ports_forwarding_tip"]) + # So otherwise we report a success + # And in addition we report an info about the failure in IPv6 + # *with a different meta* (important to avoid conflicts when + # fetching the other info...) + else: + yield dict(meta={"port": port}, + data={"service": service, "category": category}, + status="SUCCESS", + summary="diagnosis_ports_ok", + details=["diagnosis_ports_needed_by"]) + yield dict(meta={"test": "ipv6", "port": port}, + data={"service": service, "category": category, "passed": passed, "failed": failed}, + status="INFO", + summary="diagnosis_ports_partially_unreachable", + details=["diagnosis_ports_needed_by", "diagnosis_ports_forwarding_tip"]) def main(args, env, loggers): From 4306db7cf1a8ee89ee7a3090f53ea4cb81d213c4 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Thu, 16 Apr 2020 23:59:42 +0200 Subject: [PATCH 44/91] Be more flexible about SPF record: just 'a mx -all' is enough, no need to specify IP. (also fix TXT record validation in diagnosis) --- data/hooks/diagnosis/12-dnsrecords.py | 8 ++++++-- src/yunohost/domain.py | 9 +-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index c4996de38..3853350bd 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -127,8 +127,12 @@ class DNSRecordsDiagnoser(Diagnoser): # Split expected/current # from "v=DKIM1; k=rsa; p=hugekey;" # to a set like {'v=DKIM1', 'k=rsa', 'p=...'} - expected = set(r["value"].strip(' "').strip(";").replace(" ", "").split()) - current = set(r["current"].strip(' "').strip(";").replace(" ", "").split()) + expected = set(r["value"].strip(';" ').replace(";", " ").split()) + current = set(r["current"].strip(';" ').replace(";", " ").split()) + + # For SPF, ignore parts starting by ip4: or ip6: + if r["name"] == "@": + current = {part for part in current if not part.startswith("ip4:") and not part.startswith("ip6:")} return expected == current elif r["type"] == "MX": # For MX, we want to ignore the priority diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 7910147a3..1d1e10da1 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -455,16 +455,9 @@ def _build_dns_conf(domain, ttl=3600, include_empty_AAAA_if_no_ipv6=False): # Email # ######### - spf_record = '"v=spf1 a mx' - if ipv4: - spf_record += ' ip4:{ip4}'.format(ip4=ipv4) - if ipv6: - spf_record += ' ip6:{ip6}'.format(ip6=ipv6) - spf_record += ' -all"' - mail = [ ["@", ttl, "MX", "10 %s." % domain], - ["@", ttl, "TXT", spf_record], + ["@", ttl, "TXT", "v=spf1 a mx -all"], ] # DKIM/DMARC record From 2c269613142032cbca8bb0a2376c5614876b8da6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Apr 2020 02:17:15 +0200 Subject: [PATCH 45/91] Fix bad copy pasta for string key --- data/hooks/diagnosis/14-ports.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 809407be3..b63971b71 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -47,7 +47,7 @@ class PortsDiagnoser(Diagnoser): ipversion=ipversion) results[ipversion] = r["ports"] except Exception as e: - raise YunohostError("diagnosis_http_could_not_diagnose", error=e) + raise YunohostError("diagnosis_ports_could_not_diagnose", error=e) for port, service in sorted(ports.items()): From 040bc1d09fd5c6ba89ab87ccfaeb99d954b32265 Mon Sep 17 00:00:00 2001 From: Kayou Date: Fri, 17 Apr 2020 03:20:02 +0200 Subject: [PATCH 46/91] Yolo fix locales (#936) * Yolo fix locales * Fix bad copy pasta for string key * Yolo fix locales * Add diagnosis_ports_could_not_diagnose back Co-authored-by: Alexandre Aubin --- locales/ca.json | 17 ++++++++--------- locales/en.json | 4 ++-- locales/eo.json | 17 ++++++++--------- locales/es.json | 17 ++++++++--------- locales/fr.json | 17 ++++++++--------- locales/oc.json | 17 ++++++++--------- 6 files changed, 42 insertions(+), 47 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index 4c31e4a6c..0ea0d91f6 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -502,7 +502,7 @@ "permission_require_account": "El permís {permission} només té sentit per als usuaris que tenen un compte, i per tant no es pot activar per als visitants.", "app_remove_after_failed_install": "Eliminant l'aplicació després que hagi fallat la instal·lació…", "diagnosis_basesystem_ynh_main_version": "El servidor funciona amb YunoHost {main_version} ({repo})", - "diagnosis_ram_low": "El sistema només té {available_abs_MB} MB ({available_percent}%) de memòria RAM disponibles d'un total de {total_abs_MB} MB. Aneu amb compte.", + "diagnosis_ram_low": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}. Aneu amb compte.", "diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de 256 MB de swap per evitar situacions en les que el sistema es queda sense memòria.", "diagnosis_regenconf_manually_modified": "El fitxer de configuració {file} ha estat modificat manualment.", "diagnosis_security_vulnerable_to_meltdown_details": "Per arreglar-ho, hauríeu d'actualitzar i reiniciar el sistema per tal de carregar el nou nucli de linux (o contactar amb el proveïdor del servidor si no funciona). Vegeu https://meltdownattack.com/ per a més informació.", @@ -538,13 +538,13 @@ "diagnosis_dns_missing_record": "Segons la configuració DNS recomanada, hauríeu d'afegir un registre DNS\ntipus: {type}\nnom: {name}\nvalor: {value}.", "diagnosis_dns_discrepancy": "El registre DNS de tipus {type} i nom {name} no concorda amb la configuració recomanada.\nValor actual: {current}\nValor esperat: {value}", "diagnosis_services_bad_status": "El servei {service} està {status} :(", - "diagnosis_diskusage_verylow": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) només té disponibles {free_abs_GB} GB ({free_percent}%). Hauríeu de considerar alliberar una mica d'espai.", - "diagnosis_diskusage_low": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) només té disponibles {free_abs_GB} GB ({free_percent}%). Aneu amb compte.", - "diagnosis_diskusage_ok": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) encara té {free_abs_GB} GB ({free_percent}%) lliures!", - "diagnosis_ram_verylow": "El sistema només té {available_abs_MB} MB ({available_percent}%) de memòria RAM disponibles! (d'un total de {total_abs_MB} MB)", - "diagnosis_ram_ok": "El sistema encara té {available_abs_MB} MB ({available_percent}%) de memòria RAM disponibles d'un total de {total_abs_MB} MB.", - "diagnosis_swap_notsomuch": "El sistema només té {total_MB} MB de swap. Hauríeu de considerar tenir un mínim de 256 MB per evitar situacions en les que el sistema es queda sense memòria.", - "diagnosis_swap_ok": "El sistema té {total_MB} MB de swap!", + "diagnosis_diskusage_verylow": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) només té disponibles {free} ({free_percent}%). Hauríeu de considerar alliberar una mica d'espai.", + "diagnosis_diskusage_low": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) només té disponibles {free} ({free_percent}%). Aneu amb compte.", + "diagnosis_diskusage_ok": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) encara té {free} ({free_percent}%) lliures!", + "diagnosis_ram_verylow": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles! (d'un total de {total})", + "diagnosis_ram_ok": "El sistema encara té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}.", + "diagnosis_swap_notsomuch": "El sistema només té {total} de swap. Hauríeu de considerar tenir un mínim de 256 MB per evitar situacions en les que el sistema es queda sense memòria.", + "diagnosis_swap_ok": "El sistema té {total} de swap!", "diagnosis_regenconf_allgood": "Tots els fitxers de configuració estan en acord amb la configuració recomanada!", "diagnosis_regenconf_manually_modified_details": "No hauria de ser cap problema sempre i quan sapigueu el que esteu fent ;) !", "diagnosis_regenconf_manually_modified_debian": "El fitxer de configuració {file} ha estat modificat manualment respecte al fitxer per defecte de Debian.", @@ -581,7 +581,6 @@ "diagnosis_no_cache": "Encara no hi ha memòria cau pel diagnòstic de la categoria «{category}»", "diagnosis_http_timeout": "S'ha exhaurit el temps d'esperar intentant connectar amb el servidor des de l'exterior. Sembla que no s'hi pot accedir. S'hauria de comprovar que el reenviament del port 80 és correcte, que NGINX funciona, i que el tallafocs no està interferint.", "diagnosis_http_connection_error": "Error de connexió: no s'ha pogut connectar amb el domini demanat, segurament és inaccessible.", - "diagnosis_http_unknown_error": "Hi ha hagut un error intentant accedir al domini, segurament és inaccessible.", "yunohost_postinstall_end_tip": "S'ha completat la post-instal·lació. Per acabar la configuració, considereu:\n - afegir un primer usuari a través de la secció «Usuaris» a la pàgina web d'administració (o emprant «yunohost user create » a la línia d'ordres);\n - diagnosticar possibles problemes a través de la secció «Diagnòstics» a la pàgina web d'administració (o emprant «yunohost diagnosis run» a la línia d'ordres);\n - llegir les seccions «Finalizing your setup» i «Getting to know Yunohost» a la documentació per administradors: https://yunohost.org/admindoc.", "migration_description_0014_remove_app_status_json": "Eliminar els fitxers d'aplicació status.json heretats", "diagnosis_services_running": "El servei {service} s'està executant!", diff --git a/locales/en.json b/locales/en.json index cf1b8d552..27fb19444 100644 --- a/locales/en.json +++ b/locales/en.json @@ -205,6 +205,7 @@ "diagnosis_description_security": "Security checks", "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside. Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", + "diagnosis_ports_partially_unreachable": "Port {port} is not reachable from outside in IPv{failed}.", "diagnosis_ports_ok": "Port {port} is reachable from outside.", "diagnosis_ports_needed_by": "Exposing this port is needed for {category} features (service {service})", "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", @@ -214,10 +215,9 @@ "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", - "diagnosis_http_unknown_error": "An error happened while trying to reach your domain, it's very likely unreachable.", "diagnosis_http_bad_status_code": "Timed-out while trying to contact your server from outside. It might be that another machine answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", - "diagnosis_http_partiallu_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network in IPv{failed}, though it works in IPv{passed}.", + "diagnosis_http_partially_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network in IPv{failed}, though it works in IPv{passed}.", "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", "diagnosis_http_nginx_conf_not_up_to_date_details": "To fix the situation, inspect the difference with the command line using yunohost tools regen-conf nginx --dry-run --with-diff and if you're ok, apply the changes with yunohost tools regen-conf nginx --force.", "diagnosis_unknown_categories": "The following categories are unknown: {categories}", diff --git a/locales/eo.json b/locales/eo.json index 127e7df39..87e062ea2 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -513,8 +513,8 @@ "diagnosis_display_tip_cli": "Vi povas aranĝi 'yunohost diagnosis show --issues' por aperigi la trovitajn problemojn.", "diagnosis_failed_for_category": "Diagnozo malsukcesis por kategorio '{category}': {error}", "app_upgrade_script_failed": "Eraro okazis en la skripto pri ĝisdatiga programo", - "diagnosis_diskusage_verylow": "Stokado {mountpoint} (sur aparato {device)) restas nur {free_abs_GB} GB ({free_percent}%) spaco. Vi vere konsideru purigi iom da spaco.", - "diagnosis_ram_verylow": "La sistemo nur restas {available_abs_MB} MB ({available_percent}%) RAM! (el {total_abs_MB} MB)", + "diagnosis_diskusage_verylow": "Stokado {mountpoint} (sur aparato {device)) restas nur {free} ({free_percent}%) spaco. Vi vere konsideru purigi iom da spaco.", + "diagnosis_ram_verylow": "La sistemo nur restas {available} ({available_percent}%) RAM! (el {total})", "diagnosis_mail_ougoing_port_25_blocked": "Eliranta haveno 25 ŝajnas esti blokita. Vi devas provi malŝlosi ĝin en via agorda panelo de provizanto (aŭ gastiganto). Dume la servilo ne povos sendi retpoŝtojn al aliaj serviloj.", "diagnosis_http_bad_status_code": "Ne povis atingi vian servilon kiel atendite, ĝi redonis malbonan statuskodon. Povas esti, ke alia maŝino respondis anstataŭ via servilo. Vi devus kontroli, ke vi ĝuste redonas la havenon 80, ke via nginx-agordo ĝisdatigas kaj ke reverso-prokuro ne interbatalas.", "main_domain_changed": "La ĉefa domajno estis ŝanĝita", @@ -530,9 +530,9 @@ "diagnosis_ip_weird_resolvconf_details": "Anstataŭe, ĉi tiu dosiero estu ligilo kun /etc/resolvconf/run/resolv.conf mem montrante al 127.0.0.1 (dnsmasq). La efektivaj solvantoj devas agordi per /etc/resolv.dnsmasq.conf.", "diagnosis_dns_good_conf": "Bona DNS-agordo por domajno {domain} (kategorio {category})", "diagnosis_dns_bad_conf": "Malbona / mankas DNS-agordo por domajno {domain} (kategorio {category})", - "diagnosis_ram_ok": "La sistemo ankoraŭ havas {available_abs_MB} MB ({available_percent}%) RAM forlasita de {total_abs_MB} MB.", + "diagnosis_ram_ok": "La sistemo ankoraŭ havas {available} ({available_percent}%) RAM forlasita de {total}.", "diagnosis_swap_none": "La sistemo tute ne havas interŝanĝon. Vi devus pripensi aldoni almenaŭ 256 MB da interŝanĝo por eviti situaciojn en kiuj la sistemo restas sen memoro.", - "diagnosis_swap_notsomuch": "La sistemo havas nur {total_MB} MB-interŝanĝon. Vi konsideru havi almenaŭ 256 MB por eviti situaciojn en kiuj la sistemo restas sen memoro.", + "diagnosis_swap_notsomuch": "La sistemo havas nur {total}-interŝanĝon. Vi konsideru havi almenaŭ 256 MB por eviti situaciojn en kiuj la sistemo restas sen memoro.", "diagnosis_regenconf_manually_modified_details": "Ĉi tio probable estas bona tiel longe kiel vi scias kion vi faras;)!", "diagnosis_regenconf_manually_modified_debian": "Agordodosiero {file} estis modifita permane kompare kun la defaŭlta Debian.", "diagnosis_regenconf_manually_modified_debian_details": "Ĉi tio probable estas bona, sed devas observi ĝin...", @@ -545,8 +545,8 @@ "diagnosis_dns_discrepancy": "La DNS-registro kun tipo {type} kaj nomo {name} ne kongruas kun la rekomendita agordo.\nNuna valoro: {current}\nEsceptita valoro: {value}", "diagnosis_services_conf_broken": "Agordo estas rompita por servo {service} !", "diagnosis_services_bad_status": "Servo {service} estas {status} :(", - "diagnosis_ram_low": "La sistemo havas {available_abs_MB} MB ({available_percent}%) RAM forlasita de {total_abs_MB} MB. Estu zorgema.", - "diagnosis_swap_ok": "La sistemo havas {total_MB} MB da interŝanĝoj!", + "diagnosis_ram_low": "La sistemo havas {available} ({available_percent}%) RAM forlasita de {total}. Estu zorgema.", + "diagnosis_swap_ok": "La sistemo havas {total} da interŝanĝoj!", "diagnosis_mail_ougoing_port_25_ok": "Eliranta haveno 25 ne estas blokita kaj retpoŝto povas esti sendita al aliaj serviloj.", "diagnosis_regenconf_allgood": "Ĉiuj agordaj dosieroj kongruas kun la rekomendita agordo!", "diagnosis_regenconf_manually_modified": "Agordodosiero {file} estis permane modifita.", @@ -564,13 +564,12 @@ "log_domain_main_domain": "Faru '{}' kiel ĉefa domajno", "diagnosis_http_timeout": "Tempolimigita dum provado kontakti vian servilon de ekstere. Ĝi ŝajnas esti neatingebla. Vi devus kontroli, ke vi ĝuste redonas la havenon 80, ke nginx funkcias kaj ke fajroŝirmilo ne interbatalas.", "diagnosis_http_connection_error": "Rilata eraro: ne povis konektiĝi al la petita domajno, tre probable ĝi estas neatingebla.", - "diagnosis_http_unknown_error": "Eraro okazis dum provado atingi vian domajnon, tre probable ĝi estas neatingebla.", "migration_description_0013_futureproof_apps_catalog_system": "Migru al la nova katalogosistemo pri estontecaj programoj", "diagnosis_ignored_issues": "(+ {nb_ignored} ignorataj aferoj))", "diagnosis_found_errors": "Trovis {errors} signifa(j) afero(j) rilata al {category}!", "diagnosis_found_errors_and_warnings": "Trovis {errors} signifaj problemo (j) (kaj {warnings} averto) rilataj al {category}!", - "diagnosis_diskusage_low": "Stokado {mountpoint} (sur aparato {device)) restas nur {free_abs_GB} GB ({free_percent}%) spaco. Estu zorgema.", - "diagnosis_diskusage_ok": "Stokado {mountpoint} (sur aparato {device) ankoraŭ restas {free_abs_GB} GB ({free_percent}%) spaco!", + "diagnosis_diskusage_low": "Stokado {mountpoint} (sur aparato {device)) restas nur {free} ({free_percent}%) spaco. Estu zorgema.", + "diagnosis_diskusage_ok": "Stokado {mountpoint} (sur aparato {device) ankoraŭ restas {free} ({free_percent}%) spaco!", "global_settings_setting_pop3_enabled": "Ebligu la protokolon POP3 por la poŝta servilo", "diagnosis_unknown_categories": "La jenaj kategorioj estas nekonataj: {categories}", "diagnosis_services_running": "Servo {service} funkcias!", diff --git a/locales/es.json b/locales/es.json index b72665066..6a55378da 100644 --- a/locales/es.json +++ b/locales/es.json @@ -529,7 +529,7 @@ "diagnosis_ip_not_connected_at_all": "¿¡Está conectado el servidor a internet!?", "diagnosis_ip_broken_resolvconf": "DNS parece no funcionar en tu servidor, lo que parece estar relacionado con /etc/resolv.conf no apuntando a 127.0.0.1.", "diagnosis_dns_missing_record": "Según la configuración DNS recomendada, deberías añadir un registro DNS\ntipo: {type}\nnombre: {name}\nvalor: {value}", - "diagnosis_diskusage_low": "El almacenamiento {mountpoint} (en dispositivo {device}) solo tiene {free_abs_GB} GB ({free_percent}%) de espacio disponible. Ten cuidado.", + "diagnosis_diskusage_low": "El almacenamiento {mountpoint} (en dispositivo {device}) solo tiene {free} ({free_percent}%) de espacio disponible. Ten cuidado.", "diagnosis_services_bad_status_tip": "Puedes intentar reiniciar el servicio, y si no funciona, echar un vistazo a los logs del servicio usando 'yunohost service log {service}' o a través de la sección 'Servicios' en webadmin.", "diagnosis_ip_connected_ipv6": "¡El servidor está conectado a internet a través de IPv6!", "diagnosis_ip_no_ipv6": "El servidor no cuenta con IPv6 funcional.", @@ -541,18 +541,18 @@ "diagnosis_dns_bad_conf": "Configuración mala o faltante de los DNS para el dominio {domain} (categoría {category})", "diagnosis_dns_discrepancy": "El registro DNS con tipo {type} y nombre {name} no se corresponde a la configuración recomendada.\nValor actual: {current}\nValor esperado: {value}", "diagnosis_services_bad_status": "El servicio {service} está {status} :(", - "diagnosis_diskusage_verylow": "El almacenamiento {mountpoint} (en el dispositivo {device}) sólo tiene {free_abs_GB} GB ({free_percent}%) de espacio disponible. Deberías considerar la posibilidad de limpiar algo de espacio.", - "diagnosis_diskusage_ok": "¡El almacenamiento {mountpoint} (en el dispositivo {device}) todavía tiene {free_abs_GB} GB ({free_percent}%) de espacio libre!", + "diagnosis_diskusage_verylow": "El almacenamiento {mountpoint} (en el dispositivo {device}) sólo tiene {free} ({free_percent}%) de espacio disponible. Deberías considerar la posibilidad de limpiar algo de espacio.", + "diagnosis_diskusage_ok": "¡El almacenamiento {mountpoint} (en el dispositivo {device}) todavía tiene {free} ({free_percent}%) de espacio libre!", "diagnosis_services_conf_broken": "¡Mala configuración para el servicio {service}!", "diagnosis_services_running": "¡El servicio {service} está en ejecución!", "diagnosis_failed": "No se ha podido obtener el resultado del diagnóstico para la categoría '{category}': {error}", "diagnosis_ip_connected_ipv4": "¡El servidor está conectado a internet a través de IPv4!", "diagnosis_security_vulnerable_to_meltdown_details": "Para corregir esto, debieras actualizar y reiniciar tu sistema para cargar el nuevo kernel de Linux (o contacta tu proveedor si esto no funciona). Mas información en https://meltdownattack.com/", - "diagnosis_ram_verylow": "Al sistema le queda solamente {available_abs_MB} MB ({available_percent}%) de RAM! (De un total de {total_abs_MB} MB)", - "diagnosis_ram_low": "Al sistema le queda {available_abs_MB} MB ({available_percent}%) de RAM de un total de {total_abs_MB} MB. Cuidado.", - "diagnosis_ram_ok": "El sistema aun tiene {available_abs_MB} MB ({available_percent}%) de RAM de un total de {total_abs_MB} MB.", + "diagnosis_ram_verylow": "Al sistema le queda solamente {available} ({available_percent}%) de RAM! (De un total de {total})", + "diagnosis_ram_low": "Al sistema le queda {available} ({available_percent}%) de RAM de un total de {total}. Cuidado.", + "diagnosis_ram_ok": "El sistema aun tiene {available} ({available_percent}%) de RAM de un total de {total}.", "diagnosis_swap_none": "El sistema no tiene mas espacio de intercambio. Considera agregar por lo menos 256 MB de espacio de intercambio para evitar que el sistema se quede sin memoria.", - "diagnosis_swap_notsomuch": "Al sistema le queda solamente {total_MB} MB de espacio de intercambio. Considera agregar al menos 256 MB para evitar que el sistema se quede sin memoria.", + "diagnosis_swap_notsomuch": "Al sistema le queda solamente {total} de espacio de intercambio. Considera agregar al menos 256 MB para evitar que el sistema se quede sin memoria.", "diagnosis_mail_ougoing_port_25_ok": "El puerto de salida 25 no esta bloqueado y los correos electrónicos pueden ser enviados a otros servidores.", "diagnosis_mail_ougoing_port_25_blocked": "El puerto de salida 25 parece estar bloqueado. Intenta desbloquearlo con el panel de configuración de tu proveedor de servicios de Internet (o proveedor de halbergue). Mientras tanto, el servidor no podrá enviar correos electrónicos a otros servidores.", "diagnosis_regenconf_allgood": "Todos los archivos de configuración están en linea con la configuración recomendada!", @@ -568,7 +568,7 @@ "diagnosis_description_services": "Comprobación del estado de los servicios", "diagnosis_description_ports": "Exposición de puertos", "diagnosis_description_systemresources": "Recursos del sistema", - "diagnosis_swap_ok": "El sistema tiene {total_MB} MB de espacio de intercambio!", + "diagnosis_swap_ok": "El sistema tiene {total} de espacio de intercambio!", "diagnosis_ports_needed_by": "La apertura de este puerto es requerida para la funcionalidad {category} (service {service})", "diagnosis_ports_ok": "El puerto {port} es accesible desde internet.", "diagnosis_ports_unreachable": "El puerto {port} no es accesible desde internet.", @@ -592,7 +592,6 @@ "diagnosis_unknown_categories": "Las siguientes categorías están desconocidas: {categories}", "diagnosis_http_unreachable": "El dominio {domain} esta fuera de alcance desde internet y a través de HTTP.", "diagnosis_http_bad_status_code": "El sistema de diagnostico no pudo comunicarse con su servidor. Puede ser otra maquina que contesto en lugar del servidor. Debería verificar en su firewall que el re-direccionamiento del puerto 80 esta correcto.", - "diagnosis_http_unknown_error": "Hubo un error durante la búsqueda de su dominio, parece inalcanzable.", "diagnosis_http_connection_error": "Error de conexión: Ne se pudo conectar al dominio solicitado,", "diagnosis_http_timeout": "El intento de contactar a su servidor desde internet corrió fuera de tiempo. Al parece esta incomunicado. Debería verificar que nginx corre en el puerto 80, y que la redireción del puerto 80 no interfiere con en el firewall.", "diagnosis_http_ok": "El Dominio {domain} es accesible desde internet a través de HTTP.", diff --git a/locales/fr.json b/locales/fr.json index c8dfd12a9..f029a1d13 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -510,8 +510,8 @@ "diagnosis_ip_weird_resolvconf": "La résolution DNS semble fonctionner, mais soyez prudent en utilisant un fichier /etc/resolv.conf personnalisé.", "diagnosis_ip_weird_resolvconf_details": "Au lieu de cela, ce fichier devrait être un lien symbolique vers /etc/resolvconf/run/resolv.conf lui-même pointant vers 127.0.0.1 (dnsmasq). Les résolveurs réels doivent être configurés dans /etc/resolv.dnsmasq.conf.", "diagnosis_dns_missing_record": "Selon la configuration DNS recommandée, vous devez ajouter un enregistrement DNS\nType: {type}\nNom: {name}\nValeur {value}", - "diagnosis_diskusage_ok": "Le stockage {mountpoint} (sur le périphérique {device}) a encore {free_abs_GB} Go ({free_percent}%) d'espace libre !", - "diagnosis_ram_ok": "Le système dispose encore de {available_abs_MB} MB ({available_percent}%) de RAM sur {total_abs_MB} MB.", + "diagnosis_diskusage_ok": "Le stockage {mountpoint} (sur le périphérique {device}) a encore {free} ({free_percent}%) d'espace libre !", + "diagnosis_ram_ok": "Le système dispose encore de {available} ({available_percent}%) de RAM sur {total}.", "diagnosis_regenconf_allgood": "Tous les fichiers de configuration sont conformes à la configuration recommandée !", "diagnosis_security_vulnerable_to_meltdown": "Vous semblez vulnérable à la vulnérabilité de sécurité critique de Meltdown", "diagnosis_basesystem_host": "Le serveur utilise Debian {debian_version}", @@ -537,13 +537,13 @@ "diagnosis_dns_bad_conf": "Configuration DNS incorrecte ou manquante pour le domaine {domain} (catégorie {category})", "diagnosis_dns_discrepancy": "L'enregistrement DNS de type {type} et nom {name} ne correspond pas à la configuration recommandée.\nValeur actuelle: {current}\nValeur attendue: {value}", "diagnosis_services_bad_status": "Le service {service} est {status} :-(", - "diagnosis_diskusage_verylow": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free_abs_GB} Go ({free_percent}%). Vous devriez vraiment envisager de nettoyer un peu d'espace.", - "diagnosis_diskusage_low": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free_abs_GB} Go ({free_percent}%). Faites attention.", - "diagnosis_ram_verylow": "Le système ne dispose plus que de {available_abs_MB} MB ({available_percent}%)! (sur {total_abs_MB} Mo)", - "diagnosis_ram_low": "Le système n'a plus de {available_abs_MB} MB ({available_percent}%) RAM sur {total_abs_MB} MB. Faites attention.", + "diagnosis_diskusage_verylow": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free} ({free_percent}%). Vous devriez vraiment envisager de nettoyer un peu d'espace.", + "diagnosis_diskusage_low": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free} ({free_percent}%). Faites attention.", + "diagnosis_ram_verylow": "Le système ne dispose plus que de {available} ({available_percent}%)! (sur {total})", + "diagnosis_ram_low": "Le système n'a plus de {available} ({available_percent}%) RAM sur {total}. Faites attention.", "diagnosis_swap_none": "Le système n'a aucun échange. Vous devez envisager d’ajouter au moins 256 Mo de swap pour éviter les situations où le système manque de mémoire.", - "diagnosis_swap_notsomuch": "Le système ne dispose que de {total_MB} Mo de swap. Vous devez envisager d'avoir au moins 256 Mo pour éviter les situations où le système manque de mémoire.", - "diagnosis_swap_ok": "Le système dispose de {total_MB} Mo de swap !", + "diagnosis_swap_notsomuch": "Le système ne dispose que de {total} de swap. Vous devez envisager d'avoir au moins 256 Mo pour éviter les situations où le système manque de mémoire.", + "diagnosis_swap_ok": "Le système dispose de {total} de swap !", "diagnosis_regenconf_manually_modified": "Le fichier de configuration {file} a été modifié manuellement.", "diagnosis_regenconf_manually_modified_debian": "Le fichier de configuration {file} a été modifié manuellement par rapport à celui par défaut de Debian.", "diagnosis_regenconf_manually_modified_details": "C'est probablement OK tant que vous savez ce que vous faites;) !", @@ -583,7 +583,6 @@ "diagnosis_ports_forwarding_tip": "Pour résoudre ce problème, vous devez probablement configurer la redirection de port sur votre routeur Internet comme décrit sur https://yunohost.org/isp_box_config", "diagnosis_http_connection_error": "Erreur de connexion : impossible de se connecter au domaine demandé, il est probablement injoignable.", "diagnosis_no_cache": "Pas encore de cache de diagnostique pour la catégorie « {category} »", - "diagnosis_http_unknown_error": "Une erreur est survenue en essayant de joindre votre domaine, il est probablement injoignable.", "yunohost_postinstall_end_tip": "La post-installation terminée! Pour finaliser votre configuration, il est recommendé de :\n - ajouter un premier utilisateur depuis la section \"Utilisateurs\" de l'interface web (ou \"yunohost user create \" en ligne de commande);\n - diagnostiquer les potentiels problèmes dans la section \"Diagnostic\" de l'interface web (ou \"yunohost diagnosis run\" en ligne de commande);\n - lire les parties \"Finalisation de votre configuration\" et \"Découverte de Yunohost\" dans le guide de l'administrateur: https://yunohost.org/admindoc.", "diagnosis_services_bad_status_tip": "Vous pouvez essayer de redémarrer le service. Si cela ne fonctionne pas, consultez les journaux de service à l'aide de 'yunohost service log {service}' ou de la section 'Services' de l'administrateur Web.", "diagnosis_http_bad_status_code": "Le système de diagnostique n'a pas réussi à contacter votre serveur. Il se peut qu'une autre machine réponde à la place de votre serveur. Vérifiez que le port 80 est correctement redirigé, que votre configuration nginx est à jour et qu’un reverse-proxy n’interfère pas.", diff --git a/locales/oc.json b/locales/oc.json index a452b72bb..eebfaac64 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -479,8 +479,8 @@ "diagnosis_http_ok": "Lo domeni {domain} accessible de l’exterior.", "app_full_domain_unavailable": "Aquesta aplicacion a d’èsser installada sul seu pròpri domeni, mas i a d’autras aplicacions installadas sus aqueste domeni « {domain} ». Podètz utilizar allòc un josdomeni dedicat a aquesta aplicacion.", "diagnosis_dns_bad_conf": "Configuracion DNS incorrècta o inexistenta pel domeni {domain} (categoria {category})", - "diagnosis_ram_verylow": "Lo sistèma a solament {available_abs_MB} Mo ({available_percent}%) de memòria RAM disponibla ! (d’un total de {total_abs_MB} MB)", - "diagnosis_ram_ok": "Lo sistèma a encara {available_abs_MB} Mo ({available_percent}%) de memòria RAM disponibla d’un total de {total_abs_MB} MB).", + "diagnosis_ram_verylow": "Lo sistèma a solament {available} ({available_percent}%) de memòria RAM disponibla ! (d’un total de {total})", + "diagnosis_ram_ok": "Lo sistèma a encara {available} ({available_percent}%) de memòria RAM disponibla d’un total de {total}).", "permission_already_allowed": "Lo grop « {group} » a ja la permission « {permission} » activada", "permission_already_disallowed": "Lo grop « {group} » a ja la permission « {permission} » desactivada", "permission_cannot_remove_main": "La supression d’una permission màger es pas autorizada", @@ -511,7 +511,7 @@ "diagnosis_cache_still_valid": "(Memòria cache totjorn valida pel diagnostic {category}. Cap d’autre diagnostic pel moment !)", "diagnosis_found_errors": "{errors} errors importantas trobadas ligadas a {category} !", "diagnosis_services_bad_status": "Lo servici {service} es {status} :(", - "diagnosis_swap_ok": "Lo sistèma a {total_MB} MB d’escambi !", + "diagnosis_swap_ok": "Lo sistèma a {total} d’escambi !", "diagnosis_regenconf_allgood": "Totes los fichièrs de configuracion son confòrmes a la configuracion recomandada !", "diagnosis_regenconf_manually_modified": "Lo fichièr de configuracion {file} foguèt modificat manualament.", "diagnosis_regenconf_manually_modified_details": "Es probablament bon tan que sabètz çò que fasètz ;) !", @@ -527,7 +527,7 @@ "diagnosis_ports_ok": "Lo pòrt {port} es accessible de l’exterior.", "diagnosis_http_unreachable": "Lo domeni {domain} es pas accessible via HTTP de l’exterior.", "diagnosis_unknown_categories": "La categorias seguentas son desconegudas : {categories}", - "diagnosis_ram_low": "Lo sistèma a {available_abs_MB} Mo ({available_percent}%) de memòria RAM disponibla d’un total de {total_abs_MB} MB). Atencion.", + "diagnosis_ram_low": "Lo sistèma a {available} ({available_percent}%) de memòria RAM disponibla d’un total de {total}). Atencion.", "diagnosis_regenconf_manually_modified_debian": "Lo fichier de configuracion {file} foguèt modificat manualament respècte al fichièr per defaut de Debian.", "log_permission_create": "Crear la permission « {} »", "log_permission_delete": "Suprimir la permission « {} »", @@ -557,18 +557,17 @@ "diagnosis_services_running": "Lo servici {service} es lançat !", "diagnosis_services_conf_broken": "La configuracion es copada pel servici {service} !", "diagnosis_ports_needed_by": "Es necessari qu’aqueste pòrt siá accessible pel servici {service}", - "diagnosis_diskusage_low": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a solament {free_abs_GB} Go ({free_percent}%). Siatz prudent.", + "diagnosis_diskusage_low": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a solament {free} ({free_percent}%). Siatz prudent.", "migration_description_0014_remove_app_status_json": "Suprimir los fichièrs d’aplicacion status.json eretats", "dyndns_provider_unreachable": "Impossible d’atenher lo provesidor Dyndns : siá vòstre YunoHost es pas corrèctament connectat a Internet siá lo servidor dynette es copat.", "diagnosis_services_bad_status_tip": "Podètz ensajar de reaviar lo servici, e se non fonciona pas, podètz agachar los jornals en utilizant « yunohost service log {service} » o via la seccion « Servicis » de pas la pagina web d’administracion.", "diagnosis_http_connection_error": "Error de connexion : connexion impossibla al domeni demandat, benlèu qu’es pas accessible.", - "diagnosis_http_unknown_error": "Una error s’es producha en ensajar de se connectar a vòstre domeni, es benlèu pas accessible.", "group_user_already_in_group": "L’utilizaire {user} es ja dins lo grop « {group} »", "diagnosis_ip_broken_resolvconf": "La resolucion del nom de domeni sembla copada sul servidor, poiriá èsser ligada al fait que /etc/resolv.conf manda pas a 127.0.0.1.", "diagnosis_ip_weird_resolvconf": "La resolucion del nom de domeni sembla foncionar, mas siatz prudent en utilizant un fichièr /etc/resolv.con personalizat.", - "diagnosis_diskusage_verylow": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a solament {free_abs_GB} Go ({free_percent}%). Deuriatz considerar de liberar un pauc d’espaci.", + "diagnosis_diskusage_verylow": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a solament {free} ({free_percent}%). Deuriatz considerar de liberar un pauc d’espaci.", "global_settings_setting_pop3_enabled": "Activar lo protocòl POP3 pel servidor de corrièr", - "diagnosis_diskusage_ok": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a encara {free_abs_GB} Go ({free_percent}%) de liure !", + "diagnosis_diskusage_ok": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a encara {free} ({free_percent}%) de liure !", "diagnosis_swap_none": "Lo sistèma a pas cap de memòria d’escambi. Auriatz de considerar d’ajustar almens 256 Mo d’escambi per evitar las situacions ont lo sistèma manca de memòria.", - "diagnosis_swap_notsomuch": "Lo sistèma a solament {total_MB} de memòria d’escambi. Auriatz de considerar d’ajustar almens 256 Mo d’escambi per evitar las situacions ont lo sistèma manca de memòria." + "diagnosis_swap_notsomuch": "Lo sistèma a solament {total} de memòria d’escambi. Auriatz de considerar d’ajustar almens 256 Mo d’escambi per evitar las situacions ont lo sistèma manca de memòria." } From 0a3e7aa88b04c58c1795b52443c9eb8953768d85 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Fri, 17 Apr 2020 12:01:50 +0200 Subject: [PATCH 47/91] Update data/helpers.d/php Co-Authored-By: JimboJoe --- data/helpers.d/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 19e586b70..532e3ba1a 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -356,7 +356,7 @@ ynh_get_scalable_phpfpm () { fi # Define the factor to determine min_spare_servers - # To not have not enough children ready to start for heavy apps. + # to avoid having too few children ready to start for heavy apps if [ $footprint -le 20 ] then min_spare_servers_factor=8 From f90a238973dc4c9687492ec696f3922977076a74 Mon Sep 17 00:00:00 2001 From: Maniack Crudelis Date: Fri, 17 Apr 2020 12:02:11 +0200 Subject: [PATCH 48/91] Update data/helpers.d/php Co-Authored-By: JimboJoe --- data/helpers.d/php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/helpers.d/php b/data/helpers.d/php index 401c262b3..0bfbd2c29 100644 --- a/data/helpers.d/php +++ b/data/helpers.d/php @@ -402,7 +402,7 @@ ynh_get_scalable_phpfpm () { php_max_children=$max_proc fi - # Get an potential forced value for php_max_children + # Get a potential forced value for php_max_children local php_forced_max_children=$(ynh_app_setting_get --app=$app --key=php_forced_max_children) if [ -n "$php_forced_max_children" ]; then php_max_children=$php_forced_max_children From 7061c4c3fe489ffb4e5d5b4b230bef887e911ca1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Apr 2020 14:40:53 +0200 Subject: [PATCH 49/91] Improve systemd settings for slapd (#933) --- data/hooks/conf_regen/06-slapd | 10 ++++++++++ data/templates/slapd/systemd-override.conf | 9 +++++++++ 2 files changed, 19 insertions(+) create mode 100644 data/templates/slapd/systemd-override.conf diff --git a/data/hooks/conf_regen/06-slapd b/data/hooks/conf_regen/06-slapd index 2fa108baa..9b2c20138 100755 --- a/data/hooks/conf_regen/06-slapd +++ b/data/hooks/conf_regen/06-slapd @@ -63,6 +63,9 @@ do_pre_regen() { cp -a ldap.conf slapd.conf "$ldap_dir" cp -a sudo.schema mailserver.schema yunohost.schema "$schema_dir" + mkdir -p ${pending_dir}/etc/systemd/system/slapd.service.d/ + cp systemd-override.conf ${pending_dir}/etc/systemd/system/slapd.service.d/ynh-override.conf + install -D -m 644 slapd.default "${pending_dir}/etc/default/slapd" } @@ -83,6 +86,13 @@ do_post_regen() { chmod o-rwx /etc/yunohost/certs/yunohost.org/ chmod -R g+rx /etc/yunohost/certs/yunohost.org/ + # If we changed the systemd ynh-override conf + if echo "$regen_conf_files" | sed 's/,/\n/g' | grep -q "^/etc/systemd/system/slapd.service.d/ynh-override.conf$" + then + systemctl daemon-reload + systemctl restart slapd + fi + [ -z "$regen_conf_files" ] && exit 0 # check the slapd config file at first diff --git a/data/templates/slapd/systemd-override.conf b/data/templates/slapd/systemd-override.conf new file mode 100644 index 000000000..afa821bd4 --- /dev/null +++ b/data/templates/slapd/systemd-override.conf @@ -0,0 +1,9 @@ +[Service] +# Prevent slapd from getting killed by oom reaper as much as possible +OOMScoreAdjust=-1000 +# If slapd exited (for instance if got killed) the service should not be +# considered as active anymore... +RemainAfterExit=no +# Automatically restart the service if the service gets down +Restart=always +RestartSec=3 From 61ef67252e37c436b2efa346d9c8312a03c1b077 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Apr 2020 19:38:46 +0200 Subject: [PATCH 50/91] Don't contact ip6.yunohost if we can know right away that there's no IPv6 at all on the system --- data/hooks/diagnosis/10-ip.py | 2 +- src/yunohost/utils/network.py | 21 +++++++++++++++++---- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 36e04b5c1..0cb608b48 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -106,7 +106,7 @@ class IPDiagnoser(Diagnoser): # If we are indeed connected in ipv4 or ipv6, we should find a default route routes = check_output("ip -%s route" % protocol).split("\n") - if not [r for r in routes if r.startswith("default")]: + if not any(r.startswith("default") for r in routes): return False # We use the resolver file as a list of well-known, trustable (ie not google ;)) IPs that we can ping diff --git a/src/yunohost/utils/network.py b/src/yunohost/utils/network.py index 4e23516c3..3ae1ba910 100644 --- a/src/yunohost/utils/network.py +++ b/src/yunohost/utils/network.py @@ -18,10 +18,12 @@ along with this program; if not, see http://www.gnu.org/licenses """ -import logging +import os import re -import subprocess +import logging + from moulinette.utils.network import download_text +from moulinette.utils.process import check_output logger = logging.getLogger('yunohost.utils.network') @@ -36,6 +38,17 @@ def get_public_ip(protocol=4): else: raise ValueError("invalid protocol version") + # We can know that ipv6 is not available directly if this file does not exists + if protocol == 6 and not os.path.exists("/proc/net/if_inet6"): + logger.debug("IPv6 appears not at all available on the system, so assuming there's no IP address for that version") + return None + + # If we are indeed connected in ipv4 or ipv6, we should find a default route + routes = check_output("ip -%s route" % protocol).split("\n") + if not any(r.startswith("default") for r in routes): + logger.debug("No default route for IPv%s, so assuming there's no IP address for that version" % protocol) + return None + try: return download_text(url, timeout=30).strip() except Exception as e: @@ -47,7 +60,7 @@ def get_network_interfaces(): # Get network devices and their addresses (raw infos from 'ip addr') devices_raw = {} - output = subprocess.check_output('ip addr show'.split()) + output = check_output('ip addr show') for d in re.split(r'^(?:[0-9]+: )', output, flags=re.MULTILINE): # Extract device name (1) and its addresses (2) m = re.match(r'([^\s@]+)(?:@[\S]+)?: (.*)', d, flags=re.DOTALL) @@ -62,7 +75,7 @@ def get_network_interfaces(): def get_gateway(): - output = subprocess.check_output('ip route show'.split()) + output = check_output('ip route show') m = re.search(r'default via (.*) dev ([a-z]+[0-9]?)', output) if not m: return None From 485de92b50c8f826beb764225083cdc0ade5fc51 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Fri, 17 Apr 2020 21:22:02 +0200 Subject: [PATCH 51/91] Redundant message --- src/yunohost/diagnosis.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index fd7a37480..f8e3f36cc 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -385,8 +385,6 @@ class Diagnoser(): logger.error(m18n.n("diagnosis_cant_run_because_of_dep", category=self.description, dep=Diagnoser.get_description(dependency))) return 1, {} - self.logger_debug("Running diagnostic for %s" % self.id_) - items = list(self.run()) for item in items: From 08f9091257feb7423ffb9e06f72845dcd4533131 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 02:40:40 +0200 Subject: [PATCH 52/91] Handle stupid weird case where the manifest file disappeared yet the app folder is still there ... --- src/yunohost/app.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/yunohost/app.py b/src/yunohost/app.py index 39793ec1a..ed7747b29 100644 --- a/src/yunohost/app.py +++ b/src/yunohost/app.py @@ -116,7 +116,11 @@ def app_list(full=False): """ out = [] for app_id in sorted(_installed_apps()): - app_info_dict = app_info(app_id, full=full) + try: + app_info_dict = app_info(app_id, full=full) + except Exception as e: + logger.error("Failed to read info for %s : %s" % (app_id, e)) + continue app_info_dict["id"] = app_id out.append(app_info_dict) @@ -131,6 +135,7 @@ def app_info(app, full=False): raise YunohostError('app_not_installed', app=app, all_apps=_get_all_installed_apps_id()) local_manifest = _get_manifest_of_app(os.path.join(APPS_SETTING_PATH, app)) + settings = _get_app_settings(app) ret = { @@ -2026,7 +2031,7 @@ def _get_manifest_of_app(path): elif os.path.exists(os.path.join(path, "manifest.json")): return read_json(os.path.join(path, "manifest.json")) else: - return None + raise YunohostError("There doesn't seem to be any manifest file in %s ... It looks like an app was not correctly installed/removed." % path, raw_msg=True) def _get_git_last_commit_hash(repository, reference='HEAD'): From 749ca54a34f05a9751f0bbf17d84c9b067c5a592 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 02:53:15 +0200 Subject: [PATCH 53/91] More sensible cache_duration for diagnosis categories --- data/hooks/diagnosis/00-basesystem.py | 2 +- data/hooks/diagnosis/10-ip.py | 2 +- data/hooks/diagnosis/12-dnsrecords.py | 2 +- data/hooks/diagnosis/14-ports.py | 2 +- data/hooks/diagnosis/21-web.py | 2 +- data/hooks/diagnosis/24-mail.py | 2 +- data/hooks/diagnosis/50-systemresources.py | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/data/hooks/diagnosis/00-basesystem.py b/data/hooks/diagnosis/00-basesystem.py index 68a9570ce..51926924a 100644 --- a/data/hooks/diagnosis/00-basesystem.py +++ b/data/hooks/diagnosis/00-basesystem.py @@ -11,7 +11,7 @@ from yunohost.utils.packages import ynh_packages_version class BaseSystemDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 3600 * 24 + cache_duration = 600 dependencies = [] def run(self): diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 0cb608b48..6571ca556 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -13,7 +13,7 @@ from yunohost.utils.network import get_network_interfaces class IPDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 60 + cache_duration = 600 dependencies = [] def run(self): diff --git a/data/hooks/diagnosis/12-dnsrecords.py b/data/hooks/diagnosis/12-dnsrecords.py index 3853350bd..5ed7fc737 100644 --- a/data/hooks/diagnosis/12-dnsrecords.py +++ b/data/hooks/diagnosis/12-dnsrecords.py @@ -12,7 +12,7 @@ from yunohost.domain import domain_list, _build_dns_conf, _get_maindomain class DNSRecordsDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 3600 * 24 + cache_duration = 600 dependencies = ["ip"] def run(self): diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index b63971b71..6f4c808bd 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -9,7 +9,7 @@ from yunohost.service import _get_services class PortsDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 3600 + cache_duration = 600 dependencies = ["ip", "dnsrecords"] def run(self): diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 10deea28d..6e9dd6b79 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -16,7 +16,7 @@ DIAGNOSIS_SERVER = "diagnosis.yunohost.org" class WebDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 3600 + cache_duration = 600 dependencies = ["ip", "dnsrecords"] def run(self): diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 0a3a97102..3f9517bb0 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -8,7 +8,7 @@ from yunohost.diagnosis import Diagnoser class MailDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 3600 + cache_duration = 600 dependencies = ["ip"] def run(self): diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index b4e50ccf1..ab9ead7bb 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -7,7 +7,7 @@ from yunohost.diagnosis import Diagnoser class SystemResourcesDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 3600 * 24 + cache_duration = 300 dependencies = [] def run(self): From 301ced9d6ebbd350a734da40572c5312068fbb35 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 02:54:16 +0200 Subject: [PATCH 54/91] Hmf I dunno let's have this weird option if we really want to have a special case for first-run ... --- data/actionsmap/yunohost.yml | 3 +++ src/yunohost/diagnosis.py | 13 ++++++++----- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/data/actionsmap/yunohost.yml b/data/actionsmap/yunohost.yml index ded56a7c1..e1229352c 100644 --- a/data/actionsmap/yunohost.yml +++ b/data/actionsmap/yunohost.yml @@ -1686,6 +1686,9 @@ diagnosis: --force: help: Ignore the cached report even if it is still 'fresh' action: store_true + --except-if-never-ran-yet: + help: Don't run anything if diagnosis never ran yet ... (this is meant to be used by the webadmin) + action: store_true ignore: action_help: Configure some diagnosis results to be ignored and therefore not considered as actual issues diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index f8e3f36cc..f40687989 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -69,6 +69,10 @@ def diagnosis_get(category, item): def diagnosis_show(categories=[], issues=False, full=False, share=False): + if not os.path.exists(DIAGNOSIS_CACHE): + logger.warning(m18n.n("diagnosis_never_ran_yet")) + return + # Get all the categories all_categories = _list_diagnosis_categories() all_categories_names = [category for category, _ in all_categories] @@ -81,10 +85,6 @@ def diagnosis_show(categories=[], issues=False, full=False, share=False): if unknown_categories: raise YunohostError('diagnosis_unknown_categories', categories=", ".join(unknown_categories)) - if not os.path.exists(DIAGNOSIS_CACHE): - logger.warning(m18n.n("diagnosis_never_ran_yet")) - return - # Fetch all reports all_reports = [] for category in categories: @@ -146,7 +146,10 @@ def _dump_human_readable_reports(reports): return(output) -def diagnosis_run(categories=[], force=False): +def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False): + + if except_if_never_ran_yet and not os.path.exists(DIAGNOSIS_CACHE): + return # Get all the categories all_categories = _list_diagnosis_categories() From e880e775c15f730d7a9a23ecd88a425cf02bbef6 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 03:09:43 +0200 Subject: [PATCH 55/91] No need to display this message in webadmin (we're already on the diagnosis script when this happens ...) + in CLI we want to have it as a warning so that it's displayed in cron email --- locales/en.json | 3 +-- src/yunohost/diagnosis.py | 7 ++----- 2 files changed, 3 insertions(+), 7 deletions(-) diff --git a/locales/en.json b/locales/en.json index 27fb19444..f0189f8fe 100644 --- a/locales/en.json +++ b/locales/en.json @@ -143,8 +143,7 @@ "diagnosis_basesystem_ynh_single_version": "{package} version: {version} ({repo})", "diagnosis_basesystem_ynh_main_version": "Server is running YunoHost {main_version} ({repo})", "diagnosis_basesystem_ynh_inconsistent_versions": "You are running inconsistent versions of the YunoHost packages... most probably because of a failed or partial upgrade.", - "diagnosis_display_tip_web": "You can go to the Diagnosis section (in the home screen) to see the issues found.", - "diagnosis_display_tip_cli": "You can run 'yunohost diagnosis show --issues' to display the issues found.", + "diagnosis_display_tip": "To see the issues found, you can go to the Diagnosis section of the webadmin, or run 'yunohost diagnosis show --issues' from the command-line.", "diagnosis_failed_for_category": "Diagnosis failed for category '{category}': {error}", "diagnosis_cache_still_valid": "(Cache still valid for {category} diagnosis. Not re-diagnosing yet!)", "diagnosis_cant_run_because_of_dep": "Can't run diagnosis for {category} while there are important issues related to {dep}.", diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index f40687989..aba65a619 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -180,11 +180,8 @@ def diagnosis_run(categories=[], force=False, except_if_never_ran_yet=False): if report != {}: issues.extend([item for item in report["items"] if item["status"] in ["WARNING", "ERROR"]]) - if issues: - if msettings.get("interface") == "api": - logger.info(m18n.n("diagnosis_display_tip_web")) - else: - logger.info(m18n.n("diagnosis_display_tip_cli")) + if issues and msettings.get("interface") == "cli": + logger.warning(m18n.n("diagnosis_display_tip")) return From db347115fcc69c45705e505b150eed36bdab9e1e Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 03:24:31 +0200 Subject: [PATCH 56/91] In fact we can't have a hard dependency on dnsrecords here, otherwise no check are performed until the dnsrecords have no important issue ... --- data/hooks/diagnosis/14-ports.py | 2 +- data/hooks/diagnosis/21-web.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index 6f4c808bd..a4459d92f 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -10,7 +10,7 @@ class PortsDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] cache_duration = 600 - dependencies = ["ip", "dnsrecords"] + dependencies = ["ip"] def run(self): diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 6e9dd6b79..09f5b2b73 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -17,7 +17,7 @@ class WebDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] cache_duration = 600 - dependencies = ["ip", "dnsrecords"] + dependencies = ["ip"] def run(self): From fc5047838213f67db2bb3cdf76d83e15d2440f82 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 03:28:52 +0200 Subject: [PATCH 57/91] Another attempt to improve messages... --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index f0189f8fe..800a1d696 100644 --- a/locales/en.json +++ b/locales/en.json @@ -212,9 +212,9 @@ "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", - "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", + "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", - "diagnosis_http_bad_status_code": "Timed-out while trying to contact your server from outside. It might be that another machine answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", + "diagnosis_http_bad_status_code": "It looks like another machine (maybe your internet router) answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", "diagnosis_http_partially_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network in IPv{failed}, though it works in IPv{passed}.", "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", From d8feb1b72ae605100e8656f39e874209fa43172f Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 7 Apr 2020 01:53:05 +0200 Subject: [PATCH 58/91] [enh] Add RBL check --- data/hooks/diagnosis/24-mail.py | 89 ++++++++++++++++++++++++++++++++- locales/en.json | 3 ++ 2 files changed, 91 insertions(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 3f9517bb0..731267593 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -1,9 +1,42 @@ #!/usr/bin/env python import os +import dns.resolver + +from moulinette.utils.network import download_text from yunohost.diagnosis import Diagnoser +DEFAULT_BLACKLIST = [ + ('zen.spamhaus.org' , 'Spamhaus SBL, XBL and PBL' ), + ('dnsbl.sorbs.net' , 'SORBS aggregated' ), + ('safe.dnsbl.sorbs.net' , "'safe' subset of SORBS aggregated"), + ('ix.dnsbl.manitu.net' , 'Heise iX NiX Spam' ), + ('babl.rbl.webiron.net' , 'Bad Abuse' ), + ('cabl.rbl.webiron.net' , 'Chronicly Bad Abuse' ), + ('truncate.gbudb.net' , 'Exclusively Spam/Malware' ), + ('dnsbl-1.uceprotect.net' , 'Trapserver Cluster' ), + ('cbl.abuseat.org' , 'Net of traps' ), + ('dnsbl.cobion.com' , 'used in IBM products' ), + ('psbl.surriel.com' , 'passive list, easy to unlist' ), + ('dnsrbl.org' , 'Real-time black list' ), + ('db.wpbl.info' , 'Weighted private' ), + ('bl.spamcop.net' , 'Based on spamcop users' ), + ('dyna.spamrats.com' , 'Dynamic IP addresses' ), + ('spam.spamrats.com' , 'Manual submissions' ), + ('auth.spamrats.com' , 'Suspicious authentications' ), + ('dnsbl.inps.de' , 'automated and reported' ), + ('bl.blocklist.de' , 'fail2ban reports etc.' ), + ('srnblack.surgate.net' , 'feeders' ), + ('all.s5h.net' , 'traps' ), + ('rbl.realtimeblacklist.com' , 'lists ip ranges' ), + ('b.barracudacentral.org' , 'traps' ), + ('hostkarma.junkemailfilter.com', 'Autotected Virus Senders' ), + ('rbl.megarbl.net' , 'Curated Spamtraps' ), + ('ubl.unsubscore.com' , 'Collected Opt-Out Addresses' ), + ('0spam.fusionzero.com' , 'Spam Trap' ), +] + class MailDiagnoser(Diagnoser): @@ -14,6 +47,7 @@ class MailDiagnoser(Diagnoser): def run(self): # Is outgoing port 25 filtered somehow ? + self.logger_debug("Running outgoing 25 port check") if os.system('/bin/nc -z -w2 yunohost.org 25') == 0: yield dict(meta={"test": "ougoing_port_25"}, status="SUCCESS", @@ -23,9 +57,22 @@ class MailDiagnoser(Diagnoser): status="ERROR", summary="diagnosis_mail_ougoing_port_25_blocked") + # Is Reverse DNS well configured ? - # Mail blacklist using dig requests (c.f. ljf's code) + # Are IPs blacklisted ? + self.logger_debug("Running RBL detection") + blacklisted_details = tuple(self.check_blacklisted(self.get_public_ip(4))) + blacklisted_details += tuple(self.check_blacklisted(self.get_public_ip(6))) + if blacklisted_details: + yield dict(meta={}, + status="ERROR", + summary=("diagnosis_mail_blacklist_nok", {}), + details=blacklisted_details) + else: + yield dict(meta={}, + status="SUCCESS", + summary=("diagnosis_mail_blacklist_ok", {})) # SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser) @@ -37,6 +84,46 @@ class MailDiagnoser(Diagnoser): # check for unusual failed sending attempt being refused in the logs ? + def check_blacklisted(self, ip): + """ Check with dig onto blacklist DNS server + """ + if ip is None: + return + + for blacklist, description in DEFAULT_BLACKLIST: + + # Determine if we are listed on this RBL + try: + rev = dns.reversename.from_address(ip) + query = str(rev.split(3)[0]) + '.' + blacklist + # TODO add timeout lifetime + dns.resolver.query(query, "A") + except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, + dns.exception.Timeout): + continue + + # Try to get the reason + reason = "not explained" + try: + reason = str(dns.resolver.query(query, "TXT")[0]) + except Exception: + pass + + yield ('diagnosis_mail_blacklisted_by', + (ip, blacklist, reason)) + + def get_public_ip(self, protocol=4): + # TODO we might call this function from another side + assert protocol in [4, 6], "Invalid protocol version, it should be either 4 or 6 and was '%s'" % repr(protocol) + + url = 'https://ip%s.yunohost.org' % ('6' if protocol == 6 else '') + + try: + return download_text(url, timeout=30).strip() + except Exception as e: + self.logger_debug("Could not get public IPv%s : %s" % (str(protocol), str(e))) + return None + def main(args, env, loggers): return MailDiagnoser(args, env, loggers).diagnose() diff --git a/locales/en.json b/locales/en.json index 800a1d696..dbce8f367 100644 --- a/locales/en.json +++ b/locales/en.json @@ -186,6 +186,9 @@ "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", + "diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklist.", + "diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklist.", + "diagnosis_mail_blacklisted_by": "{0} is listed on {1}. Reason: {2}", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From bb162662c6007d729c6105c7e40352fba8500015 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 11 Apr 2020 19:34:34 +0200 Subject: [PATCH 59/91] [enh] Use named var in i18n --- data/hooks/diagnosis/24-mail.py | 20 ++++++++++++-------- locales/en.json | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 731267593..25d0ff984 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -62,17 +62,21 @@ class MailDiagnoser(Diagnoser): # Are IPs blacklisted ? self.logger_debug("Running RBL detection") - blacklisted_details = tuple(self.check_blacklisted(self.get_public_ip(4))) - blacklisted_details += tuple(self.check_blacklisted(self.get_public_ip(6))) + ipv4 = Diagnoser.get_cached_report_item("ip", {"test": "ipv4"}) + global_ipv4 = ipv4.get("data", {}).get("global", {}) + ipv6 = Diagnoser.get_cached_report_item("ip", {"test": "ipv6"}) + global_ipv6 = ipv6.get("data", {}).get("global", {}) + blacklisted_details = tuple(self.check_blacklisted(global_ipv4)) + blacklisted_details += tuple(self.check_blacklisted(global_ipv6)) if blacklisted_details: - yield dict(meta={}, + yield dict(meta={"test": "mail_blacklist"}, status="ERROR", - summary=("diagnosis_mail_blacklist_nok", {}), - details=blacklisted_details) + summary="diagnosis_mail_blacklist_nok", + details=list(blacklisted_details)) else: - yield dict(meta={}, + yield dict(meta={"test": "mail_blacklist"}, status="SUCCESS", - summary=("diagnosis_mail_blacklist_ok", {})) + summary="diagnosis_mail_blacklist_ok") # SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser) @@ -110,7 +114,7 @@ class MailDiagnoser(Diagnoser): pass yield ('diagnosis_mail_blacklisted_by', - (ip, blacklist, reason)) + {'ip': ip, 'blacklist': blacklist, 'reason': reason}) def get_public_ip(self, protocol=4): # TODO we might call this function from another side diff --git a/locales/en.json b/locales/en.json index dbce8f367..26c51d253 100644 --- a/locales/en.json +++ b/locales/en.json @@ -188,7 +188,7 @@ "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklist.", "diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklist.", - "diagnosis_mail_blacklisted_by": "{0} is listed on {1}. Reason: {2}", + "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist}. Reason: {reason}", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From 0b7984adf117a413b63d8604d6b54cea22bc3c87 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 12 Apr 2020 04:14:49 +0200 Subject: [PATCH 60/91] [enh] Improve DNSBL check --- data/hooks/diagnosis/24-mail.py | 120 +++++++++------------ data/other/dnsbl_list.yml | 184 ++++++++++++++++++++++++++++++++ debian/install | 1 + locales/en.json | 6 +- 4 files changed, 237 insertions(+), 74 deletions(-) create mode 100644 data/other/dnsbl_list.yml diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 25d0ff984..333d98c8a 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -4,38 +4,11 @@ import os import dns.resolver from moulinette.utils.network import download_text +from moulinette.utils.filesystem import read_yaml from yunohost.diagnosis import Diagnoser -DEFAULT_BLACKLIST = [ - ('zen.spamhaus.org' , 'Spamhaus SBL, XBL and PBL' ), - ('dnsbl.sorbs.net' , 'SORBS aggregated' ), - ('safe.dnsbl.sorbs.net' , "'safe' subset of SORBS aggregated"), - ('ix.dnsbl.manitu.net' , 'Heise iX NiX Spam' ), - ('babl.rbl.webiron.net' , 'Bad Abuse' ), - ('cabl.rbl.webiron.net' , 'Chronicly Bad Abuse' ), - ('truncate.gbudb.net' , 'Exclusively Spam/Malware' ), - ('dnsbl-1.uceprotect.net' , 'Trapserver Cluster' ), - ('cbl.abuseat.org' , 'Net of traps' ), - ('dnsbl.cobion.com' , 'used in IBM products' ), - ('psbl.surriel.com' , 'passive list, easy to unlist' ), - ('dnsrbl.org' , 'Real-time black list' ), - ('db.wpbl.info' , 'Weighted private' ), - ('bl.spamcop.net' , 'Based on spamcop users' ), - ('dyna.spamrats.com' , 'Dynamic IP addresses' ), - ('spam.spamrats.com' , 'Manual submissions' ), - ('auth.spamrats.com' , 'Suspicious authentications' ), - ('dnsbl.inps.de' , 'automated and reported' ), - ('bl.blocklist.de' , 'fail2ban reports etc.' ), - ('srnblack.surgate.net' , 'feeders' ), - ('all.s5h.net' , 'traps' ), - ('rbl.realtimeblacklist.com' , 'lists ip ranges' ), - ('b.barracudacentral.org' , 'traps' ), - ('hostkarma.junkemailfilter.com', 'Autotected Virus Senders' ), - ('rbl.megarbl.net' , 'Curated Spamtraps' ), - ('ubl.unsubscore.com' , 'Collected Opt-Out Addresses' ), - ('0spam.fusionzero.com' , 'Spam Trap' ), -] +DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" class MailDiagnoser(Diagnoser): @@ -57,17 +30,13 @@ class MailDiagnoser(Diagnoser): status="ERROR", summary="diagnosis_mail_ougoing_port_25_blocked") - # Is Reverse DNS well configured ? + # Forward-confirmed reverse DNS (FCrDNS) verification - # Are IPs blacklisted ? - self.logger_debug("Running RBL detection") - ipv4 = Diagnoser.get_cached_report_item("ip", {"test": "ipv4"}) - global_ipv4 = ipv4.get("data", {}).get("global", {}) - ipv6 = Diagnoser.get_cached_report_item("ip", {"test": "ipv6"}) - global_ipv6 = ipv6.get("data", {}).get("global", {}) - blacklisted_details = tuple(self.check_blacklisted(global_ipv4)) - blacklisted_details += tuple(self.check_blacklisted(global_ipv6)) + # Are IPs listed on a DNSBL ? + self.logger_debug("Running DNSBL detection") + + blacklisted_details = self.check_ip_dnsbl() if blacklisted_details: yield dict(meta={"test": "mail_blacklist"}, status="ERROR", @@ -88,45 +57,54 @@ class MailDiagnoser(Diagnoser): # check for unusual failed sending attempt being refused in the logs ? - def check_blacklisted(self, ip): + def check_blacklisted(self): """ Check with dig onto blacklist DNS server """ - if ip is None: - return + dns_blacklists = read_yaml(DEFAULT_DNS_BLACKLIST) + for ip in self.get_public_ips(): + for blacklist in dns_blacklists: + + if "." in ip and not blacklist.ipv4: + continue - for blacklist, description in DEFAULT_BLACKLIST: + if ":" in ip and not blacklist.ipv6: + continue + + # Determine if we are listed on this RBL + try: + rev = dns.reversename.from_address(ip) + query = str(rev.split(3)[0]) + '.' + blacklist.dns_server + # TODO add timeout lifetime + dns.resolver.query(query, "A") + except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, + dns.exception.Timeout): + continue - # Determine if we are listed on this RBL - try: - rev = dns.reversename.from_address(ip) - query = str(rev.split(3)[0]) + '.' + blacklist - # TODO add timeout lifetime - dns.resolver.query(query, "A") - except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, - dns.exception.Timeout): - continue + # Try to get the reason + reason = "not explained" + try: + reason = str(dns.resolver.query(query, "TXT")[0]) + except Exception: + pass - # Try to get the reason - reason = "not explained" - try: - reason = str(dns.resolver.query(query, "TXT")[0]) - except Exception: - pass + yield ('diagnosis_mail_blacklisted_by', { + 'ip': ip, + 'blacklist': blacklist, + 'reason': reason}) - yield ('diagnosis_mail_blacklisted_by', - {'ip': ip, 'blacklist': blacklist, 'reason': reason}) - - def get_public_ip(self, protocol=4): - # TODO we might call this function from another side - assert protocol in [4, 6], "Invalid protocol version, it should be either 4 or 6 and was '%s'" % repr(protocol) - - url = 'https://ip%s.yunohost.org' % ('6' if protocol == 6 else '') - - try: - return download_text(url, timeout=30).strip() - except Exception as e: - self.logger_debug("Could not get public IPv%s : %s" % (str(protocol), str(e))) - return None + def get_public_ips(self): + # Todo code a better way to access a data + ipv4 = Diagnoser.get_cached_report("ip", {"test": "ipv4"}) + if ipv4: + global_ipv4 = ipv4.get("data", {}).get("global", {}) + if global_ipv4: + yield global_ipv4 + + ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) + if ipv6: + global_ipv6 = ipv6.get("data", {}).get("global", {}) + if global_ipv6: + yield global_ipv6 def main(args, env, loggers): diff --git a/data/other/dnsbl_list.yml b/data/other/dnsbl_list.yml new file mode 100644 index 000000000..839aeaab6 --- /dev/null +++ b/data/other/dnsbl_list.yml @@ -0,0 +1,184 @@ +# Used by GAFAM +- name: Spamhaus ZEN + dns_server: zen.spamhaus.org + website: https://www.spamhaus.org/zen/ + ipv4: true + ipv6: true + domain: false +- name: Barracuda Reputation Block List + dns_server: b.barracudacentral.org + website: https://barracudacentral.org/rbl/ + ipv4: true + ipv6: false + domain: false +- name: Hostkarma + dns_server: hostkarma.junkemailfilter.com + website: https://ipadmin.junkemailfilter.com/remove.php + ipv4: true + ipv6: false + domain: false +- name: ImproWare IP based spamlist + dns_server: spamrbl.imp.ch + website: https://antispam.imp.ch/ + ipv4: true + ipv6: false + domain: false +- name: ImproWare IP based wormlist + dns_server: wormrbl.imp.ch + website: https://antispam.imp.ch/ + ipv4: true + ipv6: false + domain: false +- name: Backscatterer.org + dns_server: ips.backscatterer.org + website: http://www.backscatterer.org/ + ipv4: true + ipv6: false + domain: false +- name: inps.de + dns_server: dnsbl.inps.de + website: http://dnsbl.inps.de/ + ipv4: true + ipv6: false + domain: false +- name: LASHBACK + dns_server: ubl.unsubscore.com + website: https://blacklist.lashback.com/ + ipv4: true + ipv6: false + domain: false +- name: Mailspike.org + dns_server: bl.mailspike.net + website: http://www.mailspike.net/ + ipv4: true + ipv6: false + domain: false +- name: NiX Spam + dns_server: ix.dnsbl.manitu.net + website: http://www.dnsbl.manitu.net/ + ipv4: true + ipv6: false + domain: false +- name: REDHAWK + dns_server: access.redhawk.org + website: https://www.redhawk.org/SpamHawk/query.php + ipv4: true + ipv6: false + domain: false +- name: SORBS Open SMTP relays + dns_server: smtp.dnsbl.sorbs.net + website: http://www.sorbs.net/ + ipv4: true + ipv6: false + domain: false +- name: SORBS Spamhost (last 28 days) + dns_server: recent.spam.dnsbl.sorbs.net + website: http://www.sorbs.net/ + ipv4: true + ipv6: false + domain: false +- name: SORBS Spamhost (last 48 hours) + dns_server: new.spam.dnsbl.sorbs.net + website: http://www.sorbs.net/ + ipv4: true + ipv6: false + domain: false +- name: SpamCop Blocking List + dns_server: bl.spamcop.net + website: https://www.spamcop.net/bl.shtml + ipv4: true + ipv6: false + domain: false +- name: Spam Eating Monkey SEM-BACKSCATTER + dns_server: backscatter.spameatingmonkey.net + website: https://spameatingmonkey.com/services + ipv4: true + ipv6: false + domain: false +- name: Spam Eating Monkey SEM-BLACK + dns_server: bl.spameatingmonkey.net + website: https://spameatingmonkey.com/services + ipv4: true + ipv6: false + domain: false +- name: Spam Eating Monkey SEM-IPV6BL + dns_server: bl.ipv6.spameatingmonkey.net + website: https://spameatingmonkey.com/services + ipv4: false + ipv6: true + domain: false +- name: SpamRATS! all + dns_server: all.spamrats.com + website: http://www.spamrats.com/ + ipv4: true + ipv6: false + domain: false +- name: PSBL (Passive Spam Block List) + dns_server: psbl.surriel.com + website: http://psbl.surriel.com/ + ipv4: true + ipv6: false + domain: false +- name: SWINOG + dns_server: dnsrbl.swinog.ch + website: https://antispam.imp.ch/ + ipv4: true + ipv6: false + domain: false +- name: GBUdb Truncate + dns_server: truncate.gbudb.net + website: http://www.gbudb.com/truncate/index.jsp + ipv4: true + ipv6: false + domain: false +- name: Weighted Private Block List + dns_server: db.wpbl.info + website: http://www.wpbl.info/ + ipv4: true + ipv6: false + domain: false +# Used by GAFAM +- name: Composite Blocking List + dns_server: cbl.abuseat.org + website: cbl.abuseat.org + ipv4: true + ipv6: false + domain: false +# Used by GAFAM +- name: SenderScore Blacklist + dns_server: bl.score.senderscore.com + website: https://senderscore.com + ipv4: true + ipv6: false + domain: false +- name: Invaluement + dns_server: sip.invaluement.com + website: https://www.invaluement.com/ + ipv4: true + ipv6: false + domain: false +# Added cause it supports IPv6 +- name: AntiCaptcha.NET IPv6 + dns_server: dnsbl6.anticaptcha.net + website: http://anticaptcha.net/ + ipv4: false + ipv6: true + domain: false +- name: SPFBL.net RBL + dns_server: dnsbl.spfbl.net + website: https://spfbl.net/en/dnsbl/ + ipv4: true + ipv6: true + domain: true +- name: Suomispam Blacklist + dns_server: bl.suomispam.net + website: http://suomispam.net/ + ipv4: true + ipv6: true + domain: false +- name: NordSpam + dns_server: bl.nordspam.com + website: https://www.nordspam.com/ + ipv4: true + ipv6: true + domain: false diff --git a/debian/install b/debian/install index e0743cdd1..cf682d958 100644 --- a/debian/install +++ b/debian/install @@ -7,6 +7,7 @@ data/hooks/* /usr/share/yunohost/hooks/ data/other/yunoprompt.service /etc/systemd/system/ data/other/password/* /usr/share/yunohost/other/password/ data/other/dpkg-origins/yunohost /etc/dpkg/origins +data/other/dnsbl_list.yml /usr/share/yunohost/other/dnsbl_list.yml data/other/* /usr/share/yunohost/yunohost-config/moulinette/ data/templates/* /usr/share/yunohost/templates/ data/helpers /usr/share/yunohost/ diff --git a/locales/en.json b/locales/en.json index 26c51d253..37ae2a34f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -186,9 +186,9 @@ "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", - "diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklist.", - "diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklist.", - "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist}. Reason: {reason}", + "diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklists.", + "diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklists.", + "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist.name}. Reason: {reason}. See {blacklist.website}", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From 5b0698e798421c1a3d71147c279b326b4b2726a6 Mon Sep 17 00:00:00 2001 From: ljf Date: Mon, 13 Apr 2020 16:41:27 +0200 Subject: [PATCH 61/91] [fix] Bad call to dict --- data/hooks/diagnosis/24-mail.py | 19 ++++++++++--------- locales/en.json | 2 +- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 333d98c8a..f4f897e28 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -36,12 +36,13 @@ class MailDiagnoser(Diagnoser): # Are IPs listed on a DNSBL ? self.logger_debug("Running DNSBL detection") - blacklisted_details = self.check_ip_dnsbl() + blacklisted_details = list(self.check_dnsbl(self.get_public_ips())) + print(blacklisted_details) if blacklisted_details: yield dict(meta={"test": "mail_blacklist"}, status="ERROR", summary="diagnosis_mail_blacklist_nok", - details=list(blacklisted_details)) + details=blacklisted_details) else: yield dict(meta={"test": "mail_blacklist"}, status="SUCCESS", @@ -57,23 +58,22 @@ class MailDiagnoser(Diagnoser): # check for unusual failed sending attempt being refused in the logs ? - def check_blacklisted(self): + def check_dnsbl(self, ips): """ Check with dig onto blacklist DNS server """ dns_blacklists = read_yaml(DEFAULT_DNS_BLACKLIST) - for ip in self.get_public_ips(): + for ip in ips: for blacklist in dns_blacklists: - - if "." in ip and not blacklist.ipv4: + if "." in ip and not blacklist['ipv4']: continue - if ":" in ip and not blacklist.ipv6: + if ":" in ip and not blacklist['ipv6']: continue # Determine if we are listed on this RBL try: rev = dns.reversename.from_address(ip) - query = str(rev.split(3)[0]) + '.' + blacklist.dns_server + query = str(rev.split(3)[0]) + '.' + blacklist['dns_server'] # TODO add timeout lifetime dns.resolver.query(query, "A") except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, @@ -89,7 +89,8 @@ class MailDiagnoser(Diagnoser): yield ('diagnosis_mail_blacklisted_by', { 'ip': ip, - 'blacklist': blacklist, + 'blacklist_name': blacklist['name'], + 'blacklist_website': blacklist['website'], 'reason': reason}) def get_public_ips(self): diff --git a/locales/en.json b/locales/en.json index 37ae2a34f..93f7680bf 100644 --- a/locales/en.json +++ b/locales/en.json @@ -188,7 +188,7 @@ "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklists.", "diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklists.", - "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist.name}. Reason: {reason}. See {blacklist.website}", + "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist_name}. Reason: {reason}. See {blacklist_website}", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From 027a0ed73c9281fd35582d9e683348483f12f7bd Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 14 Apr 2020 03:56:35 +0200 Subject: [PATCH 62/91] [wip] Add rDNS and mailqueue check --- data/hooks/diagnosis/24-mail.py | 78 ++++++++++++++++++++++++++++++--- locales/en.json | 12 ++++- 2 files changed, 82 insertions(+), 8 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index f4f897e28..b91bfec85 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -2,11 +2,15 @@ import os import dns.resolver +import smtplib +import socket +from moulinette.utils.process import check_output from moulinette.utils.network import download_text from moulinette.utils.filesystem import read_yaml from yunohost.diagnosis import Diagnoser +from yunohost.domain import _get_maindomain DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" @@ -18,6 +22,8 @@ class MailDiagnoser(Diagnoser): dependencies = ["ip"] def run(self): + + ips = self.get_public_ips() # Is outgoing port 25 filtered somehow ? self.logger_debug("Running outgoing 25 port check") @@ -30,14 +36,56 @@ class MailDiagnoser(Diagnoser): status="ERROR", summary="diagnosis_mail_ougoing_port_25_blocked") - # Forward-confirmed reverse DNS (FCrDNS) verification + # Get HELO and be sure postfix is running + # TODO SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser) + server = None + result = dict(meta={"test": "mail_ehlo"}, + status="SUCCESS", + summary="diagnosis_mail_service_working") + try: + server = smtplib.SMTP("127.0.0.1", 25, timeout=10) + ehlo = server.ehlo() + ehlo_domain = ehlo[1].decode("utf-8").split("\n")[0] + except OSError: + result = dict(meta={"test": "mail_ehlo"}, + status="ERROR", + summary="diagnosis_mail_service_not_working") + ehlo_domain = _get_maindomain() + if server: + server.quit() + yield result + # Forward-confirmed reverse DNS (FCrDNS) verification + self.logger_debug("Running Forward-confirmed reverse DNS check") + for ip in ips: + try: + rdns_domain, _, _ = socket.gethostbyaddr(ip) + except socket.herror as e: + yield dict(meta={"test": "mail_fcrdns"}, + data={"ip": ip, "ehlo_domain": ehlo_domain}, + status="ERROR", + summary="diagnosis_mail_reverse_dns_missing") + continue + else: + if rdns_domain != ehlo_domain: + yield dict(meta={"test": "mail_fcrdns"}, + data={"ip": ip, "ehlo_domain": ehlo_domain, + "rdns_domain": rdns_domain}, + status="ERROR", + summary="diagnosis_mail_rdns_different_from_ehlo_domain") + else: + yield dict(meta={"test": "mail_fcrdns"}, + data={"ip": ip, "ehlo_domain": ehlo_domain}, + status="SUCCESS", + summary="diagnosis_mail_rdns_equal_to_ehlo_domain") + + # TODO Is a A/AAAA and MX Record ? # Are IPs listed on a DNSBL ? - self.logger_debug("Running DNSBL detection") + self.logger_debug("Running DNS Blacklist detection") + # TODO Test if domain are blacklisted too blacklisted_details = list(self.check_dnsbl(self.get_public_ips())) - print(blacklisted_details) if blacklisted_details: yield dict(meta={"test": "mail_blacklist"}, status="ERROR", @@ -48,11 +96,29 @@ class MailDiagnoser(Diagnoser): status="SUCCESS", summary="diagnosis_mail_blacklist_ok") - # SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser) + # TODO Are outgoing public IPs authorized to send mail by SPF ? + + # TODO Validate DKIM and dmarc ? - # ideally, SPF / DMARC / DKIM validation ... (c.f. https://github.com/alexAubin/yunoScripts/blob/master/yunoDKIM.py possibly though that looks horrible) - # check that the mail queue is not filled with hundreds of email pending + # Is mail queue filled with hundreds of email pending ? + command = 'postqueue -p | grep -c "^[A-Z0-9]"' + output = check_output(command).strip() + try: + pending_emails = int(output) + except ValueError: + yield dict(meta={"test": "mail_queue"}, + status="ERROR", + summary="diagnosis_mail_cannot_get_queue") + else: + if pending_emails > 300: + yield dict(meta={"test": "mail_queue"}, + status="WARNING", + summary="diagnosis_mail_queue_too_many_pending_emails") + else: + yield dict(meta={"test": "mail_queue"}, + status="INFO", + summary="diagnosis_mail_queue_ok") # check that the recent mail logs are not filled with thousand of email sending (unusual number of mail sent) diff --git a/locales/en.json b/locales/en.json index 93f7680bf..978ceb831 100644 --- a/locales/en.json +++ b/locales/en.json @@ -186,9 +186,17 @@ "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", - "diagnosis_mail_blacklist_ok": "Your server public IP are not listed on email blacklists.", - "diagnosis_mail_blacklist_nok": "Your server public IPs are listed on email blacklists.", + "diagnosis_mail_blacklist_ok": "The public IPs of this instance are not listed on email blacklists.", + "diagnosis_mail_blacklist_nok": "Some of the public IPs of this instance are listed on email blacklists.", "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist_name}. Reason: {reason}. See {blacklist_website}", + "diagnosis_mail_service_working": "Postfix mail service answer correctly.", + "diagnosis_mail_service_not_working": "Postfix mail service don't answer to EHLO request.", + "diagnosis_mail_reverse_dns_missing": "No reverse DNS defined for the ip {ip}.", + "diagnosis_mail_rdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}.", + "diagnosis_mail_rdns_equal_to_ehlo_domain": "Your reverse DNS is equal to your EHLO domain {ehlo_domain} on {ip}.", + "diagnosis_mail_queue_unavailable": "Can not consult number of pending emails in queue", + "diagnosis_mail_queue_too_big": "The mail queue has {nb_pending} pending emails in the mail queue. It seems abnormal.", + "diagnosis_mail_queue_unavailable": "The mail queue has {nb_pending} pending emails in the mail queue.", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From da6ae405dd426fabb72d9673bfc2b5ac02accbe2 Mon Sep 17 00:00:00 2001 From: ljf Date: Tue, 14 Apr 2020 03:59:33 +0200 Subject: [PATCH 63/91] [fix] Missing pending number args --- data/hooks/diagnosis/24-mail.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index b91bfec85..f1a267641 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -113,10 +113,12 @@ class MailDiagnoser(Diagnoser): else: if pending_emails > 300: yield dict(meta={"test": "mail_queue"}, + data={'nb_pending': pending_emails}, status="WARNING", summary="diagnosis_mail_queue_too_many_pending_emails") else: yield dict(meta={"test": "mail_queue"}, + data={'nb_pending': pending_emails}, status="INFO", summary="diagnosis_mail_queue_ok") From a17adc274c90517b42bdcdf31d9a12b58f43d7d9 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 18 Apr 2020 17:08:09 +0200 Subject: [PATCH 64/91] [wip] Small refactoring for mail diagnoser --- data/hooks/diagnosis/24-mail.py | 279 ++++++++++++++++++-------------- locales/en.json | 15 +- 2 files changed, 165 insertions(+), 129 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index f1a267641..1336e8c2b 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -2,15 +2,21 @@ import os import dns.resolver -import smtplib import socket +import re + +from subprocess import CalledProcessError +from types import FunctionType from moulinette.utils.process import check_output from moulinette.utils.network import download_text from moulinette.utils.filesystem import read_yaml from yunohost.diagnosis import Diagnoser -from yunohost.domain import _get_maindomain +from yunohost.domain import _get_maindomain, domain_list +from yunohost.utils.error import YunohostError + +DIAGNOSIS_SERVER = "diagnosis.yunohost.org" DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" @@ -22,126 +28,124 @@ class MailDiagnoser(Diagnoser): dependencies = ["ip"] def run(self): - - ips = self.get_public_ips() - # Is outgoing port 25 filtered somehow ? - self.logger_debug("Running outgoing 25 port check") - if os.system('/bin/nc -z -w2 yunohost.org 25') == 0: - yield dict(meta={"test": "ougoing_port_25"}, - status="SUCCESS", - summary="diagnosis_mail_ougoing_port_25_ok") - else: - yield dict(meta={"test": "outgoing_port_25"}, - status="ERROR", - summary="diagnosis_mail_ougoing_port_25_blocked") + self.ehlo_domain = _get_maindomain() + self.mail_domains = domain_list()["domains"] + self.ipversions, self.ips = self.get_ips_checked() - # Get HELO and be sure postfix is running - # TODO SMTP reachability (c.f. check-smtp to be implemented on yunohost's remote diagnoser) - server = None - result = dict(meta={"test": "mail_ehlo"}, - status="SUCCESS", - summary="diagnosis_mail_service_working") - try: - server = smtplib.SMTP("127.0.0.1", 25, timeout=10) - ehlo = server.ehlo() - ehlo_domain = ehlo[1].decode("utf-8").split("\n")[0] - except OSError: - result = dict(meta={"test": "mail_ehlo"}, - status="ERROR", - summary="diagnosis_mail_service_not_working") - ehlo_domain = _get_maindomain() - if server: - server.quit() - yield result + # TODO Is a A/AAAA and MX Record ? + # TODO Are outgoing public IPs authorized to send mail by SPF ? + # TODO Validate DKIM and dmarc ? + # TODO check that the recent mail logs are not filled with thousand of email sending (unusual number of mail sent) + # TODO check for unusual failed sending attempt being refused in the logs ? + checks = [name for name, value in MailDiagnoser.__dict__.items() + if type(value) == FunctionType and name.startswith("check_")] + for check in checks: + self.logger_debug("Running " + check) + for report in getattr(self, check): + yield report + else: + name = checks[6:] + yield dict(meta={"test": "mail_" + name}, + status="SUCCESS", + summary="diagnosis_mail_" + name + "_ok") - # Forward-confirmed reverse DNS (FCrDNS) verification - self.logger_debug("Running Forward-confirmed reverse DNS check") - for ip in ips: + + def check_outgoing_port_25(self): + """ + Check outgoing port 25 is open and not blocked by router + This check is ran on IPs we could used to send mail. + """ + + for ipversion in self.ipversions: + cmd = '/bin/nc -{ipversion} -z -w2 yunohost.org 25'.format({ + 'ipversion': ipversion}) + if os.system(cmd) != 0: + yield dict(meta={"test": "outgoing_port_25", "ipversion": ipversion}, + data={}, + status="ERROR", + summary="diagnosis_mail_ougoing_port_25_blocked") + + + def check_ehlo(self): + """ + Check the server is reachable from outside and it's the good one + This check is ran on IPs we could used to send mail. + """ + + for ipversion in self.ipversions: + try: + r = Diagnoser.remote_diagnosis('check-smtp', + data={}, + ipversion=ipversion) + except Exception as e: + yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, + data={"error": e}, + status="WARNING", + summary="diagnosis_mail_ehlo_could_not_diagnose") + continue + + if r["status"] == "error_smtp_unreachable": + yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, + data={}, + status="ERROR", + summary="diagnosis_mail_ehlo_unavailable") + elif r["helo"] != self.ehlo_domain: + yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, + data={"wrong_ehlo": r["helo"], "right_ehlo": self.ehlo_domain}, + status="ERROR", + summary="diagnosis_mail_ehlo_wrong") + + + def check_fcrdns(self): + """ + Check the reverse DNS is well defined by doing a Forward-confirmed + reverse DNS check + This check is ran on IPs we could used to send mail. + """ + + for ip in self.ips: try: rdns_domain, _, _ = socket.gethostbyaddr(ip) - except socket.herror as e: - yield dict(meta={"test": "mail_fcrdns"}, - data={"ip": ip, "ehlo_domain": ehlo_domain}, + except socket.herror: + yield dict(meta={"test": "mail_fcrdns", "ip": ip}, + data={"ehlo_domain": self.ehlo_domain}, status="ERROR", summary="diagnosis_mail_reverse_dns_missing") continue - else: - if rdns_domain != ehlo_domain: - yield dict(meta={"test": "mail_fcrdns"}, - data={"ip": ip, "ehlo_domain": ehlo_domain, - "rdns_domain": rdns_domain}, - status="ERROR", - summary="diagnosis_mail_rdns_different_from_ehlo_domain") - else: - yield dict(meta={"test": "mail_fcrdns"}, - data={"ip": ip, "ehlo_domain": ehlo_domain}, - status="SUCCESS", - summary="diagnosis_mail_rdns_equal_to_ehlo_domain") - - # TODO Is a A/AAAA and MX Record ? - - # Are IPs listed on a DNSBL ? - self.logger_debug("Running DNS Blacklist detection") - # TODO Test if domain are blacklisted too - - blacklisted_details = list(self.check_dnsbl(self.get_public_ips())) - if blacklisted_details: - yield dict(meta={"test": "mail_blacklist"}, - status="ERROR", - summary="diagnosis_mail_blacklist_nok", - details=blacklisted_details) - else: - yield dict(meta={"test": "mail_blacklist"}, - status="SUCCESS", - summary="diagnosis_mail_blacklist_ok") - - # TODO Are outgoing public IPs authorized to send mail by SPF ? - - # TODO Validate DKIM and dmarc ? + if rdns_domain != self.ehlo_domain: + yield dict(meta={"test": "mail_fcrdns", "ip": ip}, + data={"ehlo_domain": self.ehlo_domain, + "rdns_domain": rdns_domain}, + status="ERROR", + summary="diagnosis_mail_rdns_different_from_ehlo_domain") - # Is mail queue filled with hundreds of email pending ? - command = 'postqueue -p | grep -c "^[A-Z0-9]"' - output = check_output(command).strip() - try: - pending_emails = int(output) - except ValueError: - yield dict(meta={"test": "mail_queue"}, - status="ERROR", - summary="diagnosis_mail_cannot_get_queue") - else: - if pending_emails > 300: - yield dict(meta={"test": "mail_queue"}, - data={'nb_pending': pending_emails}, - status="WARNING", - summary="diagnosis_mail_queue_too_many_pending_emails") - else: - yield dict(meta={"test": "mail_queue"}, - data={'nb_pending': pending_emails}, - status="INFO", - summary="diagnosis_mail_queue_ok") - - # check that the recent mail logs are not filled with thousand of email sending (unusual number of mail sent) - - # check for unusual failed sending attempt being refused in the logs ? - - def check_dnsbl(self, ips): - """ Check with dig onto blacklist DNS server + def check_blacklist(self): """ + Check with dig onto blacklist DNS server + This check is ran on IPs and domains we could used to send mail. + """ + dns_blacklists = read_yaml(DEFAULT_DNS_BLACKLIST) - for ip in ips: + for item in self.ips + self.mail_domains: for blacklist in dns_blacklists: - if "." in ip and not blacklist['ipv4']: + item_type = "domain" + if ":" in item: + item_type = 'ipv6' + elif re.match(r'^\d+\.\d+\.\d+\.\d+$', item): + item_type = 'ipv4' + + if not blacklist[item_type]: continue - if ":" in ip and not blacklist['ipv6']: - continue - # Determine if we are listed on this RBL try: - rev = dns.reversename.from_address(ip) - query = str(rev.split(3)[0]) + '.' + blacklist['dns_server'] + subdomain = item + if item_type != "domain": + rev = dns.reversename.from_address(item) + subdomain = str(rev.split(3)[0]) + query = subdomain + '.' + blacklist['dns_server'] # TODO add timeout lifetime dns.resolver.query(query, "A") except (dns.resolver.NXDOMAIN, dns.resolver.NoNameservers, dns.resolver.NoAnswer, @@ -149,32 +153,63 @@ class MailDiagnoser(Diagnoser): continue # Try to get the reason - reason = "not explained" try: reason = str(dns.resolver.query(query, "TXT")[0]) except Exception: - pass + reason = "-" - yield ('diagnosis_mail_blacklisted_by', { - 'ip': ip, - 'blacklist_name': blacklist['name'], - 'blacklist_website': blacklist['website'], - 'reason': reason}) + yield dict(meta={"test": "mail_blacklist", "item": item, + "blacklist": blacklist["dns_server"]}, + data={'blacklist_name': blacklist['name'], + 'blacklist_website': blacklist['website'], + 'reason': reason}, + status="ERROR", + summary='diagnosis_mail_blacklist_listed_by') - def get_public_ips(self): - # Todo code a better way to access a data - ipv4 = Diagnoser.get_cached_report("ip", {"test": "ipv4"}) - if ipv4: + def check_queue(self): + """ + Check mail queue is not filled with hundreds of email pending + """ + + command = 'postqueue -p | grep -v "Mail queue is empty" | grep -c "^[A-Z0-9]"' + try: + output = check_output(command).strip() + pending_emails = int(output) + except (ValueError, CalledProcessError) as e: + yield dict(meta={"test": "mail_queue"}, + data={"error": e}, + status="ERROR", + summary="diagnosis_mail_cannot_get_queue") + else: + if pending_emails > 100: + yield dict(meta={"test": "mail_queue"}, + data={'nb_pending': pending_emails}, + status="WARNING", + summary="diagnosis_mail_queue_too_many_pending_emails") + else: + yield dict(meta={"test": "mail_queue"}, + data={'nb_pending': pending_emails}, + status="SUCCESS", + summary="diagnosis_mail_queue_ok") + + + def get_ips_checked(self): + outgoing_ipversions = [] + outgoing_ips = [] + ipv4 = Diagnoser.get_cached_report("ip", {"test": "ipv4"}) or {} + if ipv4.get("status") == "SUCCESS": + outgoing_ipversions.append(4) global_ipv4 = ipv4.get("data", {}).get("global", {}) if global_ipv4: - yield global_ipv4 - - ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) - if ipv6: + outgoing_ips.append(global_ipv4) + + ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) or {} + if ipv6.get("status") == "SUCCESS": + outgoing_ipversions.append(6) global_ipv6 = ipv6.get("data", {}).get("global", {}) if global_ipv6: - yield global_ipv6 - + outgoing_ips.append(global_ipv6) + return (outgoing_ipversions, outgoing_ips) def main(args, env, loggers): return MailDiagnoser(args, env, loggers).diagnose() diff --git a/locales/en.json b/locales/en.json index 978ceb831..1a17c484f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -185,18 +185,19 @@ "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", - "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", - "diagnosis_mail_blacklist_ok": "The public IPs of this instance are not listed on email blacklists.", - "diagnosis_mail_blacklist_nok": "Some of the public IPs of this instance are listed on email blacklists.", - "diagnosis_mail_blacklisted_by": "{ip} is listed on {blacklist_name}. Reason: {reason}. See {blacklist_website}", - "diagnosis_mail_service_working": "Postfix mail service answer correctly.", - "diagnosis_mail_service_not_working": "Postfix mail service don't answer to EHLO request.", + "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", + "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", + "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}.", + "diagnosis_mail_ehlo_wrong": "A mail server answer {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}.", + "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside. Error: {error}", "diagnosis_mail_reverse_dns_missing": "No reverse DNS defined for the ip {ip}.", "diagnosis_mail_rdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}.", "diagnosis_mail_rdns_equal_to_ehlo_domain": "Your reverse DNS is equal to your EHLO domain {ehlo_domain} on {ip}.", + "diagnosis_mail_blacklist_ok": "The public IPs of this instance are not listed on email blacklists.", + "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}. Reason: {reason}. See {blacklist_website}", "diagnosis_mail_queue_unavailable": "Can not consult number of pending emails in queue", "diagnosis_mail_queue_too_big": "The mail queue has {nb_pending} pending emails in the mail queue. It seems abnormal.", - "diagnosis_mail_queue_unavailable": "The mail queue has {nb_pending} pending emails in the mail queue.", + "diagnosis_mail_queue_ok": "The mail queue has {nb_pending} pending emails in the mail queue.", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From b1124b7080aae3c1750503b430cfc4c067184f7c Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 18 Apr 2020 19:06:45 +0200 Subject: [PATCH 65/91] [fix] Maildiagnoser typo --- data/hooks/diagnosis/24-mail.py | 22 +++++++++++----------- locales/en.json | 10 +++++----- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 1336e8c2b..4c36d7ca0 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -24,7 +24,7 @@ DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" class MailDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 600 + cache_duration = 0 dependencies = ["ip"] def run(self): @@ -42,10 +42,11 @@ class MailDiagnoser(Diagnoser): if type(value) == FunctionType and name.startswith("check_")] for check in checks: self.logger_debug("Running " + check) - for report in getattr(self, check): + reports = list(getattr(self, check)()) + for report in reports: yield report - else: - name = checks[6:] + if not reports: + name = check[6:] yield dict(meta={"test": "mail_" + name}, status="SUCCESS", summary="diagnosis_mail_" + name + "_ok") @@ -58,8 +59,7 @@ class MailDiagnoser(Diagnoser): """ for ipversion in self.ipversions: - cmd = '/bin/nc -{ipversion} -z -w2 yunohost.org 25'.format({ - 'ipversion': ipversion}) + cmd = '/bin/nc -{ipversion} -z -w2 yunohost.org 25'.format(ipversion=ipversion) if os.system(cmd) != 0: yield dict(meta={"test": "outgoing_port_25", "ipversion": ipversion}, data={}, @@ -80,7 +80,7 @@ class MailDiagnoser(Diagnoser): ipversion=ipversion) except Exception as e: yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, - data={"error": e}, + data={"error": str(e)}, status="WARNING", summary="diagnosis_mail_ehlo_could_not_diagnose") continue @@ -111,14 +111,14 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "mail_fcrdns", "ip": ip}, data={"ehlo_domain": self.ehlo_domain}, status="ERROR", - summary="diagnosis_mail_reverse_dns_missing") + summary="diagnosis_mail_fcrdns_dns_missing") continue if rdns_domain != self.ehlo_domain: yield dict(meta={"test": "mail_fcrdns", "ip": ip}, data={"ehlo_domain": self.ehlo_domain, "rdns_domain": rdns_domain}, status="ERROR", - summary="diagnosis_mail_rdns_different_from_ehlo_domain") + summary="diagnosis_mail_fcrdns_different_from_ehlo_domain") def check_blacklist(self): @@ -177,9 +177,9 @@ class MailDiagnoser(Diagnoser): pending_emails = int(output) except (ValueError, CalledProcessError) as e: yield dict(meta={"test": "mail_queue"}, - data={"error": e}, + data={"error": str(e)}, status="ERROR", - summary="diagnosis_mail_cannot_get_queue") + summary="diagnosis_mail_queue_unavailable") else: if pending_emails > 100: yield dict(meta={"test": "mail_queue"}, diff --git a/locales/en.json b/locales/en.json index 1a17c484f..327dba2a9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -184,15 +184,15 @@ "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least 256 MB of swap to avoid situations where the system runs out of memory.", "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", - "diagnosis_mail_ougoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", - "diagnosis_mail_ougoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", + "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", + "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}.", "diagnosis_mail_ehlo_wrong": "A mail server answer {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}.", "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside. Error: {error}", - "diagnosis_mail_reverse_dns_missing": "No reverse DNS defined for the ip {ip}.", - "diagnosis_mail_rdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}.", - "diagnosis_mail_rdns_equal_to_ehlo_domain": "Your reverse DNS is equal to your EHLO domain {ehlo_domain} on {ip}.", + "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}.", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}.", + "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured.", "diagnosis_mail_blacklist_ok": "The public IPs of this instance are not listed on email blacklists.", "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}. Reason: {reason}. See {blacklist_website}", "diagnosis_mail_queue_unavailable": "Can not consult number of pending emails in queue", From a30ed783da379f4085ec24309002aaa23b8b60e8 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sat, 18 Apr 2020 20:33:30 +0200 Subject: [PATCH 66/91] Improve message about error 500 --- src/yunohost/diagnosis.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index aba65a619..f7d2830b6 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -525,7 +525,7 @@ class Diagnoser(): socket.getaddrinfo = old_getaddrinfo if r.status_code not in [200, 400]: - raise Exception("Bad response from diagnosis server.\nURL: %s\nStatus code: %s\nMessage: %s" % (url, r.status_code, r.content)) + raise Exception("The remote diagnosis server failed miserably while trying to diagnose your server. This is most likely an error on Yunohost's infrastructure and not on your side. Please contact the YunoHost team an provide them with the following information.\nURL:
%s
\nStatus code:
%s
" % (url, r.status_code)) if r.status_code == 400: raise Exception("Diagnosis request was refused: %s" % r.content) From 0014fe29033c6eeb2e4238b7283ea342ff72fc34 Mon Sep 17 00:00:00 2001 From: ljf Date: Sat, 18 Apr 2020 20:40:18 +0200 Subject: [PATCH 67/91] [fix] Order of mail checks and mail queue --- data/hooks/diagnosis/24-mail.py | 26 ++++++++++++++++---------- locales/en.json | 30 ++++++++++++++++++------------ 2 files changed, 34 insertions(+), 22 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 4c36d7ca0..b122e876a 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -6,15 +6,12 @@ import socket import re from subprocess import CalledProcessError -from types import FunctionType from moulinette.utils.process import check_output -from moulinette.utils.network import download_text from moulinette.utils.filesystem import read_yaml from yunohost.diagnosis import Diagnoser from yunohost.domain import _get_maindomain, domain_list -from yunohost.utils.error import YunohostError DIAGNOSIS_SERVER = "diagnosis.yunohost.org" @@ -38,8 +35,8 @@ class MailDiagnoser(Diagnoser): # TODO Validate DKIM and dmarc ? # TODO check that the recent mail logs are not filled with thousand of email sending (unusual number of mail sent) # TODO check for unusual failed sending attempt being refused in the logs ? - checks = [name for name, value in MailDiagnoser.__dict__.items() - if type(value) == FunctionType and name.startswith("check_")] + checks = ["check_outgoing_port_25", "check_ehlo", "check_fcrdns", + "check_blacklist", "check_queue"] for check in checks: self.logger_debug("Running " + check) reports = list(getattr(self, check)()) @@ -64,7 +61,9 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "outgoing_port_25", "ipversion": ipversion}, data={}, status="ERROR", - summary="diagnosis_mail_ougoing_port_25_blocked") + summary="diagnosis_mail_ougoing_port_25_blocked", + details=["diagnosis_mail_ougoing_port_25_blocked_details", + "diagnosis_mail_outgoing_port_25_blocked_relay_vpn"]) def check_ehlo(self): @@ -82,7 +81,8 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, data={"error": str(e)}, status="WARNING", - summary="diagnosis_mail_ehlo_could_not_diagnose") + summary="diagnosis_mail_ehlo_could_not_diagnose", + details=["diagnosis_mail_ehlo_could_not_diagnose_details"]) continue if r["status"] == "error_smtp_unreachable": @@ -153,25 +153,30 @@ class MailDiagnoser(Diagnoser): continue # Try to get the reason + details = [] try: reason = str(dns.resolver.query(query, "TXT")[0]) + details.append("diagnosis_mail_blacklist_reason") except Exception: reason = "-" + details.append("diagnosis_mail_blacklist_website") + yield dict(meta={"test": "mail_blacklist", "item": item, "blacklist": blacklist["dns_server"]}, data={'blacklist_name': blacklist['name'], 'blacklist_website': blacklist['website'], 'reason': reason}, status="ERROR", - summary='diagnosis_mail_blacklist_listed_by') + summary='diagnosis_mail_blacklist_listed_by', + details=details) def check_queue(self): """ Check mail queue is not filled with hundreds of email pending """ - command = 'postqueue -p | grep -v "Mail queue is empty" | grep -c "^[A-Z0-9]"' + command = 'postqueue -p | grep -v "Mail queue is empty" | grep -c "^[A-Z0-9]" || true' try: output = check_output(command).strip() pending_emails = int(output) @@ -179,7 +184,8 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "mail_queue"}, data={"error": str(e)}, status="ERROR", - summary="diagnosis_mail_queue_unavailable") + summary="diagnosis_mail_queue_unavailable", + details="diagnosis_mail_queue_unavailable_details") else: if pending_emails > 100: yield dict(meta={"test": "mail_queue"}, diff --git a/locales/en.json b/locales/en.json index 327dba2a9..d2f4a925b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -184,20 +184,26 @@ "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least 256 MB of swap to avoid situations where the system runs out of memory.", "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", - "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is not blocked and email can be sent to other servers.", - "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}. You should try to unblock it in your internet service provider (or hosting provider) configuration panel. Meanwhile, the server won't be able to send emails to other servers.", + "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is open, emails can be sent", + "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be bloecked in IPv{ipversion}", + "diagnosis_mail_outgoing_port_25_blocked_details": "You should first try to unblock it in your internet service provider (or hosting provider) configuration panel or by sending a ticket to your hosting provider. Meanwhile, the server won't be able to send emails to other servers.", + "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Some providers won't let you unblock outgoing port 25 because they don't care about Net Neutrality.
- Some of them provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", - "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}.", - "diagnosis_mail_ehlo_wrong": "A mail server answer {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}.", - "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside. Error: {error}", - "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}.", - "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}.", - "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured.", - "diagnosis_mail_blacklist_ok": "The public IPs of this instance are not listed on email blacklists.", - "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}. Reason: {reason}. See {blacklist_website}", + "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}", + "diagnosis_mail_ehlo_wrong": "A mail server answers {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}", + "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside", + "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", + "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured", + "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}", + "diagnosis_mail_blacklist_ok": "IPs and domains used by this server to send mail are not on most used email blacklists", + "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}", + "diagnosis_mail_blacklist_reason": "The blacklist explains: {reason}", + "diagnosis_mail_blacklist_website": "After identifying why you are listed and fixed it, feel free to ask for delisting on {blacklist_website}", + "diagnosis_mail_queue_ok": "{nb_pending} pending emails in the mail queues", "diagnosis_mail_queue_unavailable": "Can not consult number of pending emails in queue", - "diagnosis_mail_queue_too_big": "The mail queue has {nb_pending} pending emails in the mail queue. It seems abnormal.", - "diagnosis_mail_queue_ok": "The mail queue has {nb_pending} pending emails in the mail queue.", + "diagnosis_mail_queue_unavailable_details": "Error: {error}", + "diagnosis_mail_queue_too_big": "Too many pending emails in mail queue ({nb_pending} emails)", "diagnosis_regenconf_allgood": "All configurations files are in line with the recommended configuration!", "diagnosis_regenconf_manually_modified": "Configuration file {file} appears to have been manually modified.", "diagnosis_regenconf_manually_modified_details": "This is probably OK if you know what you're doing! YunoHost will stop updating this file automatically... But beware that YunoHost upgrades could contain important recommended changes. If you want to, you can inspect the differences with yunohost tools regen-conf {category} --dry-run --with-diff and force the reset to the recommended configuration with yunohost tools regen-conf {category} --force", From 7818eb39464846bbfb1c65aea7d6326520b48c86 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 00:26:33 +0200 Subject: [PATCH 68/91] Better handling of failure to use the remote-diagnosis --- data/hooks/diagnosis/14-ports.py | 10 +++++++++- data/hooks/diagnosis/21-web.py | 11 ++++++++++- data/hooks/diagnosis/24-mail.py | 2 -- locales/en.json | 10 ++++++---- src/yunohost/diagnosis.py | 2 +- 5 files changed, 26 insertions(+), 9 deletions(-) diff --git a/data/hooks/diagnosis/14-ports.py b/data/hooks/diagnosis/14-ports.py index a4459d92f..bd68c60d6 100644 --- a/data/hooks/diagnosis/14-ports.py +++ b/data/hooks/diagnosis/14-ports.py @@ -47,8 +47,16 @@ class PortsDiagnoser(Diagnoser): ipversion=ipversion) results[ipversion] = r["ports"] except Exception as e: - raise YunohostError("diagnosis_ports_could_not_diagnose", error=e) + yield dict(meta={"reason": "remote_diagnosis_failed", "ipversion": ipversion}, + data={"error": str(e)}, + status="WARNING", + summary="diagnosis_ports_could_not_diagnose", + details=["diagnosis_ports_could_not_diagnose_details"]) + continue + ipversions = results.keys() + if not ipversions: + return for port, service in sorted(ports.items()): port = str(port) diff --git a/data/hooks/diagnosis/21-web.py b/data/hooks/diagnosis/21-web.py index 09f5b2b73..c1f6d912a 100644 --- a/data/hooks/diagnosis/21-web.py +++ b/data/hooks/diagnosis/21-web.py @@ -96,7 +96,16 @@ class WebDiagnoser(Diagnoser): ipversion=ipversion) results[ipversion] = r["http"] except Exception as e: - raise YunohostError("diagnosis_http_could_not_diagnose", error=e) + yield dict(meta={"reason": "remote_diagnosis_failed", "ipversion": ipversion}, + data={"error": str(e)}, + status="WARNING", + summary="diagnosis_http_could_not_diagnose", + details=["diagnosis_http_could_not_diagnose_details"]) + continue + + ipversions = results.keys() + if not ipversions: + return for domain in domains: diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index b122e876a..0ce1f3f25 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -13,8 +13,6 @@ from moulinette.utils.filesystem import read_yaml from yunohost.diagnosis import Diagnoser from yunohost.domain import _get_maindomain, domain_list -DIAGNOSIS_SERVER = "diagnosis.yunohost.org" - DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" diff --git a/locales/en.json b/locales/en.json index d2f4a925b..4a0aefca8 100644 --- a/locales/en.json +++ b/locales/en.json @@ -191,8 +191,8 @@ "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}", "diagnosis_mail_ehlo_wrong": "A mail server answers {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}", - "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside", - "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", + "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside in IPv{ipversion}.", + "diagnosis_mail_ehlo_could_not_diagnose_details": "{error}", "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured", "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}", "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}", @@ -220,7 +220,8 @@ "diagnosis_description_mail": "Email", "diagnosis_description_regenconf": "System configurations", "diagnosis_description_security": "Security checks", - "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside. Error: {error}", + "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside in IPv{ipversion}.", + "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", "diagnosis_ports_partially_unreachable": "Port {port} is not reachable from outside in IPv{failed}.", "diagnosis_ports_ok": "Port {port} is reachable from outside.", @@ -228,7 +229,8 @@ "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", "diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.", "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", - "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", + "diagnosis_http_could_not_diagnose": "Could not diagnose if domains are reachable from outside in IPv{ipversion}.", + "diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index f7d2830b6..bd52f57f8 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -525,7 +525,7 @@ class Diagnoser(): socket.getaddrinfo = old_getaddrinfo if r.status_code not in [200, 400]: - raise Exception("The remote diagnosis server failed miserably while trying to diagnose your server. This is most likely an error on Yunohost's infrastructure and not on your side. Please contact the YunoHost team an provide them with the following information.\nURL:
%s
\nStatus code:
%s
" % (url, r.status_code)) + raise Exception("The remote diagnosis server failed miserably while trying to diagnose your server. This is most likely an error on Yunohost's infrastructure and not on your side. Please contact the YunoHost team an provide them with the following information.
URL: %s
Status code: %s" % (url, r.status_code)) if r.status_code == 400: raise Exception("Diagnosis request was refused: %s" % r.content) From 3c174389b64581dd91581c424f7299f637e1f00c Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 00:48:54 +0200 Subject: [PATCH 69/91] [enh] Add some details --- data/hooks/diagnosis/24-mail.py | 13 ++++++++----- locales/ca.json | 2 +- locales/en.json | 19 +++++++++++-------- locales/eo.json | 2 +- locales/es.json | 2 +- locales/fr.json | 2 +- tests/test_i18n_keys.py | 7 +++++++ 7 files changed, 30 insertions(+), 17 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 0ce1f3f25..27903c9e9 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -59,8 +59,8 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "outgoing_port_25", "ipversion": ipversion}, data={}, status="ERROR", - summary="diagnosis_mail_ougoing_port_25_blocked", - details=["diagnosis_mail_ougoing_port_25_blocked_details", + summary="diagnosis_mail_outgoing_port_25_blocked", + details=["diagnosis_mail_outgoing_port_25_blocked_details", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn"]) @@ -76,18 +76,21 @@ class MailDiagnoser(Diagnoser): data={}, ipversion=ipversion) except Exception as e: - yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, + yield dict(meta={"test": "mail_ehlo", "reason": "remote_server_failed", + "ipversion": ipversion}, data={"error": str(e)}, status="WARNING", summary="diagnosis_mail_ehlo_could_not_diagnose", details=["diagnosis_mail_ehlo_could_not_diagnose_details"]) continue - if r["status"] == "error_smtp_unreachable": + if r["status"] != "ok": + summary = r["status"].replace("error_smtp_", "diagnosis_mail_ehlo_") yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, data={}, status="ERROR", - summary="diagnosis_mail_ehlo_unavailable") + summary=summary, + details=[summary + "_details"]) elif r["helo"] != self.ehlo_domain: yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, data={"wrong_ehlo": r["helo"], "right_ehlo": self.ehlo_domain}, diff --git a/locales/ca.json b/locales/ca.json index 0ea0d91f6..c20b94d6e 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -571,7 +571,7 @@ "apps_catalog_obsolete_cache": "La memòria cau del catàleg d'aplicacions és buida o obsoleta.", "apps_catalog_update_success": "S'ha actualitzat el catàleg d'aplicacions!", "diagnosis_mail_ougoing_port_25_ok": "El port de sortida 25 no està bloquejat i els correus es poden enviar a altres servidors.", - "diagnosis_mail_ougoing_port_25_blocked": "Sembla que el port de sortida 25 està bloquejat. Hauríeu d'intentar desbloquejar-lo al panell de configuració del proveïdor d'accés a internet (o allotjador). Mentrestant, el servidor no podrà enviar correus a altres servidors.", + "diagnosis_mail_outgoing_port_25_blocked": "Sembla que el port de sortida 25 està bloquejat. Hauríeu d'intentar desbloquejar-lo al panell de configuració del proveïdor d'accés a internet (o allotjador). Mentrestant, el servidor no podrà enviar correus a altres servidors.", "diagnosis_description_mail": "Correu electrònic", "migration_description_0013_futureproof_apps_catalog_system": "Migrar al nou sistema de catàleg d'aplicacions resistent al pas del temps", "app_upgrade_script_failed": "Hi ha hagut un error en el script d'actualització de l'aplicació", diff --git a/locales/en.json b/locales/en.json index 4a0aefca8..63cef236b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -189,12 +189,17 @@ "diagnosis_mail_outgoing_port_25_blocked_details": "You should first try to unblock it in your internet service provider (or hosting provider) configuration panel or by sending a ticket to your hosting provider. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Some providers won't let you unblock outgoing port 25 because they don't care about Net Neutrality.
- Some of them provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", - "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}", - "diagnosis_mail_ehlo_wrong": "A mail server answers {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}", - "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside in IPv{ipversion}.", - "diagnosis_mail_ehlo_could_not_diagnose_details": "{error}", + "diagnosis_mail_ehlo_unreachable": "SMTP server unreachable on IPv{ipversion}", + "diagnosis_mail_ehlo_unreachable_details": "Could not open a connection on port 25 through IPv{ipversion}, probably because of a firewall, port forwarding issue or postfix service down", + "diagnosis_mail_ehlo_bad_answer": "A non-SMTP service answered on port 25 on IPv{ipversion}", + "diagnosis_mail_ehlo_bad_answer_details": "It could be due to an other machine answering instead of your server.", + "diagnosis_mail_ehlo_wrong": "An other SMTP server answers on IPv{ipversion}", + "diagnosis_mail_ehlo_wrong_details": "The remote diagnoser return a wrong EHLO answer from your IPv{ipversion}.
Received: {wrong_ehlo}
Expected: {right_ehlo}
You probably have a port forwarding issue or a reverse proxy server unconfigured for mail.", + "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside", + "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured", "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}", + "diagnosis_mail_fcrdns_dns_missing_details": "You can configure it on ", "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}", "diagnosis_mail_blacklist_ok": "IPs and domains used by this server to send mail are not on most used email blacklists", "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}", @@ -220,8 +225,7 @@ "diagnosis_description_mail": "Email", "diagnosis_description_regenconf": "System configurations", "diagnosis_description_security": "Security checks", - "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside in IPv{ipversion}.", - "diagnosis_ports_could_not_diagnose_details": "Error: {error}", + "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside. Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", "diagnosis_ports_partially_unreachable": "Port {port} is not reachable from outside in IPv{failed}.", "diagnosis_ports_ok": "Port {port} is reachable from outside.", @@ -229,8 +233,7 @@ "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", "diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.", "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", - "diagnosis_http_could_not_diagnose": "Could not diagnose if domains are reachable from outside in IPv{ipversion}.", - "diagnosis_http_could_not_diagnose_details": "Error: {error}", + "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", diff --git a/locales/eo.json b/locales/eo.json index 87e062ea2..9c1aed008 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -515,7 +515,7 @@ "app_upgrade_script_failed": "Eraro okazis en la skripto pri ĝisdatiga programo", "diagnosis_diskusage_verylow": "Stokado {mountpoint} (sur aparato {device)) restas nur {free} ({free_percent}%) spaco. Vi vere konsideru purigi iom da spaco.", "diagnosis_ram_verylow": "La sistemo nur restas {available} ({available_percent}%) RAM! (el {total})", - "diagnosis_mail_ougoing_port_25_blocked": "Eliranta haveno 25 ŝajnas esti blokita. Vi devas provi malŝlosi ĝin en via agorda panelo de provizanto (aŭ gastiganto). Dume la servilo ne povos sendi retpoŝtojn al aliaj serviloj.", + "diagnosis_mail_outgoing_port_25_blocked": "Eliranta haveno 25 ŝajnas esti blokita. Vi devas provi malŝlosi ĝin en via agorda panelo de provizanto (aŭ gastiganto). Dume la servilo ne povos sendi retpoŝtojn al aliaj serviloj.", "diagnosis_http_bad_status_code": "Ne povis atingi vian servilon kiel atendite, ĝi redonis malbonan statuskodon. Povas esti, ke alia maŝino respondis anstataŭ via servilo. Vi devus kontroli, ke vi ĝuste redonas la havenon 80, ke via nginx-agordo ĝisdatigas kaj ke reverso-prokuro ne interbatalas.", "main_domain_changed": "La ĉefa domajno estis ŝanĝita", "yunohost_postinstall_end_tip": "La post-instalado finiĝis! Por fini vian agordon, bonvolu konsideri:\n - aldonado de unua uzanto tra la sekcio 'Uzantoj' de la retadreso (aŭ 'yunohost user create ' en komandlinio);\n - diagnozi problemojn atendantajn solvi por ke via servilo funkciu kiel eble plej glate tra la sekcio 'Diagnosis' de la retadministrado (aŭ 'yunohost diagnosis run' en komandlinio);\n - legante la partojn 'Finigi vian agordon' kaj 'Ekkoni Yunohost' en la administra dokumentado: https://yunohost.org/admindoc.", diff --git a/locales/es.json b/locales/es.json index 6a55378da..de9eb91c6 100644 --- a/locales/es.json +++ b/locales/es.json @@ -554,7 +554,7 @@ "diagnosis_swap_none": "El sistema no tiene mas espacio de intercambio. Considera agregar por lo menos 256 MB de espacio de intercambio para evitar que el sistema se quede sin memoria.", "diagnosis_swap_notsomuch": "Al sistema le queda solamente {total} de espacio de intercambio. Considera agregar al menos 256 MB para evitar que el sistema se quede sin memoria.", "diagnosis_mail_ougoing_port_25_ok": "El puerto de salida 25 no esta bloqueado y los correos electrónicos pueden ser enviados a otros servidores.", - "diagnosis_mail_ougoing_port_25_blocked": "El puerto de salida 25 parece estar bloqueado. Intenta desbloquearlo con el panel de configuración de tu proveedor de servicios de Internet (o proveedor de halbergue). Mientras tanto, el servidor no podrá enviar correos electrónicos a otros servidores.", + "diagnosis_mail_outgoing_port_25_blocked": "El puerto de salida 25 parece estar bloqueado. Intenta desbloquearlo con el panel de configuración de tu proveedor de servicios de Internet (o proveedor de halbergue). Mientras tanto, el servidor no podrá enviar correos electrónicos a otros servidores.", "diagnosis_regenconf_allgood": "Todos los archivos de configuración están en linea con la configuración recomendada!", "diagnosis_regenconf_manually_modified": "El archivo de configuración {file} fue modificado manualmente.", "diagnosis_regenconf_manually_modified_details": "Esto este probablemente BIEN siempre y cuando sepas lo que estas haciendo ;) !", diff --git a/locales/fr.json b/locales/fr.json index f029a1d13..faf2837a3 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -551,7 +551,7 @@ "diagnosis_security_all_good": "Aucune vulnérabilité de sécurité critique n'a été trouvée.", "apps_catalog_init_success": "Système de catalogue d'applications initialisé !", "apps_catalog_failed_to_download": "Impossible de télécharger le catalogue des applications {apps_catalog}:{error}", - "diagnosis_mail_ougoing_port_25_blocked": "Le port sortant 25 semble être bloqué. Vous devriez essayer de le débloquer dans le panneau de configuration de votre fournisseur de services Internet (ou hébergeur). En attendant, le serveur ne pourra pas envoyer de courrier électronique à d'autres serveurs.", + "diagnosis_mail_outgoing_port_25_blocked": "Le port sortant 25 semble être bloqué. Vous devriez essayer de le débloquer dans le panneau de configuration de votre fournisseur de services Internet (ou hébergeur). En attendant, le serveur ne pourra pas envoyer de courrier électronique à d'autres serveurs.", "domain_cannot_remove_main_add_new_one": "Vous ne pouvez pas supprimer '{domain:s}' car il s'agit du domaine principal et de votre seul domaine. Vous devez d'abord ajouter un autre domaine à l'aide de 'yunohost domain add ', puis définir comme domaine principal à l'aide de ' yunohost domain main-domain -n ' et vous pouvez ensuite supprimer le domaine '{domain:s}' à l'aide de 'yunohost domain remove {domain:s}'.'", "diagnosis_security_vulnerable_to_meltdown_details": "Pour résoudre ce problème, vous devez mettre à niveau votre système et redémarrer pour charger le nouveau noyau Linux (ou contacter votre fournisseur de serveur si cela ne fonctionne pas). Voir https://meltdownattack.com/ pour plus d'informations.", "diagnosis_description_basesystem": "Système de base", diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 0d5af33f6..20e9dd8a0 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -122,6 +122,13 @@ def find_expected_string_keys(): yield "password_listed" for i in [1, 2, 3, 4]: yield "password_too_simple_%s" % i + + checks = ["outgoing_port_25_ok", "ehlo_ok", "fcrdns_ok", + "blacklist_ok", "queue_ok", "ehlo_bad_answer", + "ehlo_unreachable", "ehlo_bad_answer_details", + "ehlo_unreachable_details", ] + for check in checks: + yield "diagnosis_mail_%" ############################################################################### # Load en locale json keys # From 55957d77b09386c48362590702a306029983fe9d Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 00:52:28 +0200 Subject: [PATCH 70/91] [fix] Key queue_to_big --- data/hooks/diagnosis/24-mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 27903c9e9..608bfd931 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -192,7 +192,7 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "mail_queue"}, data={'nb_pending': pending_emails}, status="WARNING", - summary="diagnosis_mail_queue_too_many_pending_emails") + summary="diagnosis_mail_queue_too_big") else: yield dict(meta={"test": "mail_queue"}, data={'nb_pending': pending_emails}, From dae8adff4b21aaa74657ae317c9caf2b717ae42f Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 01:02:30 +0200 Subject: [PATCH 71/91] [fix] rebase issue --- locales/en.json | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 63cef236b..3be85fd35 100644 --- a/locales/en.json +++ b/locales/en.json @@ -225,7 +225,8 @@ "diagnosis_description_mail": "Email", "diagnosis_description_regenconf": "System configurations", "diagnosis_description_security": "Security checks", - "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside. Error: {error}", + "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside.", + "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", "diagnosis_ports_partially_unreachable": "Port {port} is not reachable from outside in IPv{failed}.", "diagnosis_ports_ok": "Port {port} is reachable from outside.", @@ -233,7 +234,8 @@ "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", "diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.", "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", - "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside. Error: {error}", + "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside.", + "diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", From 0ac1cfb31aea189c44671f3b889057c2e6c4c1cc Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 01:04:02 +0200 Subject: [PATCH 72/91] [fix] rebase issue --- locales/en.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index 3be85fd35..92067229f 100644 --- a/locales/en.json +++ b/locales/en.json @@ -225,7 +225,7 @@ "diagnosis_description_mail": "Email", "diagnosis_description_regenconf": "System configurations", "diagnosis_description_security": "Security checks", - "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside.", + "diagnosis_ports_could_not_diagnose": "Could not diagnose if ports are reachable from outside in IPv{ipversion}.", "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_unreachable": "Port {port} is not reachable from outside.", "diagnosis_ports_partially_unreachable": "Port {port} is not reachable from outside in IPv{failed}.", @@ -234,7 +234,7 @@ "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", "diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.", "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", - "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside.", + "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside in IPv{ipversion}.", "diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", From a7a0f93102b617f7a344498a496c3fbc5d84b09a Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 01:05:36 +0200 Subject: [PATCH 73/91] [fix] rebase issue --- locales/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/en.json b/locales/en.json index 92067229f..8272fc86c 100644 --- a/locales/en.json +++ b/locales/en.json @@ -234,7 +234,7 @@ "diagnosis_ports_forwarding_tip": "To fix this issue, you most probably need to configure port forwarding on your internet router as described in https://yunohost.org/isp_box_config", "diagnosis_http_hairpinning_issue": "Your local network does not seem to have hairpinning enabled.", "diagnosis_http_hairpinning_issue_details": "This is probably because of your ISP box / router. As a result, people from outside your local network will be able to access your server as expected, but not people from inside the local network (like you, probably?). You may be able to improve the situation by having a look at https://yunohost.org/dns_local_network", - "diagnosis_http_could_not_diagnose": "Could not diagnose if domain is reachable from outside in IPv{ipversion}.", + "diagnosis_http_could_not_diagnose": "Could not diagnose if domains are reachable from outside in IPv{ipversion}.", "diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", From 91a07bdf08ee4eecdcb1734493fed31fccb7ecd3 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 01:07:06 +0200 Subject: [PATCH 74/91] [fix] tests i18n key --- tests/test_i18n_keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 20e9dd8a0..c845a2e3e 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -128,7 +128,7 @@ def find_expected_string_keys(): "ehlo_unreachable", "ehlo_bad_answer_details", "ehlo_unreachable_details", ] for check in checks: - yield "diagnosis_mail_%" + yield "diagnosis_mail_%" % check ############################################################################### # Load en locale json keys # From 9d0074d71bb53f9a0ac6a6d28acf1a74aef7b521 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 01:15:21 +0200 Subject: [PATCH 75/91] [fix] tests i18n key --- tests/test_i18n_keys.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index c845a2e3e..7546f51aa 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -128,7 +128,7 @@ def find_expected_string_keys(): "ehlo_unreachable", "ehlo_bad_answer_details", "ehlo_unreachable_details", ] for check in checks: - yield "diagnosis_mail_%" % check + yield "diagnosis_mail_%s" % check ############################################################################### # Load en locale json keys # From 8aced5b4ce2e7b55da7681d602085eec3d78a861 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 01:30:22 +0200 Subject: [PATCH 76/91] Let's cache mail diagnosis with a duration similar to other checks... --- data/hooks/diagnosis/24-mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 0ce1f3f25..c2f898e65 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -19,7 +19,7 @@ DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" class MailDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 0 + cache_duration = 600 dependencies = ["ip"] def run(self): From 3cb47a226f948af74d8625b7e9197cf5bd9bf67c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 01:43:27 +0200 Subject: [PATCH 77/91] More flexible warning about swap size... Move it to 512 MiB 'cause 256 MiB really aint much I think --- data/hooks/diagnosis/50-systemresources.py | 4 ++-- locales/ca.json | 4 ++-- locales/en.json | 4 ++-- locales/eo.json | 4 ++-- locales/es.json | 4 ++-- locales/fr.json | 4 ++-- locales/oc.json | 4 ++-- 7 files changed, 14 insertions(+), 14 deletions(-) diff --git a/data/hooks/diagnosis/50-systemresources.py b/data/hooks/diagnosis/50-systemresources.py index ab9ead7bb..417b88ae7 100644 --- a/data/hooks/diagnosis/50-systemresources.py +++ b/data/hooks/diagnosis/50-systemresources.py @@ -43,11 +43,11 @@ class SystemResourcesDiagnoser(Diagnoser): swap = psutil.swap_memory() item = dict(meta={"test": "swap"}, - data={"total": human_size(swap.total)}) + data={"total": human_size(swap.total), "recommended": "512 MiB"}) if swap.total <= 1 * MB: item["status"] = "ERROR" item["summary"] = "diagnosis_swap_none" - elif swap.total <= 256 * MB: + elif swap.total <= 512 * MB: item["status"] = "WARNING" item["summary"] = "diagnosis_swap_notsomuch" else: diff --git a/locales/ca.json b/locales/ca.json index 0ea0d91f6..07e2c6f27 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -503,7 +503,7 @@ "app_remove_after_failed_install": "Eliminant l'aplicació després que hagi fallat la instal·lació…", "diagnosis_basesystem_ynh_main_version": "El servidor funciona amb YunoHost {main_version} ({repo})", "diagnosis_ram_low": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}. Aneu amb compte.", - "diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de 256 MB de swap per evitar situacions en les que el sistema es queda sense memòria.", + "diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de {recommended} de swap per evitar situacions en les que el sistema es queda sense memòria.", "diagnosis_regenconf_manually_modified": "El fitxer de configuració {file} ha estat modificat manualment.", "diagnosis_security_vulnerable_to_meltdown_details": "Per arreglar-ho, hauríeu d'actualitzar i reiniciar el sistema per tal de carregar el nou nucli de linux (o contactar amb el proveïdor del servidor si no funciona). Vegeu https://meltdownattack.com/ per a més informació.", "diagnosis_http_could_not_diagnose": "No s'ha pogut diagnosticar si el domini és accessible des de l'exterior. Error: {error}", @@ -543,7 +543,7 @@ "diagnosis_diskusage_ok": "El lloc d'emmagatzematge {mountpoint} (en l'aparell {device}) encara té {free} ({free_percent}%) lliures!", "diagnosis_ram_verylow": "El sistema només té {available} ({available_percent}%) de memòria RAM disponibles! (d'un total de {total})", "diagnosis_ram_ok": "El sistema encara té {available} ({available_percent}%) de memòria RAM disponibles d'un total de {total}.", - "diagnosis_swap_notsomuch": "El sistema només té {total} de swap. Hauríeu de considerar tenir un mínim de 256 MB per evitar situacions en les que el sistema es queda sense memòria.", + "diagnosis_swap_notsomuch": "El sistema només té {total} de swap. Hauríeu de considerar tenir un mínim de {recommended} per evitar situacions en les que el sistema es queda sense memòria.", "diagnosis_swap_ok": "El sistema té {total} de swap!", "diagnosis_regenconf_allgood": "Tots els fitxers de configuració estan en acord amb la configuració recomanada!", "diagnosis_regenconf_manually_modified_details": "No hauria de ser cap problema sempre i quan sapigueu el que esteu fent ;) !", diff --git a/locales/en.json b/locales/en.json index 4a0aefca8..a5048b8c9 100644 --- a/locales/en.json +++ b/locales/en.json @@ -181,8 +181,8 @@ "diagnosis_ram_verylow": "The system has only {available} ({available_percent}%) RAM available! (out of {total})", "diagnosis_ram_low": "The system has {available} ({available_percent}%) RAM available (out of {total}). Be careful.", "diagnosis_ram_ok": "The system still has {available} ({available_percent}%) RAM available out of {total}.", - "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least 256 MB of swap to avoid situations where the system runs out of memory.", - "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", + "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least {recommended} of swap to avoid situations where the system runs out of memory.", + "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least {recommended} to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is open, emails can be sent", "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be bloecked in IPv{ipversion}", diff --git a/locales/eo.json b/locales/eo.json index 87e062ea2..36396d6f1 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -531,8 +531,8 @@ "diagnosis_dns_good_conf": "Bona DNS-agordo por domajno {domain} (kategorio {category})", "diagnosis_dns_bad_conf": "Malbona / mankas DNS-agordo por domajno {domain} (kategorio {category})", "diagnosis_ram_ok": "La sistemo ankoraŭ havas {available} ({available_percent}%) RAM forlasita de {total}.", - "diagnosis_swap_none": "La sistemo tute ne havas interŝanĝon. Vi devus pripensi aldoni almenaŭ 256 MB da interŝanĝo por eviti situaciojn en kiuj la sistemo restas sen memoro.", - "diagnosis_swap_notsomuch": "La sistemo havas nur {total}-interŝanĝon. Vi konsideru havi almenaŭ 256 MB por eviti situaciojn en kiuj la sistemo restas sen memoro.", + "diagnosis_swap_none": "La sistemo tute ne havas interŝanĝon. Vi devus pripensi aldoni almenaŭ {recommended} da interŝanĝo por eviti situaciojn en kiuj la sistemo restas sen memoro.", + "diagnosis_swap_notsomuch": "La sistemo havas nur {total}-interŝanĝon. Vi konsideru havi almenaŭ {recommended} por eviti situaciojn en kiuj la sistemo restas sen memoro.", "diagnosis_regenconf_manually_modified_details": "Ĉi tio probable estas bona tiel longe kiel vi scias kion vi faras;)!", "diagnosis_regenconf_manually_modified_debian": "Agordodosiero {file} estis modifita permane kompare kun la defaŭlta Debian.", "diagnosis_regenconf_manually_modified_debian_details": "Ĉi tio probable estas bona, sed devas observi ĝin...", diff --git a/locales/es.json b/locales/es.json index 6a55378da..c21585e7b 100644 --- a/locales/es.json +++ b/locales/es.json @@ -551,8 +551,8 @@ "diagnosis_ram_verylow": "Al sistema le queda solamente {available} ({available_percent}%) de RAM! (De un total de {total})", "diagnosis_ram_low": "Al sistema le queda {available} ({available_percent}%) de RAM de un total de {total}. Cuidado.", "diagnosis_ram_ok": "El sistema aun tiene {available} ({available_percent}%) de RAM de un total de {total}.", - "diagnosis_swap_none": "El sistema no tiene mas espacio de intercambio. Considera agregar por lo menos 256 MB de espacio de intercambio para evitar que el sistema se quede sin memoria.", - "diagnosis_swap_notsomuch": "Al sistema le queda solamente {total} de espacio de intercambio. Considera agregar al menos 256 MB para evitar que el sistema se quede sin memoria.", + "diagnosis_swap_none": "El sistema no tiene mas espacio de intercambio. Considera agregar por lo menos {recommended} de espacio de intercambio para evitar que el sistema se quede sin memoria.", + "diagnosis_swap_notsomuch": "Al sistema le queda solamente {total} de espacio de intercambio. Considera agregar al menos {recommended} para evitar que el sistema se quede sin memoria.", "diagnosis_mail_ougoing_port_25_ok": "El puerto de salida 25 no esta bloqueado y los correos electrónicos pueden ser enviados a otros servidores.", "diagnosis_mail_ougoing_port_25_blocked": "El puerto de salida 25 parece estar bloqueado. Intenta desbloquearlo con el panel de configuración de tu proveedor de servicios de Internet (o proveedor de halbergue). Mientras tanto, el servidor no podrá enviar correos electrónicos a otros servidores.", "diagnosis_regenconf_allgood": "Todos los archivos de configuración están en linea con la configuración recomendada!", diff --git a/locales/fr.json b/locales/fr.json index f029a1d13..7e77cdc7a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -541,8 +541,8 @@ "diagnosis_diskusage_low": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free} ({free_percent}%). Faites attention.", "diagnosis_ram_verylow": "Le système ne dispose plus que de {available} ({available_percent}%)! (sur {total})", "diagnosis_ram_low": "Le système n'a plus de {available} ({available_percent}%) RAM sur {total}. Faites attention.", - "diagnosis_swap_none": "Le système n'a aucun échange. Vous devez envisager d’ajouter au moins 256 Mo de swap pour éviter les situations où le système manque de mémoire.", - "diagnosis_swap_notsomuch": "Le système ne dispose que de {total} de swap. Vous devez envisager d'avoir au moins 256 Mo pour éviter les situations où le système manque de mémoire.", + "diagnosis_swap_none": "Le système n'a aucun échange. Vous devez envisager d’ajouter au moins {recommended} de swap pour éviter les situations où le système manque de mémoire.", + "diagnosis_swap_notsomuch": "Le système ne dispose que de {total} de swap. Vous devez envisager d'avoir au moins {recommended} pour éviter les situations où le système manque de mémoire.", "diagnosis_swap_ok": "Le système dispose de {total} de swap !", "diagnosis_regenconf_manually_modified": "Le fichier de configuration {file} a été modifié manuellement.", "diagnosis_regenconf_manually_modified_debian": "Le fichier de configuration {file} a été modifié manuellement par rapport à celui par défaut de Debian.", diff --git a/locales/oc.json b/locales/oc.json index eebfaac64..97978bb18 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -568,6 +568,6 @@ "diagnosis_diskusage_verylow": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a solament {free} ({free_percent}%). Deuriatz considerar de liberar un pauc d’espaci.", "global_settings_setting_pop3_enabled": "Activar lo protocòl POP3 pel servidor de corrièr", "diagnosis_diskusage_ok": "Lo lòc d’emmagazinatge {mountpoint} (sul periferic {device}) a encara {free} ({free_percent}%) de liure !", - "diagnosis_swap_none": "Lo sistèma a pas cap de memòria d’escambi. Auriatz de considerar d’ajustar almens 256 Mo d’escambi per evitar las situacions ont lo sistèma manca de memòria.", - "diagnosis_swap_notsomuch": "Lo sistèma a solament {total} de memòria d’escambi. Auriatz de considerar d’ajustar almens 256 Mo d’escambi per evitar las situacions ont lo sistèma manca de memòria." + "diagnosis_swap_none": "Lo sistèma a pas cap de memòria d’escambi. Auriatz de considerar d’ajustar almens {recommended} d’escambi per evitar las situacions ont lo sistèma manca de memòria.", + "diagnosis_swap_notsomuch": "Lo sistèma a solament {total} de memòria d’escambi. Auriatz de considerar d’ajustar almens {recommended} d’escambi per evitar las situacions ont lo sistèma manca de memòria." } From 97ab8c91f86c43dcec728253a2e3bd6a8304208c Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 02:03:35 +0200 Subject: [PATCH 78/91] Fix the fix for stupid 'search' stuff in resolvconf ... + let's in fact ignore it if it does exists in /etc/resolv.conf >.> --- data/hooks/conf_regen/43-dnsmasq | 6 +++--- data/hooks/diagnosis/10-ip.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/data/hooks/conf_regen/43-dnsmasq b/data/hooks/conf_regen/43-dnsmasq index d6ab8648c..59a1f8a06 100755 --- a/data/hooks/conf_regen/43-dnsmasq +++ b/data/hooks/conf_regen/43-dnsmasq @@ -59,9 +59,9 @@ do_post_regen() { sed -E "s/^(domain|search)/#\1/g" -i /run/resolvconf/interface/*.dhclient fi - grep -q '^supersede domain-name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo '^supersede domain-name "";' >> /etc/dhcp/dhclient.conf - grep -q '^supersede domain-search "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo '^supersede domain-search "";' >> /etc/dhcp/dhclient.conf - grep -q '^supersede name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo '^supersede name "";' >> /etc/dhcp/dhclient.conf + grep -q '^supersede domain-name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede domain-name "";' >> /etc/dhcp/dhclient.conf + grep -q '^supersede domain-search "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede domain-search "";' >> /etc/dhcp/dhclient.conf + grep -q '^supersede name "";' /etc/dhcp/dhclient.conf 2>/dev/null || echo 'supersede name "";' >> /etc/dhcp/dhclient.conf systemctl restart resolvconf fi diff --git a/data/hooks/diagnosis/10-ip.py b/data/hooks/diagnosis/10-ip.py index 6571ca556..c0d35278c 100644 --- a/data/hooks/diagnosis/10-ip.py +++ b/data/hooks/diagnosis/10-ip.py @@ -134,7 +134,7 @@ class IPDiagnoser(Diagnoser): def good_resolvconf(self): content = read_file("/etc/resolv.conf").strip().split("\n") # Ignore comments and empty lines - content = [l.strip() for l in content if l.strip() and not l.strip().startswith("#")] + content = [l.strip() for l in content if l.strip() and not l.strip().startswith("#") and not l.strip().startswith("search")] # We should only find a "nameserver 127.0.0.1" return len(content) == 1 and content[0].split() == ["nameserver", "127.0.0.1"] From 4686673bb52c6181cbed60105917cb32d9c43a3d Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 02:30:23 +0200 Subject: [PATCH 79/91] [enh] Be able to disable ipv6 for smtp --- data/hooks/conf_regen/19-postfix | 3 ++- data/hooks/diagnosis/24-mail.py | 31 ++++++++++++++++++++++--------- locales/en.json | 9 ++++++--- src/yunohost/settings.py | 1 + 4 files changed, 31 insertions(+), 13 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 0f09f0299..172438f37 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -35,7 +35,8 @@ do_pre_regen() { > "${default_dir}/postsrsd" # adapt it for IPv4-only hosts - if [ ! -f /proc/net/if_inet6 ]; then + ipv6="$(yunohost settings get 'smtp.ipv6')" + if [ "$ipv6" == "False" ] || [ ! -f /proc/net/if_inet6 ]; then sed -i \ 's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \ "${postfix_dir}/main.cf" diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 608bfd931..022b24114 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -12,6 +12,7 @@ from moulinette.utils.filesystem import read_yaml from yunohost.diagnosis import Diagnoser from yunohost.domain import _get_maindomain, domain_list +from yunohost.settings import settings_get DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" @@ -95,7 +96,8 @@ class MailDiagnoser(Diagnoser): yield dict(meta={"test": "mail_ehlo", "ipversion": ipversion}, data={"wrong_ehlo": r["helo"], "right_ehlo": self.ehlo_domain}, status="ERROR", - summary="diagnosis_mail_ehlo_wrong") + summary="diagnosis_mail_ehlo_wrong", + details=["diagnosis_mail_ehlo_wrong_details"]) def check_fcrdns(self): @@ -106,20 +108,30 @@ class MailDiagnoser(Diagnoser): """ for ip in self.ips: + if ":" in ip: + details = ["diagnosis_mail_fcrdns_nok_details", + "diagnosis_mail_fcrdns_nok_alternatives_6"] + else: + details = ["diagnosis_mail_fcrdns_nok_details", + "diagnosis_mail_fcrdns_nok_alternatives_4"] + try: rdns_domain, _, _ = socket.gethostbyaddr(ip) except socket.herror: yield dict(meta={"test": "mail_fcrdns", "ip": ip}, data={"ehlo_domain": self.ehlo_domain}, status="ERROR", - summary="diagnosis_mail_fcrdns_dns_missing") + summary="diagnosis_mail_fcrdns_dns_missing", + details=details) continue if rdns_domain != self.ehlo_domain: + details = ["diagnosis_mail_fcrdns_different_from_ehlo_domain_details"] + details yield dict(meta={"test": "mail_fcrdns", "ip": ip}, data={"ehlo_domain": self.ehlo_domain, "rdns_domain": rdns_domain}, status="ERROR", - summary="diagnosis_mail_fcrdns_different_from_ehlo_domain") + summary="diagnosis_mail_fcrdns_different_from_ehlo_domain", + details=details) def check_blacklist(self): @@ -210,12 +222,13 @@ class MailDiagnoser(Diagnoser): if global_ipv4: outgoing_ips.append(global_ipv4) - ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) or {} - if ipv6.get("status") == "SUCCESS": - outgoing_ipversions.append(6) - global_ipv6 = ipv6.get("data", {}).get("global", {}) - if global_ipv6: - outgoing_ips.append(global_ipv6) + if settings_get("smtp.ipv6"): + ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) or {} + if ipv6.get("status") == "SUCCESS": + outgoing_ipversions.append(6) + global_ipv6 = ipv6.get("data", {}).get("global", {}) + if global_ipv6: + outgoing_ips.append(global_ipv6) return (outgoing_ipversions, outgoing_ips) def main(args, env, loggers): diff --git a/locales/en.json b/locales/en.json index 8272fc86c..0fc9ca777 100644 --- a/locales/en.json +++ b/locales/en.json @@ -185,7 +185,7 @@ "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least 256 MB to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is open, emails can be sent", - "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be bloecked in IPv{ipversion}", + "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}", "diagnosis_mail_outgoing_port_25_blocked_details": "You should first try to unblock it in your internet service provider (or hosting provider) configuration panel or by sending a ticket to your hosting provider. Meanwhile, the server won't be able to send emails to other servers.", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Some providers won't let you unblock outgoing port 25 because they don't care about Net Neutrality.
- Some of them provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", @@ -199,8 +199,11 @@ "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured", "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}", - "diagnosis_mail_fcrdns_dns_missing_details": "You can configure it on ", - "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}", + "diagnosis_mail_fcrdns_nok_details": "You should first try to configure the reverse DNS with {ehlo_domain} on your internet service provider (or hosting provider) config panel or by sending a ticket to your hosting provider. Meanwhile, some outgoing mails won't be delivered.", + "diagnosis_mail_fcrdns_nok_alternatives_4": "Some providers won't let you configure it or the feature is broken on their config panel. If you are experiencing some server refusing your email for this reason, you could try those solutions:
- Some ISP provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", + "diagnosis_mail_fcrdns_nok_alternatives_6": "Some providers won't let you configure it or the feature is broken on their config panel in IPv6. If your reverse DNS is ok in IPv4, you can try to disable the use of IPv6 to send mail by running yunohost settings set smtp.ipv6 -v off ; yunohost tools regen-conf postfix. Note: with this last solution you won't be able to send or received emails from the rare ipv6 only servers.", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "The reverse DNS is different from your EHLO domain on {ip}", + "diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "Current reverse DNS: {rdns_domain}
Expected value: {ehlo_domain}", "diagnosis_mail_blacklist_ok": "IPs and domains used by this server to send mail are not on most used email blacklists", "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}", "diagnosis_mail_blacklist_reason": "The blacklist explains: {reason}", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index 72477e4de..c016e0809 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -70,6 +70,7 @@ DEFAULTS = OrderedDict([ ("security.postfix.compatibility", {"type": "enum", "default": "intermediate", "choices": ["intermediate", "modern"]}), ("pop3.enabled", {"type": "bool", "default": False}), + ("smtp.ipv6", {"type": "bool", "default": True}), ]) From ed75108142840090b7dd6f249ad4e39ffac6000c Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 02:32:15 +0200 Subject: [PATCH 80/91] [fix] Cache duration --- data/hooks/diagnosis/24-mail.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 022b24114..0c89fd7e0 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -20,7 +20,7 @@ DEFAULT_DNS_BLACKLIST = "/usr/share/yunohost/other/dnsbl_list.yml" class MailDiagnoser(Diagnoser): id_ = os.path.splitext(os.path.basename(__file__))[0].split("-")[1] - cache_duration = 0 + cache_duration = 12 * 3600 dependencies = ["ip"] def run(self): From a33ae634c3d78245791585959c9326b0c492e3ad Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 02:39:36 +0200 Subject: [PATCH 81/91] We need those quotes around spf --- src/yunohost/domain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yunohost/domain.py b/src/yunohost/domain.py index 1d1e10da1..c725b58c9 100644 --- a/src/yunohost/domain.py +++ b/src/yunohost/domain.py @@ -457,7 +457,7 @@ def _build_dns_conf(domain, ttl=3600, include_empty_AAAA_if_no_ipv6=False): mail = [ ["@", ttl, "MX", "10 %s." % domain], - ["@", ttl, "TXT", "v=spf1 a mx -all"], + ["@", ttl, "TXT", '"v=spf1 a mx -all"'], ] # DKIM/DMARC record From da112a3668df850d007fba9cadc8f4d284c02fe1 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 02:44:39 +0200 Subject: [PATCH 82/91] Let's push also CAA for nohost.me ... Assuming dynette will eventually allow it .. --- src/yunohost/dyndns.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/yunohost/dyndns.py b/src/yunohost/dyndns.py index 6e597fbbf..efa25f23f 100644 --- a/src/yunohost/dyndns.py +++ b/src/yunohost/dyndns.py @@ -259,11 +259,6 @@ def dyndns_update(operation_logger, dyn_host="dyndns.yunohost.org", domain=None, dns_conf = _build_dns_conf(domain) - for i, record in enumerate(dns_conf["extra"]): - # Ignore CAA record ... not sure why, we could probably enforce it... - if record[3] == "CAA": - del dns_conf["extra"][i] - # Delete custom DNS records, we don't support them (have to explicitly # authorize them on dynette) for category in dns_conf.keys(): From f78af06a355ad4590e7b27937bec897a01d75720 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 02:59:16 +0200 Subject: [PATCH 83/91] Lazy loading for performance, possibly --- src/yunohost/diagnosis.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/yunohost/diagnosis.py b/src/yunohost/diagnosis.py index bd52f57f8..bfb2619eb 100644 --- a/src/yunohost/diagnosis.py +++ b/src/yunohost/diagnosis.py @@ -27,8 +27,6 @@ import re import os import time -import requests -import socket from moulinette import m18n, msettings from moulinette.utils import log @@ -496,6 +494,10 @@ class Diagnoser(): @staticmethod def remote_diagnosis(uri, data, ipversion, timeout=30): + # Lazy loading for performance + import requests + import socket + # Monkey patch socket.getaddrinfo to force request() to happen in ipv4 # or 6 ... # Inspired by https://stackoverflow.com/a/50044152 From b53695af2743a300fe3dc3dc4ff6ef478dabe200 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 03:08:34 +0200 Subject: [PATCH 84/91] Fix _could_not_diagnose string consistency --- locales/ca.json | 6 ++++-- locales/en.json | 2 +- locales/eo.json | 6 ++++-- locales/es.json | 6 ++++-- locales/fr.json | 6 ++++-- locales/oc.json | 6 ++++-- 6 files changed, 21 insertions(+), 11 deletions(-) diff --git a/locales/ca.json b/locales/ca.json index 07e2c6f27..6416307f6 100644 --- a/locales/ca.json +++ b/locales/ca.json @@ -506,7 +506,8 @@ "diagnosis_swap_none": "El sistema no té swap. Hauríeu de considerar afegir un mínim de {recommended} de swap per evitar situacions en les que el sistema es queda sense memòria.", "diagnosis_regenconf_manually_modified": "El fitxer de configuració {file} ha estat modificat manualment.", "diagnosis_security_vulnerable_to_meltdown_details": "Per arreglar-ho, hauríeu d'actualitzar i reiniciar el sistema per tal de carregar el nou nucli de linux (o contactar amb el proveïdor del servidor si no funciona). Vegeu https://meltdownattack.com/ per a més informació.", - "diagnosis_http_could_not_diagnose": "No s'ha pogut diagnosticar si el domini és accessible des de l'exterior. Error: {error}", + "diagnosis_http_could_not_diagnose": "No s'ha pogut diagnosticar si el domini és accessible des de l'exterior.", + "diagnosis_http_could_not_diagnose_details": "Error: {error}", "domain_cannot_remove_main_add_new_one": "No es pot eliminar «{domain:s}» ja que és el domini principal i únic domini, primer s'ha d'afegir un altre domini utilitzant «yunohost domain add », i després fer-lo el domini principal amb «yunohost domain main-domain -n » i després es pot eliminar el domini «{domain:s}» utilitzant «yunohost domain remove {domain:s}».", "diagnosis_basesystem_host": "El servidor funciona amb Debian {debian_version}", "diagnosis_basesystem_kernel": "El servidor funciona amb el nucli de Linux {kernel_version}", @@ -559,7 +560,8 @@ "diagnosis_description_ports": "Exposició dels ports", "diagnosis_description_regenconf": "Configuració del sistema", "diagnosis_description_security": "Verificacions de seguretat", - "diagnosis_ports_could_not_diagnose": "No s'ha pogut diagnosticar si els ports són accessibles des de l'exterior. Error: {error}", + "diagnosis_ports_could_not_diagnose": "No s'ha pogut diagnosticar si els ports són accessibles des de l'exterior.", + "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_unreachable": "El port {port} no és accessible des de l'exterior.", "diagnosis_ports_ok": "El port {port} és accessible des de l'exterior.", "diagnosis_http_ok": "El domini {domain} és accessible per mitjà de HTTP des de fora de la xarxa local.", diff --git a/locales/en.json b/locales/en.json index a5048b8c9..b850b5b41 100644 --- a/locales/en.json +++ b/locales/en.json @@ -192,7 +192,7 @@ "diagnosis_mail_ehlo_unavailable": "Postfix mail service don't answer to EHLO request on IPv{ipversion}", "diagnosis_mail_ehlo_wrong": "A mail server answers {wrong_ehlo} instead {right_ehlo} on IPv{ipversion}", "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside in IPv{ipversion}.", - "diagnosis_mail_ehlo_could_not_diagnose_details": "{error}", + "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured", "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}", "diagnosis_mail_fcrdns_different_from_ehlo_domain": "Your reverse DNS {rdns_domain} is different from your EHLO domain {ehlo_domain} on {ip}", diff --git a/locales/eo.json b/locales/eo.json index 36396d6f1..64571e7e7 100644 --- a/locales/eo.json +++ b/locales/eo.json @@ -555,7 +555,8 @@ "diagnosis_description_services": "Servo kontrolas staton", "diagnosis_description_systemresources": "Rimedaj sistemoj", "diagnosis_description_security": "Sekurecaj kontroloj", - "diagnosis_ports_could_not_diagnose": "Ne povis diagnozi, ĉu haveblaj havenoj de ekstere. Eraro: {error}", + "diagnosis_ports_could_not_diagnose": "Ne povis diagnozi, ĉu haveblaj havenoj de ekstere.", + "diagnosis_ports_could_not_diagnose_details": "Eraro: {error}", "diagnosis_services_bad_status_tip": "Vi povas provi rekomenci la servon, kaj se ĝi ne funkcias, trarigardu la servajn protokolojn uzante 'yunohost service log {service}' aŭ tra la sekcio 'Servoj' de la retadreso.", "diagnosis_security_vulnerable_to_meltdown_details": "Por ripari tion, vi devas ĝisdatigi vian sistemon kaj rekomenci por ŝarĝi la novan linux-kernon (aŭ kontaktu vian servilan provizanton se ĉi tio ne funkcias). Vidu https://meltdownattack.com/ por pliaj informoj.", "diagnosis_description_basesystem": "Baza sistemo", @@ -577,7 +578,8 @@ "diagnosis_ports_ok": "Haveno {port} atingeblas de ekstere.", "diagnosis_ports_needed_by": "Eksponi ĉi tiun havenon necesas por servo {service}", "diagnosis_ports_forwarding_tip": "Por solvi ĉi tiun problemon, plej probable vi devas agordi la plusendon de haveno en via interreta enkursigilo kiel priskribite en https://yunohost.org/isp_box_config", - "diagnosis_http_could_not_diagnose": "Ne povis diagnozi, ĉu atingeblas domajno de ekstere. Eraro: {error}", + "diagnosis_http_could_not_diagnose": "Ne povis diagnozi, ĉu atingeblas domajno de ekstere.", + "diagnosis_http_could_not_diagnose_details": "Eraro: {error}", "diagnosis_http_ok": "Domajno {domain} atingeblas de ekstere.", "diagnosis_http_unreachable": "Domajno {domain} estas atingebla per HTTP de ekstere.", "domain_cannot_remove_main_add_new_one": "Vi ne povas forigi '{domain:s}' ĉar ĝi estas la ĉefa domajno kaj via sola domajno, vi devas unue aldoni alian domajnon uzante ''yunohost domain add ', tiam agordi kiel ĉefan domajnon uzante 'yunohost domain main-domain -n ' kaj tiam vi povas forigi la domajnon' {domain:s} 'uzante' yunohost domain remove {domain:s} '.'", diff --git a/locales/es.json b/locales/es.json index c21585e7b..beefb838c 100644 --- a/locales/es.json +++ b/locales/es.json @@ -572,7 +572,8 @@ "diagnosis_ports_needed_by": "La apertura de este puerto es requerida para la funcionalidad {category} (service {service})", "diagnosis_ports_ok": "El puerto {port} es accesible desde internet.", "diagnosis_ports_unreachable": "El puerto {port} no es accesible desde internet.", - "diagnosis_ports_could_not_diagnose": "No se puede comprobar si los puertos están accesibles desde el exterior. Error: {error}", + "diagnosis_ports_could_not_diagnose": "No se puede comprobar si los puertos están accesibles desde el exterior.", + "diagnosis_ports_could_not_diagnose_details": "Error: {error}", "diagnosis_description_security": "Validación de seguridad", "diagnosis_description_regenconf": "Configuraciones de sistema", "diagnosis_description_mail": "Correo electrónico", @@ -595,6 +596,7 @@ "diagnosis_http_connection_error": "Error de conexión: Ne se pudo conectar al dominio solicitado,", "diagnosis_http_timeout": "El intento de contactar a su servidor desde internet corrió fuera de tiempo. Al parece esta incomunicado. Debería verificar que nginx corre en el puerto 80, y que la redireción del puerto 80 no interfiere con en el firewall.", "diagnosis_http_ok": "El Dominio {domain} es accesible desde internet a través de HTTP.", - "diagnosis_http_could_not_diagnose": "No se pudo verificar si el dominio es accesible desde internet. Error: {error}", + "diagnosis_http_could_not_diagnose": "No se pudo verificar si el dominio es accesible desde internet.", + "diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_ports_forwarding_tip": "Para solucionar este incidente, debería configurar el \"port forwading\" en su router como especificado en https://yunohost.org/isp_box_config" } diff --git a/locales/fr.json b/locales/fr.json index 7e77cdc7a..94cb76a5a 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -562,7 +562,8 @@ "diagnosis_description_ports": "Exposition des ports", "diagnosis_description_regenconf": "Configurations système", "diagnosis_description_security": "Contrôles de sécurité", - "diagnosis_ports_could_not_diagnose": "Impossible de diagnostiquer si les ports sont accessibles de l'extérieur. Erreur: {error}", + "diagnosis_ports_could_not_diagnose": "Impossible de diagnostiquer si les ports sont accessibles de l'extérieur.", + "diagnosis_ports_could_not_diagnose_details": "Erreur: {error}", "apps_catalog_updating": "Mise à jour du catalogue d'applications…", "apps_catalog_obsolete_cache": "Le cache du catalogue d'applications est vide ou obsolète.", "apps_catalog_update_success": "Le catalogue des applications a été mis à jour !", @@ -570,7 +571,8 @@ "diagnosis_description_mail": "Email", "diagnosis_ports_unreachable": "Le port {port} n'est pas accessible de l'extérieur.", "diagnosis_ports_ok": "Le port {port} est accessible de l'extérieur.", - "diagnosis_http_could_not_diagnose": "Impossible de diagnostiquer si le domaine est accessible de l'extérieur. Erreur: {error}", + "diagnosis_http_could_not_diagnose": "Impossible de diagnostiquer si le domaine est accessible de l'extérieur.", + "diagnosis_http_could_not_diagnose_details": "Erreur: {error}", "diagnosis_http_ok": "Le domaine {domain} est accessible au travers de HTTP depuis l'extérieur.", "diagnosis_http_unreachable": "Le domaine {domain} est inaccessible au travers de HTTP depuis l'extérieur.", "diagnosis_unknown_categories": "Les catégories suivantes sont inconnues: {categories}", diff --git a/locales/oc.json b/locales/oc.json index 97978bb18..95f581851 100644 --- a/locales/oc.json +++ b/locales/oc.json @@ -539,8 +539,10 @@ "diagnosis_dns_missing_record": "Segon la configuracion DNS recomandada, vos calriá ajustar un enregistrament DNS\ntipe: {type}\nnom: {name}\nvalor: {value}", "diagnosis_dns_discrepancy": "Segon la configuracion DNS recomandada, la valor per l’enregistrament DNS\ntipe: {type}\nnom: {name}\ndeuriá èsser: {current}\nallòc de: {value}", "diagnosis_regenconf_manually_modified_debian_details": "Es pas problematic, mas car téner d’agacher...", - "diagnosis_ports_could_not_diagnose": "Impossible de diagnosticar se los pòrts son accessibles de l’exterior. Error : {error}", - "diagnosis_http_could_not_diagnose": "Impossible de diagnosticar se lo domeni es accessible de l’exterior. Error : {error}", + "diagnosis_ports_could_not_diagnose": "Impossible de diagnosticar se los pòrts son accessibles de l’exterior.", + "diagnosis_ports_could_not_diagnose_details": "Error : {error}", + "diagnosis_http_could_not_diagnose": "Impossible de diagnosticar se lo domeni es accessible de l’exterior.", + "diagnosis_http_could_not_diagnose_details": "Error : {error}", "apps_catalog_updating": "Actualizacion del catalòg d’aplicacion…", "apps_catalog_failed_to_download": "Telecargament impossible del catalòg d’aplicacions {apps_catalog} : {error}", "apps_catalog_obsolete_cache": "La memòria cache del catalòg d’aplicacion es voida o obsolèta.", From 70566b70220d427ec0215c6da310db19f9eaef22 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 03:19:34 +0200 Subject: [PATCH 85/91] Ignore some string keys which are only fragments concatenated with other stuff --- tests/test_i18n_keys.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_i18n_keys.py b/tests/test_i18n_keys.py index 0d5af33f6..d6df56452 100644 --- a/tests/test_i18n_keys.py +++ b/tests/test_i18n_keys.py @@ -49,6 +49,9 @@ def find_expected_string_keys(): for python_file in glob.glob("data/hooks/diagnosis/*.py"): content = open(python_file).read() for m in p3.findall(content): + if m.endswith("_"): + # Ignore some name fragments which are actually concatenated with other stuff.. + continue yield m yield "diagnosis_description_" + os.path.basename(python_file)[:-3].split("-")[-1] From e6f0091f59b37be9bc6c365da98b26e5c50d59f5 Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 03:45:59 +0200 Subject: [PATCH 86/91] [fix] Rename ipv6 mail settings + desc --- data/hooks/conf_regen/19-postfix | 2 +- locales/en.json | 1 + src/yunohost/settings.py | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/data/hooks/conf_regen/19-postfix b/data/hooks/conf_regen/19-postfix index 172438f37..10076b680 100755 --- a/data/hooks/conf_regen/19-postfix +++ b/data/hooks/conf_regen/19-postfix @@ -35,7 +35,7 @@ do_pre_regen() { > "${default_dir}/postsrsd" # adapt it for IPv4-only hosts - ipv6="$(yunohost settings get 'smtp.ipv6')" + ipv6="$(yunohost settings get 'smtp.allow_ipv6')" if [ "$ipv6" == "False" ] || [ ! -f /proc/net/if_inet6 ]; then sed -i \ 's/ \[::ffff:127.0.0.0\]\/104 \[::1\]\/128//g' \ diff --git a/locales/en.json b/locales/en.json index 0fc9ca777..fc4726aed 100644 --- a/locales/en.json +++ b/locales/en.json @@ -312,6 +312,7 @@ "global_settings_setting_security_postfix_compatibility": "Compatibility vs. security tradeoff for the Postfix server. Affects the ciphers (and other security-related aspects)", "global_settings_unknown_setting_from_settings_file": "Unknown key in settings: '{setting_key:s}', discard it and save it in /etc/yunohost/settings-unknown.json", "global_settings_setting_service_ssh_allow_deprecated_dsa_hostkey": "Allow the use of (deprecated) DSA hostkey for the SSH daemon configuration", + "global_settings_setting_smtp_allow_ipv6": "Allow the use of IPv6 to receive and send mail", "global_settings_unknown_type": "Unexpected situation, the setting {setting:s} appears to have the type {unknown_type:s} but it is not a type supported by the system.", "good_practices_about_admin_password": "You are now about to define a new administration password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to use a variation of characters (uppercase, lowercase, digits and special characters).", "good_practices_about_user_password": "You are now about to define a new user password. The password should be at least 8 characters long—though it is good practice to use a longer password (i.e. a passphrase) and/or to a variation of characters (uppercase, lowercase, digits and special characters).", diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index c016e0809..db94e7429 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -70,7 +70,7 @@ DEFAULTS = OrderedDict([ ("security.postfix.compatibility", {"type": "enum", "default": "intermediate", "choices": ["intermediate", "modern"]}), ("pop3.enabled", {"type": "bool", "default": False}), - ("smtp.ipv6", {"type": "bool", "default": True}), + ("smtp.allow_ipv6", {"type": "bool", "default": True}), ]) From 40141c84f39b1f17a387ca55aa4505046a729e3c Mon Sep 17 00:00:00 2001 From: ljf Date: Sun, 19 Apr 2020 03:55:50 +0200 Subject: [PATCH 87/91] [enh] Auto update postfix on smtp.allow_ipv6 change --- src/yunohost/settings.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/yunohost/settings.py b/src/yunohost/settings.py index db94e7429..c1edadb93 100644 --- a/src/yunohost/settings.py +++ b/src/yunohost/settings.py @@ -321,6 +321,7 @@ def reconfigure_ssh(setting_name, old_value, new_value): if old_value != new_value: service_regen_conf(names=['ssh']) +@post_change_hook("smtp.allow_ipv6") @post_change_hook("security.postfix.compatibility") def reconfigure_postfix(setting_name, old_value, new_value): if old_value != new_value: From 6e334eba955439f30a1511ef0ceeb02f170fd93d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 04:14:48 +0200 Subject: [PATCH 88/91] Wording / weird translation.. --- locales/en.json | 2 +- locales/fr.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/locales/en.json b/locales/en.json index b59b53325..5c97c01fc 100644 --- a/locales/en.json +++ b/locales/en.json @@ -145,7 +145,7 @@ "diagnosis_basesystem_ynh_inconsistent_versions": "You are running inconsistent versions of the YunoHost packages... most probably because of a failed or partial upgrade.", "diagnosis_display_tip": "To see the issues found, you can go to the Diagnosis section of the webadmin, or run 'yunohost diagnosis show --issues' from the command-line.", "diagnosis_failed_for_category": "Diagnosis failed for category '{category}': {error}", - "diagnosis_cache_still_valid": "(Cache still valid for {category} diagnosis. Not re-diagnosing yet!)", + "diagnosis_cache_still_valid": "(Cache still valid for {category} diagnosis. Won't re-diagnose it yet!)", "diagnosis_cant_run_because_of_dep": "Can't run diagnosis for {category} while there are important issues related to {dep}.", "diagnosis_ignored_issues": "(+ {nb_ignored} ignored issue(s))", "diagnosis_found_errors": "Found {errors} significant issue(s) related to {category}!", diff --git a/locales/fr.json b/locales/fr.json index 2431af8da..6b8ddcabe 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -521,7 +521,7 @@ "diagnosis_basesystem_ynh_inconsistent_versions": "Vous exécutez des versions incohérentes des packages YunoHost ... probablement à cause d'une mise à niveau partielle ou échouée.", "diagnosis_display_tip_cli": "Vous pouvez exécuter 'yunohost diagnosis show --issues' pour afficher les problèmes détectés.", "diagnosis_failed_for_category": "Échec du diagnostic pour la catégorie '{category}': {error}", - "diagnosis_cache_still_valid": "(Le cache est toujours valide pour le diagnostic {category}. Pas re-diagnostiquer pour le moment!)", + "diagnosis_cache_still_valid": "(Le cache est encore valide pour le diagnostic {category}. Il ne sera pas re-diagnostiqué pour le moment!)", "diagnosis_ignored_issues": "(+ {nb_ignored} questions ignorée(s))", "diagnosis_found_warnings": "Trouvé {warnings} objet(s) pouvant être amélioré(s) pour {category}.", "diagnosis_everything_ok": "Tout semble bien pour {category} !", From 39f0aa3ef32d54c5c45a4621d052e51cb4586061 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 04:44:09 +0200 Subject: [PATCH 89/91] Improve wording --- data/hooks/diagnosis/24-mail.py | 13 ++++++----- locales/en.json | 40 ++++++++++++++++----------------- 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/data/hooks/diagnosis/24-mail.py b/data/hooks/diagnosis/24-mail.py index 5457c5890..4ced72959 100644 --- a/data/hooks/diagnosis/24-mail.py +++ b/data/hooks/diagnosis/24-mail.py @@ -109,25 +109,28 @@ class MailDiagnoser(Diagnoser): for ip in self.ips: if ":" in ip: + ipversion = 6 details = ["diagnosis_mail_fcrdns_nok_details", "diagnosis_mail_fcrdns_nok_alternatives_6"] else: + ipversion = 4 details = ["diagnosis_mail_fcrdns_nok_details", "diagnosis_mail_fcrdns_nok_alternatives_4"] try: rdns_domain, _, _ = socket.gethostbyaddr(ip) except socket.herror: - yield dict(meta={"test": "mail_fcrdns", "ip": ip}, - data={"ehlo_domain": self.ehlo_domain}, + yield dict(meta={"test": "mail_fcrdns", "ipversion": ipversion}, + data={"ip": ip, "ehlo_domain": self.ehlo_domain}, status="ERROR", summary="diagnosis_mail_fcrdns_dns_missing", details=details) continue if rdns_domain != self.ehlo_domain: details = ["diagnosis_mail_fcrdns_different_from_ehlo_domain_details"] + details - yield dict(meta={"test": "mail_fcrdns", "ip": ip}, - data={"ehlo_domain": self.ehlo_domain, + yield dict(meta={"test": "mail_fcrdns", "ipversion": ipversion}, + data={"ip": ip, + "ehlo_domain": self.ehlo_domain, "rdns_domain": rdns_domain}, status="ERROR", summary="diagnosis_mail_fcrdns_different_from_ehlo_domain", @@ -222,7 +225,7 @@ class MailDiagnoser(Diagnoser): if global_ipv4: outgoing_ips.append(global_ipv4) - if settings_get("smtp.ipv6"): + if settings_get("smtp.allow_ipv6"): ipv6 = Diagnoser.get_cached_report("ip", {"test": "ipv6"}) or {} if ipv6.get("status") == "SUCCESS": outgoing_ipversions.append(6) diff --git a/locales/en.json b/locales/en.json index 5c97c01fc..400413e3d 100644 --- a/locales/en.json +++ b/locales/en.json @@ -184,29 +184,29 @@ "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least {recommended} of swap to avoid situations where the system runs out of memory.", "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least {recommended} to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", - "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is open, emails can be sent", - "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}", - "diagnosis_mail_outgoing_port_25_blocked_details": "You should first try to unblock it in your internet service provider (or hosting provider) configuration panel or by sending a ticket to your hosting provider. Meanwhile, the server won't be able to send emails to other servers.", - "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Some providers won't let you unblock outgoing port 25 because they don't care about Net Neutrality.
- Some of them provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", - "diagnosis_mail_ehlo_ok": "Postfix mail service answer correctly from outside", - "diagnosis_mail_ehlo_unreachable": "SMTP server unreachable on IPv{ipversion}", - "diagnosis_mail_ehlo_unreachable_details": "Could not open a connection on port 25 through IPv{ipversion}, probably because of a firewall, port forwarding issue or postfix service down", + "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is open, emails can be sent!", + "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}. This prevent emails from being sent to other servers.", + "diagnosis_mail_outgoing_port_25_blocked_details": "You should first try to unblock outgoing port 25 in your internet router interface or your hosting provider interface. (Some hosting provider may require you to send them a support ticket for this).", + "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Some providers won't let you unblock outgoing port 25 because they don't care about Net Neutrality.
- Some of them provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- You can also consider switching to a more net neutrality-friendly provider", + "diagnosis_mail_ehlo_ok": "The SMTP mail server is reachable from the outside, which allows to receive email.", + "diagnosis_mail_ehlo_unreachable": "The SMTP mail server is unreachable from the outside on IPv{ipversion}. It won't be able to receive emails.", + "diagnosis_mail_ehlo_unreachable_details": "Could not open a connection on port 25 to your server in IPv{ipversion}. It appears to be unreachable.
1. The most common cause for this issue is that port 25 is not correctly forwarded to your server.
2. You should also make sure that service postfix is running.
3. On more complex setups: make sure that no firewall or reverse-proxy is interfering.", "diagnosis_mail_ehlo_bad_answer": "A non-SMTP service answered on port 25 on IPv{ipversion}", "diagnosis_mail_ehlo_bad_answer_details": "It could be due to an other machine answering instead of your server.", - "diagnosis_mail_ehlo_wrong": "An other SMTP server answers on IPv{ipversion}", - "diagnosis_mail_ehlo_wrong_details": "The remote diagnoser return a wrong EHLO answer from your IPv{ipversion}.
Received: {wrong_ehlo}
Expected: {right_ehlo}
You probably have a port forwarding issue or a reverse proxy server unconfigured for mail.", + "diagnosis_mail_ehlo_wrong": "A different SMTP server answers on IPv{ipversion}. It will probably not be able to receive emails.", + "diagnosis_mail_ehlo_wrong_details": "The EHLO received by the remote diagnoser in IPv{ipversion} is different from your server's domain.
Received EHLO: {wrong_ehlo}
Expected: {right_ehlo}
The most common cause for this issue is that port 25 is not correctly forwarded to your server. Alternatively, make sure that no firewall or reverse-proxy is interfering.", "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside in IPv{ipversion}.", "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", - "diagnosis_mail_fcrdns_ok": "Your reverse DNS is well configured", - "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS defined for the ip {ip}", - "diagnosis_mail_fcrdns_nok_details": "You should first try to configure the reverse DNS with {ehlo_domain} on your internet service provider (or hosting provider) config panel or by sending a ticket to your hosting provider. Meanwhile, some outgoing mails won't be delivered.", - "diagnosis_mail_fcrdns_nok_alternatives_4": "Some providers won't let you configure it or the feature is broken on their config panel. If you are experiencing some server refusing your email for this reason, you could try those solutions:
- Some ISP provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", - "diagnosis_mail_fcrdns_nok_alternatives_6": "Some providers won't let you configure it or the feature is broken on their config panel in IPv6. If your reverse DNS is ok in IPv4, you can try to disable the use of IPv6 to send mail by running yunohost settings set smtp.ipv6 -v off ; yunohost tools regen-conf postfix. Note: with this last solution you won't be able to send or received emails from the rare ipv6 only servers.", - "diagnosis_mail_fcrdns_different_from_ehlo_domain": "The reverse DNS is different from your EHLO domain on {ip}", + "diagnosis_mail_fcrdns_ok": "Your reverse DNS is correctly configured!", + "diagnosis_mail_fcrdns_dns_missing": "No reverse DNS is defined in IPv{ipversion}. Some emails may fail to get delivered or may get flagged as spam.", + "diagnosis_mail_fcrdns_nok_details": "You should first try to configure the reverse DNS with {ehlo_domain} in your internet router interface or your hosting provider interface. (Some hosting provider may require you to send them a support ticket for this).", + "diagnosis_mail_fcrdns_nok_alternatives_4": "Some providers won't let you configure your reverse DNS (or their feature might be broken...). If you are experiencing issues because of this, consider the following solutions:
- Some ISP provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- Finally, it's also possible to change of provider", + "diagnosis_mail_fcrdns_nok_alternatives_6": "Some providers won't let you configure your reverse DNS (or their feature might be broken...). If your reverse DNS is correctly configured for IPv4, you can try disabling the use of IPv6 when sending emails by running yunohost settings set smtp.allow_ipv6 -v off. Note: this last solution means that you won't be able to send or receive emails from the few IPv6-only servers out there.", + "diagnosis_mail_fcrdns_different_from_ehlo_domain": "The reverse DNS is not correctly configured in IPv{ipversion}. Some emails may fail to get delivered or may get flagged as spam.", "diagnosis_mail_fcrdns_different_from_ehlo_domain_details": "Current reverse DNS: {rdns_domain}
Expected value: {ehlo_domain}", - "diagnosis_mail_blacklist_ok": "IPs and domains used by this server to send mail are not on most used email blacklists", - "diagnosis_mail_blacklist_listed_by": "{item} is blacklisted on {blacklist_name}", - "diagnosis_mail_blacklist_reason": "The blacklist explains: {reason}", + "diagnosis_mail_blacklist_ok": "The IPs and domains used by this server do not appear to be blacklisted", + "diagnosis_mail_blacklist_listed_by": "Your IP or domain {item} is blacklisted on {blacklist_name}", + "diagnosis_mail_blacklist_reason": "The blacklist reason is: {reason}", "diagnosis_mail_blacklist_website": "After identifying why you are listed and fixed it, feel free to ask for delisting on {blacklist_website}", "diagnosis_mail_queue_ok": "{nb_pending} pending emails in the mail queues", "diagnosis_mail_queue_unavailable": "Can not consult number of pending emails in queue", @@ -240,9 +240,9 @@ "diagnosis_http_could_not_diagnose": "Could not diagnose if domains are reachable from outside in IPv{ipversion}.", "diagnosis_http_could_not_diagnose_details": "Error: {error}", "diagnosis_http_ok": "Domain {domain} is reachable through HTTP from outside the local network.", - "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. Also make sure that the web server nginx is running
3. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", + "diagnosis_http_timeout": "Timed-out while trying to contact your server from outside. It appears to be unreachable.
1. The most common cause for this issue is that port 80 (and 443) are not correctly forwarded to your server.
2. You should also make sure that the service nginx is running
3. On more complex setups: make sure that no firewall or reverse-proxy is interfering.", "diagnosis_http_connection_error": "Connection error: could not connect to the requested domain, it's very likely unreachable.", - "diagnosis_http_bad_status_code": "It looks like another machine (maybe your internet router) answered instead of your server.
1. The most common cause for this issue is that you did not correctly configure port forwarding for port 80.
2. On more complex setups: make sure that a firewall or reverse-proxy is not interfering.", + "diagnosis_http_bad_status_code": "It looks like another machine (maybe your internet router) answered instead of your server.
1. The most common cause for this issue is that port 80 (and 443) are not correctly forwarded to your server.
2. On more complex setups: make sure that no firewall or reverse-proxy is interfering.", "diagnosis_http_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network.", "diagnosis_http_partially_unreachable": "Domain {domain} appears unreachable through HTTP from outside the local network in IPv{failed}, though it works in IPv{passed}.", "diagnosis_http_nginx_conf_not_up_to_date": "This domain's nginx configuration appears to have been modified manually, and prevents YunoHost from diagnosing if it's reachable on HTTP.", From 878bb82d9df14cb14fc40dec3b775a9a6e9fd5c0 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 05:10:32 +0200 Subject: [PATCH 90/91] Hmgn bad fr translation --- locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locales/fr.json b/locales/fr.json index 6b8ddcabe..3f7776009 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -541,7 +541,7 @@ "diagnosis_diskusage_low": "Le stockage {mountpoint} (sur le périphérique {device}) ne dispose que de {free} ({free_percent}%). Faites attention.", "diagnosis_ram_verylow": "Le système ne dispose plus que de {available} ({available_percent}%)! (sur {total})", "diagnosis_ram_low": "Le système n'a plus de {available} ({available_percent}%) RAM sur {total}. Faites attention.", - "diagnosis_swap_none": "Le système n'a aucun échange. Vous devez envisager d’ajouter au moins {recommended} de swap pour éviter les situations où le système manque de mémoire.", + "diagnosis_swap_none": "Le système n'a aucun espace de swap. Vous devriez envisager d’ajouter au moins {recommended} de swap pour éviter les situations où le système manque de mémoire.", "diagnosis_swap_notsomuch": "Le système ne dispose que de {total} de swap. Vous devez envisager d'avoir au moins {recommended} pour éviter les situations où le système manque de mémoire.", "diagnosis_swap_ok": "Le système dispose de {total} de swap !", "diagnosis_regenconf_manually_modified": "Le fichier de configuration {file} a été modifié manuellement.", From 4c95d52c37864fc2ff5d5c632e3e8fd390af4d77 Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 19 Apr 2020 05:29:32 +0200 Subject: [PATCH 91/91] More small wording/translation improvement.. --- locales/en.json | 8 ++++---- locales/fr.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/locales/en.json b/locales/en.json index 400413e3d..c2c087031 100644 --- a/locales/en.json +++ b/locales/en.json @@ -184,16 +184,16 @@ "diagnosis_swap_none": "The system has no swap at all. You should consider adding at least {recommended} of swap to avoid situations where the system runs out of memory.", "diagnosis_swap_notsomuch": "The system has only {total} swap. You should consider having at least {recommended} to avoid situations where the system runs out of memory.", "diagnosis_swap_ok": "The system has {total} of swap!", - "diagnosis_mail_outgoing_port_25_ok": "Outgoing port 25 is open, emails can be sent!", - "diagnosis_mail_outgoing_port_25_blocked": "Outgoing port 25 appears to be blocked in IPv{ipversion}. This prevent emails from being sent to other servers.", + "diagnosis_mail_outgoing_port_25_ok": "The SMTP mail server is able to send emails (outgoing port 25 is not blocked).", + "diagnosis_mail_outgoing_port_25_blocked": "The SMTP mail server cannot send emails to other servers because outgoing port 25 is blocked in IPv{ipversion}.", "diagnosis_mail_outgoing_port_25_blocked_details": "You should first try to unblock outgoing port 25 in your internet router interface or your hosting provider interface. (Some hosting provider may require you to send them a support ticket for this).", "diagnosis_mail_outgoing_port_25_blocked_relay_vpn": "Some providers won't let you unblock outgoing port 25 because they don't care about Net Neutrality.
- Some of them provide the alternative of using a mail server relay though it implies that the relay will be able to spy on your email traffic.
- A privacy-friendly alternative is to use a VPN *with a dedicated public IP* to bypass this kind of limits. See https://yunohost.org/#/vpn_advantage
- You can also consider switching to a more net neutrality-friendly provider", - "diagnosis_mail_ehlo_ok": "The SMTP mail server is reachable from the outside, which allows to receive email.", + "diagnosis_mail_ehlo_ok": "The SMTP mail server is reachable from the outside and therefore is able to receive emails!", "diagnosis_mail_ehlo_unreachable": "The SMTP mail server is unreachable from the outside on IPv{ipversion}. It won't be able to receive emails.", "diagnosis_mail_ehlo_unreachable_details": "Could not open a connection on port 25 to your server in IPv{ipversion}. It appears to be unreachable.
1. The most common cause for this issue is that port 25 is not correctly forwarded to your server.
2. You should also make sure that service postfix is running.
3. On more complex setups: make sure that no firewall or reverse-proxy is interfering.", "diagnosis_mail_ehlo_bad_answer": "A non-SMTP service answered on port 25 on IPv{ipversion}", "diagnosis_mail_ehlo_bad_answer_details": "It could be due to an other machine answering instead of your server.", - "diagnosis_mail_ehlo_wrong": "A different SMTP server answers on IPv{ipversion}. It will probably not be able to receive emails.", + "diagnosis_mail_ehlo_wrong": "A different SMTP mail server answers on IPv{ipversion}. It will probably not be able to receive emails.", "diagnosis_mail_ehlo_wrong_details": "The EHLO received by the remote diagnoser in IPv{ipversion} is different from your server's domain.
Received EHLO: {wrong_ehlo}
Expected: {right_ehlo}
The most common cause for this issue is that port 25 is not correctly forwarded to your server. Alternatively, make sure that no firewall or reverse-proxy is interfering.", "diagnosis_mail_ehlo_could_not_diagnose": "Could not diagnose if postfix mail server is reachable from outside in IPv{ipversion}.", "diagnosis_mail_ehlo_could_not_diagnose_details": "Error: {error}", diff --git a/locales/fr.json b/locales/fr.json index 3f7776009..c86ed244c 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -579,7 +579,7 @@ "migration_description_0013_futureproof_apps_catalog_system": "Migrer vers le nouveau système de catalogue d'applications à l'épreuve du temps", "app_upgrade_script_failed": "Une erreur s'est produite durant l’exécution du script de mise à niveau de l'application", "migration_description_0014_remove_app_status_json": "Supprimer les fichiers d'application status.json hérités", - "diagnosis_services_running": "Le service {service} s'exécute correctement !", + "diagnosis_services_running": "Le service {service} est en cours de fonctionnement !", "diagnosis_services_conf_broken": "La configuration est cassée pour le service {service} !", "diagnosis_ports_needed_by": "Rendre ce port accessible est nécessaire pour les fonctionnalités de type {category} (service {service})", "diagnosis_ports_forwarding_tip": "Pour résoudre ce problème, vous devez probablement configurer la redirection de port sur votre routeur Internet comme décrit sur https://yunohost.org/isp_box_config",