Skip to content

Instantly share code, notes, and snippets.

@guillego
Last active June 3, 2024 04:57
Show Gist options
  • Save guillego/86b31452e639d7558c0e63b6937606bc to your computer and use it in GitHub Desktop.
Save guillego/86b31452e639d7558c0e63b6937606bc to your computer and use it in GitHub Desktop.
Getting started with NervesHubWeb (december 2023)

Getting started with NervesHub

1. Clone the NervesHubWeb Repository

git clone git@github.com:nerves-hub/nerves_hub_web.git

2. Set Up the Project

Navigate to the nerves_hub_web directory in your terminal.

Follow the instructions in the Project Overview and Setup section of the README. This involves installing dependencies and setting up your development environment. Ensure you have the correct version of Python and node! I'm using the latest Elixir and Erlang/OTP versions.

3. Configure the Postgres Database (in Docker) and environment

Run the following command to create a Postgres database container using Docker:

docker run -p 5432:5432 --name pgnerveshub_dev -e POSTGRES_PASSWORD=postgres -e POSTGRES_USER=postgres -e POSTGRES_DB=nerves_hub_dev -d postgres

Create a file named .env.dev.local in the nerves_hub_web directory. Add the following line to it:

export DATABASE_URL=postgres://postgres:postgres@localhost:5432/nerves_hub_dev

This contains an environment variable for your database URL, which NERVES_HUB_WEB will use to connect to the database.

In config/config.exs, modify both DeviceRepo and ObanRepo sections to include the following:

url: System.get_env("DATABASE_URL") || "postgres://localhost/nerves_hub_dev",

Or ensure in some other way that both Ecto Repos know what the correct path to the db is.

4. Server Setup

Set the environment for development

export MIX_ENV=dev
source .env.dev.local

Install the dependencies and compile the project:

mix do deps.get, compile

Reset the database to initial state:

mix ecto.reset

