Skip to content

Commit d8cc799

Browse files
committed
fix(interop/ivpn): Handle local service port connections
The changes add support for tracking and properly routing connections to the IVPN client's local service port, which is needed when the default firewall action is to block unknown connections. The verdict handler is now registered earlier in the connection flow to handle connections while the client is connecting. safing/portmaster-shadow#34
1 parent a3f746d commit d8cc799

File tree

3 files changed

+37
-12
lines changed

3 files changed

+37
-12
lines changed

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ require (
3030
github.com/hashicorp/go-multierror v1.1.1
3131
github.com/hashicorp/go-version v1.7.0
3232
github.com/hectane/go-acl v0.0.0-20230122075934-ca0b05cb1adb
33-
github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260326085211-75eceae81a57
33+
github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260403070528-13364be7597e
3434
github.com/jackc/puddle/v2 v2.2.2
3535
github.com/jaswdr/faker/v2 v2.9.0
3636
github.com/lmittmann/tint v1.1.2

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,8 +225,8 @@ github.com/inconshreveable/log15 v0.0.0-20170622235902-74a0988b5f80/go.mod h1:cO
225225
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
226226
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
227227
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
228-
github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260326085211-75eceae81a57 h1:UbPzCftqy05OKk7spUtASQz5d8jutgbneAwpJPoiWvA=
229-
github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260326085211-75eceae81a57/go.mod h1:dvedOGgEXvFCK/gz+BJQhd5BSTQvIygmOr/22xPdgIw=
228+
github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260403070528-13364be7597e h1:UzZ/4Ya1jvWuC/NjyhnqfD70rkJ2QklcFpatbCqBjeg=
229+
github.com/ivpn/desktop-app/daemon/protocol/ivpnclient v0.0.0-20260403070528-13364be7597e/go.mod h1:dvedOGgEXvFCK/gz+BJQhd5BSTQvIygmOr/22xPdgIw=
230230
github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=
231231
github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
232232
github.com/jaswdr/faker/v2 v2.9.0 h1:Sqqpp+pxduDO+MGOhYE3UHtI9Sowt9j95f8h8nVvips=

service/interop/ivpn/ivpn.go

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/safing/portmaster/service/mgr"
1414
"github.com/safing/portmaster/service/netenv"
1515
"github.com/safing/portmaster/service/network"
16+
"github.com/safing/portmaster/service/network/netutils"
1617
"github.com/safing/portmaster/service/network/packet"
1718
"github.com/safing/portmaster/spn/hub"
1819
)
@@ -43,6 +44,7 @@ type vpnConnectionInfo struct {
4344
// and its current VPN connection status, used for providing context in firewall verdicts.
4445
type clientStatus struct {
4546
serviceBinary string
47+
servicePort uint16
4648
vpnConnection vpnConnectionInfo // VPN server endpoint
4749
connectedInfo *ivpnclient.ConnectedResp // info about already established VPN connection, if any
4850
}
@@ -133,14 +135,28 @@ func (i *InteropIvpn) connectIvpnClient(wc *mgr.WorkerCtx) error {
133135
notifWarnOldVersion.Store(nil)
134136
}
135137

136-
ci := ivpnclient.ClientInfo{
137-
Type: ivpnclient.ClientPortmaster,
138-
Name: "Portmaster",
139-
Version: info.Version()}
138+
servicePort, _, err := ivpnclient.GetConnectionPortInfo()
139+
if err != nil {
140+
return err
141+
}
142+
// Save ServicePort.
143+
status := *i.getStatus()
144+
status.servicePort = uint16(servicePort)
145+
i.setStatus(&status)
146+
// Now we know the service port, we can register the verdict handler
147+
// to allow accepting connections to the IVPN client service port while the client is connecting.
148+
// This is needed for case when Portmaster default action is to block unknown connections.
149+
i.owner.EnsureVerdictHandlerRegistered()
140150

141151
// Create client.
142152
// Ignoring error here, since it is expected that the client may not be in running state
143-
client, err := ivpnclient.NewClientAsRoot(nil, time.Second*10, ci)
153+
client, err := ivpnclient.NewClientAsRoot(
154+
nil,
155+
time.Second*10,
156+
ivpnclient.ClientInfo{
157+
Type: ivpnclient.ClientPortmaster,
158+
Name: "Portmaster",
159+
Version: info.Version()})
144160
if err != nil {
145161
return nil
146162
}
@@ -183,15 +199,12 @@ func (i *InteropIvpn) connectIvpnClient(wc *mgr.WorkerCtx) error {
183199
}
184200

185201
// Save ServiceBinary.
186-
status := *i.getStatus()
202+
status = *i.getStatus()
187203
status.serviceBinary = helloResp.ServiceBinary
188204
i.setStatus(&status)
189205

190206
// The status.vpnConnection must be already initialized (ConnectionStarting message already received).
191207
i.setFirstTryDone()
192-
// Notify owner that we can now provide verdicts for firewall module
193-
i.owner.EnsureVerdictHandlerRegistered()
194-
195208
wc.Debug(fmt.Sprintf("Connected to IVPN client %s", helloResp.Version))
196209

197210
// Show UI notification if not suppressed by user
@@ -270,6 +283,7 @@ func (i *InteropIvpn) VerdictHandler(conn *network.Connection) (verdict network.
270283
return network.VerdictUndecided, "", false
271284
}
272285

286+
// Connection to remote VPN server
273287
if status.vpnConnection.dstPort != 0 {
274288
if conn.Entity.Port == status.vpnConnection.dstPort &&
275289
conn.Entity.Protocol == status.vpnConnection.protocol &&
@@ -283,6 +297,17 @@ func (i *InteropIvpn) VerdictHandler(conn *network.Connection) (verdict network.
283297
}
284298
}
285299

300+
// connections to IVPN service port
301+
if conn.LocalIPScope == netutils.HostLocal && conn.IPProtocol == packet.TCP {
302+
if conn.Inbound && conn.LocalPort == status.servicePort {
303+
return network.VerdictAccept, "IVPN Local Service connection", true
304+
}
305+
if !conn.Inbound && conn.Entity.Port == status.servicePort {
306+
return network.VerdictAccept, "IVPN Local Service connection", true
307+
}
308+
}
309+
310+
// Connections from/to IVPN service (only when serviceBinary initialized)
286311
if status.serviceBinary != "" && conn.Process().Path == status.serviceBinary {
287312
return network.VerdictAccept, "IVPN Service connection", true
288313
}

0 commit comments

Comments
 (0)