diff --git a/conf/nginx/redirect_to_admin.conf b/conf/nginx/redirect_to_admin.conf index 22748daa3..1d7933c6a 100644 --- a/conf/nginx/redirect_to_admin.conf +++ b/conf/nginx/redirect_to_admin.conf @@ -1,3 +1,3 @@ location / { - return 302 https://$http_host/yunohost/admin; + return 302 https://$host/yunohost/admin; } diff --git a/conf/nginx/server.tpl.conf b/conf/nginx/server.tpl.conf index 16b5c46c2..ccba8a082 100644 --- a/conf/nginx/server.tpl.conf +++ b/conf/nginx/server.tpl.conf @@ -25,7 +25,7 @@ server { {# Note that this != "False" is meant to be failure-safe, in the case the redrect_to_https would happen to contain empty string or whatever value. We absolutely don't want to disable the HTTPS redirect *except* when it's explicitly being asked to be disabled. #} {% if redirect_to_https != "False" %} location / { - return 301 https://$http_host$request_uri; + return 301 https://$host$request_uri; } {# The app config snippets are not included in the HTTP conf unless HTTPS redirect is disabled, because app's location may blocks will conflict or bypass/ignore the HTTPS redirection. #} {% else %} diff --git a/conf/nginx/yunohost_api.conf.inc b/conf/nginx/yunohost_api.conf.inc index c9ae34f82..f434dbe96 100644 --- a/conf/nginx/yunohost_api.conf.inc +++ b/conf/nginx/yunohost_api.conf.inc @@ -4,7 +4,7 @@ location /yunohost/api/ { proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; - proxy_set_header Host $http_host; + proxy_set_header Host $host; {% if webadmin_allowlist_enabled == "True" %} {% for ip in webadmin_allowlist.split(',') %} diff --git a/debian/changelog b/debian/changelog index b368e0768..263a3c73a 100644 --- a/debian/changelog +++ b/debian/changelog @@ -4,6 +4,13 @@ yunohost (12.0.0) unstable; urgency=low -- Alexandre Aubin Thu, 04 May 2023 20:30:19 +0200 +yunohost (11.1.21.4) stable; urgency=low + + - regenconf: Get rid of previous tmp hack about /dev/null for people that went through the very first 11.1.21, because it's causing issue in unpriviledged LXC or similar context (8242cab7) + - apps: don't attempt to del password key if it doesn't exist (29338f79) + + -- Alexandre Aubin Wed, 14 Jun 2023 15:48:33 +0200 + yunohost (11.1.21.3) stable; urgency=low - Fix again /var/www/.well-known/ynh-diagnosis/ perms which are too broad and could be exploited to serve malicious files x_x (84984ad8) diff --git a/helpers/apps b/helpers/apps index 4b253ff90..7a93298c0 100644 --- a/helpers/apps +++ b/helpers/apps @@ -124,7 +124,7 @@ ynh_remove_apps() { # Requires YunoHost version 11.0.* or higher, and that the app relies on packaging v2 or higher. # The spawned shell will have environment variables loaded and environment files sourced # from the app's service configuration file (defaults to $app.service, overridable by the packager with `service` setting). -# If the app relies on a specific PHP version, then `php` will be aliased that version. +# If the app relies on a specific PHP version, then `php` will be aliased that version. The PHP command will also be appended with the `phpflags` settings. ynh_spawn_app_shell() { # Declare an array to define the options of this helper. local legacy_args=a @@ -176,9 +176,10 @@ ynh_spawn_app_shell() { # Force `php` to its intended version # We use `eval`+`export` since `alias` is not propagated to subshells, even with `export` local phpversion=$(ynh_app_setting_get --app=$app --key=phpversion) + local phpflags=$(ynh_app_setting_get --app=$app --key=phpflags) if [ -n "$phpversion" ] then - eval "php() { php${phpversion} \"\$@\"; }" + eval "php() { php${phpversion} ${phpflags} \"\$@\"; }" export -f php fi diff --git a/share/actionsmap.yml b/share/actionsmap.yml index 444533a1d..1622c9675 100644 --- a/share/actionsmap.yml +++ b/share/actionsmap.yml @@ -872,6 +872,8 @@ app: ### app_shell() shell: action_help: Open an interactive shell with the app environment already loaded + # Here we set a GET only not to lock the command line. There is no actual API endpoint for app_shell() + api: GET /apps//shell arguments: app: help: App ID diff --git a/src/app.py b/src/app.py index 4d11b47fb..dc654650f 100644 --- a/src/app.py +++ b/src/app.py @@ -84,7 +84,7 @@ re_app_instance_name = re.compile( ) APP_REPO_URL = re.compile( - r"^https://[a-zA-Z0-9-_.]+/[a-zA-Z0-9-_./~]+/[a-zA-Z0-9-_.]+_ynh(/?(-/)?tree/[a-zA-Z0-9-_.]+)?(\.git)?/?$" + r"^https://[a-zA-Z0-9-_.]+/[a-zA-Z0-9-_./~]+/[a-zA-Z0-9-_.]+_ynh(/?(-/)?(tree|src/(branch|tag|commit))/[a-zA-Z0-9-_.]+)?(\.git)?/?$" ) APP_FILES_TO_COPY = [ @@ -1193,7 +1193,8 @@ def app_install( for question in questions: # Or should it be more generally question.redact ? if question.type == "password": - del env_dict_for_logging[f"YNH_APP_ARG_{question.name.upper()}"] + if f"YNH_APP_ARG_{question.name.upper()}" in env_dict_for_logging: + del env_dict_for_logging[f"YNH_APP_ARG_{question.name.upper()}"] if question.name in env_dict_for_logging: del env_dict_for_logging[question.name] diff --git a/src/diagnosers/12-dnsrecords.py b/src/diagnosers/12-dnsrecords.py index 2d46f979c..be9bf5418 100644 --- a/src/diagnosers/12-dnsrecords.py +++ b/src/diagnosers/12-dnsrecords.py @@ -182,6 +182,10 @@ class MyDiagnoser(Diagnoser): if success != "ok": return None else: + if type_ == "TXT" and isinstance(answers,list): + for part in answers: + if part.startswith('"v=spf1'): + return part return answers[0] if len(answers) == 1 else answers def current_record_match_expected(self, r): diff --git a/src/firewall.py b/src/firewall.py index ccd7e6d88..8f0833160 100644 --- a/src/firewall.py +++ b/src/firewall.py @@ -331,7 +331,7 @@ def firewall_reload(skip_upnp=False): # Refresh port forwarding with UPnP firewall_upnp(no_refresh=False) - _run_service_command("reload", "fail2ban") + _run_service_command("restart", "fail2ban") if errors: logger.warning(m18n.n("firewall_rules_cmd_failed")) diff --git a/src/tests/test_appurl.py b/src/tests/test_appurl.py index 8e5b14d34..0c51b62fc 100644 --- a/src/tests/test_appurl.py +++ b/src/tests/test_appurl.py @@ -69,8 +69,19 @@ def test_repo_url_definition(): assert _is_app_repo_url("git@github.com:YunoHost-Apps/foobar_ynh.git") assert _is_app_repo_url("https://git.super.host/~max/foobar_ynh") + ### Gitea + assert _is_app_repo_url("https://gitea.instance.tld/user/repo_ynh") + assert _is_app_repo_url("https://gitea.instance.tld/user/repo_ynh/src/branch/branch_name") + assert _is_app_repo_url("https://gitea.instance.tld/user/repo_ynh/src/tag/tag_name") + assert _is_app_repo_url("https://gitea.instance.tld/user/repo_ynh/src/commit/abcd1234") + + ### Invalid patterns + + # no schema assert not _is_app_repo_url("github.com/YunoHost-Apps/foobar_ynh") + # http assert not _is_app_repo_url("http://github.com/YunoHost-Apps/foobar_ynh") + # does not end in `_ynh` assert not _is_app_repo_url("https://github.com/YunoHost-Apps/foobar_wat") assert not _is_app_repo_url("https://github.com/YunoHost-Apps/foobar_ynh_wat") assert not _is_app_repo_url("https://github.com/YunoHost-Apps/foobar/tree/testing") diff --git a/src/tools.py b/src/tools.py index 740f92c9d..488ed516b 100644 --- a/src/tools.py +++ b/src/tools.py @@ -16,6 +16,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # +import pwd import re import os import subprocess @@ -174,6 +175,12 @@ def tools_postinstall( raw_msg=True, ) + # Crash early if the username is already a system user, which is + # a common confusion. We don't want to crash later and end up in an half-configured state. + all_existing_usernames = {x.pw_name for x in pwd.getpwall()} + if username in all_existing_usernames: + raise YunohostValidationError("system_username_exists") + if username in ADMIN_ALIASES: raise YunohostValidationError( f"Unfortunately, {username} cannot be used as a username", raw_msg=True diff --git a/src/utils/resources.py b/src/utils/resources.py index 9891fe9c6..7f6f263de 100644 --- a/src/utils/resources.py +++ b/src/utils/resources.py @@ -22,7 +22,7 @@ import shutil import random import tempfile import subprocess -from typing import Dict, Any, List +from typing import Dict, Any, List, Union from moulinette import m18n from moulinette.utils.process import check_output @@ -1011,16 +1011,16 @@ class AptDependenciesAppResource(AppResource): ##### Example ```toml [resources.apt] - packages = "nyancat, lolcat, sl" + packages = ["nyancat", "lolcat", "sl"] # (this part is optional and corresponds to the legacy ynh_install_extra_app_dependencies helper) extras.yarn.repo = "deb https://dl.yarnpkg.com/debian/ stable main" extras.yarn.key = "https://dl.yarnpkg.com/debian/pubkey.gpg" - extras.yarn.packages = "yarn" + extras.yarn.packages = ["yarn"] ``` ##### Properties - - `packages`: Comma-separated list of packages to be installed via `apt` + - `packages`: List of packages to be installed via `apt` - `packages_from_raw_bash`: A multi-line bash snippet (using triple quotes as open/close) which should echo additional packages to be installed. Meant to be used for packages to be conditionally installed depending on architecture, debian version, install questions, or other logic. - `extras`: A dict of (repo, key, packages) corresponding to "extra" repositories to fetch dependencies from @@ -1044,20 +1044,14 @@ class AptDependenciesAppResource(AppResource): packages: List = [] packages_from_raw_bash: str = "" - extras: Dict[str, Dict[str, str]] = {} + extras: Dict[str, Dict[str, Union[str, List]]] = {} def __init__(self, properties: Dict[str, Any], *args, **kwargs): - for key, values in properties.get("extras", {}).items(): - if not all( - isinstance(values.get(k), str) for k in ["repo", "key", "packages"] - ): - raise YunohostError( - "In apt resource in the manifest: 'extras' repo should have the keys 'repo', 'key' and 'packages' defined and be strings", - raw_msg=True, - ) - super().__init__(properties, *args, **kwargs) + if isinstance(self.packages, str): + self.packages = [value.strip() for value in self.packages.split(",")] + if self.packages_from_raw_bash: out, err = self.check_output_bash_snippet(self.packages_from_raw_bash) if err: @@ -1065,17 +1059,32 @@ class AptDependenciesAppResource(AppResource): "Error while running apt resource packages_from_raw_bash snippet:" ) logger.error(err) - self.packages += ", " + out.replace("\n", ", ") + self.packages += out.split("\n") + + for key, values in self.extras.items(): + if isinstance(values.get("packages"), str): + values["packages"] = [value.strip() for value in values["packages"].split(",")] # type: ignore + + if not isinstance(values.get("repo"), str) \ + or not isinstance(values.get("key"), str) \ + or not isinstance(values.get("packages"), list): + raise YunohostError( + "In apt resource in the manifest: 'extras' repo should have the keys 'repo', 'key' defined as strings and 'packages' defined as list", + raw_msg=True, + ) def provision_or_update(self, context: Dict = {}): - script = [f"ynh_install_app_dependencies {self.packages}"] + script = " ".join(["ynh_install_app_dependencies", *self.packages]) for repo, values in self.extras.items(): - script += [ - f"ynh_install_extra_app_dependencies --repo='{values['repo']}' --key='{values['key']}' --package='{values['packages']}'" - ] + script += "\n" + " ".join([ + "ynh_install_extra_app_dependencies", + f"--repo='{values['repo']}'", + f"--key='{values['key']}'", + f"--package='{' '.join(values['packages'])}'" + ]) # FIXME : we're feeding the raw value of values['packages'] to the helper .. if we want to be consistent, may they should be comma-separated, though in the majority of cases, only a single package is installed from an extra repo.. - self._run_script("provision_or_update", "\n".join(script)) + self._run_script("provision_or_update", script) def deprovision(self, context: Dict = {}): self._run_script("deprovision", "ynh_remove_app_dependencies") @@ -1166,7 +1175,7 @@ class PortsResource(AppResource): port_value = self.get_setting(setting_name) if not port_value and name != "main": # Automigrate from legacy setting foobar_port (instead of port_foobar) - legacy_setting_name = "{name}_port" + legacy_setting_name = f"{name}_port" port_value = self.get_setting(legacy_setting_name) if port_value: self.set_setting(setting_name, port_value)