nerves-hub / nerves_hub_cli Goto Github PK
View Code? Open in Web Editor NEWNervesHub Mix command line interface
License: Apache License 2.0
NervesHub Mix command line interface
License: Apache License 2.0
The UUID is an unambiguous way of referring to firmware and it would be great to see it as well in the list.
This was brought up during the conference training at ElixirConf EU 2019 in Prague.
There was a bit of trouble with confusion around the password for the account and the password for the certificate. People mix them up and most people are familiar with the difference in terminology between password (for an account) and pass-phrase (for an encryption key).
Justin (@mobileoverlord) wanted an issue about. So here we are.
Currently you're asked once for your password and if you mess it up, you need to start over again. It seems like we should be nicer and allow for three attempts.
Also see #76.
Using mix nerves_hub.device list
for an org that has no devices fails on the command line. It might be better to handle it more gently with a specific message so don't freak out at the error.
jonjon@ ๐ {nerves_hub_cli} $ mix nerves_hub.device list
NervesHub server: api.nerves-hub.org:443
NervesHub organization: jonjon
Local NervesHub user password:
** (TableRex.Error) Table must have at least one row before being rendered
(table_rex) lib/table_rex.ex:28: TableRex.quick_render!/3
(nerves_hub_cli) lib/mix/tasks/nerves_hub.device.ex:155: Mix.Tasks.NervesHub.Device.list/2
(mix) lib/mix/task.ex:331: Mix.Task.run_task/3
(mix) lib/mix/cli.ex:79: Mix.CLI.run_task/2
nerves_team_device $ mix nerves_hub.deployment update Test 1 is_active true
NervesHub server: api.nerves-hub.org:443
NervesHub organization: aselder
** (Mix) Invalid arguments to mix nerves_hub.deployment
.
Usage:
mix nerves_hub.deployment list
mix nerves_hub.deployment create
mix nerves_hub.deployment update DEPLOYMENT_NAME KEY VALUE
Run mix help nerves_hub.deployment
for more information.
** (Protocol.UndefinedError) protocol Enumerable not implemented for "invalid_signature"
(elixir) lib/enum.ex:1: Enumerable.impl_for!/1
(elixir) lib/enum.ex:141: Enumerable.reduce/3
(elixir) lib/enum.ex:2979: Enum.reduce/3
(nerves_hub_cli) lib/mix/nerves_hub/shell.ex:90: Mix.NervesHubCLI.Shell.render_error/1
(mix) lib/mix/task.ex:316: Mix.Task.run_task/3
(mix) lib/mix/cli.ex:79: Mix.CLI.run_task/2
According to the docs on nerves_hub
if one doesn't use the default org created with Nerves-Hub
config :nerves_hub,
public_keys: [:prod, :staging, :"staging-2"]
You get a error:
== Compilation error in file lib/nerves_hub/certificate.ex ==
** (Mix.Error) NervesHub is unable to find key: :prod
(mix) lib/mix.ex:323: Mix.raise/1
lib/nerves_hub_cli.ex:27: anonymous fn/3 in NervesHubCLI.public_keys/1
(elixir) lib/enum.ex:1925: Enum."-reduce/3-lists^foldl/2-0-"/3
lib/nerves_hub/certificate.ex:3: (module)
It would be useful to be able to import keys that have already been created with fwup -g
.
Maybe something like:
mix nerves_hub.key import NAME PUBLIC_KEY_FILE PRIVATE_KEY_FILE
Hello from the workshop!
I just put invalid stuff like
config :nerves_hub, |~
fwup_public_keys: ["supersecret"]
into rel/config.exs
instead of config/config.exs
.
mix firmware
aborted with some exit status, but bare any error message:
> mix firmware
Nerves environment
MIX_TARGET: rpi0
MIX_ENV: dev
|nerves_bootstrap| Building OTP Release...
โ # exit status != 0 in my shell prompt
Failed commands exit with a 0 exit status. This breaks at least CircleCI.
mix nerves_hub.deployment
should let you create deployments.
if you upload or create a key, and then export it to give to someone else, and try to import it, it will succeed, but give an error from the API when trying to upload it to nerves-hub
Unhandled error: {:error, %{"errors" => %{"name" => ["has already been taken"]}}}
For every mix nerves_hub.*
command, I have to enter my Local NervesHub user password.
I gather this is the passphrase for my local user certificate.
Can I avoid this?
mix nerves_hub.user cert export
cd ~/nerves-hub/
tar -xvzf ./nerves_hub-certs.tar.gz
export NERVES_HUB_CERT=`cat ./cert.pem`
export NERVES_HUB_KEY=`cat ./key.pem`
It seems the directory structure changed, and this task now fails:
mix nerves_hub.key export prod --org farmbot-production
NervesHub server: api.nerves-hub.org:443
NervesHub organization: farmbot-production
Local signing key password for 'prod':
Unhandled error: {:error, "Couldn't find /home/connor/.nerves-hub/keys/farmbot-production/prod.pub or /home/connor/.nerves-hub/keys/farmbot-production/prod.priv"}
We need to capture constraint errors and present them in a pretty way.
Deleting remote signing key test
Unhandled error: {:error, {:error, %Jason.DecodeError{data: "# Ecto.ConstraintError at DELETE /orgs/nerveshub/keys/test\n\nException:\n\n ** (Ecto.ConstraintError) constraint error when attempting to delete struct:\n \n * foreign_key: firmwares_tenant_key_id_fkey\n \n If you would like to convert this constraint into an error, please\n call foreign_key_constraint/3 in your changeset and define the proper\n constraint name. The changeset has not defined any constraint.\n \n (ecto) lib/ecto/repo/schema.ex:574: anonymous fn/4 in Ecto.Repo.Schema.constraints_to_errors/3\n (elixir) lib/enum.ex:1314: Enum.\"-map/2-lists^map/1-0-\"/2\n (ecto) lib/ecto/repo/schema.ex:559: Ecto.Repo.Schema.constraints_to_errors/3\n (ecto) lib/ecto/repo/schema.ex:386: anonymous fn/9 in Ecto.Repo.Schema.do_delete/4\n (ecto) lib/ecto/repo/schema.ex:774: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6\n (ecto) lib/ecto/adapters/sql.ex:576: anonymous fn/3 in Ecto.Adapters.SQL.do_transaction/3\n (db_connection) lib/db_connection.ex:1283: DBConnection.transaction_run/4\n (db_connection) lib/db_connection.ex:1207: DBConnection.run_begin/3\n (db_connection) lib/db_connection.ex:798: DBConnection.transaction/3\n (nerves_hub_api) lib/nerves_hub_api_web/controllers/key_controller.ex:34: NervesHubAPIWeb.KeyController.delete/2\n (nerves_hub_api) lib/nerves_hub_api_web/controllers/key_controller.ex:1: NervesHubAPIWeb.KeyController.action/2\n (nerves_hub_api) lib/nerves_hub_api_web/controllers/key_controller.ex:1: NervesHubAPIWeb.KeyController.phoenix_controller_pipeline/2\n (nerves_hub_api) lib/nerves_hub_api_web/endpoint.ex:1: NervesHubAPIWeb.Endpoint.instrument/4\n (phoenix) lib/phoenix/router.ex:275: Phoenix.Router.__call__/1\n (nerves_hub_api) lib/nerves_hub_api_web/endpoint.ex:1: NervesHubAPIWeb.Endpoint.plug_builder_call/2\n (nerves_hub_api) lib/plug/debugger.ex:122: NervesHubAPIWeb.Endpoint.\"call (overridable 3)\"/2\n (nerves_hub_api) lib/nerves_hub_api_web/endpoint.ex:1: NervesHubAPIWeb.Endpoint.call/2\n (phoenix) lib/phoenix/endpoint/cowboy2_handler.ex:33: Phoenix.Endpoint.Cowboy2Handler.init/2\n (cowboy) /Users/jschneck/Developer/nerves/nerves_hub_web/deps/cowboy/src/cowboy_handler.erl:37: :cowboy_handler.execute/2\n (cowboy) /Users/jschneck/Developer/nerves/nerves_hub_web/deps/cowboy/src/cowboy_stream_h.erl:274: :cowboy_stream_h.execute/3\n \n\n## Connection details\n\n### Params\n\n %{\"name\" => \"test\", \"org_name\" => \"nerveshub\"}\n\n### Request info\n\n * URI: https://0.0.0.0:4002/orgs/nerveshub/keys/test\n * Query string: \n\n### Headers\n \n * content-type: application/json\n * host: 0.0.0.0:4002\n * user-agent: hackney/1.13.0\n\n### Session\n\n %{}\n", position: 0, token: nil}}}
The title is pretty self-explanatory, does anybody have thoughts or cares around this?
If you skip forward instead of reading docs, as I do you will hit a few snags.
no_public_keys
was a limited error :)
I have >1000 devices to add to NervesHub and it would be really convenient if nerves_hub_cli
could take a file that has information for all of the devices.
I have a list of serial numbers from the factory and some information. I can write a program to convert that list to any format that makes sense for nerves_hub_cli
.
I'm open to any format that's not too hard to generate, but how about a .csv file with the following columns:
serial number, "list of tags separated by commas and surrounded by double quotes", description
Similar to NERVES_HUB_CERT
and NERVES_HUB_KEY
, NERVES_HUB_PRIV_KEY
should be available for CI.
After a while of not using nerves_hub, it's not particularly easy to find the command for certain things. It would be nice if mix help nerves_hub
listed all the available tasks and how to get more help for each of them
If you try to register a new user and enter the e-mail address with capital letters, eg [email protected]
then you get an authentication failure after entering the local password.
Generated demo_fw app
NervesHub server: nerveshub.fly.dev:443
Username or email address: connorrigby
NervesHub password:
Authenticating...
** (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.user.ex:108: Mix.Tasks.NervesHub.User.auth/1
This happens when using the official docs here
$ echo $NERVES_HUB_CERT
-----BEGIN CERTIFICATE-----
MIIB4jCCA <SNIP>
-----END CERTIFICATE-----
$ echo $NERVES_HUB_KEY
g3QAAAAFZAAKYP <SNIP>
$ mix nerves_hub.device cert create West100
Nerves environment
MIX_TARGET: print_server_rpi3
MIX_ENV: dev
NervesHub server: api.nerves-hub.org:443
NervesHub organization: crowdcow
Creating certificate for West100
** (MatchError) no match of right hand side value: {:error, :not_found}
(x509 0.8.1) lib/x509/private_key.ex:215: X509.PrivateKey.from_pem!/2
(nerves_hub_cli 0.9.1) lib/mix/nerves_hub/shell.ex:40: Mix.NervesHubCLI.Shell.request_auth/1
(nerves_hub_cli 0.9.1) lib/mix/tasks/nerves_hub.device.ex:350: Mix.Tasks.NervesHub.Device.cert_create/5
(mix 1.10.2) lib/mix/task.ex:330: Mix.Task.run_task/3
(mix 1.10.2) lib/mix/cli.ex:82: Mix.CLI.run_task/2
$ mix nerves_hub.product create
NervesHub server: api.nerves-hub.org:443
NervesHub organization: konnorrigby
Creating product 'name with space'...
Local NervesHub user password:
Product 'name with space' created.
$ mix nerves_hub.device create
NervesHub server: api.nerves-hub.org:443
NervesHub organization: konnorrigby
Identifier (e.g., serial number): test-rpi0
Description: rpi0 test board
One or more comma-separated tags: test
Local NervesHub user password:
Invalid product: name with space
The error message is currently {:error, :unknown}, which seemed like it might be something else when it happened.
It would be nicer if it said something like it couldn't find the file.
I have setup custom nerves hub instance via terraform repo. I am able to access it on my custom domain and able to perform different actions via web e.g. register, login etc.
When I try to authenticate / register via command line, I am getting this error:
NervesHub server: api.staging.customdomain.com:443
Username or email address: meraj
NervesHub password:
Authenticating...
Unhandled error: {:error, :timeout}
I am using custom instance details from environment variables. I don't know why it is not working from command line while both register / auth is working from web. Do we need to make any configuration changes for this to work?
A Product name which includes space like below is created successfully by "mix nerves_hub.product create".
def project do
[
app: :hello_nerves,
name: "Hello Nerves"
...
]
But ' mix nerves_hub.product delete "Hello Nerves" ' causes "Invalid product: Hello+Nerves" Error.
I investigated this error little, then found below command finished successfully.
mix nerves_hub.product delete "Hello%20Nerves"
So this Error may be caused by url encoding of API path.
Currently, the process will exit with
** (Mix) Invalid password
We should allow this to be tried again if the first attempt was incorrect.
Add a command to export the private signing key. Currently, it's locked in a password-protected envelope which is great, but it's not usable outside of the mix
tooling. Actually, if it is usable outside of mix
by using some other Unix-y programs, then I think it's sufficient for my purposes to just document that.
mix nerves_hub.firmware publish
should show a progress bar when uploading firmware to the server.
I recently went through using mix nerves_hub.user auth
successfully. But in subsequent uses of the CLI I noticed it kept defaulting my org
to my username and looks like that is saved to the config by default
I think it would be nice to allow specifying the default org in the initial auth, or some easy way to change the config (which I haven't really found yet)
Currently when you publish a bad firmware, it just asks for the local user cert password and then starts the upload. Then it presents the failure message after going through that whole upload process. So slow connections means it takes a long time to fail.
It would be nice if we could validate everything on the firmware locally before doing an upload.
"They aren't magic if you document them"
This prevents it from being used on devices that don't auto-detect.
Generated klaster app
NervesHub server: manage.nervescloud.com:443
NervesHub organization: lawik
Delete product 'klaster'? [Yn] y
Unhandled error: {:ok, nil}
Right now you have to provide the --key
option even if you are providing cert and key via env vars just to get it to actually sign.
It would be nice if it detected the env vars and didn't require the dummy option.
The following error messages gets printed with the user cert expires:
TLS :client: In state :connection received SERVER ALERT: Fatal - Certificate Expired
The solution is to run mix nerves_hub.user auth
.
It would be nice if the mix nerves_hub.*
utilities would print what you're supposed to do when this happens.
This phenomenon may be related to pbcs 0.1.3. Because getting it back to 0.1.2 this doesn't occur.
$ asdf current
elixir 1.11.2-otp-23 /home/pojiro/.tool-versions
erlang 23.3.4 /home/pojiro/.tool-versions
Below is the error log, nerves_hub_cli and pbcs version are printed in
$ mix nerves_hub.user auth
NervesHub server: api.nerves-hub.org:443
Username or email address: pojiro
NervesHub password:
Authenticating...
Success
NervesHub uses client-side SSL certificates to authenticate CLI requests.
The next step will create an SSL certificate and store it in your
'/home/pojiro/.nerves-hub' directory. A password is required to protect it. This password
does not need to be your NervesHub password. It will never be sent to NervesHub
or any other computer. If you lose it, you will need to run
'mix nerves_hub.user auth' and create a new certificate.
Please enter a local password:
** (ArgumentError) argument error
:erlang.iolist_to_binary({<<...>>, "-----BEGIN EC PRIVATE KEY-----\n...\n-----END EC PRIVATE KEY-----\n\n"})
(crypto 4.9) crypto.erl:1404: :crypto.crypto_one_time/5
(pbcs 0.1.3) lib/pbcs.ex:28: PBCS.encrypt/3
(nerves_hub_cli 0.11.1) lib/nerves_hub_cli/user.ex:17: NervesHubCLI.User.save_certs/3
(nerves_hub_cli 0.11.1) lib/mix/tasks/nerves_hub.user.ex:223: Mix.Tasks.NervesHub.User.generate_certificate/3
(mix 1.11.2) lib/mix/task.ex:394: Mix.Task.run_task/3
(mix 1.11.2) lib/mix/cli.ex:84: Mix.CLI.run_task/2
As other info, nerves_hub_cli with pbcs 0.1.2 still has a weird behavior, like below.
iex(3)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(4)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(5)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(6)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(7)> NervesHubCLI.Crypto.decrypt(encrypted_priv, password)
{:ok, "decrypted_priv"}
iex(8)> PBCS.decrypt({"", encrypted_priv}, password: password)
{:ok, "decrypted_priv"}
Once invoke from NervesHubCLI.Crypto.decrypt
, PBCS.decrypt
wil be ok. (I cannot understand why this happens)
see. https://github.com/nerves-hub/nerves_hub_cli/blob/main/lib/nerves_hub_cli/crypto.ex
With pbcs 0.1.3 this never return {:ok, "decrypted_priv"}, like below.
iex(3)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(4)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(5)> PBCS.decrypt({"", encrypted_priv}, password: password)
:error
iex(6)> NervesHubCLI.Crypto.decrypt(encrypted_priv, password)
{:error, "unknown"}
iex(7)> NervesHubCLI.Crypto.decrypt(encrypted_priv, password)
{:error, "unknown"}
iex(8)> NervesHubCLI.Crypto.decrypt(encrypted_priv, password)
{:error, "unknown"}
Please let me know if you need any other information. Thank you.
It would be nice if after I entered my CLI password I wouldn't need to enter it again for period
. Where period
could be time or event-based, a discussion can illuminate that.
The cli should let you perform the following actions on products
Right now when creating a device, you're prompted to create a device certificate. If you have a NervesKey on the device, you never want to do this. However, the prompt is vague on that. Also, after you've created device certs, it doesn't say what happens to them next. The docs describe that they're needed for initial provisioning, but it would be helpful to have a blurb of text here on what to do next.
Here's what happens when the user cert expires:
$ mix nerves_hub.<task that needs remote access>
Local NervesHub user password:
21:11:55.015 [info] TLS client: In state cipher received SERVER ALERT: Fatal - Certificate Expired
Unhandled error: {:error, {:tls_alert, {:certificate_expired, 'TLS client: In state cipher received SERVER ALERT: Fatal - Certificate Expired\n'}}}
The fix is to reauthenticate:
mix nerves_hub.user auth
It would be nice to handle the error and mention the fix.
i did a mix nerves_hub.key import keyname /path/to/key.pub /path/to/key.priv
and just mashed enter
until it errored out, but even tho there was an error, it actually imported the key. when i did mix nerves_hub.key delete
and mashed enter it gave an error, and didn't delete it.
I'm fine with not allowing empty password, but right now it gets left in a limbo state if you try this.
This is more of a nitpick/quirky behavior. But say you're like me and always fat finger passwords then have to delete back a few characters or ctl-u
the whole line. Those characters currently get printed out and create new lines for the password prompt:
jonjon@ ๐ {nerves_hub} $ mix nerves_hub.firmware list --product smartrent_hub_fw
NervesHub server: 0.0.0.0:4002
NervesHub organization: jonjon
Local NervesHub user password: ^R
Local NervesHub user password: ^R
Local NervesHub user password: ^R
Local NervesHub user password: ^U
Local NervesHub user password:
It still works and deletes those characters. Just odd to see if all get printed out multiple times. Would be nice to have it work silently like other password prompts at the terminal
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.