diff --git a/ssh/Dockerfile b/ssh/Dockerfile index b7acbd3d..e38b6bf2 100644 --- a/ssh/Dockerfile +++ b/ssh/Dockerfile @@ -42,6 +42,7 @@ RUN \ colordiff=1.0.22-r0 \ docker-bash-completion=29.5.3-r0 \ docker-zsh-completion=29.5.3-r0 \ + fish \ gcompat=1.1.0-r4 \ git=2.54.0-r0 \ htop=3.5.1-r1 \ @@ -66,7 +67,7 @@ RUN \ nmap-ncat=7.99-r0 \ openssh=10.3_p1-r0 \ openssl=3.5.7-r0 \ - procps-ng=4.0.6-r0 \ + procps-ng=4.0.6-r1 \ pwgen=2.08-r3 \ pulseaudio-utils=17.0-r7 \ py3-pip=26.1.2-r0 \ @@ -80,6 +81,7 @@ RUN \ tmux=3.6b-r0 \ ttyd=1.7.7-r0 \ wget=1.25.0-r3 \ + zellij \ zip=3.0-r13 \ zsh-autosuggestions=0.7.1-r0 \ zsh-syntax-highlighting=0.8.0-r1 \ @@ -93,8 +95,10 @@ RUN \ \ && chmod a+x /usr/bin/ha \ && ha completion bash > /usr/share/bash-completion/completions/ha \ + && mkdir -p /usr/share/fish/vendor_completions.d \ + && ha completion fish > /usr/share/fish/vendor_completions.d/ha.fish \ \ - && sed -i -e "s#bin/sh#bin/zsh#" /etc/passwd \ + && sed -i -e "s#/bin/sh#/usr/bin/fish#" /etc/passwd \ \ && cp /usr/bin/docker /usr/local/bin/.undocked \ \ diff --git a/ssh/config.yaml b/ssh/config.yaml index 13668960..9bf6fd2f 100644 --- a/ssh/config.yaml +++ b/ssh/config.yaml @@ -63,7 +63,9 @@ options: allow_agent_forwarding: false allow_remote_port_forwarding: false allow_tcp_forwarding: false - zsh: true + shell: fish + session_backend: zellij + zsh: false share_sessions: false packages: [] init_commands: [] @@ -79,7 +81,9 @@ schema: allow_agent_forwarding: bool allow_remote_port_forwarding: bool allow_tcp_forwarding: bool - zsh: bool + shell: list(fish|zsh|bash) + session_backend: list(zellij|tmux) + zsh: bool? share_sessions: bool packages: - str diff --git a/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-ssh/run b/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-ssh/run index b6fa82fc..01ed84e9 100755 --- a/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-ssh/run +++ b/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-ssh/run @@ -98,8 +98,9 @@ username=$(bashio::string.lower "${username}") # Create user account if the user isn't root if [[ "${username}" != "root" ]]; then - # Create an user account - adduser -D "${username}" -s "/bin/zsh" \ + # Create an user account. Keep the SSH login user on /bin/sh so it can + # consistently hand off to the configured root login shell via sudo -i. + adduser -D "${username}" -s "/bin/sh" \ || bashio::exit.nok 'Failed creating the user account' # Add new user to the wheel group @@ -107,7 +108,7 @@ if [[ "${username}" != "root" ]]; then || bashio::exit.nok 'Failed adding user to wheel group' # Ensure new user switches to root after login - echo 'exec sudo -i' > "/home/${username}/.zprofile" \ + echo 'exec sudo -i' > "/home/${username}/.profile" \ || bashio::exit.nok 'Failed configuring user profile' fi @@ -129,7 +130,7 @@ if bashio::config.has_value 'ssh.authorized_keys'; then fi # Port -sed -i "s/Port\\ .*/Port\\ ${port}/" "${SSH_CONFIG_PATH}" \ +sed -i "s/Port\ .*/Port\ ${port}/" "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed configuring port' # SFTP access @@ -140,16 +141,16 @@ fi # Allow specified user to log in if [[ "${username}" != "root" ]]; then - sed -i "s/AllowUsers\\ .*/AllowUsers\\ ${username}/" "${SSH_CONFIG_PATH}" \ + sed -i "s/AllowUsers\ .*/AllowUsers\ ${username}/" "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed opening SSH for the configured user' else - sed -i "s/PermitRootLogin\\ .*/PermitRootLogin\\ yes/" "${SSH_CONFIG_PATH}" \ + sed -i "s/PermitRootLogin\ .*/PermitRootLogin\ yes/" "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed opening SSH for the root user' fi # Enable password authentication when password is set if bashio::config.has_value 'ssh.password'; then - sed -i "s/PasswordAuthentication.*/PasswordAuthentication\\ yes/" \ + sed -i "s/PasswordAuthentication.*/PasswordAuthentication\ yes/" \ "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed to setup SSH password authentication' fi @@ -166,21 +167,21 @@ fi # Enable Agent forwarding if bashio::config.true 'ssh.allow_agent_forwarding'; then - sed -i "s/AllowAgentForwarding.*/AllowAgentForwarding\\ yes/" \ + sed -i "s/AllowAgentForwarding.*/AllowAgentForwarding\ yes/" \ "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed to setup SSH Agent Forwarding' fi # Allow remote port forwarding if bashio::config.true 'ssh.allow_remote_port_forwarding'; then - sed -i "s/GatewayPorts.*/GatewayPorts\\ yes/" \ + sed -i "s/GatewayPorts.*/GatewayPorts\ yes/" \ "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed to setup remote port forwarding' fi # Allow TCP forewarding if bashio::config.true 'ssh.allow_tcp_forwarding'; then - sed -i "s/AllowTcpForwarding.*/AllowTcpForwarding\\ yes/" \ + sed -i "s/AllowTcpForwarding.*/AllowTcpForwarding\ yes/" \ "${SSH_CONFIG_PATH}" \ || bashio::exit.nok 'Failed to setup SSH TCP Forwarding' fi diff --git a/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-user/run b/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-user/run index 113b8e86..3283a5bb 100755 --- a/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-user/run +++ b/ssh/rootfs/etc/s6-overlay/s6-rc.d/init-user/run @@ -7,6 +7,10 @@ readonly -a DIRECTORIES=(addon_configs addons backup homeassistant media share ssl) readonly BASH_HISTORY_FILE=/root/.bash_history readonly BASH_HISTORY_PERSISTENT_FILE=/data/.bash_history +readonly FISH_CONF_D_PATH=/root/.config/fish/conf.d +readonly FISH_HOME_ASSISTANT_CONF_D_FILE=/root/.config/fish/conf.d/homeassistant.fish +readonly FISH_HISTORY_FILE=/root/.local/share/fish/fish_history +readonly FISH_HISTORY_PERSISTENT_FILE=/data/fish_history readonly GIT_CONFIG=/data/.gitconfig readonly HOME_ASSISTANT_PROFILE_D_FILE=/etc/profile.d/homeassistant.sh readonly SSH_USER_PATH=/data/.ssh @@ -14,6 +18,104 @@ readonly VSCODE_SERVER_PATH=/root/.vscode-server readonly VSCODE_SERVER_PERSISTENT_PATH=/data/.vscode-server readonly ZSH_HISTORY_FILE=/root/.zsh_history readonly ZSH_HISTORY_PERSISTENT_FILE=/data/.zsh_history +declare login_shell +declare session_backend +declare shell_path + +get_login_shell() { + if bashio::config.has_value 'shell'; then + bashio::config 'shell' + elif bashio::config.true 'zsh'; then + echo 'zsh' + else + echo 'bash' + fi +} + +get_shell_path() { + case "$1" in + fish) + echo '/usr/bin/fish' + ;; + zsh) + echo '/bin/zsh' + ;; + bash) + echo '/bin/bash' + ;; + *) + bashio::exit.nok "Unsupported shell: $1" + ;; + esac +} + +get_session_backend() { + if bashio::config.has_value 'session_backend'; then + bashio::config 'session_backend' + else + echo 'zellij' + fi +} + +set_root_shell() { + sed -i -r -e "s|^(root:[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:).*|\1$1|" /etc/passwd* \ + || bashio::exit.nok 'Failed setting the root login shell' +} + +write_bash_profile() { + cat > /root/.bash_profile < /root/.zprofile < "${FISH_HOME_ASSISTANT_CONF_D_FILE}" <> "${FISH_HOME_ASSISTANT_CONF_D_FILE}" <> "${HOME_ASSISTANT_PROFILE_D_FILE}" \ @@ -88,17 +213,20 @@ if ! bashio::fs.directory_exists "${VSCODE_SERVER_PERSISTENT_PATH}"; then fi ln -s "${VSCODE_SERVER_PERSISTENT_PATH}" "${VSCODE_SERVER_PATH}" -# Disable SSH & Web Terminal session sharing if configured -if ! bashio::config.true 'share_sessions'; then +# Configure SSH & Web Terminal session sharing if requested. +if bashio::config.true 'share_sessions'; then + write_bash_profile + write_zsh_profile +else bashio::log.notice 'Session sharing has been disabled!' - rm /root/.bash_profile - rm /root/.zprofile + rm -f /root/.bash_profile + rm -f /root/.zprofile fi # Install user configured/requested packages # # Failures here are intentionally non-fatal: if the package indexes or a -# package cannot be fetched (e.g. broken DNS or no network), we still want +# package cannot be fetched (e.g., broken DNS or no network), we still want # the terminal to come up so the host remains reachable for debugging. if bashio::config.has_value 'packages'; then if apk update; then @@ -119,11 +247,11 @@ if bashio::config.has_value 'init_commands'; then # Use bashio::config to properly iterate over the array, preserving multi-line commands length=$(bashio::config 'init_commands | length') \ || bashio::exit.nok 'Failed to get init_commands array length' - + for (( i=0; i