Skip to content

Commit 976787d

Browse files
committed
Merge remote-tracking branch 'upstream/main' into feature/remote-debug-clean
2 parents 536b000 + 43c9a51 commit 976787d

File tree

141 files changed

+4690
-1440
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

141 files changed

+4690
-1440
lines changed

client/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
# sudo podman build -t localhost/netbird:latest -f client/Dockerfile --ignorefile .dockerignore-client .
55
# sudo podman run --rm -it --cap-add={BPF,NET_ADMIN,NET_RAW} localhost/netbird:latest
66

7-
FROM alpine:3.22.0
7+
FROM alpine:3.22.2
88
# iproute2: busybox doesn't display ip rules properly
99
RUN apk add --no-cache \
1010
bash \

client/cmd/debug.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ func runForDuration(cmd *cobra.Command, args []string) error {
168168

169169
client := proto.NewDaemonServiceClient(conn)
170170

171-
stat, err := client.Status(cmd.Context(), &proto.StatusRequest{})
171+
stat, err := client.Status(cmd.Context(), &proto.StatusRequest{ShouldRunProbes: true})
172172
if err != nil {
173173
return fmt.Errorf("failed to get status: %v", status.Convert(err).Message())
174174
}
@@ -303,12 +303,18 @@ func setSyncResponsePersistence(cmd *cobra.Command, args []string) error {
303303

304304
func getStatusOutput(cmd *cobra.Command, anon bool) string {
305305
var statusOutputString string
306-
statusResp, err := getStatus(cmd.Context())
306+
statusResp, err := getStatus(cmd.Context(), true)
307307
if err != nil {
308308
cmd.PrintErrf("Failed to get status: %v\n", err)
309309
} else {
310+
pm := profilemanager.NewProfileManager()
311+
var profName string
312+
if activeProf, err := pm.GetActiveProfile(); err == nil {
313+
profName = activeProf.Name
314+
}
315+
310316
statusOutputString = nbstatus.ParseToFullDetailSummary(
311-
nbstatus.ConvertToStatusOutputOverview(statusResp.GetFullStatus(), anon, statusResp.GetDaemonVersion(), "", nil, nil, nil, "", ""),
317+
nbstatus.ConvertToStatusOutputOverview(statusResp.GetFullStatus(), anon, statusResp.GetDaemonVersion(), "", nil, nil, nil, "", profName),
312318
)
313319
}
314320
return statusOutputString

client/cmd/login.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"fmt"
66
"os"
7+
"os/exec"
78
"os/user"
89
"runtime"
910
"strings"
@@ -356,13 +357,21 @@ func openURL(cmd *cobra.Command, verificationURIComplete, userCode string, noBro
356357
cmd.Println("")
357358

358359
if !noBrowser {
359-
if err := open.Run(verificationURIComplete); err != nil {
360+
if err := openBrowser(verificationURIComplete); err != nil {
360361
cmd.Println("\nAlternatively, you may want to use a setup key, see:\n\n" +
361362
"https://docs.netbird.io/how-to/register-machines-using-setup-keys")
362363
}
363364
}
364365
}
365366

367+
// openBrowser opens the URL in a browser, respecting the BROWSER environment variable.
368+
func openBrowser(url string) error {
369+
if browser := os.Getenv("BROWSER"); browser != "" {
370+
return exec.Command(browser, url).Start()
371+
}
372+
return open.Run(url)
373+
}
374+
366375
// isUnixRunningDesktop checks if a Linux OS is running desktop environment
367376
func isUnixRunningDesktop() bool {
368377
if runtime.GOOS != "linux" && runtime.GOOS != "freebsd" {

client/cmd/status.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ func statusFunc(cmd *cobra.Command, args []string) error {
6868

6969
ctx := internal.CtxInitState(cmd.Context())
7070

71-
resp, err := getStatus(ctx)
71+
resp, err := getStatus(ctx, false)
7272
if err != nil {
7373
return err
7474
}
@@ -121,7 +121,7 @@ func statusFunc(cmd *cobra.Command, args []string) error {
121121
return nil
122122
}
123123

124-
func getStatus(ctx context.Context) (*proto.StatusResponse, error) {
124+
func getStatus(ctx context.Context, shouldRunProbes bool) (*proto.StatusResponse, error) {
125125
conn, err := DialClientGRPCServer(ctx, daemonAddr)
126126
if err != nil {
127127
return nil, fmt.Errorf("failed to connect to daemon error: %v\n"+
@@ -130,7 +130,7 @@ func getStatus(ctx context.Context) (*proto.StatusResponse, error) {
130130
}
131131
defer conn.Close()
132132

133-
resp, err := proto.NewDaemonServiceClient(conn).Status(ctx, &proto.StatusRequest{GetFullPeerStatus: true, ShouldRunProbes: true})
133+
resp, err := proto.NewDaemonServiceClient(conn).Status(ctx, &proto.StatusRequest{GetFullPeerStatus: true, ShouldRunProbes: shouldRunProbes})
134134
if err != nil {
135135
return nil, fmt.Errorf("status failed: %v", status.Convert(err).Message())
136136
}

client/firewall/iptables/acl_linux.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,6 @@ func transformIPsetName(ipsetName string, sPort, dPort *firewall.Port, action fi
400400
return ""
401401
}
402402

403-
// Include action in the ipset name to prevent squashing rules with different actions
404403
actionSuffix := ""
405404
if action == firewall.ActionDrop {
406405
actionSuffix = "-drop"

client/firewall/iptables/manager_linux.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,22 @@ func (m *Manager) UpdateSet(set firewall.Set, prefixes []netip.Prefix) error {
260260
return m.router.UpdateSet(set, prefixes)
261261
}
262262

263+
// AddInboundDNAT adds an inbound DNAT rule redirecting traffic from NetBird peers to local services.
264+
func (m *Manager) AddInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
265+
m.mutex.Lock()
266+
defer m.mutex.Unlock()
267+
268+
return m.router.AddInboundDNAT(localAddr, protocol, sourcePort, targetPort)
269+
}
270+
271+
// RemoveInboundDNAT removes an inbound DNAT rule.
272+
func (m *Manager) RemoveInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
273+
m.mutex.Lock()
274+
defer m.mutex.Unlock()
275+
276+
return m.router.RemoveInboundDNAT(localAddr, protocol, sourcePort, targetPort)
277+
}
278+
263279
func getConntrackEstablished() []string {
264280
return []string{"-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}
265281
}

client/firewall/iptables/router_linux.go

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,54 @@ func (r *router) UpdateSet(set firewall.Set, prefixes []netip.Prefix) error {
880880
return nberrors.FormatErrorOrNil(merr)
881881
}
882882

883+
// AddInboundDNAT adds an inbound DNAT rule redirecting traffic from NetBird peers to local services.
884+
func (r *router) AddInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
885+
ruleID := fmt.Sprintf("inbound-dnat-%s-%s-%d-%d", localAddr.String(), protocol, sourcePort, targetPort)
886+
887+
if _, exists := r.rules[ruleID]; exists {
888+
return nil
889+
}
890+
891+
dnatRule := []string{
892+
"-i", r.wgIface.Name(),
893+
"-p", strings.ToLower(string(protocol)),
894+
"--dport", strconv.Itoa(int(sourcePort)),
895+
"-d", localAddr.String(),
896+
"-m", "addrtype", "--dst-type", "LOCAL",
897+
"-j", "DNAT",
898+
"--to-destination", ":" + strconv.Itoa(int(targetPort)),
899+
}
900+
901+
ruleInfo := ruleInfo{
902+
table: tableNat,
903+
chain: chainRTRDR,
904+
rule: dnatRule,
905+
}
906+
907+
if err := r.iptablesClient.Append(ruleInfo.table, ruleInfo.chain, ruleInfo.rule...); err != nil {
908+
return fmt.Errorf("add inbound DNAT rule: %w", err)
909+
}
910+
r.rules[ruleID] = ruleInfo.rule
911+
912+
r.updateState()
913+
return nil
914+
}
915+
916+
// RemoveInboundDNAT removes an inbound DNAT rule.
917+
func (r *router) RemoveInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
918+
ruleID := fmt.Sprintf("inbound-dnat-%s-%s-%d-%d", localAddr.String(), protocol, sourcePort, targetPort)
919+
920+
if dnatRule, exists := r.rules[ruleID]; exists {
921+
if err := r.iptablesClient.Delete(tableNat, chainRTRDR, dnatRule...); err != nil {
922+
return fmt.Errorf("delete inbound DNAT rule: %w", err)
923+
}
924+
delete(r.rules, ruleID)
925+
}
926+
927+
r.updateState()
928+
return nil
929+
}
930+
883931
func applyPort(flag string, port *firewall.Port) []string {
884932
if port == nil {
885933
return nil

client/firewall/manager/firewall.go

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,14 +151,20 @@ type Manager interface {
151151

152152
DisableRouting() error
153153

154-
// AddDNATRule adds a DNAT rule
154+
// AddDNATRule adds outbound DNAT rule for forwarding external traffic to the NetBird network.
155155
AddDNATRule(ForwardRule) (Rule, error)
156156

157-
// DeleteDNATRule deletes a DNAT rule
157+
// DeleteDNATRule deletes the outbound DNAT rule.
158158
DeleteDNATRule(Rule) error
159159

160160
// UpdateSet updates the set with the given prefixes
161161
UpdateSet(hash Set, prefixes []netip.Prefix) error
162+
163+
// AddInboundDNAT adds an inbound DNAT rule redirecting traffic from NetBird peers to local services
164+
AddInboundDNAT(localAddr netip.Addr, protocol Protocol, sourcePort, targetPort uint16) error
165+
166+
// RemoveInboundDNAT removes inbound DNAT rule
167+
RemoveInboundDNAT(localAddr netip.Addr, protocol Protocol, sourcePort, targetPort uint16) error
162168
}
163169

164170
func GenKey(format string, pair RouterPair) string {

client/firewall/nftables/manager_linux.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,22 @@ func (m *Manager) UpdateSet(set firewall.Set, prefixes []netip.Prefix) error {
376376
return m.router.UpdateSet(set, prefixes)
377377
}
378378

379+
// AddInboundDNAT adds an inbound DNAT rule redirecting traffic from NetBird peers to local services.
380+
func (m *Manager) AddInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
381+
m.mutex.Lock()
382+
defer m.mutex.Unlock()
383+
384+
return m.router.AddInboundDNAT(localAddr, protocol, sourcePort, targetPort)
385+
}
386+
387+
// RemoveInboundDNAT removes an inbound DNAT rule.
388+
func (m *Manager) RemoveInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
389+
m.mutex.Lock()
390+
defer m.mutex.Unlock()
391+
392+
return m.router.RemoveInboundDNAT(localAddr, protocol, sourcePort, targetPort)
393+
}
394+
379395
func (m *Manager) createWorkTable() (*nftables.Table, error) {
380396
tables, err := m.rConn.ListTablesOfFamily(nftables.TableFamilyIPv4)
381397
if err != nil {

client/firewall/nftables/router_linux.go

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1350,6 +1350,103 @@ func (r *router) UpdateSet(set firewall.Set, prefixes []netip.Prefix) error {
13501350
return nil
13511351
}
13521352

1353+
// AddInboundDNAT adds an inbound DNAT rule redirecting traffic from NetBird peers to local services.
1354+
func (r *router) AddInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
1355+
ruleID := fmt.Sprintf("inbound-dnat-%s-%s-%d-%d", localAddr.String(), protocol, sourcePort, targetPort)
1356+
1357+
if _, exists := r.rules[ruleID]; exists {
1358+
return nil
1359+
}
1360+
1361+
protoNum, err := protoToInt(protocol)
1362+
if err != nil {
1363+
return fmt.Errorf("convert protocol to number: %w", err)
1364+
}
1365+
1366+
exprs := []expr.Any{
1367+
&expr.Meta{Key: expr.MetaKeyIIFNAME, Register: 1},
1368+
&expr.Cmp{
1369+
Op: expr.CmpOpEq,
1370+
Register: 1,
1371+
Data: ifname(r.wgIface.Name()),
1372+
},
1373+
&expr.Meta{Key: expr.MetaKeyL4PROTO, Register: 2},
1374+
&expr.Cmp{
1375+
Op: expr.CmpOpEq,
1376+
Register: 2,
1377+
Data: []byte{protoNum},
1378+
},
1379+
&expr.Payload{
1380+
DestRegister: 3,
1381+
Base: expr.PayloadBaseTransportHeader,
1382+
Offset: 2,
1383+
Len: 2,
1384+
},
1385+
&expr.Cmp{
1386+
Op: expr.CmpOpEq,
1387+
Register: 3,
1388+
Data: binaryutil.BigEndian.PutUint16(sourcePort),
1389+
},
1390+
}
1391+
1392+
exprs = append(exprs, applyPrefix(netip.PrefixFrom(localAddr, 32), false)...)
1393+
1394+
exprs = append(exprs,
1395+
&expr.Immediate{
1396+
Register: 1,
1397+
Data: localAddr.AsSlice(),
1398+
},
1399+
&expr.Immediate{
1400+
Register: 2,
1401+
Data: binaryutil.BigEndian.PutUint16(targetPort),
1402+
},
1403+
&expr.NAT{
1404+
Type: expr.NATTypeDestNAT,
1405+
Family: uint32(nftables.TableFamilyIPv4),
1406+
RegAddrMin: 1,
1407+
RegProtoMin: 2,
1408+
RegProtoMax: 0,
1409+
},
1410+
)
1411+
1412+
dnatRule := &nftables.Rule{
1413+
Table: r.workTable,
1414+
Chain: r.chains[chainNameRoutingRdr],
1415+
Exprs: exprs,
1416+
UserData: []byte(ruleID),
1417+
}
1418+
r.conn.AddRule(dnatRule)
1419+
1420+
if err := r.conn.Flush(); err != nil {
1421+
return fmt.Errorf("add inbound DNAT rule: %w", err)
1422+
}
1423+
1424+
r.rules[ruleID] = dnatRule
1425+
1426+
return nil
1427+
}
1428+
1429+
// RemoveInboundDNAT removes an inbound DNAT rule.
1430+
func (r *router) RemoveInboundDNAT(localAddr netip.Addr, protocol firewall.Protocol, sourcePort, targetPort uint16) error {
1431+
if err := r.refreshRulesMap(); err != nil {
1432+
return fmt.Errorf(refreshRulesMapError, err)
1433+
}
1434+
1435+
ruleID := fmt.Sprintf("inbound-dnat-%s-%s-%d-%d", localAddr.String(), protocol, sourcePort, targetPort)
1436+
1437+
if rule, exists := r.rules[ruleID]; exists {
1438+
if err := r.conn.DelRule(rule); err != nil {
1439+
return fmt.Errorf("delete inbound DNAT rule %s: %w", ruleID, err)
1440+
}
1441+
if err := r.conn.Flush(); err != nil {
1442+
return fmt.Errorf("flush delete inbound DNAT rule: %w", err)
1443+
}
1444+
delete(r.rules, ruleID)
1445+
}
1446+
1447+
return nil
1448+
}
1449+
13531450
// applyNetwork generates nftables expressions for networks (CIDR) or sets
13541451
func (r *router) applyNetwork(
13551452
network firewall.Network,

0 commit comments

Comments
 (0)