Install assets (should work well if you have the correct node and python versions, though you'll get lots of deprecation warnings):

mix assets.install

Now run NervesHubWeb

WEB_HOST=192.168.1.108 mix phx.server

Set web host to whatever your LAN IP address is!

5. Initial Configuration of NervesHubWeb

  • Open a web browser and navigate to localhost:4000.
  • Log in using the default credentials provided in the NervesHubWeb README (nerveshub/nerveshub).
  • Create a new organization, for example, MyOrg.
  • Go to "My account" and create an access token. Save this token somewhere as you will need it later for nerves_hub_cli.

6. Clone and Configure Nerves Key

We need this for generating signer certs, even if we don't use a physical NervesKey.

git clone git@github.com:nerves-hub/nerves_key && cd nerves_key

Create a signer certificate for your devices:

mix nerves_key.signer create my-ca-device-signer --years-valid 20

This creates a signer certificate. Store the key and certificate in a secure location. The key should ideally be offline (USB key or similar).

7. Configure Your Nerves Device Project

Clone or navigate to your Nerves device project repository. Add the following mix dependencies to your mix.exs file:

(using git versions since they are undergoing heavy refactoring)

{:nerves_hub_link, git: "https://github.com/nerves-hub/nerves_hub_link.git", branch: "main"},
{:nerves_hub_cli, git: "https://github.com/nerves-hub/nerves_hub_cli.git", branch: "main"},

Ensure your project has a name in your project's mix.exs file under the @app and @name attributes:

    @app :my_nerves_device
    @name "MyNervesDevice"

Run mix deps.get to fetch the new dependencies.

8. Enable NervesHub Provisioning and CLI Configuration

In your config.exs, enable NervesHub firmware provisioning:

config :nerves, :firmware,
  rootfs_overlay: "rootfs_overlay",
  provisioning: :nerves_hub_link

Also, add the following configuration for nerves_hub_cli:

config :nerves_hub_cli,
  home_dir: Path.expand(".nerves-hub"),
  org: "MyOrg",
  host: "localhost",
  port: 4000

Create a file called .env.nerves_hub_cli (add it to your .gitignore to avoid committing it) and add the following line:

export NERVES_HUB_TOKEN=YOUR_PREVIOUSLY_GENERATED_TOKEN

Replace YOUR_PREVIOUSLY_GENERATED_TOKEN with the access token you saved earlier from NervesHubWeb.

Run source .env.nerves_hub_cli to load the token into your environment.

9. Register Device and Certificates

Create a product on NervesHub for your device:

mix nerves_hub.product create --name "MyNervesDevice"

Create a unique device:

mix nerves_hub.device create --identifier nerves_device_0001 --tag dev --description "My test nerves device"

Generate certificates for this device:

mix nerves_hub.device cert create nerves_device_0001 --signer-key path/to/my-ca-device-signer.key --signer-cert path/to/my-ca-device-signer.cert

Replace path/to/my-ca-device-signer.key and path/to/my-ca-device-signer.cert with the actual paths to your signer key and certificate (that you generated in the nerves_key repo).

The device certificates will be now registered on NervesHub and saved in the .nerves-hub directory within your project.

Register the signer CA certificate with NervesHub:

    mix nerves_hub.ca_certificate register path/to/my-ca-device-signer.cert

10. Convert Certificates to DER Format

Convert the device certificates to DER format using OpenSSL:

openssl x509 -outform der -in .nerves-hub/nerves_device_0001-cert.pem -out .nerves-hub/nerves_device_0001-cert.der

openssl ec -outform der -in .nerves-hub/nerves_device_0001-key.pem -out .nerves-hub/nerves_device_0001-key.der

Also, convert the Device signer CA cert to DER format:

openssl x509 -outform der -in path/to/my-ca-device-signer.cert -out .nerves-hub/my-ca-device-signer-cert.der

11. Configure NervesHub Link in Your Project

In config.exs, add the NervesHub link configuration:

base_path = ".nerves-hub"
cert = File.read!("#{base_path}/nerves_device_0001-cert.der")
key_der = File.read!("#{base_path}/nerves_device_0001-key.der")
cacerts = [File.read!("#{base_path}/my-ca-device-signer-cert.der")]

config :nerves_hub_link,
  remote_iex: true,
  socket: [
    json_library: Jason,
    heartbeat_interval: 45_000
  ],
  device_api_host: "192.168.1.108", # Replace with your local IP
  device_api_port: 4001,
  device_api_sni: ~c"device.nerves-hub.org",
  configurator: NervesHubLink.Configurator.Default,
  ssl: [
    cert: cert,
    key: {:ECPrivateKey, key_der},
    cacerts: cacerts
  ]

Now we have a registered device on nerveshub with all the required certificates for establishing a secure connection. The last thing is to add a firmware signing key so that we can push remote updates.

12. Add a Firmware Signing Key

Create a firmware signing key:

mix nerves_hub.key create devkey

Copy the public key and add it to your local NervesHubWeb browser UI under your organization (MyOrg > Firmware keys).

Then add the public key to your project's config.exs:

config :nerves_hub_link,
  fwup_public_keys: [
    # devkey
    "YOUR_FIRMWARE_PUBLIC_KEY" # Replace with your key
  ]

13. Compile, Sign, and Burn Firmware

Now our device knows the public signature of the firmware all that remains is to compile and sign it.

Compile the firmware:

mix firmware

Sign the firmware:

mix nerves_hub.firmware sign --key devkey

Publish the firmware (or burn it for initial loading):

mix nerves_hub.firmware publish
mix burn

Burn the firmware to an SD card and start your device. SSH into your device and check for errors using RingLogger.next.

In your local nerveshub instance go to devices and you should see your device online!

@gabrielmancini
Copy link

gabrielmancini commented Feb 23, 2024

i have follow the gist and i have an warning

➜  firmeware mix nerves_hub.product create --name "MyNervesDevice"
===> Analyzing applications...
===> Compiling certifi
==> nerves
===> Analyzing applications...
===> Compiling certifi
===> Analyzing applications...
===> Compiling certifi
===> Analyzing applications...
===> Compiling certifi
==> firmeware

Nerves environment
  MIX_TARGET:   rpi4
  MIX_ENV:      dev

Generated firmeware app
NervesHub server: 192.168.0.88:4000
NervesHub organization: MyOrg

Creating product 'MyNervesDevice'...

14:01:32.754 [warning] [NervesHubLink] No CA store or :cacerts have been specified. Request will fail
** (ArgumentError) errors were found at the given arguments:

  * 1st argument: not a binary

    :erlang.binary_to_atom(nil, :utf8)
    (tesla 1.8.0) lib/tesla/adapter/mint.ex:161: Tesla.Adapter.Mint.open_conn/2
    (tesla 1.8.0) lib/tesla/adapter/mint.ex:121: Tesla.Adapter.Mint.do_request/5
    (tesla 1.8.0) lib/tesla/adapter/mint.ex:61: Tesla.Adapter.Mint.call/2
    (tesla 1.8.0) lib/tesla/middleware/json.ex:57: Tesla.Middleware.JSON.call/3
    (tesla 1.8.0) lib/tesla/middleware/follow_redirects.ex:46: Tesla.Middleware.FollowRedirects.redirect/3
    (nerves_hub_cli 2.0.0) lib/nerves_hub_cli/api.ex:42: NervesHubCLI.API.request/4
    (nerves_hub_cli 2.0.0) lib/mix/tasks/nerves_hub.product.ex:191: Mix.Tasks.NervesHub.Product.create/2

what i have to do here?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment