From 258ae108c0a860e93110904200f7fc1df3cb87cc Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 30 Aug 2024 14:10:43 +0200 Subject: [PATCH 1/2] try with creds file --- .../providers/filesystem_smb/__init__.py | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/music_assistant/server/providers/filesystem_smb/__init__.py b/music_assistant/server/providers/filesystem_smb/__init__.py index e8192b001..9a7a4c98a 100644 --- a/music_assistant/server/providers/filesystem_smb/__init__.py +++ b/music_assistant/server/providers/filesystem_smb/__init__.py @@ -6,6 +6,8 @@ import platform from typing import TYPE_CHECKING +import aiofiles + from music_assistant.common.helpers.util import get_ip_from_host from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType from music_assistant.common.models.enums import ConfigEntryType @@ -162,6 +164,14 @@ async def mount(self) -> None: password = self.config.get_value(CONF_PASSWORD) share = str(self.config.get_value(CONF_SHARE)) + # somehow alpine doesn't accept the PASSWD env var and passing it in the options string + # means we cant support any special characters, so alternative is to use a credentials file + creds_file = f"/tmp/{self.instance_id}_smb_creds" # noqa: S108 + async with aiofiles.open(creds_file, "w") as _file: + await _file.write(f"username={username}\n") + if password: + await _file.write(f"password={password}\n") + # handle optional subfolder subfolder = str(self.config.get_value(CONF_SUBFOLDER)) if subfolder: @@ -186,15 +196,8 @@ async def mount(self) -> None: options = ["rw"] if mount_options := str(self.config.get_value(CONF_MOUNT_OPTIONS)): options += mount_options.split(",") - options.append(f"username={username}") + options.append(f"credentials={creds_file}") options_str = ",".join(options) - - env_vars = { - **os.environ, - } - if password: - env_vars["PASSWD"] = str(password) - mount_cmd = [ "mount", "-t", @@ -214,7 +217,8 @@ async def mount(self) -> None: "Using mount command: %s", " ".join([m.replace(str(password), "########") if password else m for m in mount_cmd]), ) - returncode, output = await check_output(*mount_cmd, env=env_vars) + returncode, output = await check_output(*mount_cmd) + self.mass.create_task(os.remove, creds_file) if returncode != 0: msg = f"SMB mount failed with error: {output.decode()}" raise LoginFailed(msg) From f9a43fd46938512b6719016204cbb27966080ff8 Mon Sep 17 00:00:00 2001 From: Marcel van der Veldt Date: Fri, 30 Aug 2024 14:28:02 +0200 Subject: [PATCH 2/2] final fix for smb mounting --- Dockerfile | 2 +- .../providers/filesystem_smb/__init__.py | 26 +++++++++---------- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/Dockerfile b/Dockerfile index b2c49dc33..7dce44d22 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,7 +14,7 @@ RUN set -x \ wget \ tzdata \ sox \ - samba \ + cifs-utils \ # install ffmpeg from community repo && apk add --no-cache ffmpeg --repository=https://dl-cdn.alpinelinux.org/alpine/v3.20/community \ # install snapcast from community repo diff --git a/music_assistant/server/providers/filesystem_smb/__init__.py b/music_assistant/server/providers/filesystem_smb/__init__.py index 9a7a4c98a..1168f2f14 100644 --- a/music_assistant/server/providers/filesystem_smb/__init__.py +++ b/music_assistant/server/providers/filesystem_smb/__init__.py @@ -6,8 +6,6 @@ import platform from typing import TYPE_CHECKING -import aiofiles - from music_assistant.common.helpers.util import get_ip_from_host from music_assistant.common.models.config_entries import ConfigEntry, ConfigValueType from music_assistant.common.models.enums import ConfigEntryType @@ -164,14 +162,6 @@ async def mount(self) -> None: password = self.config.get_value(CONF_PASSWORD) share = str(self.config.get_value(CONF_SHARE)) - # somehow alpine doesn't accept the PASSWD env var and passing it in the options string - # means we cant support any special characters, so alternative is to use a credentials file - creds_file = f"/tmp/{self.instance_id}_smb_creds" # noqa: S108 - async with aiofiles.open(creds_file, "w") as _file: - await _file.write(f"username={username}\n") - if password: - await _file.write(f"password={password}\n") - # handle optional subfolder subfolder = str(self.config.get_value(CONF_SUBFOLDER)) if subfolder: @@ -196,8 +186,17 @@ async def mount(self) -> None: options = ["rw"] if mount_options := str(self.config.get_value(CONF_MOUNT_OPTIONS)): options += mount_options.split(",") - options.append(f"credentials={creds_file}") options_str = ",".join(options) + + # pass the username+password using (scoped) env variables + # to prevent leaking in the process list and special chars supported + env_vars = { + **os.environ, + "USER": username, + } + if password: + env_vars["PASSWD"] = str(password) + mount_cmd = [ "mount", "-t", @@ -215,10 +214,9 @@ async def mount(self) -> None: self.logger.log( VERBOSE_LOG_LEVEL, "Using mount command: %s", - " ".join([m.replace(str(password), "########") if password else m for m in mount_cmd]), + " ".join(mount_cmd), ) - returncode, output = await check_output(*mount_cmd) - self.mass.create_task(os.remove, creds_file) + returncode, output = await check_output(*mount_cmd, env=env_vars) if returncode != 0: msg = f"SMB mount failed with error: {output.decode()}" raise LoginFailed(msg)