Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NervesSSH.SystemShell #89

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
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
92 changes: 92 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,98 @@ If you are migrating from `:nerves_firmware_ssh`, or updating to `:nerves_pack
[SSHSubsystemFwup](https://hexdocs.pm/ssh_subsystem_fwup/readme.html) for
other supported options

## Experimental: Adding a Unix shell

Nerves devices typically only expose an Elixir or Erlang shell prompt. While this is handy,
some tasks are easier to run in a more `bash`-like shell environment. `:nerves_ssh` supports
running a separate SSH daemon that launches a system shell (busybox's `ash` by default).

To enable this functionality, you need to add `erlexec` as a dependency to your project
(at least version `2.0`):

```elixir
def deps do
[
{:erlexec, "~> 2.0"}
]
end
```

You also have to configure `erlexec` to allow running as `root`:

```elixir
# config/target.exs
config :erlexec,
root: true,
user: "root",
limit_users: ["root"]
```

Then, change the `erlinit` configuration to set the `SHELL` environment variable:

```elixir
# config/target.exs
config :nerves,
erlinit: [
hostname_pattern: "nerves-%s",
# add this
env: "SHELL=/bin/sh"
]
```

If you use a custom base system with another shell installed, you can change this path,
e.g. to `/bin/bash`.

The last step is to start the separate daemon in your application. This assumes that you
configured the default daemon using the application environment:

```elixir
# application.ex
def children(_target) do
[
# run a second ssh daemon on another port
# but with all other options being the same
# as the default daemon on port 22
{NervesSSH,
NervesSSH.Options.with_defaults(
Application.get_all_env(:nerves_ssh)
|> Keyword.merge(
name: :shell,
port: 2222,
shell: :disabled,
daemon_option_overrides: [{:ssh_cli, {NervesSSH.SystemShell, []}}]
)
)}
]
end
```

As an alternative to the last step, you may also run the Unix shell in a subsystem
similar to the firmware update functionality. This allows all SSH functionality to run
on a single TCP port, but has the following known issues that cannot be fixed:

* the terminal is only sized correctly after resizing it for the first time
* direct command execution is not possible (e.g. `ssh my-nerves-device -s shell echo foo` will not work)
* correct interactivity requires your ssh client to force pty allocation (e.g. `ssh my-nerves-device -tt -s shell`)
* setting environment variables is not supported (e.g. `ssh -o SetEnv="FOO=Bar" my-nerves-device`)

You can enable the shell subsystem by adding it to the default configuration:

```elixir
# config/target.exs
config :nerves_ssh,
subsystems: [
:ssh_sftpd.subsystem_spec(cwd: '/'),
{'shell', {NervesSSH.SystemShellSubsystem, []}},
],
# ...
```

Then, connect using `ssh your-nerves-device -tt -s shell` (`shell` being the name set in your
configuration).

Please report any issues you find when trying this functionality.

## Goals

* [X] Support public key authentication
Expand Down
7 changes: 7 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ config :nerves_runtime,
target: "host"

config :nerves_runtime, Nerves.Runtime.KV.Mock, %{"nerves_fw_devpath" => "/dev/will_not_work"}

if System.get_env("CI") == "true" or System.cmd("whoami", []) == {"root\n", 0} do
config :erlexec,
root: true,
user: "root",
limit_users: ["root"]
end
Loading