texas2step 2.2.3__tar.gz → 2.3.0__tar.gz
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.
- {texas2step-2.2.3 → texas2step-2.3.0}/PKG-INFO +1 -1
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/access.md +152 -23
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/api.md +17 -12
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/configuration.md +4 -2
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/install.md +35 -8
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/methodology.md +8 -1
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/roadmap.md +23 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/security.md +33 -4
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/traffic.md +3 -1
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/__init__.py +1 -1
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/access.py +47 -20
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/api/access.py +104 -27
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/api/meta.py +16 -6
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/api/results.py +10 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/api/wire.py +25 -3
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/app.py +130 -33
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/cli.py +190 -12
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/cluster.py +30 -5
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/config.py +14 -4
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/config_template.toml +4 -1
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/lockouts.py +175 -9
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/rtc.py +24 -5
- texas2step-2.3.0/src/texas2step/static/assets/app.css +1 -0
- texas2step-2.3.0/src/texas2step/static/assets/app.js +8 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/index.html +2 -2
- texas2step-2.2.3/src/texas2step/static/assets/app.css +0 -1
- texas2step-2.2.3/src/texas2step/static/assets/app.js +0 -8
- {texas2step-2.2.3 → texas2step-2.3.0}/.gitignore +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/LICENSE.md +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/README.md +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/nginx/texas2step-http.conf +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/nginx/texas2step-log.conf +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/nginx/texas2step-speedtest.conf +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/nginx/texas2step.conf +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/texas2step-update.service +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/texas2step-update.timer +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/texas2step.default +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/deploy/texas2step.service +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/development.md +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/experience.md +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/images/hero-dark.jpg +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/images/results-dark.jpg +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/images/results-light.jpg +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/docs/images/upload-light.jpg +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/legacy/README.md +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/pyproject.toml +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/__main__.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/api/__init__.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/api/measure.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/config_edit.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/config_merge.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/engine.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/iface.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/logs.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/page.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/payload.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/quality.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/assets/swagger/LICENSE +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/assets/swagger/NOTICE +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/assets/swagger/swagger-ui-bundle.js +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/assets/swagger/swagger-ui-bundle.js.LICENSE.txt +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/assets/swagger/swagger-ui.css +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/static/favicon.svg +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/stats.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/store.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/traffic.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/update.py +0 -0
- {texas2step-2.2.3 → texas2step-2.3.0}/src/texas2step/words.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: texas2step
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.3.0
|
|
4
4
|
Summary: Web-centric throughput, latency, jitter and packet-loss measurement between endpoints.
|
|
5
5
|
Project-URL: Homepage, https://github.com/robert-patrick-texas/texas2step
|
|
6
6
|
Project-URL: Documentation, https://robert-patrick-texas.github.io/texas2step/
|
|
@@ -18,7 +18,8 @@ no user names or passwords. The desk's own tools stay on your networks: saved re
|
|
|
18
18
|
works out the same code from it, with nothing shared between them while they run.
|
|
19
19
|
* **A code buys a pass.** The visitor enters the code once. The server answers with a
|
|
20
20
|
pass, good for 12 hours (`access_pass_hours`), which the browser then sends with every
|
|
21
|
-
request, to every server in the picker. A server with the same key accepts it.
|
|
21
|
+
request, to every server in the picker. A server with the same key accepts it. To end
|
|
22
|
+
every pass at once, see [Ending every pass](#ending-every-pass).
|
|
22
23
|
* **The desk's tools stay inside.** Visitors from addresses in `internal_networks` need no
|
|
23
24
|
code, and only they get History, the API tab, saved results and the code itself.
|
|
24
25
|
Everyone else gets the Test, Monitor and Diag tabs.
|
|
@@ -88,7 +89,8 @@ To turn codes off again: `sudo texas2step config set access_code=off --write`, t
|
|
|
88
89
|
| `access_timezone` | `America/New_York` | The time zone whose midnight starts a new code; the same on every server |
|
|
89
90
|
| `access_words` | `""` | Your own word list; empty uses the built-in words |
|
|
90
91
|
| `access_digits` | `2` | The digits that end a code: 2, 3 or 4 |
|
|
91
|
-
| `access_pass_hours` | `12` | How long
|
|
92
|
+
| `access_pass_hours` | `12` | How long a pass lasts from when it was issued (1 to 168). Each server applies its own as passes are used, so a change applies to passes already given; use the same on every server. |
|
|
93
|
+
| `access_note` | `Contact the service desk for assistance.` | The line under the code box, such as how to reach the desk; `""` for none |
|
|
92
94
|
| `access_key_file` | `/etc/texas2step/access.key` | The key |
|
|
93
95
|
| `access_phrases_file` | `/etc/texas2step/access-phrases.toml` | [Standing phrases](#standing-phrases), if the file exists |
|
|
94
96
|
| `internal_networks` | `[]` | Your own networks: no code, and the desk's tools. A list of IPv4 and IPv6 addresses and networks in CIDR form, mixed freely: `["192.168.0.0/16", "2001:db8::/32", "203.0.113.7"]`. An address alone is a single host. Bits past the prefix are ignored, so `10.2.1.30/24` means all of `10.2.1.0/24`. Give the addresses as the server sees them: staff who come in through your firewall's NAT arrive from its public address. |
|
|
@@ -112,8 +114,9 @@ tests until it changes, but for a short list, or a `fixed` code, consider
|
|
|
112
114
|
`access_digits = 3` or `4`.
|
|
113
115
|
|
|
114
116
|
**When it changes.** A new code starts at midnight in `access_timezone`. The previous
|
|
115
|
-
code keeps working for 24 hours after the change, for a call that spans midnight.
|
|
116
|
-
`fixed` code
|
|
117
|
+
code keeps working for 24 hours after the change, for a call that spans midnight. To
|
|
118
|
+
change it before then, or to change a `fixed` code, [shuffle the codes](#shuffling-the-codes),
|
|
119
|
+
or make a new key:
|
|
117
120
|
|
|
118
121
|
```bash
|
|
119
122
|
sudo texas2step access key --new
|
|
@@ -123,6 +126,42 @@ That makes a new key at once: every code and pass from the old one stops working
|
|
|
123
126
|
the new key to the other servers as in step 1, and restart the service on each. Without
|
|
124
127
|
`--yes`, it asks first.
|
|
125
128
|
|
|
129
|
+
### Shuffling the codes
|
|
130
|
+
|
|
131
|
+
When a code has gone further than it should, shuffle the codes. The current code and the
|
|
132
|
+
previous one stop working at once, and a new one takes their place:
|
|
133
|
+
|
|
134
|
+
```console
|
|
135
|
+
$ sudo texas2step access shuffle
|
|
136
|
+
Shuffle the codes? The current and previous codes stop working, and the desk reads the new one. [y/N] y
|
|
137
|
+
Codes shuffled here. Today's code is now copper.lantern.42; the codes before it don't work.
|
|
138
|
+
Passes already given keep working: add --end-passes to end them as well.
|
|
139
|
+
Other servers: sent to dallas, houston.
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
A shuffle is a random value mixed into every code from then on. It is kept in
|
|
143
|
+
`access_lockouts_file`, so it needs no new key and no restart, and outlasts a restart.
|
|
144
|
+
Every code entered after it is checked against it, on every worker. The desk reads the
|
|
145
|
+
new code at the top of History, or with `sudo texas2step access show`, which also says
|
|
146
|
+
when the codes were shuffled. There is no previous code until the next change: at
|
|
147
|
+
midnight, the shuffled code becomes yesterday's and works for 24 hours as usual. Phrases
|
|
148
|
+
are not codes, so they keep working.
|
|
149
|
+
|
|
150
|
+
Every server must hold the same shuffle, or they give different codes. With
|
|
151
|
+
[servers that defend together](#servers-that-defend-together), it goes to every server in
|
|
152
|
+
`[[locations]]`, and each keeps the latest it is given. Without them, the command prints
|
|
153
|
+
the value for the others; run that on each:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
sudo texas2step access shuffle --to 3f9a0c7e12b4d856
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
`access show`'s fingerprint includes the shuffle, so servers that agree show the same one.
|
|
160
|
+
|
|
161
|
+
Passes already given keep working. Add `--end-passes` to [end them](#ending-every-pass)
|
|
162
|
+
too, which has everyone outside enter the new code: the same as a new key, without
|
|
163
|
+
copying a key to every server.
|
|
164
|
+
|
|
126
165
|
**Typing it.** Case and separators don't matter: `Maple River 42`, `maple-river-42` and
|
|
127
166
|
`maple.river.42` are the same code, and `7` counts for `07`.
|
|
128
167
|
|
|
@@ -204,7 +243,8 @@ journalctl -u texas2step --grep 'access phrase'
|
|
|
204
243
|
stays guessable: several words that don't belong together, such as
|
|
205
244
|
`copper lantern harbor violet`, rather than `vip access`. texas2step takes any phrase
|
|
206
245
|
you choose. To withdraw one, `sudo texas2step access phrase remove vip` on every server,
|
|
207
|
-
and restart
|
|
246
|
+
and restart. Passes it already bought last until they run out (`access_pass_hours`),
|
|
247
|
+
unless you [end every pass](#ending-every-pass).
|
|
208
248
|
A new key doesn't change the phrases.
|
|
209
249
|
|
|
210
250
|
A phrase buys the same pass as the day's code, so the visitor gets what any visitor
|
|
@@ -234,18 +274,29 @@ texas2step run https://speedtest.example.net/texas2step --code maple.river.42
|
|
|
234
274
|
|
|
235
275
|
| | Needs |
|
|
236
276
|
|---|---|
|
|
237
|
-
| The page at `/`, which asks for the code; its scripts, styles and logo; `/api/v1/health` (for load balancers, without the version); `/api/v1/access` | Nothing |
|
|
238
|
-
| The Monitor and Diag
|
|
277
|
+
| The page, at `/`, `/monitor` or `/diag`, which asks for the code; its scripts, styles and logo; `/api/v1/health` (for load balancers, without the version); `/api/v1/access` | Nothing |
|
|
278
|
+
| The tests, Monitor and Diag's measurements, and saving a result | A pass, or an address in `internal_networks` |
|
|
239
279
|
| History, the API tab, `/r/<id>`, reading saved results, `/api/docs`, `/api/openapi.json`, `/api/v1/measure/*`, the current code, [lockouts and blocks](#lockouts-and-blocks) | An address in `internal_networks`; others get 404 |
|
|
240
280
|
|
|
241
|
-
The
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
281
|
+
The page keeps the pass in the browser's local storage and sends it with every request
|
|
282
|
+
it makes. To its own server it goes in an `Authorization: Bearer` header, and a
|
|
283
|
+
WebSocket's in its subprotocol, which keeps the pass out of the URLs that
|
|
284
|
+
TLS-inspecting proxies log. To the other servers in the picker it goes as `?pass=`: a
|
|
285
|
+
header there would make the browser ask permission (a CORS preflight) before each timed
|
|
286
|
+
request and skew the timings. The server takes the pass in any of these, so the CLI's
|
|
287
|
+
`?pass=` works as before. There is no cookie: a page needs no pass to load, and asks for
|
|
288
|
+
the code itself. A cookie an earlier version set is removed at the next right code.
|
|
289
|
+
|
|
290
|
+
With `internal_networks` set, only those networks see the version. The page,
|
|
291
|
+
`/api/v1/info` and `/api/v1/health` leave it out for everyone else, even after they enter
|
|
292
|
+
a code, and a result they save gets it from the server. Without `internal_networks`, a
|
|
293
|
+
visitor sees it once they have entered a code; until then the page leaves it out and
|
|
294
|
+
`/api/v1/health` answers only `{"status":"ok"}`. This slows an attacker down rather than
|
|
295
|
+
stopping one: the page's scripts are those of a published release.
|
|
296
|
+
|
|
297
|
+
The desk's functions also refuse, with `403`, a browser's request made for a page on
|
|
298
|
+
another site, so that a website an employee opens can't use them through the employee's
|
|
299
|
+
browser ([Security](security.md#the-desks-tools)).
|
|
249
300
|
|
|
250
301
|
Loopback addresses are always inside, so commands on the server itself need no code. The
|
|
251
302
|
service sees each visitor's address through nginx (`trusted_proxies`), which the packaged
|
|
@@ -262,6 +313,30 @@ inside.
|
|
|
262
313
|
Without `internal_networks`, every visitor needs a code, and anyone with one can read the
|
|
263
314
|
saved results. `texas2step access show` warns about that.
|
|
264
315
|
|
|
316
|
+
### Inside on one address family
|
|
317
|
+
|
|
318
|
+
A visitor with both IPv4 and IPv6 comes from two addresses, and `internal_networks` may
|
|
319
|
+
list only one of them: an office's IPv6 network, say, but not the NAT address its IPv4
|
|
320
|
+
leaves by. The page asks its own server, at the address it was opened at, whether the
|
|
321
|
+
code is needed, and the browser reaches that over either family. When the answer is no
|
|
322
|
+
but another address the page tests refuses it, the page asks for the code over itself,
|
|
323
|
+
in a box the visitor can put aside with **Not now**. That other address is this server's
|
|
324
|
+
IPv4-only or IPv6-only name ([IPv4 and IPv6](configuration.md#ipv4-and-ipv6)), or another
|
|
325
|
+
server in the picker. The pass the code buys works over both families and on every
|
|
326
|
+
server with the same key.
|
|
327
|
+
|
|
328
|
+
The box comes up by itself until the visitor puts it aside, which holds until the page
|
|
329
|
+
is loaded again. Meanwhile Test and Monitor say the function is restricted over that
|
|
330
|
+
address, with a button to enter the code, and Diag's rows for it say **Restricted**, with
|
|
331
|
+
the same button.
|
|
332
|
+
Diag then tries that address every 10 seconds instead of every second, and opens no
|
|
333
|
+
WebSocket there until the code is in. A server with `access_code = "off"` has no pass to
|
|
334
|
+
give, so its page doesn't ask: it only says which address wants a code.
|
|
335
|
+
|
|
336
|
+
A pass that hasn't run out but is refused anyway, as from a network blocked by hand
|
|
337
|
+
([lockouts and blocks](#lockouts-and-blocks)), isn't asked for again by itself. To have
|
|
338
|
+
neither family asked, list both of a site's addresses in `internal_networks`.
|
|
339
|
+
|
|
265
340
|
## Lockouts and blocks
|
|
266
341
|
|
|
267
342
|
Wrong codes count against the visitor's network, its IPv4 /24 or IPv6 /48, not its
|
|
@@ -276,7 +351,8 @@ codes in a row from a network, it is locked out:
|
|
|
276
351
|
* A network quiet for 30 minutes starts counting again from nothing, and so does one that
|
|
277
352
|
enters a right code.
|
|
278
353
|
* Visitors from `internal_networks` and loopback need no code, so they are never counted
|
|
279
|
-
or locked out. Passes already given keep working
|
|
354
|
+
or locked out. Passes already given keep working through a lockout; a block set by hand
|
|
355
|
+
ends them (below).
|
|
280
356
|
|
|
281
357
|
| Setting | Default | |
|
|
282
358
|
|---|---|---|
|
|
@@ -324,6 +400,14 @@ longer if it keeps trying; blocking it again keeps it going. A block can't overl
|
|
|
324
400
|
take effect at once, with no restart, and with
|
|
325
401
|
[servers that defend together](#servers-that-defend-together), on every server.
|
|
326
402
|
|
|
403
|
+
A block also ends the passes used from the network it covers. Until it ends, their
|
|
404
|
+
requests are refused as if they carried none (`401`), and the WebSockets and UDP
|
|
405
|
+
sessions they have open close within a few seconds. The page goes back to asking for the
|
|
406
|
+
code, which the blocked network can't enter. A lockout from wrong codes ends no passes,
|
|
407
|
+
so callers who share a network with a guesser keep testing with the pass they hold. A
|
|
408
|
+
block set or ended through the API counts at once. One from the command line reaches
|
|
409
|
+
each of the service's workers within 5 seconds, and so does one that ends.
|
|
410
|
+
|
|
327
411
|
**From automation.** The same, as an API that answers only `internal_networks` and the
|
|
328
412
|
server itself, so a script can carry a block from the server that saw an attack to all
|
|
329
413
|
the others, and renew it there:
|
|
@@ -335,8 +419,9 @@ curl -s -X POST https://houston.example.net/texas2step/api/v1/access/blocks \
|
|
|
335
419
|
curl -s -X DELETE 'https://houston.example.net/texas2step/api/v1/access/blocks?network=198.51.100.0/24'
|
|
336
420
|
```
|
|
337
421
|
|
|
338
|
-
A list gives each lockout's `network`, `source`, `wrong_codes`, `since
|
|
339
|
-
`counting`, the networks with wrong codes
|
|
422
|
+
A list gives each lockout's `network`, `source`, `wrong_codes`, `since`, `until` and
|
|
423
|
+
`by_hand` (a block, which ends passes too), and `counting`, the networks with wrong codes
|
|
424
|
+
in a row. A block that overlaps
|
|
340
425
|
`internal_networks`, or is wider than a /16 or a /32, is refused with `400`. These calls
|
|
341
426
|
change only the server they are sent to; for one that reaches every server by itself, see
|
|
342
427
|
[Servers that defend together](#servers-that-defend-together).
|
|
@@ -356,6 +441,44 @@ every 30 minutes. One with 10,000 networks gets about 1.4 million a day:
|
|
|
356
441
|
For a server open to the Internet, use `access_digits = 4`, and standing phrases of
|
|
357
442
|
several unrelated words.
|
|
358
443
|
|
|
444
|
+
## Ending every pass
|
|
445
|
+
|
|
446
|
+
A pass holds the time it was issued, signed with the key: `1793030400.q7GxN0x2rY8vLmKp3TfWcA9b`.
|
|
447
|
+
No server keeps a list of them. Each server checks a pass as it is used:
|
|
448
|
+
|
|
449
|
+
* The signature matches, so a server with the same key issued it.
|
|
450
|
+
* It was issued less than `access_pass_hours` ago, by this server's setting now. Lower
|
|
451
|
+
the setting, and restart, and passes already given end sooner; raise it, and they last
|
|
452
|
+
longer.
|
|
453
|
+
* It wasn't issued more than 5 minutes ahead of this server's clock.
|
|
454
|
+
* It was issued after passes were last ended.
|
|
455
|
+
|
|
456
|
+
To end every pass given so far, such as when a code or a phrase went further than it
|
|
457
|
+
should have:
|
|
458
|
+
|
|
459
|
+
```console
|
|
460
|
+
$ sudo texas2step access passes --end
|
|
461
|
+
End every pass issued so far? Everyone testing from outside enters the code again. [y/N] y
|
|
462
|
+
Every pass issued before 2026-10-25 14:02 EDT is ended here, within seconds; tests running with one stop too.
|
|
463
|
+
Other servers: sent to dallas, houston.
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
The time is kept in `access_lockouts_file`, beside the lockouts, so it needs no restart
|
|
467
|
+
and outlasts one. Each of the service's workers reads it within 5 seconds. From then on,
|
|
468
|
+
a pass issued before it is refused as if there were none (`401`), and the WebSockets and
|
|
469
|
+
UDP sessions opened with one close within a few seconds. The page asks for the code
|
|
470
|
+
again. The codes and phrases still work, so everyone with the current code is back in at
|
|
471
|
+
once, with a new pass. Visitors from `internal_networks` need no pass, so nothing changes
|
|
472
|
+
for them.
|
|
473
|
+
|
|
474
|
+
With [servers that defend together](#servers-that-defend-together), the end goes to every
|
|
475
|
+
server in `[[locations]]`, and each keeps the later of its own and the one it is sent.
|
|
476
|
+
Without them, run the command on each server: until then, the others still take the
|
|
477
|
+
passes. `sudo texas2step access passes` says how long passes last here and when they were
|
|
478
|
+
last ended, and `access show` does too. To end the codes too, use
|
|
479
|
+
`sudo texas2step access shuffle --end-passes` ([Shuffling the codes](#shuffling-the-codes)).
|
|
480
|
+
A new key also ends every pass, and every code with it.
|
|
481
|
+
|
|
359
482
|
## Servers that defend together
|
|
360
483
|
|
|
361
484
|
When a lockout begins on one server, the others can block that network too, so an
|
|
@@ -370,9 +493,12 @@ access_peer_networks = ["198.51.100.20", "2001:db8:20::20", "203.0.113.30"]
|
|
|
370
493
|
* **What goes to the others.** A lockout here goes, as a block, to every server in
|
|
371
494
|
`[[locations]]`, at once. While the attacker keeps trying here, it goes again every
|
|
372
495
|
third of `access_lockout_minutes`, so the others' blocks don't run out first.
|
|
373
|
-
`sudo texas2step access block
|
|
374
|
-
`--clear` on any one of them lets a caller
|
|
375
|
-
|
|
496
|
+
`sudo texas2step access block`, `access lockouts --clear`, `access passes --end` and
|
|
497
|
+
`access shuffle` go to every server too: `--clear` on any one of them lets a caller
|
|
498
|
+
back in everywhere. The
|
|
499
|
+
[API's blocks](#lockouts-and-blocks) stay on the server they are sent to. A block from
|
|
500
|
+
`access block` ends passes on every server, as here; a lockout ends none anywhere. A
|
|
501
|
+
server on an earlier version sends either as a lockout.
|
|
376
502
|
* **What a server takes.** Only requests from an address in its `access_peer_networks`,
|
|
377
503
|
signed with a key the servers work out from the access key they share. Use the
|
|
378
504
|
addresses the other servers' requests leave from, which behind NAT may not be the
|
|
@@ -384,6 +510,10 @@ access_peer_networks = ["198.51.100.20", "2001:db8:20::20", "203.0.113.30"]
|
|
|
384
510
|
compromised: a block from another server covers at most a /24 or a /48, never
|
|
385
511
|
`internal_networks` or loopback, lasts at most a week, and isn't passed on. A server
|
|
386
512
|
holds at most 1,000 of them. A wider block by hand stays on the server where it was set.
|
|
513
|
+
Another server can end every pass issued before its request, which at worst has
|
|
514
|
+
everyone enter the code again, but not set that time later than the receiving server's
|
|
515
|
+
clock allows, which would stamp new passes there too far ahead. It can shuffle the
|
|
516
|
+
codes, which at worst has the desk read the new one.
|
|
387
517
|
* **Counts stay on each server.** Wrong codes count where they are entered, so with three
|
|
388
518
|
servers an attacker gets up to about seven guesses, two at each and one more, before
|
|
389
519
|
the whole cluster locks them out.
|
|
@@ -488,8 +618,7 @@ many as you need (`results_max_rows`). `sudo texas2step results clear` deletes t
|
|
|
488
618
|
| Your `access_words` file | Yours: kept by `apt purge` |
|
|
489
619
|
| `/var/lib/texas2step/access.db` | Wrong codes, lockouts and blocks, and with servers that defend together, the one-time values of their requests; made by the service when access codes are on; removed with `apt purge` |
|
|
490
620
|
|
|
491
|
-
The pass lives in the visitor's browser
|
|
492
|
-
pages) until it expires. Passes are not written to the journal: the request log shows
|
|
621
|
+
The pass lives in the visitor's browser, in local storage, until it expires. Passes are not written to the journal: the request log shows
|
|
493
622
|
`pass=*` in their place. nginx's access log doesn't have them either: the packaged
|
|
494
623
|
snippets log texas2step's requests to `/var/log/nginx/access.log` without their query
|
|
495
624
|
strings (the `texas2step` log format, in `/etc/nginx/conf.d/texas2step-log.conf`).
|
|
@@ -78,7 +78,8 @@ sending the offer. The offer must include data channels:
|
|
|
78
78
|
profiles the session runs. A voice or video session counts toward a
|
|
79
79
|
[traffic limit](traffic.md#monitors-call-profiles). The answer is
|
|
80
80
|
`{"op":"profile","ok":true,"name":"voice"}`, or `"ok": false` with a `reason`: the
|
|
81
|
-
server withholds
|
|
81
|
+
server withholds that profile (`monitor_voice` or `monitor_video` is `false`, as
|
|
82
|
+
`/api/v1/info`'s `monitor` says), or a traffic limit is reached, when
|
|
82
83
|
the site's `message` and `url` come with it. Past a limit while a session runs, the
|
|
83
84
|
server sends `{"op":"limit","message":…,"url":…}` unasked and from then on echoes only
|
|
84
85
|
probes of up to 64 bytes.
|
|
@@ -91,11 +92,11 @@ every port in its `rtc_port_min`–`rtc_port_max` range is taken.
|
|
|
91
92
|
|
|
92
93
|
| Method | Path | Returns |
|
|
93
94
|
|---|---|---|
|
|
94
|
-
| GET | `/api/v1/info` | name, version, server identity, capabilities (`stamps`: the UDP probe can [stamp](#udp-probe-protocol) its echoes), limits, ICE servers, UI defaults (`one_stream`: whether a test downloads over one stream too, unless asked otherwise), `monitor` (`profile`: Monitor's until a visitor picks; `video`: whether the Video call
|
|
95
|
+
| GET | `/api/v1/info` | name, version (left out for clients outside `internal_networks`), server identity, capabilities (`stamps`: the UDP probe can [stamp](#udp-probe-protocol) its echoes), limits, ICE servers, UI defaults (`one_stream`: whether a test downloads over one stream too, unless asked otherwise), `monitor` (`profile`: Monitor's until a visitor picks; `voice`, `video`: whether the Voice call and Video call profiles are offered); with a [traffic limit](traffic.md), `traffic` (below) |
|
|
95
96
|
| GET | `/api/v1/whoami` | client IP, port, family, scheme, HTTP version, user agent |
|
|
96
97
|
| GET | `/api/v1/diag/http` | both ends of this HTTP connection, its HTTP version and TLS, the server's TCP RTT and clock (below) |
|
|
97
98
|
| GET | `/api/v1/locations` | servers offered in the picker (this one first) |
|
|
98
|
-
| GET | `/api/v1/health` | `{"status": "ok", "version": …}`; without the version for clients that need an [access code](#access-codes) |
|
|
99
|
+
| GET | `/api/v1/health` | `{"status": "ok", "version": …}`; without the version for clients outside `internal_networks`, or that need an [access code](#access-codes) |
|
|
99
100
|
|
|
100
101
|
With a traffic limit, `traffic` says whether this client may start a test, and if not,
|
|
101
102
|
what it is told:
|
|
@@ -175,21 +176,25 @@ curl -s -X POST https://dallas.example.net/texas2step/api/v1/measure/full \
|
|
|
175
176
|
## Access codes
|
|
176
177
|
|
|
177
178
|
On a server with `access_code` on ([Access codes](access.md)), a client from outside
|
|
178
|
-
`internal_networks` sends a pass
|
|
179
|
-
|
|
180
|
-
|
|
179
|
+
`internal_networks` sends a pass with each request under `/api/v1/` (except `/health`
|
|
180
|
+
and `/access`); without a valid one it gets `401`. It goes as `?pass=<pass>`, or in an
|
|
181
|
+
`Authorization: Bearer <pass>` header; a WebSocket can also carry it in its subprotocol,
|
|
182
|
+
offering `texas2step` and `texas2step.pass.<pass>`, and is answered with `texas2step`. With `internal_networks` set, clients from outside them get `404` for
|
|
181
183
|
reading results, `/api/v1/measure/*`, `/api/docs`, `/api/openapi.json`, the current code and
|
|
182
|
-
the lockouts
|
|
184
|
+
the lockouts, and the browser's requests to these made for a page on another site get
|
|
185
|
+
`403`. With `internal_networks` set, `/api/v1/info` and `/api/v1/health` leave out the
|
|
186
|
+
version for clients outside them; without them, `/api/v1/health` leaves it out for
|
|
187
|
+
clients that would need a pass.
|
|
183
188
|
|
|
184
189
|
| Method | Path | Purpose |
|
|
185
190
|
|---|---|---|
|
|
186
|
-
| GET | `/api/v1/access?pass=<pass>` | `{"required", "desk"}`: whether this client needs a pass,
|
|
187
|
-
| POST | `/api/v1/access` | `{"code": "maple.river.42"}` (any case and separators, or a standing phrase) returns `{"pass", "expires"}
|
|
191
|
+
| GET | `/api/v1/access?pass=<pass>` | `{"required", "desk", "codes", "note"}`: whether this client needs a pass, gets the desk's functions, and whether this server gives passes at all (`access_code` is on), and the site's line under the code box (`access_note`, empty for none); with `pass`, also whether that pass is valid and when it expires |
|
|
192
|
+
| POST | `/api/v1/access` | `{"code": "maple.river.42"}` (any case and separators, or a standing phrase) returns `{"pass", "expires"}`. A wrong code is `403`. After `access_lockout_after` wrong codes in a row from one /24 or /48, every code from it gets that same `403`, unread, until `access_lockout_minutes` pass without one ([Lockouts and blocks](access.md#lockouts-and-blocks)). A body over 1 KB is `413` |
|
|
188
193
|
| GET | `/api/v1/access/code` | The current code, when it changes, and the previous one while it still works; only from `internal_networks` |
|
|
189
|
-
| GET | `/api/v1/access/blocks` | `{"lockouts": [{"network", "source", "wrong_codes", "since", "until"}], "counting"}
|
|
190
|
-
| POST | `/api/v1/access/blocks` | `{"network": "198.51.100.0/24", "minutes": 120}` locks a network out by hand, or keeps its lockout going; `minutes` defaults to `access_lockout_minutes`. One that overlaps `internal_networks`, or is wider than a /16 or a /32, is `400` |
|
|
194
|
+
| GET | `/api/v1/access/blocks` | `{"lockouts": [{"network", "source", "wrong_codes", "since", "until", "by_hand"}], "counting"}` (`by_hand`: a block, which ends the passes used from the network too); only from `internal_networks` and the server itself |
|
|
195
|
+
| POST | `/api/v1/access/blocks` | `{"network": "198.51.100.0/24", "minutes": 120}` locks a network out by hand, or keeps its lockout going, and ends the passes used from it; `minutes` defaults to `access_lockout_minutes`. One that overlaps `internal_networks`, or is wider than a /16 or a /32, is `400` |
|
|
191
196
|
| DELETE | `/api/v1/access/blocks?network=<network>` | Ends the lockouts that overlap it (`all`: every one); returns `{"cleared": [...]}` |
|
|
192
|
-
| POST | `/api/v1/access/peer` | For [servers that defend together](access.md#servers-that-defend-together): `{"from", "to", "action": "block" \| "clear" \| "check", "network", "minutes"}`, signed in `X-Texas2step-Time`, `-Nonce` and `-Signature`. Only from `access_peer_networks`, with `access_share_lockouts` on; `404` to everyone else |
|
|
197
|
+
| POST | `/api/v1/access/peer` | For [servers that defend together](access.md#servers-that-defend-together): `{"from", "to", "action": "block" \| "clear" \| "check" \| "end-passes" \| "shuffle", "network", "minutes", "kind": "lockout" \| "block", "at", "value"}` (`kind`: a block set by hand, which ends passes there too; `lockout` when left out. `end-passes` ends every pass issued before `at`, in Unix seconds; `shuffle` mixes `value`, 16 hex digits, into the codes from `at`, the latest shuffle staying; neither `at` may be later than the receiving server's clock allows), signed in `X-Texas2step-Time`, `-Nonce` and `-Signature`. Only from `access_peer_networks`, with `access_share_lockouts` on; `404` to everyone else |
|
|
193
198
|
|
|
194
199
|
```bash
|
|
195
200
|
pass=$(curl -s -X POST https://speedtest.example.net/texas2step/api/v1/access \
|
|
@@ -38,7 +38,8 @@ file up to date with the new version's template and keeps your values; see
|
|
|
38
38
|
| `page_test` / `page_monitor` / `page_diag` / `page_history` / `page_api` | `true` | Show that tab of the web UI. Any can be hidden, but not all. A hidden page's address opens the first page shown. The API stays on: `results_enabled` and `measure_enabled` turn parts of it off, and `/api/docs` stays reachable without the API tab. |
|
|
39
39
|
| `default_streams` / `default_seconds` / `default_packets` | 4 / 10 / 500 | Defaults advertised by `/api/v1/info` |
|
|
40
40
|
| `monitor_profile` | `path` | Monitor's profile until a visitor picks one: `path` (small probes, to follow the path), `voice` or `video` (a call's packets, scored every 10 seconds); see [Live monitor](methodology.md#live-monitor) |
|
|
41
|
-
| `
|
|
41
|
+
| `monitor_voice` | `true` | Offer Monitor's Voice call profile, about 150 kb/s each way while a tab runs it. With `false`, the page shows no Voice call button and the server refuses the profile. |
|
|
42
|
+
| `monitor_video` | `true` | Offer Monitor's Video call profile, about 1.4 Mb/s each way while a tab runs it. With `false`, the page shows no Video call button and the server refuses the profile. With both `false`, Monitor runs Path only, and shows no profile switch. Voice and video count toward a [traffic limit](traffic.md#monitors-call-profiles). |
|
|
42
43
|
| `default_one_stream` | `true` | Whether a test also downloads over [one stream](methodology.md#one-stream), unless the visitor chooses in Test options or the command line with `--one-stream` or `--no-one-stream`. It can add up to half again to a test's traffic, so a server whose traffic is billed may want `false`; see [Traffic limits](traffic.md#what-a-test-moves). |
|
|
43
44
|
| `cors_origins` | `["*"]` | Origins allowed to call the API from a browser |
|
|
44
45
|
| `trusted_proxies` | `["127.0.0.1", "::1"]` | Peers whose `X-Forwarded-For`, `X-Forwarded-Proto`, `X-Forwarded-Prefix` and `X-Client-Protocol` are believed |
|
|
@@ -48,7 +49,8 @@ file up to date with the new version's template and keeps your values; see
|
|
|
48
49
|
| `access_timezone` | `America/New_York` | The time zone whose midnight starts a new code |
|
|
49
50
|
| `access_words` | `""` | A file of your own words for codes, one entry per line; empty uses the built-in words |
|
|
50
51
|
| `access_digits` | 2 | The digits that end a code: 2, 3 or 4 |
|
|
51
|
-
| `access_pass_hours` | 12 | How long
|
|
52
|
+
| `access_pass_hours` | 12 | How long a pass lasts from when it was issued; applied as passes are used, so a change applies to passes already given. See [Ending every pass](access.md#ending-every-pass). |
|
|
53
|
+
| `access_note` | `Contact the service desk for assistance.` | The line under the code box, such as how to reach the desk; `""` for none |
|
|
52
54
|
| `access_key_file` | `/etc/texas2step/access.key` | The key codes come from; the same on every server of an organization |
|
|
53
55
|
| `access_phrases_file` | `/etc/texas2step/access-phrases.toml` | Standing phrases that work as codes, if the file exists; see [Standing phrases](access.md#standing-phrases) |
|
|
54
56
|
| `access_lockout_after` | 3 | Wrong codes in a row from one /24 or /48 that lock it out of entering codes; see [Lockouts and blocks](access.md#lockouts-and-blocks) |
|
|
@@ -51,7 +51,8 @@ To do the same by hand, follow steps 1 to 5 below instead.
|
|
|
51
51
|
```
|
|
52
52
|
|
|
53
53
|
It logs to `texas2step-install.log` beside itself. When it finishes, the service
|
|
54
|
-
answers both on `127.0.0.1:8080` and through nginx, and it
|
|
54
|
+
answers both on `127.0.0.1:8080` (or the `--port` you gave) and through nginx, and it
|
|
55
|
+
prints the address to open.
|
|
55
56
|
You can run it again safely: it skips each step that is already done.
|
|
56
57
|
|
|
57
58
|
What the check looks at, and what the script then does:
|
|
@@ -66,7 +67,7 @@ What the check looks at, and what the script then does:
|
|
|
66
67
|
| Peers | Each `--peer URL` is asked who it is (`/api/v1/info`). A peer that can't be reached is left out, with the command to add it later. | `texas2step config add-location ... --write` |
|
|
67
68
|
| nginx | nginx is installed and running, its configuration passes `nginx -t`, and it loads `conf.d/*.conf`. It finds your HTTPS site, and checks that nothing else on that site uses `/texas2step/`. If texas2step is already published, nginx is left alone. | Adds `include snippets/texas2step.conf;` after the site's `server_name` line, runs `nginx -t` and reloads. The file is backed up to `/var/backups/texas2step/` first, and put back if the test fails. |
|
|
68
69
|
| UDP | The probe's port range: the one you set, or 61000–61999. It checks this range against the firewall and the kernel's ephemeral ports. | Pins the range in the settings. Opens it in ufw or firewalld if one of them is active. |
|
|
69
|
-
| Port
|
|
70
|
+
| Port | Nothing else listens on texas2step's TCP port: 8080, `LISTEN_PORT` in `/etc/default/texas2step`, or `--port`. nginx's `upstream` in `/etc/nginx/conf.d/texas2step.conf` points at that port. | With `--port`, sets `LISTEN_PORT` and the `upstream` before the package is installed, so the service never starts on 8080. An `upstream` that points elsewhere is pointed at the port. |
|
|
70
71
|
|
|
71
72
|
Options:
|
|
72
73
|
|
|
@@ -79,6 +80,7 @@ Options:
|
|
|
79
80
|
| `--path PATH` | Publish texas2step at `PATH` instead of `/texas2step/`, e.g. `/speedtest`. The check moves the package's snippet to that path and shows it in the install script, which writes it as `snippets/texas2step-at-speedtest.conf` and includes that. Each later run writes it again from the new package. Use the same path in `--url-v4` and `--url-v6`. |
|
|
80
81
|
| `--no-nginx` | Install the service only, and publish it yourself |
|
|
81
82
|
| `--udp-ports MIN-MAX` | The probe's UDP range, e.g. `61000-61999` |
|
|
83
|
+
| `--port PORT` | The TCP port texas2step listens on, on `127.0.0.1` behind nginx, when another program needs 8080. Choose one from 1024 to 32767. Later runs and `texas2step update` keep it. See [Another port](#another-port). |
|
|
82
84
|
| `--url-v4 URL`, `--url-v6 URL` | This server's IPv4-only and IPv6-only base URLs, for the header's IP drop-down; see [IPv4 and IPv6](configuration.md#ipv4-and-ipv6). The check looks up each name: it should have only an A record (or only AAAA), pointing at this server. After the install, the script checks that nginx answers for each name. |
|
|
83
85
|
| `--no-firewall` | Leave the firewall alone; the report lists the rule to add |
|
|
84
86
|
| `--allow-downgrade` | Allow installing an older version than the one installed |
|
|
@@ -128,6 +130,32 @@ yourself.
|
|
|
128
130
|
To make the second server by cloning the first's virtual machine instead, see
|
|
129
131
|
[Clone a server](#clone-a-server).
|
|
130
132
|
|
|
133
|
+
### Another port
|
|
134
|
+
|
|
135
|
+
texas2step listens on `127.0.0.1:8080`, and nginx reaches it there through the
|
|
136
|
+
`upstream` in `/etc/nginx/conf.d/texas2step.conf`. When another program needs 8080,
|
|
137
|
+
give the setup tool another port, the same way for a new install or an installed server:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
sudo ./texas2step-setup --port 8081
|
|
141
|
+
sudo ./texas2step-install.sh
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
The install script sets `LISTEN_PORT=8081` in `/etc/default/texas2step` and points the
|
|
145
|
+
`upstream` at `127.0.0.1:8081`, keeping a dated copy of each file it changes in
|
|
146
|
+
`/var/backups/texas2step/`. It does this before installing the package, so the service
|
|
147
|
+
never starts on 8080, even on a new install. Then it restarts texas2step, reloads nginx
|
|
148
|
+
and checks both.
|
|
149
|
+
|
|
150
|
+
Choose a port from 1024 to 32767. texas2step runs as its own user, which can't listen
|
|
151
|
+
below 1024, and Linux takes the ports of outgoing connections from 32768 up. If the port
|
|
152
|
+
is in use, the check says by what. Both files are kept on upgrades, and the setup tool
|
|
153
|
+
and `texas2step update` read the port from `LISTEN_PORT`. When a new version changes the
|
|
154
|
+
`upstream` file, the update installs the new one with your port in it.
|
|
155
|
+
|
|
156
|
+
If you changed `LISTEN_PORT` by hand and not the `upstream`, the next run of the setup
|
|
157
|
+
tool points the `upstream` at it.
|
|
158
|
+
|
|
131
159
|
## 1. Install the package
|
|
132
160
|
|
|
133
161
|
Download the newest `.deb` from the
|
|
@@ -264,12 +292,11 @@ choose the default theme (Light, Dark, America, Energy, Federal, Military or Tex
|
|
|
264
292
|
that site directly. Sites can also measure each other with `POST /api/v1/measure/full`,
|
|
265
293
|
but only toward listed locations.
|
|
266
294
|
|
|
267
|
-
**Listen address and workers.** These are set in `/etc/default/texas2step`.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
it.
|
|
295
|
+
**Listen address and workers.** These are set in `/etc/default/texas2step`. To move the
|
|
296
|
+
service to another port, see [Another port](#another-port). More workers help on
|
|
297
|
+
multi-gigabit links, where Python's per-connection CPU cost is the limit. Each worker
|
|
298
|
+
keeps its own UDP sessions, which is fine, because a session lives entirely in the
|
|
299
|
+
worker that answered it.
|
|
273
300
|
|
|
274
301
|
## 4. Firewall and UDP
|
|
275
302
|
|
|
@@ -272,7 +272,10 @@ counted as lost. Statistics cover the visible window (1, 5 or 15 minutes).
|
|
|
272
272
|
A **profile** sets what is sent. Anyone can switch it beside the Start button. Options
|
|
273
273
|
holds the exact sizes and rates, and the page's address carries them, so the service
|
|
274
274
|
desk can send a caller a link such as `/monitor?profile=voice`. The site picks the
|
|
275
|
-
profile a visitor starts with (`monitor_profile`)
|
|
275
|
+
profile a visitor starts with (`monitor_profile`), and can withhold the call profiles
|
|
276
|
+
(`monitor_voice`, `monitor_video`): the switch then shows only the ones offered, and none
|
|
277
|
+
at all when only Path is. A link or a saved choice for a withheld profile starts with
|
|
278
|
+
the nearest one offered.
|
|
276
279
|
|
|
277
280
|
| Profile | Sends | Each way, as the network carries it |
|
|
278
281
|
|---|---|---|
|
|
@@ -365,6 +368,10 @@ and closes its connections. Its rows and events stay, and the timelines show the
|
|
|
365
368
|
An attempt not answered within 2 s (1.5 s for WebSocket and UDP) counts as missed. A
|
|
366
369
|
method that has answered before shows a miss as loss (red). One that hasn't answered
|
|
367
370
|
yet stays grey and keeps trying. One the server doesn't offer is grey and isn't tried.
|
|
371
|
+
One whose address asks for the access code, as a server's IPv4-only name can for a
|
|
372
|
+
visitor inside on IPv6 only, says **Restricted** (amber) and doesn't count as loss:
|
|
373
|
+
its HTTPS is tried every 10 s until the code is in, and no WebSocket is opened there
|
|
374
|
+
meanwhile ([Inside on one address family](access.md#inside-on-one-address-family)).
|
|
368
375
|
The clock offset is the server's time minus the browser's at the middle of the round
|
|
369
376
|
trip, ± half the round trip.
|
|
370
377
|
|
|
@@ -116,6 +116,29 @@ links and footer credit. Three places still say texas2step:
|
|
|
116
116
|
|
|
117
117
|
Newest first. Each release's notes have the details and its upgrade steps.
|
|
118
118
|
|
|
119
|
+
### [2.3.0](https://github.com/robert-patrick-texas/texas2step/releases/tag/v2.3.0) (2026-10-25)
|
|
120
|
+
|
|
121
|
+
- [x] Passes carry when they were issued: `access_pass_hours` applies to passes already
|
|
122
|
+
given, and `texas2step access passes --end` ends every pass, on every server with
|
|
123
|
+
lockouts shared.
|
|
124
|
+
- [x] `texas2step access shuffle` changes the codes at once, without a new key.
|
|
125
|
+
- [x] A visitor inside on one address family and outside on the other is asked for the
|
|
126
|
+
code in a box over the page; Diag says which address is restricted.
|
|
127
|
+
- [x] The code screen's words are the organization's, with `access_note` under the box.
|
|
128
|
+
- [x] `texas2step-setup --port` for servers where another program needs 8080.
|
|
129
|
+
- [x] `monitor_voice` withholds Monitor's Voice call profile, as `monitor_video` does
|
|
130
|
+
the Video call one.
|
|
131
|
+
|
|
132
|
+
### [2.2.4](https://github.com/robert-patrick-texas/texas2step/releases/tag/v2.2.4) (2026-10-25)
|
|
133
|
+
|
|
134
|
+
- [x] The desk's functions refuse requests made for pages on other sites; with
|
|
135
|
+
`internal_networks` set, only those networks see the version.
|
|
136
|
+
- [x] API answers are kept in no browser cache.
|
|
137
|
+
- [x] A block set by hand ends the passes used from its network, sessions already open
|
|
138
|
+
included.
|
|
139
|
+
- [x] No cookie: the pass stays in local storage and goes to the page's own server in a
|
|
140
|
+
header; a Content-Security-Policy on the page and the API explorer.
|
|
141
|
+
|
|
119
142
|
### [2.2.3](https://github.com/robert-patrick-texas/texas2step/releases/tag/v2.2.3) (2026-10-24)
|
|
120
143
|
|
|
121
144
|
- [x] Spaces around any setting or typed value are ignored; a stray one in
|
|
@@ -37,10 +37,20 @@ pass.
|
|
|
37
37
|
the service (`0640 root:texas2step`). Anyone holding it can work out every code, so it
|
|
38
38
|
is kept out of the world-readable `config.toml`. `texas2step access show` prints a
|
|
39
39
|
fingerprint to compare servers by, which reveals nothing about the key.
|
|
40
|
-
* **Passes.** A right code buys a pass for `access_pass_hours` (12 by default):
|
|
41
|
-
|
|
42
|
-
as
|
|
43
|
-
|
|
40
|
+
* **Passes.** A right code buys a pass for `access_pass_hours` (12 by default): the time
|
|
41
|
+
it was issued, signed with the key, checked by every server that holds it against its
|
|
42
|
+
`access_pass_hours` as it is then. The page keeps it in
|
|
43
|
+
local storage, with no cookie, and sends it to its own server in an `Authorization`
|
|
44
|
+
header (a WebSocket's in its subprotocol), so it stays out of URLs that proxies log;
|
|
45
|
+
to the other servers, as `?pass=`. A pass is a bearer token: it isn't tied to
|
|
46
|
+
an address or a browser, and works until it runs out. A
|
|
47
|
+
[block set by hand](access.md#lockouts-and-blocks) ends it for the network blocked,
|
|
48
|
+
WebSockets and UDP sessions already open included.
|
|
49
|
+
[`texas2step access passes --end`](access.md#ending-every-pass) ends every pass issued
|
|
50
|
+
so far, on every server with lockouts shared, while the codes keep working.
|
|
51
|
+
[`texas2step access shuffle`](access.md#shuffling-the-codes) replaces the current and
|
|
52
|
+
previous codes with a new one, and with `--end-passes` ends the passes too. A new access
|
|
53
|
+
key ends every pass and code.
|
|
44
54
|
* **Standing phrases.** For the desk away from a PC, in their own `0640` file
|
|
45
55
|
(`access-phrases.toml`), with an optional last day. Each use is a journal warning with
|
|
46
56
|
the phrase's name, never the phrase. The examples published in the docs are refused.
|
|
@@ -97,10 +107,22 @@ server itself), and `404` to everyone else, as if they weren't there:
|
|
|
97
107
|
* the API explorer (`/api/docs`, `/api/openapi.json`);
|
|
98
108
|
* the server-side measurements (`/api/v1/measure/*`).
|
|
99
109
|
|
|
110
|
+
They also refuse, with `403`, a browser's request made for a page on another site. They
|
|
111
|
+
trust the address a request comes from, and CORS lets any page read the answer, so a
|
|
112
|
+
website an employee opens inside could otherwise read History, the current code and the
|
|
113
|
+
blocks through that employee's browser, or set blocks. The browser says where a request
|
|
114
|
+
came from in its `Sec-Fetch-Site` header, or, without one, in its `Origin`. A link from
|
|
115
|
+
elsewhere still opens the desk's pages, because the other site can't read them. Scripts,
|
|
116
|
+
the CLI and monitoring send neither header and work as before.
|
|
117
|
+
|
|
100
118
|
Server-side measurements only reach the servers in `[[locations]]`, unless
|
|
101
119
|
`allow_any_target` is set, so the server can't be used to make requests to other hosts.
|
|
102
120
|
At most two run at once on each worker.
|
|
103
121
|
|
|
122
|
+
**The version** would tell an attacker which flaws to try. With `internal_networks` set,
|
|
123
|
+
only those networks see it: the page, `/api/v1/info` and `/api/v1/health` leave it out for
|
|
124
|
+
everyone else, even after they enter a code. A result they save gets it from the server.
|
|
125
|
+
|
|
104
126
|
## Abuse of the service
|
|
105
127
|
|
|
106
128
|
A speed test exists to move a lot of data, so each part has a cap:
|
|
@@ -138,6 +160,13 @@ A speed test exists to move a lot of data, so each part has a cap:
|
|
|
138
160
|
figures) belong to the service user. The traffic figures hold no addresses.
|
|
139
161
|
* **No third parties.** The web page loads nothing from other sites: no CDN, fonts,
|
|
140
162
|
analytics or telemetry. No public STUN server is used unless listed in `ice_servers`.
|
|
163
|
+
* **A Content-Security-Policy** on the page and the API explorer lets only their own
|
|
164
|
+
scripts run, and the one written into each page by its hash, and lets them connect
|
|
165
|
+
only to this server and the others in `[[locations]]`. A script injected some other
|
|
166
|
+
way can't run, or send what it finds to another site.
|
|
167
|
+
* **Its own hostname.** A browser shares local storage across a whole origin, the scheme,
|
|
168
|
+
host and port. Where texas2step sits beside other applications on one hostname, any page
|
|
169
|
+
there could read the pass, so serve it from a hostname of its own where you can.
|
|
141
170
|
|
|
142
171
|
## Cryptography
|
|
143
172
|
|
|
@@ -142,7 +142,9 @@ acknowledgements the server sends back, as measured over IPv6. A tab left runnin
|
|
|
142
142
|
When the limit is reached, the server holds those sessions to the small probes of the
|
|
143
143
|
Path profile, which aren't counted, and Monitor says so with the site's message and link.
|
|
144
144
|
Until the limit opens again, a call profile is refused at its start.
|
|
145
|
-
`monitor_video = false` in the settings withholds the Video call profile altogether
|
|
145
|
+
`monitor_video = false` in the settings withholds the Video call profile altogether, and
|
|
146
|
+
`monitor_voice = false` the Voice call one: the page doesn't offer them, and the server
|
|
147
|
+
refuses them. With both, Monitor runs only the uncounted Path profile.
|
|
146
148
|
|
|
147
149
|
## What isn't counted
|
|
148
150
|
|