[GH-ISSUE #1201] Working only in bash shell only #444

Open
opened 2026-10-13 20:21:35 +00:00 by bsakaguchi · 8 comments
Owner

Originally created by @YouROK on GitHub (Oct 25, 2024).
Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/1201

Describe the bug
If a default shell other than bash is installed on the server, the installation fails.

To Reproduce
Steps to reproduce the behavior:
On vps server:

apt install fish
chsh
# enter "/usr/bin/fish"

On host in AmneziaVPN select Self-hosted VPN and all next. VPN protocol I select awg.
And click Install button

Expected behavior
Execute scripts in the bash, not in the default shell.

Screenshots
image

Desktop (please complete the following information):

  • OS: MacOS 15.0.1
  • Version 4.8.1.9

Server (please complete the following information):

  • OS: Debian 12
Originally created by @YouROK on GitHub (Oct 25, 2024). Original GitHub issue: https://github.com/amnezia-vpn/amnezia-client/issues/1201 **Describe the bug** If a default shell other than bash is installed on the server, the installation fails. **To Reproduce** Steps to reproduce the behavior: On vps server: ``` apt install fish chsh # enter "/usr/bin/fish" ``` On host in AmneziaVPN select Self-hosted VPN and all next. VPN protocol I select awg. And click Install button **Expected behavior** Execute scripts in the bash, not in the default shell. **Screenshots** <img width="492" alt="image" src="https://github.com/user-attachments/assets/a45d7647-2a79-444e-be86-1ea8ccbb4262"> **Desktop (please complete the following information):** - OS: MacOS 15.0.1 - Version 4.8.1.9 **Server (please complete the following information):** - OS: Debian 12
Author
Owner

@vanyasem commented on GitHub (Oct 29, 2024):

The scripts should specify a shebang #!/usr/bin/env bash instead of relying on the default shell

check_user_in_sudo.sh fails to get executed on fish shell, resulting in an Error 205: ServerUserNotInSudo:

$ CUR_USER=$(whoami);\
  groups $CUR_USER
fish: Unsupported use of '='. In fish, please use 'set CUR_USER $(whoami)'.
<!-- gh-comment-id:2443843889 --> @vanyasem commented on GitHub (Oct 29, 2024): The scripts should specify a [shebang](https://en.wikipedia.org/wiki/Shebang_%28Unix%29) `#!/usr/bin/env bash` instead of relying on the default shell [check_user_in_sudo.sh](https://github.com/amnezia-vpn/amnezia-client/blob/4.8.2.3/client/server_scripts/check_user_in_sudo.sh) fails to get executed on fish shell, resulting in an [Error 205: ServerUserNotInSudo](https://docs.amnezia.org/ru/troubleshooting/error-codes/#error-205-serverusernotinsudo): ``` $ CUR_USER=$(whoami);\ groups $CUR_USER fish: Unsupported use of '='. In fish, please use 'set CUR_USER $(whoami)'. ```
Author
Owner

@lunardunno commented on GitHub (Mar 3, 2025):

  1. This script is executed by the program line by line in sh.
  2. Not all script error messages cause the server controller to stop.
  3. Your first message shows error 206, not 205, because there is something wrong with your server or with the user you created (if you are not using root).
  4. Work is underway to improve the check_user_in_sudo.sh script.
<!-- gh-comment-id:2695479408 --> @lunardunno commented on GitHub (Mar 3, 2025): 1. This script is executed by the program line by line in sh. 2. Not all script error messages cause the server controller to stop. 3. Your first message shows error 206, not 205, because there is something wrong with your server or with the user you created (if you are not using root). 4. Work is underway to improve the check_user_in_sudo.sh script.
Author
Owner

@vanyasem commented on GitHub (Mar 4, 2025):

@lunardunno

You're right, a shebang won't help, as the script is executed line-by-line. My mistake

However, it uses the default user's shell instead of directly calling sh:

  1. The script is ran in ServerController::isUserInSudo:
    github.com/amnezia-vpn/amnezia-client@157064763e/client/core/controllers/serverController.cpp (L771)

  2. ServerController::runScript sends the command to an ssh client:
    github.com/amnezia-vpn/amnezia-client@157064763e/client/core/controllers/serverController.cpp (L87)

  3. Finally, Client::executeCommand calls libssh:
    github.com/amnezia-vpn/amnezia-client@678bfffe49/client/core/sshclient.cpp (L148)

  4. ssh_channel_request_exec Run a shell command without an interactive shell.: https://api.libssh.org/master/group__libssh__channel.html#ga567d509183ade0a77190f390e2b5747d

You might get confused by the phrase This is similar to 'sh -c command'.

This implies the fact that a non-interactive shell is spawned, similar to how sh -c executes commands in a non-interactive shell. It does not mean that the commands are in fact executed in sh. Any shell has a non-interactive mode, and the default shell for the particular user is used to run the command.

There is no guarantee that the user's shell will be POSIX-compliant. For example, having fish or pwsh set as a login shell breaks the execution of the script.

This could be fixed by prefixing every line with /bin/sh -c or something similar.

At the very least, there could be a check that verifies that a supported (POSIX-compliant) shell is being used by the specified ssh user.

<!-- gh-comment-id:2696733363 --> @vanyasem commented on GitHub (Mar 4, 2025): @lunardunno You're right, a shebang won't help, as the script is executed line-by-line. My mistake However, it uses the default user's shell instead of directly calling `sh`: 1) The script is ran in `ServerController::isUserInSudo`: https://github.com/amnezia-vpn/amnezia-client/blob/157064763e0897e60fd9461bf0635ced38315a6d/client/core/controllers/serverController.cpp#L771 2) `ServerController::runScript` sends the command to an ssh client: https://github.com/amnezia-vpn/amnezia-client/blob/157064763e0897e60fd9461bf0635ced38315a6d/client/core/controllers/serverController.cpp#L87 3) Finally, `Client::executeCommand` calls `libssh`: https://github.com/amnezia-vpn/amnezia-client/blob/678bfffe492ed89911a9dcacfb125e82aa1ce1be/client/core/sshclient.cpp#L148 4) `ssh_channel_request_exec` _Run a shell command without an interactive shell._: https://api.libssh.org/master/group__libssh__channel.html#ga567d509183ade0a77190f390e2b5747d You might get confused by the phrase `This is similar to 'sh -c command'.` This implies the fact that a non-interactive shell is spawned, similar to how `sh -c` executes commands in a non-interactive shell. It does not mean that the commands are in fact executed in `sh`. Any shell has a non-interactive mode, and the default shell for the particular user is used to run the command. There is no guarantee that the user's shell will be POSIX-compliant. For example, having `fish` or `pwsh` set as a login shell breaks the execution of the script. This could be fixed by prefixing every line with `/bin/sh -c` or something similar. At the very least, there could be a check that verifies that a supported (POSIX-compliant) shell is being used by the specified ssh user.
Author
Owner

@vanyasem commented on GitHub (Mar 4, 2025):

I actually made a small example to prove that your assumption that the code is ran in /bin/sh is wrong: https://gist.github.com/vanyasem/4c931f186c48da7b98b0f5518f1c6c64

ssh_example establishes an SSH connection via a private key, and then executes ssh_channel_request_exec(channel, "echo $0"). The output of the command is printed to console. echo $0 returns the name of the currently running program, which in this case would be the name of the shell.

As you can see, changing the user's shell by calling chsh results in $SHELL being changed. If the commands were executed in sh, the value would always be sh

Screenshot 2025-03-04 at 12 04 52
<!-- gh-comment-id:2696733987 --> @vanyasem commented on GitHub (Mar 4, 2025): I actually made a small example to prove that your assumption that the code is ran in `/bin/sh` is wrong: https://gist.github.com/vanyasem/4c931f186c48da7b98b0f5518f1c6c64 `ssh_example` establishes an SSH connection via a private key, and then executes `ssh_channel_request_exec(channel, "echo $0")`. The output of the command is printed to console. `echo $0` returns the name of the currently running program, which in this case would be the name of the shell. As you can see, changing the user's shell by calling `chsh` results in `$SHELL` being changed. If the commands were executed in `sh`, the value would always be `sh` <img width="769" alt="Screenshot 2025-03-04 at 12 04 52" src="https://github.com/user-attachments/assets/8b8cb8f6-c6f3-4a19-8e15-4203e0acb0a8" />
Author
Owner

@lunardunno commented on GitHub (Mar 8, 2025):

@vanyasem
How do I write a shebang on the first line so that the line ends with ;\ and so that the ;\ at the end is not ignored because of the # at the beginning of the line?
Otherwise, the shebang will be executed as a separate script. And all the other lines will be executed as another, new script, which does not have a shebang at the beginning.

<!-- gh-comment-id:2707837444 --> @lunardunno commented on GitHub (Mar 8, 2025): @vanyasem How do I write a shebang on the first line so that the line ends with ;\ and so that the ;\ at the end is not ignored because of the # at the beginning of the line? Otherwise, the shebang will be executed as a separate script. And all the other lines will be executed as another, new script, which does not have a shebang at the beginning.
Author
Owner

@vanyasem commented on GitHub (Mar 8, 2025):

I already told you that I was incorrect about the shebang, there is no need for a shebang in this particular issue.
I wrote a long explanation of the issue with its possible solutions, including a minimal reproducible example of the issue.
What is your native language? Do you have a language barrier that prevents you from understanding the issue? If it happens that your first language is Russian, I can repeat the explanation in Russian, as it also happens to be my first language

Anyways,
Each command passed to ssh_channel_request_exec has to manually specify a shell for execution
Wrapping every line passed to ssh in sh -c would be the easiest solution
Basically modify m_sshClient.executeCommand() implementation to use sh -c "$lineToExec" instead of directly passing lineToExec to ssh

<!-- gh-comment-id:2708014435 --> @vanyasem commented on GitHub (Mar 8, 2025): I already told you that I was incorrect about the shebang, there is no need for a shebang in this particular issue. I wrote a long explanation of the issue with its possible solutions, including a minimal reproducible example of the issue. What is your native language? Do you have a language barrier that prevents you from understanding the issue? If it happens that your first language is Russian, I can repeat the explanation in Russian, as it also happens to be my first language Anyways, Each command passed to `ssh_channel_request_exec` has to manually **specify a shell** for execution Wrapping every line passed to ssh in `sh -c` would be the easiest solution Basically modify `m_sshClient.executeCommand()` implementation to use `sh -c "$lineToExec"` instead of directly passing `lineToExec` to ssh
Author
Owner

@lunardunno commented on GitHub (Mar 9, 2025):

@vanyasem
I wasn't precise. Scripts are executed in the server user's default shell, which should be POSIX compliant. Specific examples of shells that AmneziaVPN should have no problem with are BASH and SH. FISH is not POSIX compliant. For this reason, you should either change your user's default shell to BASH or create a separate user with a BASH default shell for use with AmneziaVPN.
A compatibility issue with SH was also identified. It was fixed in fix for sh #1462 and this fix should be included in the next AmneziaVPN release.

<!-- gh-comment-id:2708711761 --> @lunardunno commented on GitHub (Mar 9, 2025): @vanyasem I wasn't precise. Scripts are executed in the server user's default shell, which should be POSIX compliant. Specific examples of shells that AmneziaVPN should have no problem with are BASH and SH. FISH is not POSIX compliant. For this reason, you should either change your user's default shell to BASH or create a separate user with a BASH default shell for use with AmneziaVPN. A compatibility issue with SH was also identified. It was fixed in [fix for sh #1462](https://github.com/amnezia-vpn/amnezia-client/pull/1462) and this fix should be included in the next AmneziaVPN release.
Author
Owner

@vanyasem commented on GitHub (Mar 10, 2025):

@lunardunno

should be POSIX compliant

This is not specified anywhere in the docs, neither is it mentioned in the app's error message

Apart from that, this is very unprofessional - no app should "assume" a particular shell. If an app requires a POSIX-compliant shell, it should execute /usr/bin/env bash or /usr/bin/env sh, which would locate the closest appropriate shell on the user's system, and pass the required commands to it. Blindly passing commands to a default ssh shell is not a good option.

To drop the requirement of having the default shell set to a POSIX-compliant shell it's enough to just wrap the command passed to ssh in /usr/bin/env sh -c "$command". There is no technical reason to require having a separate user with a different shell set as default. It's a workaround made necessary by a poorly (faulty) designed system. This is a temporary hack, not a real solution to the issue at hand.

See https://github.com/koekeishiya/skhd/issues/85 for a similar issue

<!-- gh-comment-id:2710780022 --> @vanyasem commented on GitHub (Mar 10, 2025): @lunardunno > should be POSIX compliant This is not specified anywhere in the docs, neither is it mentioned in the app's error message Apart from that, this is very unprofessional - no app should "assume" a particular shell. If an app requires a POSIX-compliant shell, it should execute `/usr/bin/env bash` or `/usr/bin/env sh`, which would locate the closest appropriate shell on the user's system, and pass the required commands to it. Blindly passing commands to a default ssh shell is not a good option. To drop the requirement of having the default shell set to a POSIX-compliant shell it's enough to just wrap the command passed to ssh in `/usr/bin/env sh -c "$command"`. There is no technical reason to require having a separate user with a different shell set as default. It's a workaround made necessary by a poorly (faulty) designed system. This is a temporary hack, not a real solution to the issue at hand. See https://github.com/koekeishiya/skhd/issues/85 for a similar issue
Sign in to join this conversation.
No labels
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
amnezia-vpn/amnezia-client#444
No description provided.