OONI Probe CLI v3.19.0-alpha
Pre-releaseSummary
This release is an alpha release. It is not ready for general availability and should only be used for QA.
A stable release will follow suit hopefully in a few days from today.
On a high-level, these are the most important changes:
- ✨ Better Support for Measuring Throttling
- ✨ Introducing OONI Probe Bridges
- ✨ Improving Measurements Scrubbing
- ✨ Netemx: A Better Unit/Integration Testing Framework
- ✨ OONI Run v2 Support
- ✨ Unit Testing Improvements (
go test -short ./...
does not use the host network anymore) - 🐛 OONI Probe Bootstrap fixes
- 🐛 Web Connectivity v0.4 Fixes
- 🐛 Snowflake Fixes
Read on for more specific details!
✨ New Features
Better Support for Measuring Throttling
These changes implement a better support for measuring throttling where we periodically collect download speed samples during the lifetime of downloads performed using Web Connectivity (or any other network experiment using measurexlite
):
- ✨ chore: import memoryless from m-lab/go by @bassosimone in #1163
- ✨ feat: lightweight throttling measurements by @bassosimone in #1166
- ✨ feat(dslx): collect speed samples by @bassosimone in #1167
Note that, because these changes are measurexlite
specific, and because only Web Connectivity LTE uses measurexlite
, we would only get the benefit of better throttling measurements once we're fine switching all users to use Web Connectivity LTE. (Currently, 99% of the users are still using Web Connectivity v0.4, since we still have to finish doing A/B testing to make sure that the new version of Web Connectivity is not introducing any regressions compared to version v0.4.)
Introducing OONI Probe Bridges
We added support for OONI Probe Bridges. (We initially named this feature "beacons", which is why many pull requests below mention "beacons".) A OONI Probe Bridge is a host that allows us to connect to the OONI backend or test helpers. The current implementation knows about a single bridge, but it is possible to add more bridges (and we will add more bridges). The important concepts are that the probe knows the bridge IP address in advance and that there's the guarantee we can use pretty much any SNI with the bridge itself, even though, obviously, we use the api.ooni.io
verify hostname when verifying the certificate. By default OONI Probe tries using a bunch of predefined SNIs and remembers what works to use it consistently. This change currently only modifies how we create TLS connections with the OONI backend, but we plan on extending the scope of these changes further in subsequent releases.
There is also a mechanism to bypass the probe behavior and force it to use a specific bridge. We're documenting this mechanism here with the understanding that this functionality should be considered as a technical preview and may change in future releases without any need on our end to change the major version number. With that caveat out of the way, with this release of OONI Probe, if you create $HOME/.ooniprobe/engine/bridges.conf
, it will be used to override how we communicate with the backend. The file format is JSON with comments, and here is an example:
{
// The DomainEndpoints field maps a domain's endpoint to a list of bridges
"DomainEndpoints": {
"api.ooni.io:443": [
// A bridge is characterized by an IP address, a Port, a SNI to send on the wire, the
// hostname to use for TLS certificate verification and a delay.
//
// The delay can be useful to stagger dial attempts such that you do not try all
// the bridges at the same time. The unit of InitialDelay is nanoseconds.
{
"Address": "130.192.91.211",
"InitialDelay": 0,
"Port": "443",
"SNI": "www.example.com",
"VerifyHostname": "api.ooni.io"
}
]
}
}
There is also a new file $HOME/.ooniprobe/engine/httpsdialer.state
that keeps the on-disk state used by OONI Probe to remember which bridges worked. This file also includes information about some bridges that did not work, however we periodically prune this cache to avoid it growing too large in case there are many failures. (We're more interested on what works anyway.)
- ✨ feat(enginenetx): add configurable HTTPS dialer by @bassosimone in #1283
- ✨ feat(testingx): add code to ensure we close all conns by @bassosimone in #1284
- ✨ feat(enginenetx): make sure HTTPSDialer closes all connections by @bassosimone in #1285
- ✨ fix(enginenetx): pass context to tactics callbacks by @bassosimone in #1286
- ✨ refactor: adapt to netem pinning certificates to hosts by @bassosimone in #1287
- ✨ refactor(enginenetx): split https dialer implementation by @bassosimone in #1289
- ✨ feat(enginenetx): introduce loadable TLSDialer policy by @bassosimone in #1290
- ✨ refactor(enginenetx): introduce stats and make tactic a struct by @bassosimone in #1291
- ✨ fix(enginenetx): store endpoint into the tactic by @bassosimone in #1292
- ✨ refactor(enginenetx): rename HTTPTransport to Network by @bassosimone in #1293
- ✨ feat: add github.com/tailscale/hujson extensions by @bassosimone in #1294
- ✨ feat(enginenetx): use the new HTTPSDialer by @bassosimone in #1295
- ✨ fix(enginenetx): refine the happy-eyeballs algorithm by @bassosimone in #1296
- ✨ refactor(enginenetx): make static/loadable policy easier to use by @bassosimone in #1297
- ✨ feat(enginenetx): honor user-provided policy by @bassosimone in #1298
- ✨ feat(enginenetx): track operations and collect stats by @bassosimone in #1299
- ✨ refactor(enginenetx): make LookupTactics async by @bassosimone in #1300
- ✨ fix(enginenetx): stabilize happy eyeballs algorithm by @bassosimone in #1301
- ✨ feat(enginenetx): introduce beacons policy by @bassosimone in #1302
- ✨ refactor(enginenetx): make beacons API private by @bassosimone in #1303
- ✨ refactor(enginenetx): make stats API private by @bassosimone in #1304
- ✨ feat(enginenetx): prune old entries from stats by @bassosimone in #1305
- ✨ refactor(enginenetx): store address and port separately by @bassosimone in #1306
- ✨ refactor(enginenetx): group by domain's endpoint by @bassosimone in #1307
- ✨ refactor(enginenetx): make static-policy API private by @bassosimone in #1308
- ✨ refactor(enginenetx): make dns-policy API private by @bassosimone in #1309
- ✨ refactor(enginenetx): make https-dialer API private by @bassosimone in #1310
- ✨ feat(enginenetx): support getting stats on a domain endpoint by @bassosimone in #1311
- ✨ feat(enginenetx): add policy based on stats by @bassosimone in #1312
- ✨ feat(enginenetx): enable the stats-based policy by @bassosimone in #1313
- ✨ chore(enginenetx): more tests and robustness checks by @bassosimone in #1314
- ✨ fix(enginenetx): use dns policy with proxy (+renames) by @bassosimone in #1315
- ✨ fix(enginenetx): gracefully handle more nil cases by @bassosimone in #1316
- ✨ fix(enginenetx): periodically trim statistics by @bassosimone in #1317
- ✨ feat(enginenetx): extend beacons policy for THs by @bassosimone in #1318
- ✨ fix: rename beacons to bridges by @bassosimone in #1349
Support for HTTP/HTTPS proxies
As part of work to introduce OONI Probe bridges, we also removed limitations in the codebase that prevented to use HTTP or HTTPS proxy URLs with ooniprobe --proxy URL
and miniooni --proxy URL
. We will update mobile apps to allow using this kind of proxies as well. Before these changes, we only supported (a) SOCKS5 proxies and (b) creating proxies using Psiphon, vanilla tor, or tor along with snowflake (respectively, --proxy socks5://<addr>:<port>
, --proxy psiphon:///
, --proxy tor:///
, --proxy torsf:///
). Assuming you have, e.g., tor exposing a proxy on port 9080
, now you will be able to use ooniprobe --proxy http://127.0.0.1:9080/
.
- ✨ chore: run go fmt ./... by @bassosimone in #1272
- ✨ feat(testingx): introduce more comprehensive HTTP(S) proxy by @bassosimone in #1274
- ✨ feat(testingproxy): test HTTP(S) proxies using netem by @bassosimone in #1275
- ✨ feat(testingx): more tests for NewHTTPProxyHandler by @bassosimone in #1276
- ✨ cleanup: use testingx.NewHTTPProxyHandler as proxy by @bassosimone in #1277
- ✨ feat(UnderlyingNetwork): add support for ListenTCP by @bassosimone in #1278
- ✨ feat(netxlite): add (*Netx).ListenTCP by @bassosimone in #1279
- ✨ feat: support for testing socks5 clients by @bassosimone in #1280
- ✨ refactor(model/netx.go): TLSHandhaker now returns a TLSConn by @bassosimone in #1281
- ✨ feat(enginenetx): support HTTP and HTTPS proxies by @bassosimone in #1282
- ✨ fix(engineresolver): support HTTP/HTTPS proxies by @bassosimone in #1353
Improving Measurements Scrubbing
This set of changes ensures that we always scrub IP addresses or IP endpoints from HTTP response bodies and header values. We implemented this change because the changes required to support the bridges will make more likely, in the future and after some refactoring, that we'd discover the IPv4 user address and then use the IPv6 one for measuring (or the other way around). While improving IP addresses discovery is also a goal, with this set of changes we wanted to play safe and start making the codebase more robust ahead of future changes that may require such a robustness.
- ✨ refactor: move scrubbing logger to logx by @bassosimone in #1319
- ✨refactor(scrubber): reorganize tests and expose ScrubBytes by @bassosimone in #1320
- ✨feat(model): introduce ArchivalBinaryData by @bassosimone in #1321
- ✨chore(model): add tests for ArchivalTLSOrQUICHandshakeResult by @bassosimone in #1322
- ✨refactor(model): simplify using ArchivalBinaryData by @bassosimone in #1323
- ✨cleanup(ArchivalTLSOrQUICHandshakeResult): use ArchivalBinaryData by @bassosimone in #1324
- ✨feat: add ArchivalMaybeBinaryString type by @bassosimone in #1325
- ✨chore: write tests for ArchivalDNSLookupResult by @bassosimone in #1326
- ✨chore: write tests for ArchivalTCPConnectResult by @bassosimone in #1327
- ✨chore: write tests for ArchivalHTTPRequestResult by @bassosimone in #1328
- ✨chore: write tests for ArchivalNetworkEvent by @bassosimone in #1329
- ✨feat(ArchivalHTTPRequestResult): add tests w/ binary data and IP addrs by @bassosimone in #1330
- ✨cleanup(model): use ArchivalMaybeBinaryString for HTTP bodies by @bassosimone in #1331
- ✨cleanup: common funcs for setting headers by @bassosimone in #1332
- ✨cleanup: headers map now uses ArchivalMaybeBinaryString by @bassosimone in #1333
- ✨refactor: use ArchivalMaybeBinaryString for headers list by @bassosimone in #1334
- ✨feat(archival): unconditionally scrub HTTP headers and bodies by @bassosimone in #1335
- ✨cleanup: move ArchivalMaybeBinaryData to legacymodel by @bassosimone in #1336
Netemx: A Better Unit/Integration Testing Framework
Building on https://github.com/ooni/netem, we created a comprehensive unit/integration testing framework that works in userspace using https://gvisor.dev/ and featuring support to emulate censorship. We ported existing integration tests using jafar, the previous QA framework we were using, to use netemx and removed Jafar. We also created an A/B testing framework that allows us to compare Web Connectivity v0.4 and Web Connectivity LTE side-by-side. This framework will, hopefully, simplify our job of performing QA and finally declare Web Connectivity LTE available for general use.
- ✨ feat: netemx Environment by @kelmenhorst in #1160
- ✨ feat(netemx): redesign to simplify usage by @bassosimone in #1168
- ✨ refactor: move measurement-time functions to model by @bassosimone in #1169
- ✨ feat(webconnectivitylte): start adding test coverage by @bassosimone in #1170
- ✨ fix(webconnectivitylte): bump to 0.5.24 by @bassosimone in #1171
- ✨ feat(tcpping): rewrite tests using netem by @bassosimone in #1172
- ✨ feat(tlsping): rewrite tests to use netem by @bassosimone in #1173
- ✨ feat(simplequicping): rewrite tests to use netem by @bassosimone in #1174
- ✨ feat(netemx): add support for capturing packets by @bassosimone in #1175
- ✨ feat(dnsping): rewrite tests to use netem by @bassosimone in #1177
- ✨ feat(fbmessenger): rewrite to use netem for testing by @bassosimone in #1178
- ✨ feat(sniblocking): rewrite tests using netem by @bassosimone in #1179
- ✨ feat(netemx): allow creating custom netstacks by @bassosimone in #1180
- ✨ feat(whatsapp): rewrite tests to use netem by @bassosimone in #1181
- ✨ refactor(telegram): allow mocking DC initialization by @bassosimone in #1182
- ✨ refactor(telegram): rewrite tests using netemx by @bassosimone in #1183
- ✨ refactor(oohelperd): move code out of package main by @bassosimone in #1189
- ✨ webconnectivitylte: add test case for ooni/probe#2517 by @bassosimone in #1190
- ✨ netxlite/filtering: export DNSComposeResponse by @bassosimone in #1195
- ✨ feat(netxlite): decouple from tproxy singleton by @bassosimone in #1196
- ✨ feat(netxlite): construct types with custom UnderlyingNetwork by @bassosimone in #1197
- ✨ feat(testingx): add DNS over HTTPS server by @bassosimone in #1198
- ✨ feat(testingx): implement Ubuntu GeoIP resolver service by @bassosimone in #1199
- ✨ refactor(netemx): factor function for listening by @bassosimone in #1200
- ✨ refactor(netemx): allow running the oohelperd using netem by @bassosimone in #1201
- ✨ refactor(netemx): split qaenv.go and strip QAEnv prefix where not needed by @bassosimone in #1202
- ✨ feat(netemx): implement subset of the OONI API by @bassosimone in #1203
- ✨ feat(netemx): support simulating the oohelperd by @bassosimone in #1204
- ✨ feat(netemx): create "internet" netem-based emulation by @bassosimone in #1205
- ✨ fix(netemx): avoid suggesting more than one role is possible by @bassosimone in #1206
- ✨ feat(webconnectivityqa): infra to A/B test webconnectivity implementations by @bassosimone in #1207
- ✨ feat(webconnectivitylte): use webconnectivityqa for testing by @bassosimone in #1208
- ✨ feat(webconnectivity): add webconnectivityqa-based tests by @bassosimone in #1209
- ✨ feat(webconnectivityqa): add test for android_dns_cache_no_data by @bassosimone in #1210
- ✨ feat(webconnectivityqa): extract and compare all the relevant fields by @bassosimone in #1212
- ✨ chore(webconnectivityqa): import webconnectivity_nonexistent_domain by @bassosimone in #1213
- ✨ chore(webconnectivityqa): import webconnectivity_dns_blocking_nxdomain by @bassosimone in #1214
- ✨ chore(webconnectivityqa): import ...tcpip_blocking_with_consistent_dns by @bassosimone in #1215
- ✨ feat(netxlite): expose MaybeCustomUnderlyingNetwork by @bassosimone in #1218
- ✨ fix: avoid using 130.192.91.x for most netemx addresses by @bassosimone in #1219
- ✨ chore(webconnectivityqa): import ...tcpip_blocking_with_inconsistent_dns by @bassosimone in #1216
- ✨ refactor(netemx): move notable IP addrs in a separate file by @bassosimone in #1220
- ✨ refactor(netemx): move towards unified factory/server by @bassosimone in #1221
- ✨ feat(netemx): adapt NetStackServerFactory from telegram by @bassosimone in #1222
- ✨ feat(netemx): HTTPS server implementing NetStackServerFactory by @bassosimone in #1223
- ✨ feat(netemx): HTTP3 server implementing NetStackServerFactory by @bassosimone in #1224
- ✨ feat(netemx): implement NetStackServerFactory for DNS-over-UDP by @bassosimone in #1225
- ✨ refactor(netemx/scenario.go): start using QAEnvOptionNetStack by @bassosimone in #1226
- ✨ feat(netemx): ScenarioRoleWebServer using QAEnvOptionNetStack by @bassosimone in #1227
- ✨ fix(netemx): address issues with quic-go/quic-go by @bassosimone in #1228
- ✨ refactor(netemx): use QAEnvOptionNetStack for ScenarioRoleDNSOverHTTPS by @bassosimone in #1229
- ✨ feat(netemx): instantiate Do53 along with DoH by @bassosimone in #1230
- ✨ cleanup(netemx): always init resolvers using QAEnvOptionNetStack by @bassosimone in #1231
- ✨ cleanup(netemx): QAEnvOptionHTTPServer is now syntactic sugar by @bassosimone in #1232
- ✨ chore(webconnectivityqa): port tests where the control fails by @bassosimone in #1233
- ✨ feat: start merging filtering into testingx by @bassosimone in #1234
- ✨ refactor: merge filtering's HTTP code with testingx by @bassosimone in #1235
- ✨ refactor: finish merging filtering into testingx by @bassosimone in #1236
- ✨ feat(testingx): adapt jafar HTTP proxy by @bassosimone in #1237
- ✨ feat(testingx): import and adapt jafar TLS SNI proxy by @bassosimone in #1238
- ✨ webconnectivityqa: import test cases using proxies by @bassosimone in #1239
- ✨ feat(webconnectivityqa): start adapting test cases with redirects by @bassosimone in #1240
- ✨ feat(webconnectivityqa): convert more jafar test cases by @bassosimone in #1241
- ✨ feat(webconnectivityqa): port jafar's http-diff test cases by @bassosimone in #1242
- ✨ feat(webconnectivityqa): import misconfigured-TLS test cases by @bassosimone in #1243
- ✨ cleanup: remove jafar and the previous QA framework by @bassosimone in #1244
- ✨ fix(webconnectivityqa): Always close the QAEnv by @kelmenhorst in #1245
OONI Run v2 Support
- feat(oonimkall): experimental OONI Run v2 API by @bassosimone in #1176. This change introduces minimum support to allow OONI Probe Android and iOS to implement OONI Run v2.
Unit Testing Improvements
- ✨ refactor:
go test -short
does not use the host network by @bassosimone in #1352. This change ensures that tests marked as short either use the loopback interface or netemx, thus making the core body of tests reproducible and independent of the the place in which you are running tests.
🚧 Maintenance
Preliminary Refactoring to Enable Bridges
We merged the following refactoring pull requests to remove technical debt that prevented us from cleanly implementing bridges:
- 🚧 refactor(measurexlite): move OperationLogger to logx by @bassosimone in #1246
- 🚧 cleanup(netxlite): QUICListener -> UDPListener by @bassosimone in #1247
- 🚧 refactor(netxlite): use *Netx for the system resolver by @bassosimone in #1248
- 🚧 refactor(netxlite): use *Netx for the system dialer by @bassosimone in #1249
- 🚧 refactor(netxlite): use *Netx for creating UDP sockets by @bassosimone in #1250
- 🚧 refactor(netxlite): use *Netx for creating QUIC dialers by @bassosimone in #1251
- 🚧 refactor(netxlite): use *Netx for creating TLS handshakers by @bassosimone in #1252
- 🚧 refactor(netxlite): use *Netx for creating HTTP transports by @bassosimone in #1253
- 🚧 refactor(netxlite): use *Netx for creating HTTP3 transports by @bassosimone in #1254
- 🚧 feat(netxlite): use *Netx for creating DoH resolvers by @bassosimone in #1255
- 🚧 feat(netxlite): use *Netx for creating UTLS handshakers by @bassosimone in #1256
- 🚧 feat(netxlite): use *Netx for NewParallelUDPResolver by @bassosimone in #1257
- 🚧 feat: introduce model.MeasuringNetwork by @bassosimone in #1258
- 🚧 refactor(MeasuringNetwork): define dialers without resolver by @bassosimone in #1259
- 🚧 refactor(measurexlite): depend on model.MeasuringNetwork by @bassosimone in #1260
- 🚧 cleanup: move measurex and tracex inside ./internal/legacy by @bassosimone in #1261
- 🚧 feat(measurexlite): Trace now implements MeasuringNetwork by @bassosimone in #1262
- 🚧 refactor: rename geolocate enginelocate by @bassosimone in #1263
- 🚧 refactor: rename sessionresolver engineresolver by @bassosimone in #1264
- 🚧 feat: prepare to partially detach engine from netxlite by @bassosimone in #1265
- 🚧 cleanup: inline NewHTTPTransportWithLoggerResolverAndOptionalProxyURL by @bassosimone in #1266
- 🚧 refactor(netxlite): split http code into multiple files by @bassosimone in #1267
- 🚧 chore(netxlite): isolate and annotate quirky functions by @bassosimone in #1269
- 🚧 feat(netxlite): add flexible HTTP transport factory by @bassosimone in #1270
Dependency Upgrades
- 🚧 chore: we're now hacking on v3.19.0-alpha by @bassosimone in #1154
- 🚧 chore: use go1.20.5 by @bassosimone in #1156
- 🚧 cleanup: measurexlite should not depend on tracex by @bassosimone in #1164
- 🚧 chore: use go1.20.6 by @bassosimone in #1184
- 🚧 chore: use 2023-07 geoip database by @bassosimone in #1186
- 🚧 chore: update all the dependencies we can update by @bassosimone in #1187
- 🚧 chore: upgrade C dependencies by @bassosimone in #1194
- 🚧 chore(cdeps): upgrade to OpenSSL 3.1.2 by @kelmenhorst in #1191
- 🚧 probe-cli: Update to quic-go v0.37.3 by @kelmenhorst in #1161
- 🚧 chore: upgrade to Psiphon-Labs/psiphon-tunnel-core@3f91b1b by @bassosimone in #1338
- 🚧 chore: use go1.20.8 by @bassosimone in #1340
- 🚧 chore: use 2023-10 geoip database by @bassosimone in #1341
- 🚧 chore: use gomajor to update direct deps by @bassosimone in #1342
- 🚧 chore: run go generate ./... by @bassosimone in #1343
- 🚧 chore: update the user agent we use by @bassosimone in #1344
- 🚧 chore: upgrade C dependencies by @bassosimone in #1339
- 🚧 chore: upgrade the NDK version by @bassosimone in #1345
- 🚧 chore: upgrade most deps except snowflake by @bassosimone in #1346
Misc Changes
- 🚧 chore(oonimkall): re-enable TestTaskRunnerRun by @bassosimone in #1354
- 🚧 fix: do not mention NewDefaultCertPool by @bassosimone in #1155
- 🚧 [forwardport] fix: use correct Xcode and mingw version by @bassosimone in #1158
- 🚧 fix(.github): avoid using deprecated ::set-output by @bassosimone in #1159
- 🚧 fix: avoid submitting when tor binary is missing by @bassosimone in #1078
- 🚧 fix(tutorial): avoid broken pipe caused by passing non-JSON to jq by @imw in #1193
- 🚧 feat(MONOREPO): script to build stable android release by @bassosimone in #1188
🐛 Bug Fixes
OONI Probe Bootstrap fixes
- 🐛 fix: make bootstrap more robust (and slower 😢) by @bassosimone in #1351. This change increases the timeout used for finding out OONI Probe's IP address and modifies the default weights for DNS-over-HTTPS (DoH) resolvers and the system resolver to allow OONI Probe to cold bootstrap in networks where most DoH resolvers are blocked. This change also makes the bootstrap slower if there are many timeouts. Solving the tension between robustness and efficiency would require more refactoring, which will happen in the next release.
Web Connectivity v0.4 Fixes
- 🐛 fix: anomaly with
android_dns_cache_no_data
and inconsistent dns by @bassosimone in #1211. This change ensures we handleandroid_dns_cache_no_data
as an anomaly when the test helper could resolve the URL's domain, thus resolving a long-standing data quality issue.
Snowflake Fixes
- 🐛 chore: update obfs4, goptlib, snowflake by @bassosimone in #1347
- 🐛 fix: update Snowflake domain front by @cohosh in #1337
Misc Fixes
- 🐛 fix(quicping): support URLs with an IPv6 address by @Lanius-collaris in #1192
- 🐛 fix(logx): log when an operation is starting by @bassosimone in #1350
🗒️ Documentation
- 🗒️ doc: provide specific build instructions by @bassosimone in #1157
New Contributors
- @imw made their first contribution in #1193
- @cohosh made their first contribution in #1337
- @Lanius-collaris made their first contribution in #1192
Full Changelog: v3.18.0-alpha...v3.19.0-alpha