From fe8160d22cee7cf38d7b9503d667c8781e57cd1e Mon Sep 17 00:00:00 2001 From: Levi Date: Thu, 5 Mar 2026 09:40:28 +1000 Subject: [PATCH 1/2] chore(agent): move agent config and key into dedicated directory Create directory if doesn't exist. --- agent/installer.go | 12 ++++++++---- agent/packaging/config.json | 2 +- install.sh | 6 +++--- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/agent/installer.go b/agent/installer.go index fbd63a59d8b..d0fc63113cf 100644 --- a/agent/installer.go +++ b/agent/installer.go @@ -14,7 +14,7 @@ import ( ) const ( - agentEnvFile = "/etc/shellhub-agent.env" + agentEnvFile = "/etc/shellhub-agent/shellhub-agent.env" agentServiceFile = "/etc/systemd/system/shellhub-agent.service" agentServiceName = "shellhub-agent" ) @@ -26,7 +26,7 @@ Wants=network-online.target Requires=local-fs.target [Service] -EnvironmentFile=/etc/shellhub-agent.env +EnvironmentFile=/etc/shellhub-agent/shellhub-agent.env ExecStart={{.BinaryPath}} Restart=on-failure RestartSec=5 @@ -83,12 +83,12 @@ func registerInstallerCommands(rootCmd *cobra.Command) { installCmd.Flags().String("server-address", "", "ShellHub server address") installCmd.Flags().String("tenant-id", "", "Namespace tenant ID") - installCmd.Flags().String("private-key", "/etc/shellhub.key", "Path to the agent private key file") + installCmd.Flags().String("private-key", "/etc/shellhub-agent/shellhub.key", "Path to the agent private key file") installCmd.Flags().String("preferred-hostname", "", "Preferred device hostname") installCmd.Flags().String("preferred-identity", "", "Preferred device identity") installCmd.Flags().Uint("keepalive-interval", 30, "Keepalive interval in seconds") installCmd.MarkFlagRequired("server-address") //nolint:errcheck - installCmd.MarkFlagRequired("tenant-id") //nolint:errcheck + installCmd.MarkFlagRequired("tenant-id") //nolint:errcheck rootCmd.AddCommand(installCmd) @@ -131,6 +131,10 @@ func agentInstall(cfg installerConfig) error { return fmt.Errorf("failed to resolve symlinks: %w", err) } + if err := os.MkdirAll(filepath.Dir(agentEnvFile), 0755); err != nil { + return fmt.Errorf("failed to create directory %s: %w", filepath.Dir(agentEnvFile), err) + } + if err := writeAgentEnvFile(cfg); err != nil { return fmt.Errorf("failed to write env file: %w", err) } diff --git a/agent/packaging/config.json b/agent/packaging/config.json index 7df4c91cf84..b03de6309f4 100644 --- a/agent/packaging/config.json +++ b/agent/packaging/config.json @@ -16,7 +16,7 @@ "__PREFERRED_HOSTNAME__", "__PREFERRED_IDENTITY__", "__KEEPALIVE_INTERVAL__", - "SHELLHUB_PRIVATE_KEY=/host/etc/shellhub.key" + "SHELLHUB_PRIVATE_KEY=/host/etc/shellhub-agent/shellhub.key" ], "cwd": "/", "capabilities": { diff --git a/install.sh b/install.sh index 08fa0aff262..b5fb829e9a3 100755 --- a/install.sh +++ b/install.sh @@ -39,7 +39,7 @@ podman_install() { esac if [ -z "$MODE" ]; then - ARGS="$ARGS -e SHELLHUB_PRIVATE_KEY=${PRIVATE_KEY:-/host/etc/shellhub.key}" + ARGS="$ARGS -e SHELLHUB_PRIVATE_KEY=${PRIVATE_KEY:-/host/etc/shellhub-agent/shellhub.key}" echo "🚀 Starting ShellHub container in Agent mode..." fi @@ -106,7 +106,7 @@ docker_install() { esac if [ -z "$MODE" ]; then - ARGS="$ARGS -e SHELLHUB_PRIVATE_KEY=${PRIVATE_KEY:-/host/etc/shellhub.key}" + ARGS="$ARGS -e SHELLHUB_PRIVATE_KEY=${PRIVATE_KEY:-/host/etc/shellhub-agent/shellhub.key}" echo "🚀 Starting ShellHub container in Agent mode..." fi @@ -165,7 +165,7 @@ snap_install() { sudo snap set shellhub server-address="$SERVER_ADDRESS" sudo snap set shellhub tenant-id="$TENANT_ID" - sudo snap set shellhub private-key="${PRIVATE_KEY:-/etc/shellhub.key}" + sudo snap set shellhub private-key="${PRIVATE_KEY:-/etc/shellhub-agent/shellhub.key}" sudo snap start shellhub } || { From 35b4a999759bb3e83d6275cadb80e8e8eda99cd4 Mon Sep 17 00:00:00 2001 From: Levi Date: Wed, 11 Mar 2026 18:34:13 +1000 Subject: [PATCH 2/2] fix(agent): Improve tunnel preservation when upgrading agent over tunnel agent installer: avoid service disruption during upgrade over SSH tunnel - Remove pre-install service disable step - Enable service without starting it immediately - Restart service only after install completes Allows upgrades to run over an active SSH tunnel and ensures the tunnel is re-established after the service restarts with the updated binary. --- agent/installer.go | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/agent/installer.go b/agent/installer.go index d0fc63113cf..daf12fe062d 100644 --- a/agent/installer.go +++ b/agent/installer.go @@ -117,9 +117,9 @@ func agentInstall(cfg installerConfig) error { return fmt.Errorf("systemd is not available on this system") } - // Stop existing service before overwriting files (re-install / upgrade). - // Ignore error — service may not exist yet. - exec.Command("systemctl", "disable", "--now", agentServiceName).Run() //nolint:errcheck + // Best practice would be to disable service here before install/upgrade + // If upgrade performed over tunnel, ssh session disconnect, binary gets sighup. + // Service will restart at end of install/upgrade exe, err := os.Executable() if err != nil { @@ -147,10 +147,16 @@ func agentInstall(cfg installerConfig) error { return fmt.Errorf("failed to reload systemd daemon: %w", err) } - if err := exec.Command("systemctl", "enable", "--now", agentServiceName).Run(); err != nil { + // For upgrade over tunnel support, just enable service, service reboot later + if err := exec.Command("systemctl", "enable", agentServiceName).Run(); err != nil { return fmt.Errorf("failed to enable service: %w", err) } + // Restarts service to upgraded binary, session dies but tunnel remains active. + if err := exec.Command("systemctl", "restart", agentServiceName).Run(); err != nil { + return fmt.Errorf("failed to restart service: %w", err) + } + return nil }