frugal-iot-server 0.3.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/INSTALLATION.md +219 -61
- package/config.d/email.yaml +23 -0
- package/config.d/schema/devices.yaml +0 -2
- package/config.d/schema/modules.yaml +344 -2
- package/config.d/schema/topics.yaml +140 -46
- package/extras/aclfile +38 -0
- package/extras/mosquitto.conf +29 -0
- package/frugal-iot-createdb.sql +82 -1
- package/frugal-iot-server.js +692 -80
- package/lib/api-routes.js +7 -7
- package/lib/config-for-user.js +115 -0
- package/lib/dynsec-plan.js +246 -0
- package/lib/dynsec-server.js +204 -0
- package/lib/dynsec-sync.js +209 -0
- package/lib/dynsec.js +166 -0
- package/lib/enrol.js +288 -0
- package/lib/mailer.js +43 -0
- package/lib/replica.js +246 -0
- package/lib/resetcode.js +103 -0
- package/lib/retained.js +110 -0
- package/lib/secrets.js +148 -0
- package/package.json +12 -5
- package/public/index.html +8 -8
- package/public/service-worker.js +76 -27
- package/scripts/addbridge-pi.zsh +91 -7
- package/scripts/addbridge-prod.zsh +57 -3
- package/scripts/addorganization.zsh +5 -0
- package/scripts/clearretained.js +185 -0
- package/scripts/diagnostic.zsh +121 -2
- package/scripts/dynsec-init.js +142 -0
- package/scripts/init.zsh +64 -1
- package/scripts/install-pi.sh +83 -4
- package/scripts/migrate-permissions-project.sql +21 -0
- package/scripts/not-published.txt +5 -0
- package/scripts/rebuild-dynsec.js +190 -0
- package/scripts/resetnode.js +69 -0
- package/scripts/clearretained.zsh +0 -153
package/INSTALLATION.md
CHANGED
|
@@ -40,42 +40,6 @@ Any of these work. What differs is only how long the install takes, and how much
|
|
|
40
40
|
A Zero W runs the server perfectly well once installed. It is *installing* that is slow, because a
|
|
41
41
|
single 1 GHz core has to unpack several hundred packages and compile one of them.
|
|
42
42
|
|
|
43
|
-
## The short way: one script
|
|
44
|
-
|
|
45
|
-
Steps 3 to 8 below can be done for you. Once you can ssh into the Pi (steps 1 and 2, which need a
|
|
46
|
-
person with an SD card):
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
curl -fsSLO https://raw.githubusercontent.com/mitra42/frugal-iot-server/main/scripts/install-pi.sh
|
|
50
|
-
bash install-pi.sh --org myfarm --name "My Farm" --email you@example.com --phone +61123456789
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
It asks for anything it needs that you did not pass, says what it is doing as it goes, and stops at
|
|
54
|
-
the first thing that fails - leaving a log, and telling you to run `frugal-iot-diagnostic`. Running
|
|
55
|
-
it again after a failure carries on rather than starting over, which matters on a Pi Zero W where one
|
|
56
|
-
step takes about 40 minutes. `--dry-run` checks the arguments and shows the plan without touching the
|
|
57
|
-
machine.
|
|
58
|
-
|
|
59
|
-
**Passwords** are asked for, not invented, unless you say otherwise: it prompts for each (twice, not
|
|
60
|
-
echoed) and generates one if you just press Enter. Pass them as `--superuser-password`,
|
|
61
|
-
`--login-password` and `--broker-password` to skip the prompts, `--random-passwords` to have all
|
|
62
|
-
three generated, or `--yes` to ask nothing at all — which is what to use over ssh with no terminal.
|
|
63
|
-
A password you chose is not written to the log; a generated one has to be, since otherwise you would
|
|
64
|
-
have no way of knowing it.
|
|
65
|
-
|
|
66
|
-
**It tells you whether a reboot is needed**, rather than leaving you to wonder — it notices a kernel
|
|
67
|
-
or boot-firmware package in the upgrade, the images in `/boot` changing underneath it, and
|
|
68
|
-
`/var/run/reboot-required`. If none of those happened it says so, and there is nothing more to do.
|
|
69
|
-
|
|
70
|
-
Fetch it with `curl -O` and then run it, rather than piping curl into bash - piped, it has no
|
|
71
|
-
terminal to ask questions at.
|
|
72
|
-
|
|
73
|
-
It finishes with a live server and a tested broker. It cannot do step 9, pointing your nodes at it,
|
|
74
|
-
because a node learns its broker by being flashed with it - so it prints the settings to give them.
|
|
75
|
-
Nor step 10, HTTPS.
|
|
76
|
-
|
|
77
|
-
The steps below are what it does, in the same order, if you would rather do it by hand or need to
|
|
78
|
-
understand what went wrong.
|
|
79
43
|
|
|
80
44
|
---
|
|
81
45
|
### 0. What you need before you start
|
|
@@ -163,6 +127,7 @@ From your laptop's terminal:
|
|
|
163
127
|
```
|
|
164
128
|
ssh pi@frugaliot.local
|
|
165
129
|
```
|
|
130
|
+
> **Pi Zero W:** This can take a long time to connect, try `ping frugaliot.local` first to check it is alive
|
|
166
131
|
|
|
167
132
|
Say `yes` to the fingerprint question, then give the password you set in Imager.
|
|
168
133
|
|
|
@@ -213,6 +178,44 @@ the password — either way `nmtui` is the fix, and it is not worth more time th
|
|
|
213
178
|
(`sudo nmcli device wifi list` shows each nearby network's SSID and whether it is WPA2 or WPA3;
|
|
214
179
|
WPA3 cannot use a derived key at all.)
|
|
215
180
|
|
|
181
|
+
## 3-8. The short way: one script
|
|
182
|
+
|
|
183
|
+
Steps 3 to 8 below can be done for you. Once you can ssh into the Pi (steps 1 and 2, which need a
|
|
184
|
+
person with an SD card):
|
|
185
|
+
|
|
186
|
+
```
|
|
187
|
+
curl -fSLO https://raw.githubusercontent.com/mitra42/frugal-iot-server/main/scripts/install-pi.sh
|
|
188
|
+
bash install-pi.sh --org myfarm --name "My Farm" --email you@example.com --phone +61123456789
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
It asks for anything it needs that you did not pass, says what it is doing as it goes, and stops at
|
|
192
|
+
the first thing that fails - leaving a log, and telling you to run `frugal-iot-diagnostic`. Running
|
|
193
|
+
it again after a failure carries on rather than starting over, which matters on a Pi Zero W where one
|
|
194
|
+
step takes about 40 minutes. `--dry-run` checks the arguments and shows the plan without touching the
|
|
195
|
+
machine.
|
|
196
|
+
|
|
197
|
+
**Passwords** are asked for, not invented, unless you say otherwise: it prompts for each (twice, not
|
|
198
|
+
echoed) and generates one if you just press Enter. Pass them as `--superuser-password`,
|
|
199
|
+
`--login-password` and `--broker-password` to skip the prompts, `--random-passwords` to have all
|
|
200
|
+
three generated, or `--yes` to ask nothing at all — which is what to use over ssh with no terminal.
|
|
201
|
+
A password you chose is not written to the log; a generated one has to be, since otherwise you would
|
|
202
|
+
have no way of knowing it.
|
|
203
|
+
|
|
204
|
+
**It tells you whether a reboot is needed**, rather than leaving you to wonder — it notices a kernel
|
|
205
|
+
or boot-firmware package in the upgrade, the images in `/boot` changing underneath it, and
|
|
206
|
+
`/var/run/reboot-required`. If none of those happened it says so, and there is nothing more to do.
|
|
207
|
+
|
|
208
|
+
Fetch it with `curl -O` and then run it, rather than piping curl into bash - piped, it has no
|
|
209
|
+
terminal to ask questions at.
|
|
210
|
+
|
|
211
|
+
It finishes with a live server and a tested broker. It cannot do step 9, pointing your nodes at it,
|
|
212
|
+
because a node learns its broker by being flashed with it - so it prints the settings to give them.
|
|
213
|
+
Nor step 10, HTTPS.
|
|
214
|
+
|
|
215
|
+
The steps below are what it does, in the same order, if you would rather do it by hand or need to
|
|
216
|
+
understand what went wrong, otherwise skip to Step 9.
|
|
217
|
+
|
|
218
|
+
|
|
216
219
|
### 3. Update the operating system and install the prerequisites
|
|
217
220
|
|
|
218
221
|
Logged in, bring the system up to date and reboot:
|
|
@@ -500,9 +503,9 @@ npx --no frugal-iot-addorganization myfarm "My Farm" you@example.com +6112345678
|
|
|
500
503
|
|
|
501
504
|
The arguments are: organization id, display name, your email, your phone (`+` and digits only),
|
|
502
505
|
and a password. That one command writes `config.d/organizations/myfarm.yaml`, creates a login account
|
|
503
|
-
named after the organization (`myfarm`), grants it admin rights, creates its OTA
|
|
504
|
-
the organization's account to the broker's password file** — which is why the
|
|
505
|
-
installed first.
|
|
506
|
+
named after the organization (`myfarm`), grants it admin, read and write rights, creates its OTA
|
|
507
|
+
directory, **and adds the organization's account to the broker's password file** — which is why the
|
|
508
|
+
broker had to be installed first.
|
|
506
509
|
|
|
507
510
|
* **Organization id `myfarm`** — must be 1–10 lower-case letters or digits. It becomes the first part
|
|
508
511
|
of every MQTT topic, so it must match what your sensor nodes are configured to publish to.
|
|
@@ -692,6 +695,101 @@ mosquitto_sub -h localhost -u myfarm -P '<broker-password>' -t '#' -v
|
|
|
692
695
|
Every reading from every node should scroll past. Seeing anything here also proves the broker's
|
|
693
696
|
port 1883 is reachable from off the Pi, which is what the nodes need.
|
|
694
697
|
|
|
698
|
+
### 9a. Optional: outgoing mail, so people can reset a forgotten password
|
|
699
|
+
|
|
700
|
+
Without this, a forgotten password can only be fixed by you, on the Pi, with
|
|
701
|
+
`npx --no frugal-iot-setpassword <username> <new-password>`. The login page's "Forgot password?"
|
|
702
|
+
link says "Password reset is not available on this server" rather than pretending to send anything.
|
|
703
|
+
|
|
704
|
+
An offline Pi cannot send mail at all, so skip this unless it has internet access.
|
|
705
|
+
|
|
706
|
+
Mail sent straight from a home broadband connection is almost always treated as spam, so this
|
|
707
|
+
relays through somebody else's SMTP server rather than sending directly. Gmail is the usual choice
|
|
708
|
+
and is written out in full below; any other provider works the same way.
|
|
709
|
+
|
|
710
|
+
#### With Gmail
|
|
711
|
+
|
|
712
|
+
Use a Google account you are willing to have the Pi send as. A separate account for the purpose is
|
|
713
|
+
better than your own mailbox — the password ends up in a file on an SD card in a shed.
|
|
714
|
+
|
|
715
|
+
**1. Turn on 2-Step Verification** on that account, at
|
|
716
|
+
<https://myaccount.google.com/signinoptions/twosv>.
|
|
717
|
+
|
|
718
|
+
This is not optional and it is where most people get stuck: **app passwords do not exist until
|
|
719
|
+
2-Step Verification is on**. Until then the page in step 2 simply says the setting is not available,
|
|
720
|
+
without explaining why.
|
|
721
|
+
|
|
722
|
+
**2. Create an app password** at <https://myaccount.google.com/apppasswords>.
|
|
723
|
+
|
|
724
|
+
Type a name for it — anything you will recognise later, such as `Frugal IoT Pi` — and create it.
|
|
725
|
+
Google shows you **16 lowercase letters, in four groups of four**, once. Copy them now; you cannot
|
|
726
|
+
come back and read it again, only delete it and make another.
|
|
727
|
+
|
|
728
|
+
The groups are only there to make it readable. **Type it into the config with the spaces removed**,
|
|
729
|
+
as one 16-character word.
|
|
730
|
+
|
|
731
|
+
**3. Fill in `config.d/email.yaml`:**
|
|
732
|
+
|
|
733
|
+
```yaml
|
|
734
|
+
host: smtp.gmail.com
|
|
735
|
+
port: 587
|
|
736
|
+
user: yourname@gmail.com
|
|
737
|
+
pass: "abcdefghijklmnop" # the 16 characters from step 2, spaces removed
|
|
738
|
+
from: Frugal IoT <yourname@gmail.com>
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
Things that catch people out with Gmail specifically:
|
|
742
|
+
|
|
743
|
+
* **`from:` must be the same address as `user:`.** Gmail rewrites the sender to the account you
|
|
744
|
+
authenticated as, so a different address here does not fail — it quietly arrives as something
|
|
745
|
+
else, which is harder to diagnose than an error. (An address you have set up under Gmail's
|
|
746
|
+
"Send mail as" is the one exception.)
|
|
747
|
+
* **`user:` is the full address**, including `@gmail.com`, not just the part before it.
|
|
748
|
+
* **`Username and Password not accepted`** in the server's log means the login password was used
|
|
749
|
+
instead of an app password. Gmail has not accepted account passwords over SMTP for years.
|
|
750
|
+
* **Port 465 also works** if 587 is blocked where the Pi is; the software works out the encryption
|
|
751
|
+
from the port number, so change nothing else.
|
|
752
|
+
* **A free Gmail account can send around 500 messages a day.** Password resets will not come close.
|
|
753
|
+
* **Google Workspace administrators can switch app passwords off** for a whole domain. If step 2
|
|
754
|
+
offers you nothing on a work account, that is why, and you will need a real SMTP relay instead.
|
|
755
|
+
|
|
756
|
+
#### With any other provider
|
|
757
|
+
|
|
758
|
+
The same four settings, with that provider's SMTP host:
|
|
759
|
+
|
|
760
|
+
```yaml
|
|
761
|
+
host: smtp.example.org
|
|
762
|
+
port: 587 # 465 is implicit TLS, 587 and 25 are STARTTLS
|
|
763
|
+
user: frugaliot@example.org
|
|
764
|
+
pass: an-app-password-not-your-login-password
|
|
765
|
+
from: Frugal IoT <frugaliot@example.org>
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
Fastmail, Zoho, Proton (via its bridge) and the rest all issue app passwords in much the same way,
|
|
769
|
+
and for the same reason: one leaking off a Pi then costs you one mailbox rather than the account.
|
|
770
|
+
|
|
771
|
+
#### Checking it, either way
|
|
772
|
+
|
|
773
|
+
Restart the server and look at its output:
|
|
774
|
+
|
|
775
|
+
* `Sending mail via smtp.gmail.com:587 as ...` — configured.
|
|
776
|
+
* `Not sending mail (no host/from in config.d/email.yaml) ...` — it did not find a `host` and a
|
|
777
|
+
`from`, so the reset link will say it is unavailable.
|
|
778
|
+
|
|
779
|
+
Then use "Forgot password?" on the login page with your own account. If the mail never arrives, the
|
|
780
|
+
reason is in the server's log — the page deliberately gives the same answer whether or not the
|
|
781
|
+
address is known to it, so it cannot be used to find out who has an account here.
|
|
782
|
+
|
|
783
|
+
Two more things worth knowing:
|
|
784
|
+
|
|
785
|
+
* `pass` sits in a file on the SD card in plain text. Give that mailbox nothing else to lose.
|
|
786
|
+
* If the Pi is behind a proxy that does not set `X-Forwarded-Host`, the link in the mail will point
|
|
787
|
+
at the wrong address. Set `baseurl: https://your.address` in the same file.
|
|
788
|
+
|
|
789
|
+
The reset code itself is stored nowhere — it is a hash of the account, its current password and the
|
|
790
|
+
time, valid between five and ten minutes and dead the moment it is used. There is no table to
|
|
791
|
+
maintain and nothing to clean up. Restarting the server invalidates any code already sent.
|
|
792
|
+
|
|
695
793
|
### 10. HTTPS and over-the-air firmware updates
|
|
696
794
|
|
|
697
795
|
**To be written.** Everything above gives you a plain HTTP server on your local network, which is
|
|
@@ -726,9 +824,6 @@ It comes in three parts, and only the first is a one-off:
|
|
|
726
824
|
* **11b — authorizing this Pi on that server.** One command, run there, once per Pi.
|
|
727
825
|
* **11c — pointing this Pi at it.** One command, run here, once per Pi.
|
|
728
826
|
|
|
729
|
-
If someone has already done 11a for the server you are bridging to — the usual case once the first
|
|
730
|
-
Pi is working — **start at 11b**.
|
|
731
|
-
|
|
732
827
|
**What you get, and what you do not.** While the link is up, readings appear on production within
|
|
733
828
|
a second or so. While it is down, the Pi records everything as usual and production simply has a
|
|
734
829
|
gap — the readings taken during an outage never reach it. That is a deliberate choice: the
|
|
@@ -738,10 +833,19 @@ in the instant the link returned. A gap is honest; that would not be. Controls a
|
|
|
738
833
|
other way round, and *are* queued, so turning something on from production while the Pi is offline
|
|
739
834
|
takes effect when it reconnects.
|
|
740
835
|
|
|
836
|
+
**People, as well as readings.** A bridge relays topics, not accounts, so on its own it does not let
|
|
837
|
+
someone registered on production log in here. Step 11b also prints a *replica token*, and step 11c
|
|
838
|
+
asks for it; with it, this Pi pulls that organization's logins and permissions from production every
|
|
839
|
+
fifteen minutes, so the same people can use this Pi's dashboard — including while production is
|
|
840
|
+
unreachable, which is the point of the Pi. It is optional: leave the token blank and the bridge
|
|
841
|
+
relays readings as before. Nothing derived from a password travels either way, and each server
|
|
842
|
+
issues its own broker credentials, so the same person has different ones here and there. Revoking a
|
|
843
|
+
permission on production removes it here on the next pull. See SECURITY.md.
|
|
844
|
+
|
|
741
845
|
#### 11a. Preparing a production server to accept bridges
|
|
742
846
|
|
|
743
|
-
Once per production server, not per Pi — and
|
|
744
|
-
|
|
847
|
+
Once per production server, not per Pi — and if bridging to `frugaliot.naturalinnovation.org`
|
|
848
|
+
this has already been done. If so, skip to 11b.
|
|
745
849
|
|
|
746
850
|
**Add a listener for bridges.** A Mosquitto bridge speaks MQTT or MQTT-over-TLS and cannot use
|
|
747
851
|
WebSockets, so the existing `wss://` path that browsers and servers use cannot carry this. It needs
|
|
@@ -813,12 +917,16 @@ installed by hand, the written procedure for doing that.
|
|
|
813
917
|
> Note the asymmetry with the Pi end below: a *certificate* change needs only `reload`, but adding
|
|
814
918
|
> or changing a *bridge* needs a full `restart`, because Mosquitto does not reload bridges.
|
|
815
919
|
|
|
816
|
-
**Turn on access control
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
920
|
+
**Turn on access control** — if this broker predates the release that ships it. Since
|
|
921
|
+
`extras/aclfile` became part of the base install, a Pi set up with `install-pi.sh` already has this
|
|
922
|
+
and there is nothing to do here; the steps below are for a broker built before that, or one whose
|
|
923
|
+
configuration was written by hand.
|
|
924
|
+
|
|
925
|
+
Without it, any account that can log in to the broker can publish and subscribe anywhere on it, so
|
|
926
|
+
a per-Pi account would be no more confined than the organization's own. This is the part to plan
|
|
927
|
+
carefully, because Mosquitto's ACL file is **deny-by-default**: the moment the broker names an
|
|
928
|
+
`acl_file`, every existing account with no entry in it stops working. So the file that restricts new
|
|
929
|
+
bridge accounts has to grant the existing organizations what they already have, in the same change.
|
|
822
930
|
|
|
823
931
|
```
|
|
824
932
|
sudo tee /etc/mosquitto/aclfile >/dev/null <<'EOF'
|
|
@@ -828,12 +936,21 @@ sudo tee /etc/mosquitto/aclfile >/dev/null <<'EOF'
|
|
|
828
936
|
# first element of every Frugal IoT topic is the organization id - so this is what the nodes,
|
|
829
937
|
# dashboards and the server's own logger already do. It just stops being optional.
|
|
830
938
|
pattern readwrite %u/#
|
|
939
|
+
|
|
940
|
+
# Whether a bridge is up, for frugal-iot-diagnostic. "pattern readwrite %u/#" does not match $SYS,
|
|
941
|
+
# so without this the diagnostic reports every bridge as never having connected. Mosquitto logs a
|
|
942
|
+
# warning that this pattern contains no %u - harmless, and a "topic" line would apply to anonymous
|
|
943
|
+
# clients only, which is not what is wanted.
|
|
944
|
+
pattern read $SYS/broker/connection/+/state
|
|
831
945
|
EOF
|
|
832
|
-
sudo
|
|
946
|
+
sudo chown mosquitto:mosquitto /etc/mosquitto/aclfile
|
|
947
|
+
sudo chmod 600 /etc/mosquitto/aclfile
|
|
833
948
|
```
|
|
834
949
|
|
|
835
|
-
|
|
836
|
-
to the `mosquitto` user, so a root-only file stops it starting.
|
|
950
|
+
The ownership matters: like the password file and the TLS key, the broker reads this *after*
|
|
951
|
+
dropping to the `mosquitto` user, so a root-only file stops it starting. Mode 600 owned by
|
|
952
|
+
`mosquitto` rather than 644 owned by root — 2.0.21 accepts the latter but warns on every start that
|
|
953
|
+
a file it does not own, or one that is world readable, "will be refused by a future version". Then name it in the configuration
|
|
837
954
|
and restart:
|
|
838
955
|
|
|
839
956
|
```
|
|
@@ -854,10 +971,12 @@ out, delete `/etc/mosquitto/conf.d/zy-frugal-iot-acl.conf` and restart.
|
|
|
854
971
|
|
|
855
972
|
Nothing needs adding here per Pi: `frugal-iot-addbridge-prod` in 11b appends each bridge's own rule.
|
|
856
973
|
|
|
857
|
-
>
|
|
858
|
-
>
|
|
859
|
-
>
|
|
860
|
-
>
|
|
974
|
+
> `pattern readwrite %u/#` on its own denies `$SYS`, which would stop `frugal-iot-diagnostic`
|
|
975
|
+
> reporting whether a bridge is connected — it reads `$SYS/broker/connection/+/state` as the
|
|
976
|
+
> organization. The second rule above restores exactly that one topic and nothing else. Reading all
|
|
977
|
+
> of `$SYS` would also work and would hand every account the broker's client counts, subscription
|
|
978
|
+
> counts and traffic totals, which on a multi-organization broker tells each organization about the
|
|
979
|
+
> others.
|
|
861
980
|
|
|
862
981
|
#### 11b. Authorizing this Pi (run on the production server)
|
|
863
982
|
|
|
@@ -894,7 +1013,7 @@ Once per Pi. Run the command 11b printed, from this installation's directory:
|
|
|
894
1013
|
|
|
895
1014
|
```
|
|
896
1015
|
cd ~/frugal-iot
|
|
897
|
-
npx --no frugal-iot-addbridge-pi
|
|
1016
|
+
npx --no frugal-iot-addbridge-pi <org-id> <prod-host> bridge-<site-name>
|
|
898
1017
|
```
|
|
899
1018
|
|
|
900
1019
|
It asks for the password rather than taking it on the command line, so it stays out of your shell
|
|
@@ -1060,7 +1179,11 @@ npx --no frugal-iot-clearretained 'myfarm/lotus/+/sht/temperture/#'
|
|
|
1060
1179
|
```
|
|
1061
1180
|
|
|
1062
1181
|
Quote the pattern or the shell will expand it. That lists what is retained and changes nothing;
|
|
1063
|
-
add
|
|
1182
|
+
add the word `delete` at the end of the same command to remove it.
|
|
1183
|
+
|
|
1184
|
+
> `delete` is a bare word rather than a `--delete` flag on purpose. npm parses the command line of
|
|
1185
|
+
> an `npx` invocation itself and swallows any `--flag` it does not recognise, so `--delete` would
|
|
1186
|
+
> never reach the script and it would quietly list instead of deleting.
|
|
1064
1187
|
|
|
1065
1188
|
> Look before deleting. A node's `min`, `max`, `color` and `wired` settings are retained messages
|
|
1066
1189
|
> too, and they are how the dashboard knows how to draw it — delete those and the node has to be
|
|
@@ -1105,10 +1228,19 @@ command for each. That matters most for the files you copy somewhere else — a
|
|
|
1105
1228
|
`extras/mosquitto.conf` or `extras/frugaliot.service` does nothing until you install it again:
|
|
1106
1229
|
|
|
1107
1230
|
```
|
|
1108
|
-
sudo cp extras/mosquitto.conf /etc/mosquitto/conf.d/frugal-iot.conf
|
|
1109
|
-
sudo cp extras/
|
|
1231
|
+
sudo cp node_modules/frugal-iot-server/extras/mosquitto.conf /etc/mosquitto/conf.d/frugal-iot.conf
|
|
1232
|
+
sudo cp node_modules/frugal-iot-server/extras/aclfile /etc/mosquitto/aclfile
|
|
1233
|
+
sudo chown mosquitto:mosquitto /etc/mosquitto/aclfile && sudo chmod 600 /etc/mosquitto/aclfile
|
|
1234
|
+
sudo systemctl restart mosquitto
|
|
1235
|
+
sudo cp node_modules/frugal-iot-server/extras/frugaliot.service /etc/systemd/system/ && sudo systemctl daemon-reload && sudo systemctl restart frugaliot
|
|
1110
1236
|
```
|
|
1111
1237
|
|
|
1238
|
+
Note those read from `node_modules/frugal-iot-server/extras/`, **not** from `extras/` in this
|
|
1239
|
+
directory. `frugal-iot-init` keeps your local copy and only tells you it differs, so the local one is
|
|
1240
|
+
the *old* version — copying it would reinstall what you already have. Re-running the install script
|
|
1241
|
+
(`bash node_modules/frugal-iot-server/scripts/install-pi.sh`) does the same job and reads from the
|
|
1242
|
+
same place.
|
|
1243
|
+
|
|
1112
1244
|
> It does not compare `config.yaml`, `config.d/mqtt.yaml`, `config.d/logger.yaml` or
|
|
1113
1245
|
> `config.d/server.yaml`, because those hold your own settings and would differ every time. If a
|
|
1114
1246
|
> release note mentions a new setting in one of them, compare it yourself:
|
|
@@ -1124,6 +1256,32 @@ do. On a Pi, compare the two files above and copy across the settings you want.
|
|
|
1124
1256
|
Your organizations, accounts, database and logged data are untouched by an upgrade — they live in
|
|
1125
1257
|
this directory, not in `node_modules`.
|
|
1126
1258
|
|
|
1259
|
+
### One thing to do by hand after upgrading: grant WRITE
|
|
1260
|
+
|
|
1261
|
+
Commanding a device through the API (`/devices/action`, `/devices/property`) now requires a **WRITE**
|
|
1262
|
+
permission, which is separate from READ and is *not* implied by ADMIN. New organizations get it
|
|
1263
|
+
automatically, but an upgrade does not touch your database, so on an existing server nobody has it
|
|
1264
|
+
yet and switching a relay on answers 401.
|
|
1265
|
+
|
|
1266
|
+
Grant it from the dashboard — **Admin → People**, pick the person, choose `WRITE` — or in one
|
|
1267
|
+
statement, to everyone who already administers an organization:
|
|
1268
|
+
|
|
1269
|
+
```
|
|
1270
|
+
sqlite3 frugal-iot.db "INSERT OR IGNORE INTO permissions (id, capability, org)
|
|
1271
|
+
SELECT id, 'WRITE', org FROM permissions WHERE capability = 'ADMIN';"
|
|
1272
|
+
sudo systemctl restart frugaliot
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
Check who has it:
|
|
1276
|
+
|
|
1277
|
+
```
|
|
1278
|
+
sqlite3 frugal-iot.db "SELECT id, org FROM permissions WHERE capability = 'WRITE';"
|
|
1279
|
+
```
|
|
1280
|
+
|
|
1281
|
+
Do **not** grant WRITE to id `0`. That row means "every logged-in account", including anyone who has
|
|
1282
|
+
just registered themselves, and handing them write access is exactly what the WRITE check exists to
|
|
1283
|
+
prevent. (Read is a different matter — see the `(0, 'READ', ...)` rows, which are there on purpose.)
|
|
1284
|
+
|
|
1127
1285
|
---
|
|
1128
1286
|
|
|
1129
1287
|
## Open questions
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Outgoing mail. Only used by the password reset, which is unavailable until this is filled in -
|
|
2
|
+
# the login page says so rather than pretending to have sent something.
|
|
3
|
+
#
|
|
4
|
+
# Leave it commented out on a server that should not send mail.
|
|
5
|
+
#
|
|
6
|
+
# For Gmail - the common case, written out step by step in INSTALLATION.md section 9a:
|
|
7
|
+
# 1. Turn on 2-Step Verification: https://myaccount.google.com/signinoptions/twosv
|
|
8
|
+
# App passwords do not exist until you do, which is where most people get stuck.
|
|
9
|
+
# 2. Create an app password: https://myaccount.google.com/apppasswords
|
|
10
|
+
# You get 16 lowercase letters in four groups of four, shown once. Remove the spaces.
|
|
11
|
+
# 3. "from" must be the same address as "user" - Gmail silently rewrites anything else.
|
|
12
|
+
#
|
|
13
|
+
# host: smtp.gmail.com
|
|
14
|
+
# port: 587 # 465 is implicit TLS, 587 and 25 are STARTTLS; secure: is derived from this
|
|
15
|
+
# user: yourname@gmail.com
|
|
16
|
+
# pass: "abcdefghijklmnop" # the app password, NOT your Google account password
|
|
17
|
+
# from: Frugal IoT <yourname@gmail.com>
|
|
18
|
+
#
|
|
19
|
+
# Any other provider is the same four settings with its own SMTP host.
|
|
20
|
+
#
|
|
21
|
+
# The reset email contains a link back to this server. Set baseurl if the server cannot work out
|
|
22
|
+
# its own public address from the request - behind a proxy that does not set X-Forwarded-Host, say.
|
|
23
|
+
# baseurl: https://frugaliot.example.org
|