Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions ssh/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
Expand All @@ -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 \
\
Expand Down
8 changes: 6 additions & 2 deletions ssh/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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: []
Expand All @@ -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
Expand Down
21 changes: 11 additions & 10 deletions ssh/rootfs/etc/s6-overlay/s6-rc.d/init-ssh/run
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,17 @@ 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
adduser "${username}" wheel \
|| 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

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
188 changes: 158 additions & 30 deletions ssh/rootfs/etc/s6-overlay/s6-rc.d/init-user/run
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,115 @@
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
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 <<EOF
if [[ -z "\${TMUX}" ]] && [[ -z "\${ZELLIJ}" ]]; then
case "${session_backend}" in
zellij)
exec zellij attach --create homeassistant options --default-shell "${shell_path}"
;;
tmux)
exec tmux -u new -A -s homeassistant "${shell_path}" -l
;;
esac
fi
EOF
}

write_zsh_profile() {
cat > /root/.zprofile <<EOF
if [[ -z "\${TMUX}" ]] && [[ -z "\${ZELLIJ}" ]] && [[ -o interactive ]]; then
case "${session_backend}" in
zellij)
exec zellij attach --create homeassistant options --default-shell "${shell_path}"
;;
tmux)
exec tmux -u new -A -s homeassistant "${shell_path}" -l
;;
esac
fi
EOF
}

write_fish_config() {
mkdir -p "${FISH_CONF_D_PATH}" /root/.local/share/fish \
|| bashio::exit.nok 'Failed creating Fish configuration directories'

cat > "${FISH_HOME_ASSISTANT_CONF_D_FILE}" <<EOF
set -gx SUPERVISOR_TOKEN "${SUPERVISOR_TOKEN}"
EOF

if bashio::config.true 'share_sessions'; then
cat >> "${FISH_HOME_ASSISTANT_CONF_D_FILE}" <<EOF

if status is-interactive
if test -z "\$TMUX"; and test -z "\$ZELLIJ"
switch "${session_backend}"
case zellij
exec zellij attach --create homeassistant options --default-shell "${shell_path}"
case tmux
exec tmux -u new -A -s homeassistant "${shell_path}" -l
end
end
end
EOF
fi
}

# Links some common directories to the user's home folder for convenience
for dir in "${DIRECTORIES[@]}"; do
Expand All @@ -27,34 +129,57 @@ ln -s "/homeassistant" "/config" \
ln -s "/homeassistant" "${HOME}/config" \
|| bashio::log.warning "Failed linking common directory: ${HOME}/config"

# Sets up ZSH or Bash shell history
if bashio::config.true "zsh"; then
touch "${ZSH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok 'Failed creating a persistent ZSH history file'
login_shell=$(get_login_shell)
shell_path=$(get_shell_path "${login_shell}")
session_backend=$(get_session_backend)

chmod 600 "${ZSH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok \
'Failed setting the correct permissions to the ZSH history file'
# Sets up shell history
case "${login_shell}" in
fish)
mkdir -p /root/.local/share/fish \
|| bashio::exit.nok 'Failed creating the Fish history directory'

ln -s -f "${ZSH_HISTORY_PERSISTENT_FILE}" "${ZSH_HISTORY_FILE}" \
|| bashio::exit.nok 'Failed linking the persistent ZSH history file'
else
touch "${BASH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok 'Failed creating a persistent Bash history file'
touch "${FISH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok 'Failed creating a persistent Fish history file'

chmod 600 "${BASH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok \
'Failed setting the correct permissions to the Bash history file'
chmod 600 "${FISH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok \
'Failed setting the correct permissions to the Fish history file'

ln -s -f "${BASH_HISTORY_PERSISTENT_FILE}" "${BASH_HISTORY_FILE}" \
|| bashio::exit.nok 'Failed linking the persistent Bash history file'
fi
ln -s -f "${FISH_HISTORY_PERSISTENT_FILE}" "${FISH_HISTORY_FILE}" \
|| bashio::exit.nok 'Failed linking the persistent Fish history file'
;;
zsh)
touch "${ZSH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok 'Failed creating a persistent ZSH history file'

# Set up Bash
if ! bashio::config.true "zsh"; then
sed -i -r -e 's|^(root:.*)/bin/zsh$|\1/bin/bash|' /etc/passwd*
sed -i -e 's|/zsh$|/bash|' /root/.tmux.conf
fi
chmod 600 "${ZSH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok \
'Failed setting the correct permissions to the ZSH history file'

ln -s -f "${ZSH_HISTORY_PERSISTENT_FILE}" "${ZSH_HISTORY_FILE}" \
|| bashio::exit.nok 'Failed linking the persistent ZSH history file'
;;
bash)
touch "${BASH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok 'Failed creating a persistent Bash history file'

chmod 600 "${BASH_HISTORY_PERSISTENT_FILE}" \
|| bashio::exit.nok \
'Failed setting the correct permissions to the Bash history file'

ln -s -f "${BASH_HISTORY_PERSISTENT_FILE}" "${BASH_HISTORY_FILE}" \
|| bashio::exit.nok 'Failed linking the persistent Bash history file'
;;
esac

# Configure the selected root login shell and tmux default shell.
set_root_shell "${shell_path}"
sed -i -r -e "s|^(set-option -g default-shell ).*|\1${shell_path}|" /root/.tmux.conf \
|| bashio::exit.nok 'Failed configuring tmux default shell'

# Configure Fish, including the Supervisor token for Fish login sessions.
write_fish_config

echo "export SUPERVISOR_TOKEN=\"${SUPERVISOR_TOKEN}\"" \
>> "${HOME_ASSISTANT_PROFILE_D_FILE}" \
Expand Down Expand Up @@ -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
Expand All @@ -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<length; i++ )); do
cmd=$(bashio::config "init_commands[${i}]") \
|| bashio::exit.nok "Failed to get init command at index ${i}"

eval "${cmd}" \
|| bashio::exit.nok "Failed executing init command: ${cmd}"
done
Expand Down
Loading