Skip to content

Commit b041310

Browse files
committed
rpn: impl update ops and perma creds
1 parent 89dbcec commit b041310

File tree

8 files changed

+429
-111
lines changed

8 files changed

+429
-111
lines changed

intra/backend/ipn_proxies.go

Lines changed: 55 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
package backend
88

9+
import "fmt"
10+
911
const ( // see ipn/proxies.go
1012
// IDs for default proxies
1113

@@ -83,15 +85,62 @@ const ( // see ipn/proxies.go
8385
END = -3
8486
)
8587

88+
// RpnOps carries options that control the behaviour of Update and RegisterWin.
89+
// Fields are unexported; use the Set* methods to configure.
90+
type RpnOps struct {
91+
rotateCreds bool // force a new WireGuard keypair on the next Update
92+
permaCreds bool // use permanent WireGuard (local) addresses if available
93+
forceFetchServers bool // force server-list refresh on the next Update
94+
newPort uint16 // fixed WireGuard port; 0 = random per wsRandomPort()
95+
}
96+
97+
func NewRpnOps() *RpnOps {
98+
return &RpnOps{}
99+
}
100+
101+
func (o *RpnOps) String() string {
102+
return fmt.Sprintf("rotate: %t; perma: %t; forceFetchServers: %t; port: %d",
103+
o.rotateCreds, o.permaCreds, o.forceFetchServers, o.newPort)
104+
}
105+
106+
// SetRotateCreds forces generation of a new WireGuard keypair on the next Update.
107+
// Note: Rotate and Perma are mutually exclusive; setting one to true will set the other to false.
108+
func (o *RpnOps) SetRotateCreds(v bool) { o.rotateCreds = v; o.permaCreds = false }
109+
110+
// SetPermaCreds enables/disables using the permanent WG credential set in Conf().
111+
// Note: Rotate and Perma are mutually exclusive; setting one to true will set the other to false.
112+
func (o *RpnOps) SetPermaCreds(v bool) { o.permaCreds = v; o.rotateCreds = false }
113+
114+
// SetForceFetchServers forces the server-list refresh on the next Update.
115+
func (o *RpnOps) SetForceFetchServers(v bool) { o.forceFetchServers = v }
116+
117+
// SetPort pins a specific WireGuard port; 0 means random (default).
118+
func (o *RpnOps) SetPort(port uint16) { o.newPort = port }
119+
120+
// Rotate reports whether a new WG keypair should be generated.
121+
func (o RpnOps) Rotate() bool { return o.rotateCreds }
122+
123+
// Perma reports whether permanent WG credentials should be used in Conf().
124+
func (o RpnOps) Perma() bool { return o.permaCreds }
125+
126+
// FetchServers reports whether the server-list fetch should be forced.
127+
func (o RpnOps) FetchServers() bool { return o.forceFetchServers }
128+
129+
// Port returns the pinned WireGuard port, or 0 if none is set.
130+
func (o RpnOps) Port() uint16 {
131+
return o.newPort
132+
}
133+
86134
type Rpn interface {
87135
// EntitlementFrom returns the RpnEntitlement represented by entitlementOrStateJson.
88136
// `did` is the device identifier to use for this entitlement, if applicable; and `rpnProviderID` is the RPN provider for this entitlement, if applicable.
89137
// `rpnProviderID` is the RPN provider to use with this entitlement (ex: RpnWin, RpnSE, etc).
90138
EntitlementFrom(entitlementOrStateJson []byte, rpnProviderID, did string) (RpnEntitlement, error)
91139
// RegisterSE registers a new SurfEasy user.
92140
RegisterSE() error
93-
// RegisterWin is alias for RegisterWin.
94-
RegisterWin(entitlementOrStateJson []byte, did string) (json []byte, err error)
141+
// RegisterWin registers (or re-registers) a Windscribe account.
142+
// ops may be nil to use default behaviour.
143+
RegisterWin(entitlementOrStateJson []byte, did string, ops *RpnOps) (json []byte, err error)
95144
// UnregisterWin unregisters a Windscribe installation.
96145
UnregisterWin() bool
97146
// UnregisterSE unregisters a SurfEasy user.
@@ -163,6 +212,8 @@ type RpnAcc interface {
163212
Who() string
164213
// State returns the state (as json) of the account.
165214
State() ([]byte, error)
215+
// Ops returns the RpnOps that control the behaviour of Update.
216+
Ops() *RpnOps
166217
// Created returns the time (unix millis) currently active account was created.
167218
Created() int64
168219
// Updated returns the time (unix millis) currently active account was updated.
@@ -171,8 +222,8 @@ type RpnAcc interface {
171222
Expires() int64
172223
// Locations returns RpnServers encapsulating this proxy's worldwide server presence.
173224
Locations() (RpnServers, error)
174-
// Update updates the account creating new state.
175-
Update(rotate bool) (newstate []byte, err error)
225+
// Update updates the account creating new state; ops may be nil to retain current RpnOps.
226+
Update(ops *RpnOps) (newstate []byte, err error)
176227
}
177228

178229
// RpnEntitlement represents access to a proxy service.

intra/dns53/dot_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,7 @@ func TestWinReaches(t *testing.T) {
442442

443443
const did = "deadbeefdeadbeefdeadbeefdeadbeef" // some device id
444444
ilog.D("ws: read ent (sess? %t): %d", readWinJson, len(entjson))
445-
if wreg, err := pxr.RegisterWin(entjson, did); err != nil {
445+
if wreg, err := pxr.RegisterWin(entjson, did, nil); err != nil {
446446
t.Fatal(err)
447447
} else {
448448
entjson = wreg

intra/ipn/proxies.go

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1252,7 +1252,7 @@ func (px *proxifier) EntitlementFrom(entitlementOrStateJson []byte, id, did stri
12521252
}
12531253

12541254
// RegisterWin implements x.Rpn.
1255-
func (px *proxifier) RegisterWin(entitlementOrState []byte, did string) (stateJson []byte, err error) {
1255+
func (px *proxifier) RegisterWin(entitlementOrState []byte, did string, ops *x.RpnOps) (stateJson []byte, err error) {
12561256
defer func() {
12571257
px.lastWinErr.Store(err) // may be nil
12581258
}()
@@ -1261,10 +1261,14 @@ func (px *proxifier) RegisterWin(entitlementOrState []byte, did string) (stateJs
12611261
return nil, errNilWinDevice
12621262
}
12631263

1264+
if ops == nil {
1265+
ops = new(x.RpnOps)
1266+
}
1267+
12641268
existingStateJson := entitlementOrState
12651269
restore := len(existingStateJson) > 0
12661270

1267-
win, err := px.registerWin(existingStateJson, did)
1271+
win, err := px.registerWin(existingStateJson, did, *ops)
12681272
if err != nil || core.IsNil(win) {
12691273
log.E("proxy: ws: make failed: %v", err)
12701274
return nil, core.JoinErr(err, errNilWinCfg)
@@ -1284,12 +1288,12 @@ func (px *proxifier) RegisterWin(entitlementOrState []byte, did string) (stateJs
12841288
return nil, core.JoinErr(err, errNotRpnProxy)
12851289
}
12861290

1287-
log.I("proxy: ws: registered: %s / %d; new? %t", win.Who(), len(state), !restore)
1291+
log.I("proxy: ws: registered: %s / %d; new? %t; ops: %+v", win.Who(), len(state), !restore, ops)
12881292
return state, nil
12891293
}
12901294

1291-
func (px *proxifier) registerWin(entitlementOrStateJson []byte, did string) (RpnAcc, error) {
1292-
return px.extc.MakeWsWgFrom(entitlementOrStateJson, did)
1295+
func (px *proxifier) registerWin(entitlementOrStateJson []byte, did string, ops x.RpnOps) (RpnAcc, error) {
1296+
return px.extc.MakeWsWgFrom(entitlementOrStateJson, did, ops)
12931297
}
12941298

12951299
// RegisterSE implements x.Rpn.

intra/ipn/proxy_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ func TestProxyReaches(t *testing.T) {
213213

214214
var projson []byte
215215
var err error
216-
if projson, err = pxr.RegisterWin(nil); err != nil {
216+
if projson, err = pxr.RegisterWin(nil, "", nil); err != nil {
217217
t.Fatal(err)
218218
}
219219
if ips, err := pxr.TestWin(); err != nil {

intra/ipn/rpn.go

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -46,8 +46,6 @@ type rpnp struct {
4646
kids map[string]struct{}
4747
}
4848

49-
type RpnMakeKind = rpn.RpnMakeKind
50-
5149
var _ RpnProxy = (*rpnp)(nil)
5250
var _ RpnAcc = (*rpnp)(nil) // (useless) assertion always succeeds, see above
5351
var _ Proxy = (*rpnp)(nil) // (useless) assertion always succeeds, see above
@@ -547,8 +545,8 @@ func (r *rpnp) flattenKids() (ccs []string) {
547545
}
548546

549547
// Update implements RpnAcc.
550-
func (r *rpnp) Update(rotate bool) (newState []byte, err error) {
551-
newState, err = r.RpnAcc.Update(rotate)
548+
func (r *rpnp) Update(ops *x.RpnOps) (newState []byte, err error) {
549+
newState, err = r.RpnAcc.Update(ops)
552550
if err == nil {
553551
core.Gxe("rpn.fork."+r.ProviderID(), r.forkAll)
554552
}

intra/ipn/rpn/cfg.go

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -58,24 +58,6 @@ type RpnAcc interface {
5858
Conf(key string) (string, error)
5959
}
6060

61-
type RpnMakeKind uint8
62-
63-
func (k RpnMakeKind) Updating() bool {
64-
return k&RpnMakeUpdate == 1
65-
}
66-
67-
func (k RpnMakeKind) Rotate() bool {
68-
return k&RpnMakeRotate == 1
69-
}
70-
71-
const (
72-
// RpnMakeNew uses persisted sub-account identity when available, on enrollment or account update.
73-
RpnMakeNew RpnMakeKind = 0b0001
74-
// RpnMakeUpdate makes new sub-account identity on enrollment or account update.
75-
RpnMakeUpdate RpnMakeKind = 0b0010
76-
RpnMakeRotate RpnMakeKind = 0b0100
77-
)
78-
7961
var _ RpnAcc = (*WsClient)(nil)
8062

8163
type BaseClient struct {
@@ -110,7 +92,8 @@ func (RpnStateless) Conf(cc string) (string, error) { return "", errRpnStateless
11092

11193
type RpnUpdateless struct{}
11294

113-
func (RpnUpdateless) Update(bool) ([]byte, error) { return nil, errRpnUpdateless }
95+
func (RpnUpdateless) Ops() *x.RpnOps { return nil }
96+
func (RpnUpdateless) Update(*x.RpnOps) ([]byte, error) { return nil, errRpnUpdateless }
11497

11598
type RpnMultiCountryServers struct {
11699
all []x.RpnServer

intra/ipn/rpn/regional.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,3 +132,64 @@ func (rwg *RegionalWgConf) addrCsv() string {
132132
}
133133
return strings.Join(addrs, ",")
134134
}
135+
136+
// GenUapiConfigFrom builds an on-the-fly UAPI config string that overlays
137+
// the credentials from a permanent config (private key, address, DNS,
138+
// preshared key, allowed IPs) onto this regional config's server endpoints.
139+
// rwg.UapiWgConf is NOT modified; the generated string is returned directly.
140+
// Returns ("", false) when rwg/perma is nil or PrivateKey/Address is absent.
141+
func (rwg *RegionalWgConf) GenUapiConfigFrom(perma *WsWgCreds) (string, bool) {
142+
if rwg == nil || perma == nil || len(perma.PrivateKey) <= 0 {
143+
return "", false
144+
}
145+
146+
addr := perma.Address
147+
if len(addr) <= 0 {
148+
return "", false // not a perma config
149+
}
150+
dns := perma.DNS
151+
if len(dns) <= 0 {
152+
dns = cfdns4 // fallback
153+
}
154+
155+
// AllowedIPs from the permanent config may be comma-separated ("0.0.0.0/0, ::/0").
156+
allowedips := []string{gw4}
157+
if len(perma.AllowedIPs) > 0 {
158+
parts := strings.Split(perma.AllowedIPs, ",")
159+
allowedips = make([]string, 0, len(parts))
160+
for _, p := range parts {
161+
if t := strings.TrimSpace(p); len(t) > 0 {
162+
allowedips = append(allowedips, t)
163+
}
164+
}
165+
}
166+
167+
conf := fmt.Sprintf(`private_key=%s
168+
replace_peers=true
169+
address=%s
170+
dns=%s
171+
mtu=(auto)
172+
public_key=%s`,
173+
toHex(perma.PrivateKey),
174+
addr,
175+
dns,
176+
toHex(rwg.ServerPubKey),
177+
)
178+
if len(rwg.ServerIPPort4) > 0 {
179+
conf += "\nendpoint=" + rwg.ServerIPPort4
180+
}
181+
if len(rwg.ServerIPPort6) > 0 {
182+
conf += "\nendpoint=" + rwg.ServerIPPort6
183+
}
184+
if len(rwg.ServerDomainPort) > 0 {
185+
conf += "\nendpoint=" + rwg.ServerDomainPort
186+
}
187+
if len(perma.PresharedKey) > 0 {
188+
conf += "\npreshared_key=" + toHex(perma.PresharedKey)
189+
}
190+
for _, ip := range allowedips {
191+
conf += fmt.Sprintf("\nallowed_ip=%s", ip)
192+
}
193+
194+
return conf, true
195+
}

0 commit comments

Comments
 (0)