diff --git a/check_process b/check_process index 7d507d0..1b09d7c 100644 --- a/check_process +++ b/check_process @@ -4,6 +4,7 @@ path="/path" redirect_type="public_302" redirect_path="http://127.0.0.1" + is_public=1 ; Checks pkg_linter=1 setup_sub_dir=1 diff --git a/conf/nginx-proxy.conf b/conf/nginx-proxy.conf index 6d3a1ee..346047b 100644 --- a/conf/nginx-proxy.conf +++ b/conf/nginx-proxy.conf @@ -1,5 +1,6 @@ -location YNH_LOCATION { - proxy_pass YNH_REDIRECT_PATH; +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + proxy_pass __REDIRECT_PATH__; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; diff --git a/conf/nginx-public_301.conf b/conf/nginx-public_301.conf new file mode 100644 index 0000000..87bdf26 --- /dev/null +++ b/conf/nginx-public_301.conf @@ -0,0 +1,4 @@ +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + return 301 __REDIRECT_PATH__; +} diff --git a/conf/nginx-public_301_subpath.conf b/conf/nginx-public_301_subpath.conf new file mode 100644 index 0000000..56de814 --- /dev/null +++ b/conf/nginx-public_301_subpath.conf @@ -0,0 +1,4 @@ +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + return 301 __REDIRECT_PATH__$request_uri; +} diff --git a/conf/nginx-public_302.conf b/conf/nginx-public_302.conf new file mode 100644 index 0000000..46f8cbb --- /dev/null +++ b/conf/nginx-public_302.conf @@ -0,0 +1,4 @@ +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + return 302 __REDIRECT_PATH__; +} diff --git a/conf/nginx-public_302_subpath.conf b/conf/nginx-public_302_subpath.conf new file mode 100644 index 0000000..5064e9b --- /dev/null +++ b/conf/nginx-public_302_subpath.conf @@ -0,0 +1,4 @@ +#sub_path_only rewrite ^__PATH__$ __PATH__/ permanent; +location __PATH__/ { + return 302 __REDIRECT_PATH__$request_uri; +} diff --git a/conf/nginx-visible-301.conf b/conf/nginx-visible-301.conf deleted file mode 100644 index 2b3ffb6..0000000 --- a/conf/nginx-visible-301.conf +++ /dev/null @@ -1,3 +0,0 @@ -location YNH_LOCATION { - return 301 YNH_REDIRECT_PATH$request_uri; -} diff --git a/conf/nginx-visible-302.conf b/conf/nginx-visible-302.conf deleted file mode 100644 index e1ffc66..0000000 --- a/conf/nginx-visible-302.conf +++ /dev/null @@ -1,3 +0,0 @@ -location YNH_LOCATION { - return 302 YNH_REDIRECT_PATH$request_uri; -} diff --git a/config_panel.toml b/config_panel.toml new file mode 100644 index 0000000..9926c0f --- /dev/null +++ b/config_panel.toml @@ -0,0 +1,56 @@ +version = "1.0" + +[main] +name = "Auto-configuration" + + [main.redirect] + name = "" + optional = false + + [main.redirect.domain] + ask = "Domain" + type = "domain" + example = "domain.org" + + [main.redirect.path_url] + ask = "Path" + type = "path" + bind = "path_url:/etc/yunohost/__APP__/settings.yml" + example = "/redirect" + + [main.redirect.redirect_path] + ask = "Redirect destination path" + type = "url" + + [main.redirect.redirect_type] + ask = "Redirect type" + type = "select" + choices.public_302 = "Visible temporary redirect (HTTP 302)" + choices.public_302_subpath = "Visible temporary redirect (HTTP 302) + subpaths are propagated" + choices.public_301 = "Visible permanent redirect (HTTP 301)" + choices.public_301_subpath = "Visible permanent redirect (HTTP 301) + subpaths are propagated" + choices.proxy = "Invisible redirect using proxy (NGINX proxy_pass)" + help = "Subpaths are propagated = transfer the subpath into the redirection. So if you redirect www.example.com to example.com, request https://www.example.com/foo/bar will redirect onto https://example.com/foo/bar instead of https://example.com" + + [main.redirect.frame_allowed] + ask = "Frame allowed" + type = "boolean" + visible = "redirect_type == 'proxy'" + optional = true + + [main.redirect.frame_ancestors] + ask = "Frame ancestors" + type = "tags" + visible = "redirect_type == 'proxy' and frame_allowed" + help = "Any valid host source like `https://example.com`, know more https://developer.mozilla.org/fr/docs/Web/HTTP/Headers/Content-Security-Policy/frame-ancestors" + optional = true + + [main.redirect.client_max_body_size] + ask = "Maximum size of a requests" + type = "string" + help = "If you want to upload 100m video you should multiply by 1.4 ratio (140m) cause it will be mime-encoded. Note that temporary space is needed equal to the total size of all concurrent uploads." + pattern.regexp = "^[0-9]{1,15}(k|m|g|t)$" + pattern.error = "Provide a size like 20m or 100k" + visible = "redirect_type == 'proxy'" + optional = true + diff --git a/manifest.json b/manifest.json index 8d7b508..6621ebc 100644 --- a/manifest.json +++ b/manifest.json @@ -6,7 +6,7 @@ "en": "Create a redirection or a proxy to another path", "fr": "Créer une redirection ou un proxy vers un autre emplacement" }, - "version": "1.0.1~ynh1", + "version": "1.0.2~ynh1", "license": "AGPL-3.0-or-later", "url": "https://github.com/YunoHost-Apps/redirect_ynh", "upstream": { @@ -55,12 +55,20 @@ "fr": "Type de redirection" }, "choices": { - "public_302": "Visible redirect (302, temporary). Everybody will be able to access it.", - "public_301": "Visible redirect (301, permanent). Everybody will be able to access it.", - "public_proxy": "Proxy, invisible (NGINX proxy_pass). Everybody will be able to access it.", - "private_proxy": "Proxy, invisible (NGINX proxy_pass). Only accessible for allowed users." + "public_302": "Visible redirect (302, temporary).", + "public_302_subpath": "Visible redirect (302, temporary) + subpaths are propagated.", + "public_301": "Visible redirect (301, permanent).", + "public_301_subpath": "Visible redirect (301, permanent) + subpaths are propagated.", + "proxy": "Proxy, invisible (NGINX proxy_pass)." }, - "default": "public_302" + "default": "public_302_subpath", + "help": "Subpaths are propagated = transfer the subpath into the redirection. So if you redirect www.example.com to example.com, request https://www.example.com/foo/bar will redirect onto https://example.com/foo/bar instead of https://example.com" + }, + { + "name": "is_public", + "type": "boolean", + "default": true, + "visible": "redirect_type == 'proxy'" } ] } diff --git a/scripts/config b/scripts/config new file mode 100644 index 0000000..a9e5587 --- /dev/null +++ b/scripts/config @@ -0,0 +1,114 @@ +#!/bin/bash + +#================================================= +# GENERIC STARTING +#================================================= +# IMPORT GENERIC HELPERS +#================================================= + +source /usr/share/yunohost/helpers + +#================================================= +# MANAGE SCRIPT FAILURE +#================================================= + +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors + +#================================================= +# SPECIFIC GETTERS FOR TOML SHORT KEY +#================================================= + +get__client_max_body_size() { + grep -o -P "(?<=client_max_body_size )\d+[kmgt](?=;)" /etc/nginx/conf.d/$domain.d/$app.conf +} + +get__frame_allowed() { + if grep -E -q "Content-Security-Policy: +frame-ancestors +'none' *;" /etc/nginx/conf.d/$domain.d/$app.conf + then + echo 0 + else + echo 1 + fi +} + +get__frame_ancestors() { + if grep -E -q "Content-Security-Policy: +frame-ancestors +'none' *;" /etc/nginx/conf.d/$domain.d/$app.conf + then + grep -o -P "(?<=Content-security-Policy: frame-ancestors )[^;]+(?=;)" /etc/nginx/conf.d/$domain.d/$app.conf | sed "s/'none'//g" | xargs | sed -E "s/ /,/g" + fi +} + + +#================================================= +# SPECIFIC VALIDATORS FOR TOML SHORT KEYS +#================================================= +validate__redirect_path() { + url_regex='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]' + if [[ ! $redirect_path =~ $url_regex ]] + then + echo "Invalid destination: $redirect_path" + fi + + # Avoid uncrypted remote destination with reverse proxy mode + # Indeed the SSO send the password in all requests in HTTP headers + url_regex='^(http://(127\.[0-9]+\.[0-9]+\.[0-9]+|localhost)|https://.*)(:[0-9]+)?(/.*)?$' + if [[ "$redirect_type" = "proxy" ]] && [[ ! $redirect_path =~ $url_regex ]] + then + echo + "For secure reason, you can't use an unencrypted http remote destination couple with ssowat for your reverse proxy: $redirect_path" + fi +} +#================================================= +# SPECIFIC SETTERS FOR TOML SHORT KEYS +#================================================= +set__domain() { + ynh_secure_remove /etc/nginx/conf.d/${old[domain]}.d/$app.conf +} + +set__redirect_type() { + if [[ $redirect_type != "proxy" ]] + then + ynh_permission_update --permission="main" --add="visitors" --protected=1 + ynh_app_setting_set --app=$app --key=is_public --value=1 + else + ynh_permission_update --permission="main" --protected=0 + fi + ynh_app_setting_set --app=$app --key=redirect_type --value="$redirect_type" +} + + +set__frame_allowed() { + if [[ $frame_allowed == "0" ]] + then + frame_ancestors="'none'" + fi +} + +set__frame_ancestors() { + if [[ $frame_allowed == "0" ]] + then + frame_ancestors="'none'" + fi + frame_ancestors="${frame_ancestors//,/ }" + ynh_app_setting_set --app=$app --key=frame_ancestors --value="$frame_ancestors" +} + +#================================================= +# OVERWRITING APPLY STEP +#================================================= +ynh_app_config_apply() { + + ynh_print_info --message="Override NGINX configuration" + + + _ynh_app_config_apply + + cp ../conf/nginx-$redirect_type.conf ../conf/nginx.conf + # Create a dedicated NGINX config + ynh_add_nginx_config + + +} + +ynh_app_config_run $1 diff --git a/scripts/install b/scripts/install index 77acee9..ef17fc4 100644 --- a/scripts/install +++ b/scripts/install @@ -25,9 +25,15 @@ domain=$YNH_APP_ARG_DOMAIN path_url=$YNH_APP_ARG_PATH redirect_type=$YNH_APP_ARG_REDIRECT_TYPE redirect_path=$YNH_APP_ARG_REDIRECT_PATH +is_public=$YNH_APP_ARG_IS_PUBLIC +propagate_subpath=$(echo $YNH_APP_ARG_REDIRECT_TYPE | grep -q subpath && echo 1 || echo 0) +frame_ancestors="'none'" +client_max_body_size="1m" -# Check domain/path availability -ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url +#================================================= +# CHECK IF THE APP CAN BE INSTALLED WITH THESE ARGS +#================================================= +ynh_script_progression --message="Validating installation parameters..." --weight=1 # Validate redirect path url_regex='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]' @@ -39,44 +45,60 @@ url_regex='^(http://(127\.[0-9]+\.[0-9]+\.[0-9]+|localhost)|https://.*)(:[0-9]+) [[ "$redirect_type" = "proxy" ]] && [[ ! $redirect_path =~ $url_regex ]] && ynh_die \ "For secure reason, you can't use an unencrypted http remote destination couple with ssowat for your reverse proxy: $redirect_path" 1 +if [ $is_public -eq 0 ] && [[ $redirect_type != "proxy" ]] +then + is_public=1 + YNH_APP_ARG_IS_PUBLIC=1 + ynh_warn "HTTP private redirection are not supported. Your redirection has been reflagged as public." +fi +# Register (book) web path +ynh_webpath_register --app=$app --domain=$domain --path_url=$path_url + +#================================================= +# STORE SETTINGS FROM MANIFEST +#================================================= +ynh_script_progression --message="Storing installation settings..." --weight=1 + # Save extra settings ynh_app_setting_set --app=$app --key=redirect_type --value=$redirect_type ynh_app_setting_set --app=$app --key=redirect_path --value=$redirect_path +ynh_app_setting_set --app=$app --key=is_public --value=$is_public +ynh_app_setting_set --app=$app --key=frame_ancestors --value="'none'" +ynh_app_setting_set --app=$app --key=client_max_body_size --value="1m" #================================================= -# CONFIGURE NGINX +# SPECIFIC SETUP +#================================================= +ynh_script_progression --message="Preparing NGINX web server configuration..." --weight=1 + +cp ../conf/nginx-$redirect_type.conf ../conf/nginx.conf + +#================================================= + +#================================================= +# NGINX CONFIGURATION #================================================= ynh_script_progression --message="Configuring NGINX web server..." --weight=1 -# Nginx configuration -for FILE in $(ls ../conf/nginx-*.conf) -do - ynh_replace_string "YNH_LOCATION" "$path_url" $FILE -done -if [ "$redirect_type" = "public_302" ]; -then - ynh_replace_string "YNH_REDIRECT_PATH" "$redirect_path" ../conf/nginx-visible-302.conf - cp ../conf/nginx-visible-302.conf /etc/nginx/conf.d/$domain.d/$app.conf -elif [ "$redirect_type" = "public_301" ]; -then - ynh_replace_string "YNH_REDIRECT_PATH" "$redirect_path" ../conf/nginx-visible-301.conf - cp ../conf/nginx-visible-301.conf /etc/nginx/conf.d/$domain.d/$app.conf -elif [ "$redirect_type" = "public_proxy" ] || [ "$redirect_type" = "private_proxy" ]; -then - ynh_replace_string "YNH_REDIRECT_PATH" "$redirect_path" ../conf/nginx-proxy.conf - cp ../conf/nginx-proxy.conf /etc/nginx/conf.d/$domain.d/$app.conf -fi +# Create a dedicated NGINX config +ynh_add_nginx_config #================================================= -# CONFIGURE SSOWAT +# SETUP SSOWAT #================================================= -ynh_script_progression --message="Configuring permissions..." --weight=2 +ynh_script_progression --message="Configuring permissions..." --weight=1 # Make app public if necessary -if [ "$redirect_type" != "private_proxy" ] +if [ $is_public -eq 1 ] then - # unprotected_uris allows SSO credentials to be passed anyway. - ynh_permission_update --permission="main" --add="visitors" + # Everyone can access the app. + # The "main" permission is automatically created before the install script. + if [[ $redirect_type != "proxy" ]] + then + ynh_permission_update --permission="main" --add="visitors" --protected=1 + else + ynh_permission_update --permission="main" --add="visitors" + fi fi #================================================= diff --git a/scripts/restore b/scripts/restore index 1cade86..1a34710 100644 --- a/scripts/restore +++ b/scripts/restore @@ -25,6 +25,9 @@ domain=$(ynh_app_setting_get --app=$app --key=domain) path_url=$(ynh_app_setting_get --app=$app --key=path) redirect_type=$(ynh_app_setting_get --app=$app --key=redirect_type) redirect_path=$(ynh_app_setting_get --app=$app --key=redirect_path) +frame_ancestors=$(ynh_app_setting_get --app=$app --key=frame_ancestors) +client_max_body_size=$(ynh_app_setting_get --app=$app --key=client_max_body_size) +is_public=$(ynh_app_setting_get --app=$app --key=is_public) # Validate redirect path url_regex='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]' @@ -42,10 +45,16 @@ ynh_restore_file "$NGINX_CONF" #================================================= # Make app public if necessary -if [ "$redirect_type" != "private_proxy" ] +if [ $is_public -eq 1 ] then - # unprotected_uris allows SSO credentials to be passed anyway. - ynh_permission_update --permission="main" --add="visitors" + # Everyone can access the app. + # The "main" permission is automatically created before the install script. + if [[ $redirect_type != "proxy" ]] + then + ynh_permission_update --permission="main" --add="visitors" --protected=1 + else + ynh_permission_update --permission="main" --add="visitors" + fi fi #================================================= diff --git a/scripts/upgrade b/scripts/upgrade index 20cc495..96040a4 100644 --- a/scripts/upgrade +++ b/scripts/upgrade @@ -19,6 +19,34 @@ domain=$(ynh_app_setting_get --app=$app --key=domain) path_url=$(ynh_app_setting_get --app=$app --key=path) redirect_type=$(ynh_app_setting_get --app=$app --key=redirect_type) redirect_path=$(ynh_app_setting_get --app=$app --key=redirect_path) +frame_ancestors=$(ynh_app_setting_get --app=$app --key=frame_ancestors) +client_max_body_size=$(ynh_app_setting_get --app=$app --key=client_max_body_size) +is_public=$(ynh_app_setting_get --app=$app --key=is_public) + +#================================================= +# CHECK VERSION +#================================================= + +### This helper will compare the version of the currently installed app and the version of the upstream package. +### $upgrade_type can have 2 different values +### - UPGRADE_APP if the upstream app version has changed +### - UPGRADE_PACKAGE if only the YunoHost package has changed +### ynh_check_app_version_changed will stop the upgrade if the app is up to date. +### UPGRADE_APP should be used to upgrade the core app only if there's an upgrade to do. +upgrade_type=$(ynh_check_app_version_changed) + +#================================================= +# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP +#================================================= + +# Backup the current version of the app +ynh_backup_before_upgrade +ynh_clean_setup () { + # restore it if the upgrade fails + ynh_restore_upgradebackup +} +# Exit if an error occurs during the execution of the script +ynh_abort_if_errors #================================================= # ENSURE DOWNWARD COMPATIBILITY @@ -47,13 +75,7 @@ then is_public=1 fi - if [ "$redirect_type" == "proxy" ] && [ "$is_public" = "1" ] - then - redirect_type="public_proxy" - elif [ "$redirect_type" == "proxy" ] && [ "$is_public" = "0" ] - then - redirect_type="private_proxy" - elif [ "$redirect_type" == "visible_302" ] + if [ "$redirect_type" == "visible_302" ] then redirect_type="public_302" elif [ "$redirect_type" == "visible_301" ] @@ -62,7 +84,7 @@ then fi ynh_app_setting_set $app 'redirect_type' $redirect_type - ynh_app_setting_set $app 'is_public' + ynh_app_setting_set $app 'is_public' $is_public fi # Migrate legacy permissions to new system @@ -73,18 +95,20 @@ then ynh_app_setting_delete --app=$app --key=is_public fi -#================================================= -# BACKUP BEFORE UPGRADE THEN ACTIVE TRAP -#================================================= +# Introduce frame_ancestors +if [ -z "$frame_ancestors" ]; +then + frame_ancestors="'none'" + ynh_app_setting_set $app 'frame_ancestors' $frame_ancestors +fi + +# Introduce client_max_body_size +if [ -z "$client_max_body_size" ]; +then + client_max_body_size="1m" + ynh_app_setting_set $app 'client_max_body_size' $client_max_body_size +fi -# Backup the current version of the app -ynh_backup_before_upgrade -ynh_clean_setup () { - # restore it if the upgrade fails - ynh_restore_upgradebackup -} -# Exit if an error occurs during the execution of the script -ynh_abort_if_errors # Validate redirect path url_regex='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]' @@ -95,33 +119,24 @@ url_regex='(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%= #================================================= # Nginx configuration -for FILE in $(ls ../conf/nginx-*.conf) -do - ynh_replace_string "YNH_LOCATION" "$path_url" $FILE -done -if [ "$redirect_type" = "public_302" ]; -then - ynh_replace_string "YNH_REDIRECT_PATH" "$redirect_path" ../conf/nginx-visible-302.conf - cp ../conf/nginx-visible-302.conf /etc/nginx/conf.d/$domain.d/$app.conf -elif [ "$redirect_type" = "public_301" ]; -then - ynh_replace_string "YNH_REDIRECT_PATH" "$redirect_path" ../conf/nginx-visible-301.conf - cp ../conf/nginx-visible-301.conf /etc/nginx/conf.d/$domain.d/$app.conf -elif [ "$redirect_type" = "public_proxy" ] || [ "$redirect_type" = "private_proxy" ]; -then - ynh_replace_string "YNH_REDIRECT_PATH" "$redirect_path" ../conf/nginx-proxy.conf - cp ../conf/nginx-proxy.conf /etc/nginx/conf.d/$domain.d/$app.conf -fi +cp ../conf/nginx-$redirect_type.conf ../conf/nginx.conf +ynh_add_nginx_config #================================================= # CONFIGURE SSOWAT #================================================= # Make app public if necessary -if [ "$redirect_type" != "private_proxy" ] +if [ $is_public -eq 1 ] then - # unprotected_uris allows SSO credentials to be passed anyway. - ynh_permission_update --permission="main" --add="visitors" + # Everyone can access the app. + # The "main" permission is automatically created before the install script. + if [[ $redirect_type != "proxy" ]] + then + ynh_permission_update --permission="main" --add="visitors" --protected=1 + else + ynh_permission_update --permission="main" --add="visitors" --protected=0 + fi fi #=================================================