Skip to content

Commit db70616

Browse files
bassosimonecyBerta
andauthored
refactor(riseupvpn): handle failing API and simplify test keys (#1363)
This diff incorporates part of what has been implemented by @cyBerta in #1125 in response to my review as well as additional changes based on my own feelings about what is correct to do here. Compared to the original diff, these are the changes that I implemented: 1. I have omitted the work to fetch from riseup geo service and figure out the correct gateways to test. The main reason for not including this body of work has been to reduce the size of the diff and the amount of code to deal with. 2. I modified the logic related to failures in fetching the CA and communicating with riseup services. The test fails immediately if we cannot fetch the proper CA or we cannot contact riseup services. I did not feel comfortable disabling the CA to access riseup services and connecting to the TCP endpoints discovered w/o CA verification. 3. In the test keys, I renamed `api_failure` to `api_failures` because I do not think it's optimal to keep the same name while the type has changed from `*string` to `[]string`. The spirit of the changes is not directly compatible with what we discussed with @cyBerta. The main difference is in my decision to fail early in case we miss the preconditions. As I wrote in #1125 (review), I think we should be using richer input (and start with its simplest form) to provision to probes the data they need to perform this experiment. By provisioning the data ourselves, we remove the coupling between getting the CA, accessing riseup services to get information on what gateways we should measure, and measure the gateways, which makes the experiment several orders of magnitude more robust. Unfortunately, I do not have time, in this cycle, to perform all this richer input work. We'll try again for 3.20. This work is part of ooni/probe#1432. While there, I forced null callbacks when performing the CA fetch and contacting riseup services, otherwise we end up printing a non-monotonic progress status. Admittedly, also omitting to provide progress about these two operations is bad, but I think we won't be able to provide monotonic progress until we know what we should fetch in advance. --------- Co-authored-by: cyBerta <cyberta@riseup.net>
1 parent 85da220 commit db70616

2 files changed

Lines changed: 106 additions & 260 deletions

File tree

internal/experiment/riseupvpn/riseupvpn.go

Lines changed: 30 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import (
99
"time"
1010

1111
"github.com/ooni/probe-cli/v3/internal/experiment/urlgetter"
12-
"github.com/ooni/probe-cli/v3/internal/legacy/tracex"
1312
"github.com/ooni/probe-cli/v3/internal/model"
1413
"github.com/ooni/probe-cli/v3/internal/netxlite"
1514
)
@@ -63,21 +62,15 @@ type Config struct {
6362
// TestKeys contains riseupvpn test keys.
6463
type TestKeys struct {
6564
urlgetter.TestKeys
66-
APIFailure *string `json:"api_failure"`
67-
APIStatus string `json:"api_status"`
68-
CACertStatus bool `json:"ca_cert_status"`
69-
FailingGateways []GatewayConnection `json:"failing_gateways"`
70-
TransportStatus map[string]string `json:"transport_status"`
65+
APIFailures []string `json:"api_failures"`
66+
CACertStatus bool `json:"ca_cert_status"`
7167
}
7268

7369
// NewTestKeys creates new riseupvpn TestKeys.
7470
func NewTestKeys() *TestKeys {
7571
return &TestKeys{
76-
APIFailure: nil,
77-
APIStatus: "ok",
78-
CACertStatus: true,
79-
FailingGateways: nil,
80-
TransportStatus: nil,
72+
APIFailures: []string{},
73+
CACertStatus: true,
8174
}
8275
}
8376

@@ -88,12 +81,8 @@ func (tk *TestKeys) UpdateProviderAPITestKeys(v urlgetter.MultiOutput) {
8881
tk.Requests = append(tk.Requests, v.TestKeys.Requests...)
8982
tk.TCPConnect = append(tk.TCPConnect, v.TestKeys.TCPConnect...)
9083
tk.TLSHandshakes = append(tk.TLSHandshakes, v.TestKeys.TLSHandshakes...)
91-
if tk.APIStatus != "ok" {
92-
return // we already flipped the state
93-
}
9484
if v.TestKeys.Failure != nil {
95-
tk.APIStatus = "blocked"
96-
tk.APIFailure = v.TestKeys.Failure
85+
tk.APIFailures = append(tk.APIFailures, *v.TestKeys.Failure)
9786
return
9887
}
9988
}
@@ -104,42 +93,6 @@ func (tk *TestKeys) UpdateProviderAPITestKeys(v urlgetter.MultiOutput) {
10493
func (tk *TestKeys) AddGatewayConnectTestKeys(v urlgetter.MultiOutput, transportType string) {
10594
tk.NetworkEvents = append(tk.NetworkEvents, v.TestKeys.NetworkEvents...)
10695
tk.TCPConnect = append(tk.TCPConnect, v.TestKeys.TCPConnect...)
107-
for _, tcpConnect := range v.TestKeys.TCPConnect {
108-
if !tcpConnect.Status.Success {
109-
gatewayConnection := newGatewayConnection(tcpConnect, transportType)
110-
tk.FailingGateways = append(tk.FailingGateways, *gatewayConnection)
111-
}
112-
}
113-
}
114-
115-
func (tk *TestKeys) updateTransportStatus(openvpnGatewayCount, obfs4GatewayCount int) {
116-
failingOpenvpnGateways, failingObfs4Gateways := 0, 0
117-
for _, gw := range tk.FailingGateways {
118-
if gw.TransportType == "openvpn" {
119-
failingOpenvpnGateways++
120-
} else if gw.TransportType == "obfs4" {
121-
failingObfs4Gateways++
122-
}
123-
}
124-
if failingOpenvpnGateways < openvpnGatewayCount {
125-
tk.TransportStatus["openvpn"] = "ok"
126-
} else {
127-
tk.TransportStatus["openvpn"] = "blocked"
128-
}
129-
if failingObfs4Gateways < obfs4GatewayCount {
130-
tk.TransportStatus["obfs4"] = "ok"
131-
} else {
132-
tk.TransportStatus["obfs4"] = "blocked"
133-
}
134-
}
135-
136-
func newGatewayConnection(
137-
tcpConnect tracex.TCPConnectEntry, transportType string) *GatewayConnection {
138-
return &GatewayConnection{
139-
IP: tcpConnect.IP,
140-
Port: tcpConnect.Port,
141-
TransportType: transportType,
142-
}
14396
}
14497

14598
// AddCACertFetchTestKeys adds generic urlgetter.Get() testKeys to riseupvpn specific test keys
@@ -149,11 +102,6 @@ func (tk *TestKeys) AddCACertFetchTestKeys(testKeys urlgetter.TestKeys) {
149102
tk.Requests = append(tk.Requests, testKeys.Requests...)
150103
tk.TCPConnect = append(tk.TCPConnect, testKeys.TCPConnect...)
151104
tk.TLSHandshakes = append(tk.TLSHandshakes, testKeys.TLSHandshakes...)
152-
if testKeys.Failure != nil {
153-
tk.APIStatus = "blocked"
154-
tk.APIFailure = tk.Failure
155-
tk.CACertStatus = false
156-
}
157105
}
158106

159107
// Measurer performs the measurement.
@@ -206,20 +154,31 @@ func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
206154
FailOnHTTPError: true,
207155
}},
208156
}
209-
for entry := range multi.CollectOverall(ctx, inputs, 0, 20, "riseupvpn", callbacks) {
157+
158+
// Q: why returning early if we cannot fetch the CA or the config? Cannot we just
159+
// disable certificate verification and fetch the config?
160+
//
161+
// A: I do not feel comfortable with fetching without verying the certificates since
162+
// this means the experiment could be person-in-the-middled and forced to perform TCP
163+
// connect to arbitrary hosts, which maybe is harmless but still a bummer.
164+
//
165+
// TODO(https://github.com/ooni/probe/issues/2559): solve this problem by serving the
166+
// correct CA and the endpoints to probes using check-in v2 (aka richer input).
167+
168+
nullCallbacks := model.NewPrinterCallbacks(model.DiscardLogger)
169+
for entry := range multi.CollectOverall(ctx, inputs, 0, 20, "riseupvpn", nullCallbacks) {
210170
tk := entry.TestKeys
211171
testkeys.AddCACertFetchTestKeys(tk)
212172
if tk.Failure != nil {
213-
// TODO(bassosimone,cyberta): should we update the testkeys
214-
// in this case (e.g., APIFailure?)
215-
// See https://github.com/ooni/probe/issues/1432.
173+
testkeys.CACertStatus = false
174+
testkeys.APIFailures = append(testkeys.APIFailures, *tk.Failure)
175+
// Note well: returning nil here causes the measurement to be submitted.
216176
return nil
217177
}
218178
if ok := certPool.AppendCertsFromPEM([]byte(tk.HTTPResponseBody)); !ok {
219179
testkeys.CACertStatus = false
220-
testkeys.APIStatus = "blocked"
221-
errorValue := "invalid_ca"
222-
testkeys.APIFailure = &errorValue
180+
testkeys.APIFailures = append(testkeys.APIFailures, "invalid_ca")
181+
// Note well: returning nil here causes the measurement to be submitted.
223182
return nil
224183
}
225184
}
@@ -244,12 +203,16 @@ func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
244203
FailOnHTTPError: true,
245204
}},
246205
}
247-
for entry := range multi.CollectOverall(ctx, inputs, 1, 20, "riseupvpn", callbacks) {
206+
for entry := range multi.CollectOverall(ctx, inputs, 1, 20, "riseupvpn", nullCallbacks) {
248207
testkeys.UpdateProviderAPITestKeys(entry)
208+
tk := entry.TestKeys
209+
if tk.Failure != nil {
210+
// Note well: returning nil here causes the measurement to be submitted.
211+
return nil
212+
}
249213
}
250214

251215
// test gateways now
252-
testkeys.TransportStatus = map[string]string{}
253216
gateways := parseGateways(testkeys)
254217
openvpnEndpoints := generateMultiInputs(gateways, "openvpn")
255218
obfs4Endpoints := generateMultiInputs(gateways, "obfs4")
@@ -272,8 +235,7 @@ func (m Measurer) Run(ctx context.Context, args *model.ExperimentArgs) error {
272235
testkeys.AddGatewayConnectTestKeys(entry, "obfs4")
273236
}
274237

275-
// set transport status based on gateway test results
276-
testkeys.updateTransportStatus(len(openvpnEndpoints), len(obfs4Endpoints))
238+
// Note well: returning nil here causes the measurement to be submitted.
277239
return nil
278240
}
279241

0 commit comments

Comments
 (0)