Compare commits

..

106 Commits

Author SHA1 Message Date
Hakan Sariman
092cb33215 Add go.uber.org/mock dependency to go.mod 2025-03-18 22:18:04 +08:00
Hakan Sariman
a01e5abfee Refactor stateManager parameter to use value type instead of pointer in multiple functions 2025-03-18 20:34:44 +08:00
Hakan Sariman
01d01ac16f Remove event generation check for non-echo requests in ICMP tracker test 2025-03-18 19:22:46 +08:00
Hakan Sariman
a7be9fdcfc Update ICMP tracker test to clarify event generation for non-echo requests 2025-03-18 19:21:42 +08:00
Hakan Sariman
c6f9a36e0e Add ICMP tracking tests for outbound and inbound connections 2025-03-18 19:18:19 +08:00
Maycon Santos
1d9fced073 [management] Add redis cache (#3516)
Refactor IdP user data caching by introducing a Redis cache implementation alongside an in-memory fallback, adding a Marshaler interface for flexible serialization, and updating related tests and account management code.

- Added a new cache store implementation with support for Redis and in-memory backends.
- Introduced Marshaler and wrapper types for handling serialization with msgpack and JSON.
- Updated account and user management modules to integrate and test the new caching strategy.
2025-03-18 11:07:20 +01:00
Pascal Fischer
0fa65eab5d [client] Flow keep token data during config update (#3526) 2025-03-18 11:03:59 +01:00
Pascal Fischer
4a5cd74bfe extract signal config creation 2025-03-17 22:00:41 +01:00
hakansa
f88b93e53f [client] Add DNS and Exit Node collection configuration to Netflow (#3522)
[client] Add DNS and Exit Node collection configuration to Netflow (#3522)
2025-03-17 23:44:22 +08:00
Pascal Fischer
40d932e011 [management] peer update on extra settings change (#3513) 2025-03-17 14:23:40 +01:00
Pascal Fischer
2e3fde4024 remove log from getPeerByID 2025-03-17 14:07:44 +01:00
Hakan Sariman
7f17cd348a Merge branch 'main' into feature/flow 2025-03-17 21:06:24 +08:00
hakansa
4ee8b66c42 [client] refactor: optimize forwarder initialization checks in packet handling (#3521)
[client] refactor: optimize forwarder initialization checks in packet handling (#3521)
2025-03-17 17:17:13 +08:00
Maycon Santos
0051fac5fd Merge branch 'main' into feature/flow 2025-03-15 11:51:00 +01:00
Viktor Liu
671e77c359 Fix squashed policy ID (#3512) 2025-03-15 00:15:37 +01:00
Pascal Fischer
fe16a162e4 remove log from store 2025-03-14 21:33:41 +01:00
Pascal Fischer
1a87f6e05e [management] Flow settings (#3509) 2025-03-14 20:36:07 +01:00
Pascal Fischer
17ff31316a [management] Export resource constants (#3508) 2025-03-14 13:42:21 +01:00
hakansa
64f27aee55 [client] add resource id fields to netflow events (#3445)
* [client] add resource id fields to netflow events
2025-03-14 18:57:23 +08:00
hakansa
78b86e0beb [management] fix force-push to feature/flow branch (#3500) 2025-03-14 01:36:46 +08:00
bcmmbaga
18871b554f Merge branch 'main' into feature/flow 2025-03-11 15:06:24 +03:00
Viktor Liu
76d73548d6 Fix more conflicts 2025-03-10 18:46:01 +01:00
Viktor Liu
11828a064a Fix conflict 2025-03-10 18:35:32 +01:00
Viktor Liu
0c2a3dd937 Merge branch 'main' into feature/flow 2025-03-10 18:30:45 +01:00
Viktor Liu
47dcf8d68c Fix forwarder IP source/destination (#3463) 2025-03-10 14:55:07 +01:00
Bethuel Mmbaga
cc8f6bcaf3 [management] Fix tests circular dependency (#3460)
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-03-10 15:54:36 +03:00
Maycon Santos
d8bcf745b0 update integrations 2025-03-09 19:32:38 +01:00
Maycon Santos
8430139d80 fix missing method 2025-03-09 19:03:57 +01:00
Maycon Santos
a2962b4ce0 sync go.sum 2025-03-09 18:50:20 +01:00
Maycon Santos
16fffdb75b sync changes from #3426 2025-03-09 18:48:48 +01:00
Maycon Santos
036cecbf46 update integrations and go mod 2025-03-09 18:47:05 +01:00
Maycon Santos
3482852bb6 sync proto and sum 2025-03-09 18:02:33 +01:00
Maycon Santos
fd62665b1f Merge branch 'main' into feature/flow
# Conflicts:
#	client/cmd/testutil_test.go
#	client/firewall/iptables/router_linux.go
#	client/firewall/nftables/router_linux.go
#	client/firewall/uspfilter/allow_netbird.go
#	client/firewall/uspfilter/allow_netbird_windows.go
#	client/firewall/uspfilter/uspfilter_test.go
#	client/internal/engine.go
#	client/internal/engine_test.go
#	client/server/server_test.go
#	go.mod
#	go.sum
#	management/client/client_test.go
#	management/cmd/management.go
#	management/proto/management.pb.go
#	management/proto/management.proto
#	management/server/account.go
#	management/server/account_test.go
#	management/server/dns_test.go
#	management/server/http/handler.go
#	management/server/http/testing/testing_tools/tools.go
#	management/server/integrations/port_forwarding/controller.go
#	management/server/management_proto_test.go
#	management/server/management_test.go
#	management/server/nameserver_test.go
#	management/server/peer.go
#	management/server/peer_test.go
#	management/server/route_test.go
2025-03-09 17:42:16 +01:00
Viktor Liu
36da464413 Fix tracer test 2025-03-07 17:19:10 +01:00
Viktor Liu
86370a0e7b Use bytes for flows event id (#3439) 2025-03-07 16:12:47 +01:00
Viktor Liu
cb16d0f45f Align packet tracer behavior with actual code paths (#3424) 2025-03-07 14:03:45 +01:00
Viktor Liu
e8d8bd8f18 Add peer traffic rule IDs to allowed connections in flows (#3442) 2025-03-07 13:56:26 +01:00
Viktor Liu
8b07f21c28 Don't track intercepted packets (#3448) 2025-03-07 13:56:16 +01:00
Viktor Liu
54be772ffd Handle flow updates (#3455) 2025-03-07 13:56:00 +01:00
Viktor Liu
3c3a454e61 Fix merge regression 2025-03-06 16:54:15 +01:00
Viktor Liu
5ff77b3595 Add flow userspace counters (#3438) 2025-03-06 16:52:56 +01:00
Viktor Liu
b180edbe5c Track icmp with id only (#3447) 2025-03-06 14:51:23 +01:00
Viktor Liu
0a042ac36d Fix merge conflict 2025-03-05 19:11:20 +01:00
Viktor Liu
e9f11fb11b Replace net.IP with netip.Addr (#3425) 2025-03-05 18:28:05 +01:00
hakansa
419ed275fa Handle TCP RST flag to transition connection state to closed (#3432) 2025-03-05 18:25:42 +01:00
Viktor Liu
2d4fcaf186 Fix proto numbering (#3436) 2025-03-04 16:57:25 +01:00
Viktor Liu
acf172b52c Add kernel conntrack counters (#3434) 2025-03-04 16:46:03 +01:00
Viktor Liu
8c81a823fa Add flow ACL IDs (#3421) 2025-03-04 16:43:07 +01:00
Maycon Santos
619c549547 sync port forwarding 2025-03-04 16:29:59 +01:00
Maycon Santos
9a713a0987 Merge branch 'feature/port-forwarding' into feature/flow
# Conflicts:
#	go.mod
#	go.sum
2025-03-04 16:28:57 +01:00
Pascal Fischer
c4945cd565 add cleanup scheduler + metrics 2025-03-04 16:21:52 +01:00
Viktor Liu
1e10c17ecb Fix tcp state (#3431) 2025-03-04 11:19:54 +01:00
Viktor Liu
96d5190436 Add icmp type and code to forwarder flow event (#3413) 2025-02-28 21:04:07 +01:00
Viktor Liu
d19c26df06 Fix log direction (#3412) 2025-02-28 21:03:40 +01:00
Viktor Liu
36e36414d9 Fix forwarder log displaying (#3411) 2025-02-28 20:53:01 +01:00
bcmmbaga
7e69589e05 Update management-integrations
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-02-28 19:49:56 +00:00
bcmmbaga
aa613ab79a Update golang.org/x/crypto/ssh
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-02-28 19:27:46 +00:00
Viktor Liu
6ead0ff95e Fix log format 2025-02-28 20:24:23 +01:00
Viktor Liu
0db65a8984 Add routed packet drop flow (#3410) 2025-02-28 20:04:59 +01:00
Pascal Fischer
c138807e95 remove log message 2025-02-28 19:54:50 +01:00
Viktor Liu
637c0c8949 Add icmp type and code (#3409) 2025-02-28 19:16:42 +01:00
Viktor Liu
c72e13d8e6 Add conntrack flows (#3406) 2025-02-28 19:16:29 +01:00
Maycon Santos
f6d7bccfa0 Add flow client with sender/receiver (#3405)
add an initial version of receiver client and flow manager receiver and sender
2025-02-28 17:16:18 +00:00
bcmmbaga
e3ed01cafb go mod tidy
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-02-28 17:10:44 +00:00
Viktor Liu
fa748a7ec2 Add userspace flow implementation (#3393) 2025-02-28 11:08:35 +01:00
Maycon Santos
cccc615783 update flow proto package generated code 2025-02-28 03:09:09 +00:00
Maycon Santos
2021463ca0 update flow proto package name 2025-02-28 02:51:57 +00:00
Maycon Santos
f48cfd52e9 fix logger stop (#3403)
* fix logger stop

* use context to stop receiver

* update test
2025-02-28 00:28:17 +00:00
Pascal Fischer
6838f53f40 add getPeerByIp store method 2025-02-27 19:01:05 +01:00
Maycon Santos
8276236dfa Add netflow manager (#3398)
* Add netflow manager

* fix linter issues
2025-02-27 12:05:20 +00:00
Viktor Liu
994b923d56 Move proto and rename port and icmp info (#3399) 2025-02-27 12:52:33 +01:00
Viktor Liu
59e2432231 Add event proto fields (#3397) 2025-02-27 12:29:50 +01:00
Pascal Fischer
eee0d123e4 [management] add flow settings and credentials (#3389) 2025-02-27 12:17:07 +01:00
Viktor Liu
e943203ae2 Add event fields (#3390)
Co-authored-by: Maycon Santos <mlsmaycon@gmail.com>
2025-02-26 12:06:06 +01:00
Pedro Costa
6a775217cf rename flow proto messages 2025-02-25 16:29:54 +00:00
Maycon Santos
175674749f Add memory flow store (#3386) 2025-02-25 15:23:43 +00:00
Pascal Fischer
1e534cecf6 [management] Add flow proto (#3384) 2025-02-25 13:03:27 +01:00
Pedro Costa
aa3aa8c6a8 [management] flow proto 2025-02-25 11:22:54 +00:00
Pascal Fischer
fbdfe45c25 fix merge conflicts on management 2025-02-25 11:57:25 +01:00
Viktor Liu
81ee172db8 Fix route conflict 2025-02-25 11:44:21 +01:00
Viktor Liu
f8fd65a65f Merge branch 'main' into feature/port-forwarding 2025-02-25 11:37:52 +01:00
Bethuel Mmbaga
62b978c050 [management] Add support for tcp/udp allocations (#3381)
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-02-25 10:11:50 +00:00
Bethuel Mmbaga
4ebf1410c6 [management] Add support to allocate same port for public and internal (#3347)
Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-02-21 11:16:24 +03:00
Viktor Liu
630edf2480 Remove unused var 2025-02-20 13:24:37 +01:00
Viktor Liu
ea469d28d7 Merge branch 'main' into feature/port-forwarding 2025-02-20 13:24:05 +01:00
Pascal Fischer
597f1d47b8 fix management test suite 2025-02-20 13:08:18 +01:00
Viktor Liu
fcc96417f9 Merge branch 'main' into feature/port-forwarding 2025-02-20 11:45:30 +01:00
Viktor Liu
8755211a60 Merge branch 'main' into feature/port-forwarding 2025-02-20 11:39:06 +01:00
Pascal Fischer
e6d4653b08 [management] add cloud tag to get ingress ports api spec (#3300)
* fix tag for get endpoint

* update labels
2025-02-12 16:11:54 +01:00
Zoltan Papp
eb69f2de78 Fix nil pointer exception when load empty list and try to cast it (#3282) 2025-02-06 10:28:42 +01:00
Viktor Liu
206420c085 [client] Fix grouping of peer ACLs with different port ranges (#3289) 2025-02-06 10:28:42 +01:00
Christian Stewart
88a864c195 [relay] Use new upstream for nhooyr.io/websocket package (#3287)
The nhooyr.io/websocket package was renamed to github.com/coder/websocket when
the project was transferred to "coder" as the new maintainer.

Use the new import path and update go.mod and go.sum accordingly.

Signed-off-by: Christian Stewart <christian@aperture.us>
2025-02-06 10:28:42 +01:00
Pascal Fischer
a789e9e6d8 [management] fix duplication detection (#3286) 2025-02-05 21:42:09 +01:00
Viktor Liu
9930913e4e Merge branch 'main' into feature/port-forwarding 2025-02-05 18:55:59 +01:00
Viktor Liu
48675f579f Merge branch 'main' into feature/port-forwarding 2025-02-05 17:44:01 +01:00
Pascal Fischer
afec455f86 [management] copy port info (#3283) 2025-02-05 17:30:42 +01:00
Pascal Fischer
035c5d9f23 [management merge only unique entries on network map merge (#3277) 2025-02-05 16:50:45 +01:00
Viktor Liu
b2a5b29fb2 Merge branch 'main' into feature/port-forwarding 2025-02-05 10:15:37 +01:00
Bethuel Mmbaga
9ec61206c2 [management] Add support for filtering peers by name and IP (#3279)
* add peers ip and name filters

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>

* add get peers filter

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>

* fix get account peers

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>

* Extend GetAccountPeers store to support filtering by name and IP

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>

* Fix get peers references

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>

---------

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
2025-02-05 00:33:15 +03:00
Zoltan Papp
1b011a2d85 [client] Manage the IP forwarding sysctl setting in global way (#3270)
Add new package ipfwdstate that implements reference counting for IP forwarding
state management. This allows multiple usage to safely request IP forwarding
without interfering with each other.
2025-02-03 12:27:18 +01:00
Pascal Fischer
a85ea1ddb0 [manager] ingress ports manager support (#3268)
* add peers manager

* Extend peers manager to support retrieving all peers

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>

* add network map calc

* move integrations interface

* update management-integrations

* merge main and fix

* go mod tidy

* [management] port forwarding add peer manager fix network map (#3264)

* [management] fix testing tools (#3265)

* Fix net.IPv4 conversion to []byte

* update test to check ipv4

---------

Signed-off-by: bcmmbaga <bethuelmbaga12@gmail.com>
Co-authored-by: bcmmbaga <bethuelmbaga12@gmail.com>
Co-authored-by: Zoltán Papp <zoltan.pmail@gmail.com>
2025-02-03 09:37:37 +01:00
Zoltán Papp
829e40d2aa Fix ingress manager unnecessary creation 2025-02-01 10:58:47 +01:00
Pascal Fischer
6344e34880 [management] renamed ingress port endpoints (#3263) 2025-02-01 00:40:33 +01:00
Pascal Fischer
a76ca8c565 Merge branch 'main' into feature/port-forwarding 2025-01-29 22:28:10 +01:00
Zoltan Papp
26693e4ea8 Feature/port forwarding client ingress (#3242)
Client-side forward handling

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>

---------

Co-authored-by: Viktor Liu <17948409+lixmal@users.noreply.github.com>
2025-01-29 16:04:33 +01:00
Pascal Fischer
f6a71f4193 [management] add openapi specs and generate types for port forwarding proxy (#3236) 2025-01-27 17:47:40 +01:00
110 changed files with 1469 additions and 1804 deletions

View File

@@ -87,25 +87,25 @@ jobs:
with:
name: release
path: dist/
retention-days: 7
retention-days: 3
- name: upload linux packages
uses: actions/upload-artifact@v4
with:
name: linux-packages
path: dist/netbird_linux**
retention-days: 7
retention-days: 3
- name: upload windows packages
uses: actions/upload-artifact@v4
with:
name: windows-packages
path: dist/netbird_windows**
retention-days: 7
retention-days: 3
- name: upload macos packages
uses: actions/upload-artifact@v4
with:
name: macos-packages
path: dist/netbird_darwin**
retention-days: 7
retention-days: 3
release_ui:
runs-on: ubuntu-latest

View File

@@ -1,64 +1,148 @@
## Contributor License Agreement
# Contributor License Agreement
This Contributor License Agreement (referred to as the "Agreement") is entered into by the individual
submitting this Agreement and NetBird GmbH, c/o Max-Beer-Straße 2-4 Münzstraße 12 10178 Berlin, Germany,
referred to as "NetBird" (collectively, the "Parties"). The Agreement outlines the terms and conditions
under which NetBird may utilize software contributions provided by the Contributor for inclusion in
its software development projects. By submitting this Agreement, the Contributor confirms their acceptance
of the terms and conditions outlined below. The Contributor further represents that they are authorized to
complete this process as described herein.
We are incredibly thankful for the contributions we receive from the community.
We require our external contributors to sign a Contributor License Agreement ("CLA") in
order to ensure that our projects remain licensed under Free and Open Source licenses such
as BSD-3 while allowing NetBird to build a sustainable business.
NetBird is committed to having a true Open Source Software ("OSS") license for
our software. A CLA enables NetBird to safely commercialize our products
while keeping a standard OSS license with all the rights that license grants to users: the
ability to use the project in their own projects or businesses, to republish modified
source, or to completely fork the project.
This page gives a human-friendly summary of our CLA, details on why we require a CLA, how
contributors can sign our CLA, and more. You may view the full legal CLA document (below).
# Human-friendly summary
This is a human-readable summary of (and not a substitute for) the full agreement (below).
This highlights only some of key terms of the CLA. It has no legal value and you should
carefully review all the terms of the actual CLA before agreeing.
<li>Grant of copyright license. You give NetBird permission to use your copyrighted work
in commercial products.
</li>
<li>Grant of patent license. If your contributed work uses a patent, you give NetBird a
license to use that patent including within commercial products. You also agree that you
have permission to grant this license.
</li>
<li>No Warranty or Support Obligations.
By making a contribution, you are not obligating yourself to provide support for the
contribution, and you are not taking on any warranty obligations or providing any
assurances about how it will perform.
</li>
The CLA does not change the terms of the standard open source license used by our software
such as BSD-3 or MIT.
You are still free to use our projects within your own projects or businesses, republish
modified source, and more.
Please reference the appropriate license for the project you're contributing to to learn
more.
# Why require a CLA?
Agreeing to a CLA explicitly states that you are entitled to provide a contribution, that you cannot withdraw permission
to use your contribution at a later date, and that NetBird has permission to use your contribution in our commercial
products.
This removes any ambiguities or uncertainties caused by not having a CLA and allows users and customers to confidently
adopt our projects. At the same time, the CLA ensures that all contributions to our open source projects are licensed
under the project's respective open source license, such as BSD-3.
Requiring a CLA is a common and well-accepted practice in open source. Major open source projects require CLAs such as
Apache Software Foundation projects, Facebook projects (such as React), Google projects (including Go), Python, Django,
and more. Each of these projects remains licensed under permissive OSS licenses such as MIT, Apache, BSD, and more.
# Signing the CLA
Open a pull request ("PR") to any of our open source projects to sign the CLA. A bot will comment on the PR asking you
to sign the CLA if you haven't already.
Follow the steps given by the bot to sign the CLA. This will require you to log in with GitHub (we only request public
information from your account) and to fill in a few additional details such as your name and email address. We will only
use this information for CLA tracking; none of your submitted information will be used for marketing purposes.
You only have to sign the CLA once. Once you've signed the CLA, future contributions to any NetBird project will not
require you to sign again.
# Legal Terms and Agreement
In order to clarify the intellectual property license granted with Contributions from any person or entity, NetBird
GmbH ("NetBird") must have a Contributor License Agreement ("CLA") on file that has been signed
by each Contributor, indicating agreement to the license terms below. This license does not change your rights to use
your own Contributions for any other purpose.
You accept and agree to the following terms and conditions for Your present and future Contributions submitted to
NetBird. Except for the license granted herein to NetBird and recipients of software distributed by NetBird,
You reserve all right, title, and interest in and to Your Contributions.
1. Definitions.
```
"You" (or "Your") shall mean the copyright owner or legal entity authorized by the copyright owner
that is making this Agreement with NetBird. For legal entities, the entity making a Contribution and all other
entities that control, are controlled by, or are under common control with that entity are considered
to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect,
to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty
percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
```
```
"Contribution" shall mean any original work of authorship, including any modifications or additions to
an existing work, that is or previously has been intentionally submitted by You to NetBird for inclusion in,
or documentation of, any of the products owned or managed by NetBird (the "Work").
For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication
sent to NetBird or its representatives, including but not limited to communication on electronic mailing lists,
source code control systems, and issue tracking systems that are managed by, or on behalf of,
NetBird for the purpose of discussing and improving the Work, but excluding communication that is conspicuously
marked or otherwise designated in writing by You as "Not a Contribution."
```
2. Grant of Copyright License. Subject to the terms and conditions of this Agreement, You hereby grant to NetBird
and to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge,
royalty-free, irrevocable copyright license to reproduce, prepare derivative works of, publicly display, publicly
perform, sublicense, and distribute Your Contributions and such derivative works.
## 1 Preamble
In order to clarify the IP Rights situation with regard to Contributions from any person or entity, NetBird
must have a contributor license agreement on file to be signed by each Contributor, containing the license
terms below. This license serves as protection for both the Contributor as well as NetBird and its software users;
it does not change Contributors rights to use his/her own Contributions for any other purpose.
3. Grant of Patent License. Subject to the terms and conditions of this Agreement, You hereby grant to NetBird and
to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import,
and otherwise transfer the Work, where such license applies only to those patent claims licensable by You that are
necessarily infringed by Your Contribution(s) alone or by combination of Your Contribution(s) with the Work to which
such Contribution(s) was submitted. If any entity institutes patent litigation against You or any other entity (
including a cross-claim or counterclaim in a lawsuit) alleging that your Contribution, or the Work to which you have
contributed, constitutes direct or contributory patent infringement, then any patent licenses granted to that entity
under this Agreement for that Contribution or Work shall terminate as of the date such litigation is filed.
## 2 Definitions
2.1 “IP Rights” shall mean all industrial and intellectual property rights, whether registered or not registered, whether created by Contributor or acquired by Contributor from third parties, and similar rights, including (but not limited to) semiconductor property rights, design rights, copyrights (including in the form of database rights and rights to software), all neighbouring rights (Leistungsschutzrechte), trademarks, service marks, titles, internet domain names, trade names and other labelling rights, rights deriving from corresponding applications and registrations of such rights as well as any licenses (Nutzungsrechte) under and entitlements to any such intellectual and industrial property rights.
2.2 "Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is or previously has been intentionally Submitted by Contributor to NetBird for inclusion in, or documentation of any Work.
4. You represent that you are legally entitled to grant the above license. If your employer(s) has rights to
intellectual property that you create that includes your Contributions, you represent that you have received
permission to make Contributions on behalf of that employer, that you will have received permission from your current
and future employers for all future Contributions, that your applicable employer has waived such rights for all of
your current and future Contributions to NetBird, or that your employer has executed a separate Corporate CLA
with NetBird.
2.3 "Contributor" shall mean the copyright owner or legal entity authorized by the copyright owner that is concluding this Agreement with NetBird. For legal entities, the entity making a Contribution and all other entities that control, are controlled by, or are under common control with that entity are considered to be a single Contributor. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
2.4 "Submitted" shall mean any form of electronic, verbal, or written communication sent to NetBird or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, NetBird for the purpose of discussing and improving the Work, but excluding communication that is marked or otherwise designated in writing by Contributor as "Not a Contribution".
5. You represent that each of Your Contributions is Your original creation (see section 7 for submissions on behalf of
others). You represent that Your Contribution submissions include complete details of any third-party license or
other restriction (including, but not limited to, related patents and trademarks) of which you are personally aware
and which are associated with any part of Your Contributions.
2.5 "Work" means any of the products owned or managed by NetBird, in particular, but not exclusively, software.
## 3 Licenses
3.1 Subject to the terms and conditions of this agreement, Contributor hereby grants to NetBird and to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable license to reproduce by any means and in any form, in whole or in part, permanently or temporarily, the Contributions (including loading, displaying, executing, transmitting or storing works for the purpose of executing and processing data or transferring them to video, audio and other data carriers), including the right to distribute, display and present such Contributions and make them available to the public (e.g. via the internet) and to transmit and display such Contributions by any means. The license also includes the right to modify, translate, adapt, edit and otherwise alter the Contributions and to use these results in the same manner as the original Contributions and derivative works. Except for licenses in patents acc. to Sec. 3, such license refers to any IP Rights in the Contributions and derivative works. The Contributor acknowledges that NetBird is not required to credit them by name for their Contribution and agrees to waive any moral rights associated with their Contribution in relation to NetBird or its sublicensees.
6. You are not expected to provide support for Your Contributions, except to the extent You desire to provide support.
You may provide support for free, for a fee, or not at all. Unless required by applicable law or agreed to in
writing, You provide Your Contributions on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
express or implied, including, without limitation, any warranties or conditions of TITLE, NON- INFRINGEMENT,
MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE.
3.2 Subject to the terms and conditions of this agreement, Contributor hereby grants to NetBird and to recipients of software distributed by NetBird a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license in the Contributions to make, have made, use, sell, offer to sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by the Contributor which are necessarily infringed by Contributors Contribution(s) alone or by combination of Contributors Contribution(s) with the Work to which such Contribution(s) was Submitted.
3.3 NetBird hereby accepts such licenses.
7. Should You wish to submit work that is not Your original creation, You may submit it to NetBird separately from
any Contribution, identifying the complete details of its source and of any license or other restriction (including,
but not limited to, related patents, trademarks, and license agreements) of which you are personally aware, and
conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
## 4 Contributors Representations
4.1 Contributor represents that Contributor is legally entitled to grant the above license. If Contributors employer has IP Rights to Contributors Contributions, Contributor represent that he/she has received permission to make Contributions on behalf of such employer, that such employer has waived such IP Rights to the Contributions of Contributor to NetBird, or that such employer has executed a separate contributor license agreement with NetBird.
4.2 Contributor represents that any Contribution is his/her original creation.
4.3 Contributor represents to his/her best knowledge that any Contribution does not violate any third party IP Rights.
4.4 Contributor represents that any Contribution submission includes complete details of any third-party license or other restriction (including, but not limited to, related patents and trademarks) of which Contributor is personally aware and which are associated with any part of the Contribution.
4.5 The Contributor represents that their Contribution does not include any work distributed under a copyleft license.
## 5 Information obligation
Contributor agrees to notify NetBird of any facts or circumstances of which Contributor become aware that would make these representations inaccurate in any respect.
## 6 Submission of Third-Party works
Should Contributor wish to submit work that is not Contributors original creation, Contributor may submit it to NetBird separately from any Contribution, identifying the complete details of its source and of any license or other restriction (including, but not limited to, related patents, trademarks, and license agreements) of which Contributor are personally aware, and conspicuously marking the work as "Submitted on behalf of a third-party: [named here]".
## 7 No Consideration
Unless compensation is mandatory under statutory law, no compensation for any license under this agreement shall be payable.
## 8 Final Provisions
8.1 Laws. This Agreement is governed by the laws of the Federal Republic of Germany.
8.2 Venue. Place of jurisdiction shall, to the extent legally permissible, be Berlin, Germany.
8.3 Severability. If any provision in this agreement is unlawful, invalid or ineffective, it shall not affect the enforceability or effectiveness of the remainder of this agreement. The parties agree to replace any unlawful, invalid or ineffective provision with a provision that comes as close as possible to the commercial intent and purpose of the original provision. This section also applies accordingly to any gaps in the contract.
8.4 Variations. Any variations, amendments or supplements to this Agreement must be in writing. This also applies to any variation of this Section 8.4.
8. You agree to notify NetBird of any facts or circumstances of which you become aware that would make these
representations inaccurate in any respect.

View File

@@ -180,7 +180,7 @@ func init() {
upCmd.PersistentFlags().BoolVar(&serverSSHAllowed, serverSSHAllowedFlag, false, "Allow SSH server on peer. If enabled, the SSH server will be permitted")
upCmd.PersistentFlags().BoolVar(&autoConnectDisabled, disableAutoConnectFlag, false, "Disables auto-connect feature. If enabled, then the client won't connect automatically when the service starts.")
debugCmd.PersistentFlags().BoolVarP(&debugSystemInfoFlag, systemInfoFlag, "S", true, "Adds system information to the debug bundle")
debugCmd.PersistentFlags().BoolVarP(&debugSystemInfoFlag, systemInfoFlag, "S", false, "Adds system information to the debug bundle")
}
// SetupCloseHandler handles SIGTERM signal and exits with success

View File

@@ -6,13 +6,11 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -91,19 +89,14 @@ func startManagement(t *testing.T, config *mgmt.Config, testFile string) (*grpc.
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, iv, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
if err != nil {
t.Fatal(err)
}
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, nil, nil)
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settings.NewManagerMock(), peersUpdateManager, secretsManager, nil, nil, nil)
if err != nil {
t.Fatal(err)
}

View File

@@ -15,7 +15,7 @@ import (
)
// NewFirewall creates a firewall manager instance
func NewFirewall(iface IFaceMapper, _ *statemanager.Manager, flowLogger nftypes.FlowLogger, disableServerRoutes bool) (firewall.Manager, error) {
func NewFirewall(iface IFaceMapper, _ statemanager.Manager, flowLogger nftypes.FlowLogger, disableServerRoutes bool) (firewall.Manager, error) {
if !iface.IsUserspaceBind() {
return nil, fmt.Errorf("not implemented for this OS: %s", runtime.GOOS)
}

View File

@@ -34,7 +34,7 @@ const SKIP_NFTABLES_ENV = "NB_SKIP_NFTABLES_CHECK"
// FWType is the type for the firewall type
type FWType int
func NewFirewall(iface IFaceMapper, stateManager *statemanager.Manager, flowLogger nftypes.FlowLogger, disableServerRoutes bool) (firewall.Manager, error) {
func NewFirewall(iface IFaceMapper, stateManager statemanager.Manager, flowLogger nftypes.FlowLogger, disableServerRoutes bool) (firewall.Manager, error) {
// on the linux system we try to user nftables or iptables
// in any case, because we need to allow netbird interface traffic
// so we use AllowNetbird traffic from these firewall managers
@@ -51,7 +51,7 @@ func NewFirewall(iface IFaceMapper, stateManager *statemanager.Manager, flowLogg
return createUserspaceFirewall(iface, fm, disableServerRoutes, flowLogger)
}
func createNativeFirewall(iface IFaceMapper, stateManager *statemanager.Manager, routes bool) (firewall.Manager, error) {
func createNativeFirewall(iface IFaceMapper, stateManager statemanager.Manager, routes bool) (firewall.Manager, error) {
fm, err := createFW(iface)
if err != nil {
return nil, fmt.Errorf("create firewall: %s", err)

View File

@@ -36,7 +36,7 @@ type aclManager struct {
optionalEntries map[string][]entry
ipsetStore *ipsetStore
stateManager *statemanager.Manager
stateManager statemanager.Manager
}
func newAclManager(iptablesClient *iptables.IPTables, wgIface iFaceMapper) (*aclManager, error) {
@@ -55,7 +55,7 @@ func newAclManager(iptablesClient *iptables.IPTables, wgIface iFaceMapper) (*acl
return m, nil
}
func (m *aclManager) init(stateManager *statemanager.Manager) error {
func (m *aclManager) init(stateManager statemanager.Manager) error {
m.stateManager = stateManager
m.seedInitialEntries()

View File

@@ -60,7 +60,7 @@ func Create(wgIface iFaceMapper) (*Manager, error) {
return m, nil
}
func (m *Manager) Init(stateManager *statemanager.Manager) error {
func (m *Manager) Init(stateManager statemanager.Manager) error {
state := &ShutdownState{
InterfaceState: &InterfaceState{
NameStr: m.wgIface.Name(),
@@ -167,7 +167,7 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error {
}
// Reset firewall to the default state
func (m *Manager) Close(stateManager *statemanager.Manager) error {
func (m *Manager) Close(stateManager statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()

View File

@@ -76,7 +76,7 @@ type router struct {
wgIface iFaceMapper
legacyManagement bool
stateManager *statemanager.Manager
stateManager statemanager.Manager
ipFwdState *ipfwdstate.IPForwardingState
}
@@ -104,7 +104,7 @@ func newRouter(iptablesClient *iptables.IPTables, wgIface iFaceMapper) (*router,
return r, nil
}
func (r *router) init(stateManager *statemanager.Manager) error {
func (r *router) init(stateManager statemanager.Manager) error {
r.stateManager = stateManager
if err := r.cleanUpDefaultForwardRules(); err != nil {

View File

@@ -55,7 +55,7 @@ const (
// It declares methods which handle actions required by the
// Netbird client for ACL and routing functionality
type Manager interface {
Init(stateManager *statemanager.Manager) error
Init(stateManager statemanager.Manager) error
// AllowNetbird allows netbird interface traffic
AllowNetbird() error
@@ -103,7 +103,7 @@ type Manager interface {
SetLegacyManagement(legacy bool) error
// Close closes the firewall manager
Close(stateManager *statemanager.Manager) error
Close(stateManager statemanager.Manager) error
// Flush the changes to firewall controller
Flush() error

View File

@@ -67,7 +67,7 @@ func Create(wgIface iFaceMapper) (*Manager, error) {
}
// Init nftables firewall manager
func (m *Manager) Init(stateManager *statemanager.Manager) error {
func (m *Manager) Init(stateManager statemanager.Manager) error {
workTable, err := m.createWorkTable()
if err != nil {
return fmt.Errorf("create work table: %w", err)
@@ -243,7 +243,7 @@ func (m *Manager) SetLegacyManagement(isLegacy bool) error {
}
// Reset firewall to the default state
func (m *Manager) Close(stateManager *statemanager.Manager) error {
func (m *Manager) Close(stateManager statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()

View File

@@ -13,7 +13,7 @@ import (
)
// Reset firewall to the default state
func (m *Manager) Close(stateManager *statemanager.Manager) error {
func (m *Manager) Close(stateManager statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()

View File

@@ -23,7 +23,7 @@ const (
)
// Reset firewall to the default state
func (m *Manager) Close(*statemanager.Manager) error {
func (m *Manager) Close(statemanager.Manager) error {
m.mutex.Lock()
defer m.mutex.Unlock()

View File

@@ -132,7 +132,6 @@ func (t *ICMPTracker) track(srcIP netip.Addr, dstIP netip.Addr, id uint16, typec
ICMPCode: code,
}
conn.UpdateLastSeen()
conn.UpdateCounters(direction, size)
t.mutex.Lock()
t.connections[key] = conn

View File

@@ -1,10 +1,182 @@
package conntrack
import (
"context"
"net/netip"
"testing"
"time"
"github.com/google/gopacket/layers"
"github.com/stretchr/testify/require"
// Assume these come from your internal packages.
)
func TestICMPTracker_TrackOutbound_NonEcho(t *testing.T) {
// Use a non-echo type (for example, 3 = Destination Unreachable)
nonEchoTypeCode := layers.CreateICMPv4TypeCode(3, 0)
flowLogger.Enable()
defer flowLogger.Disable()
// Use a reasonable timeout.
tracker := NewICMPTracker(30*time.Second, logger, flowLogger)
defer tracker.Close()
localIP := netip.MustParseAddr("192.0.2.1")
remoteIP := netip.MustParseAddr("192.0.2.2")
id := uint16(1000)
size := 120
// For outbound, the function first checks for an inverse connection.
// Since none exists, TrackOutbound will call track().
tracker.TrackOutbound(localIP, remoteIP, id, nonEchoTypeCode, size)
// Since type != EchoRequest the connection should not be stored.
require.Equal(t, 0, len(tracker.connections), "Non-echo request should not be tracked")
}
func TestICMPTracker_TrackOutbound_Echo(t *testing.T) {
// Use EchoRequest type.
echoTypeCode := layers.CreateICMPv4TypeCode(8, 0)
flowLogger.Enable()
defer flowLogger.Disable()
tracker := NewICMPTracker(30*time.Second, logger, flowLogger)
defer tracker.Close()
localIP := netip.MustParseAddr("192.0.2.10")
remoteIP := netip.MustParseAddr("192.0.2.20")
id := uint16(2000)
size := 150
// This call should track the connection since it is an echo request.
tracker.TrackOutbound(localIP, remoteIP, id, echoTypeCode, size)
// The connection key is formed with (srcIP, dstIP, id).
key := ICMPConnKey{SrcIP: localIP, DstIP: remoteIP, ID: id}
tracker.mutex.RLock()
_, exists := tracker.connections[key]
tracker.mutex.RUnlock()
require.True(t, exists, "Echo request should be tracked as a connection")
}
func TestICMPTracker_TrackInbound(t *testing.T) {
// For inbound, we pass a rule ID.
echoTypeCode := layers.CreateICMPv4TypeCode(8, 0)
ruleID := []byte("rule-123")
tracker := NewICMPTracker(30*time.Second, logger, flowLogger)
defer tracker.Close()
// Here srcIP is the remote host and dstIP is local.
remoteIP := netip.MustParseAddr("203.0.113.5")
localIP := netip.MustParseAddr("203.0.113.10")
id := uint16(3000)
size := 180
tracker.TrackInbound(remoteIP, localIP, id, echoTypeCode, ruleID, size)
// The connection key for inbound echo request is (srcIP, dstIP, id).
key := ICMPConnKey{SrcIP: remoteIP, DstIP: localIP, ID: id}
tracker.mutex.RLock()
_, exists := tracker.connections[key]
tracker.mutex.RUnlock()
require.True(t, exists, "Inbound echo request should be tracked")
}
func TestICMPTracker_IsValidInbound(t *testing.T) {
// For a valid echo reply, the tracker must have stored the echo request.
echoRequest := layers.CreateICMPv4TypeCode(8, 0)
// Use a slightly short timeout for testing expiry.
tracker := NewICMPTracker(1*time.Second, logger, flowLogger)
defer tracker.Close()
localIP := netip.MustParseAddr("10.0.0.1")
remoteIP := netip.MustParseAddr("10.0.0.2")
id := uint16(4000)
size := 100
// Initiate the echo request.
tracker.TrackOutbound(localIP, remoteIP, id, echoRequest, size)
// For an echo reply, the src and dst are swapped relative to the request.
valid := tracker.IsValidInbound(remoteIP, localIP, id, uint8(layers.ICMPv4TypeEchoReply), size)
require.True(t, valid, "Valid echo reply should return true")
// Test with a wrong ICMP type (not echo reply).
invalid := tracker.IsValidInbound(remoteIP, localIP, id, 99, size)
require.False(t, invalid, "Invalid echo type should return false")
// Let the connection expire.
time.Sleep(1100 * time.Millisecond)
expired := tracker.IsValidInbound(remoteIP, localIP, id, uint8(layers.ICMPv4TypeEchoReply), size)
require.False(t, expired, "Expired connection should return false")
}
func TestICMPTracker_cleanup(t *testing.T) {
// Use a very short timeout to force cleanup.
echoRequest := layers.CreateICMPv4TypeCode(8, 0)
tracker := NewICMPTracker(50*time.Millisecond, logger, flowLogger)
defer tracker.Close()
localIP := netip.MustParseAddr("172.16.0.1")
remoteIP := netip.MustParseAddr("172.16.0.2")
id := uint16(5000)
size := 100
tracker.TrackOutbound(localIP, remoteIP, id, echoRequest, size)
key := ICMPConnKey{SrcIP: localIP, DstIP: remoteIP, ID: id}
// Confirm the connection is present.
tracker.mutex.RLock()
_, exists := tracker.connections[key]
tracker.mutex.RUnlock()
require.True(t, exists, "Connection should exist before cleanup")
// Wait for the timeout to expire.
time.Sleep(100 * time.Millisecond)
// Manually trigger cleanup.
tracker.cleanup()
tracker.mutex.RLock()
_, exists = tracker.connections[key]
tracker.mutex.RUnlock()
require.False(t, exists, "Expired connection should have been cleaned up")
}
func TestICMPTracker_Close(t *testing.T) {
echoRequest := layers.CreateICMPv4TypeCode(8, 0)
tracker := NewICMPTracker(30*time.Second, logger, flowLogger)
// Add a connection.
localIP := netip.MustParseAddr("198.51.100.1")
remoteIP := netip.MustParseAddr("198.51.100.2")
id := uint16(6000)
size := 100
tracker.TrackOutbound(localIP, remoteIP, id, echoRequest, size)
// Close the tracker.
tracker.Close()
// After Close the connections map should be nil.
tracker.mutex.RLock()
require.Nil(t, tracker.connections, "Connections map should be nil after Close")
tracker.mutex.RUnlock()
// The cleanup goroutine should also be stopped. Canceling the ticker context should end cleanupRoutine.
select {
case <-time.After(50 * time.Millisecond):
// no panic or deadlock indicates Close worked correctly.
case <-context.Background().Done():
}
}
func BenchmarkICMPTracker(b *testing.B) {
b.Run("TrackOutbound", func(b *testing.B) {
tracker := NewICMPTracker(DefaultICMPTimeout, logger, flowLogger)

View File

@@ -176,7 +176,7 @@ func (t *TCPTracker) updateIfExists(srcIP netip.Addr, dstIP netip.Addr, srcPort
// TrackOutbound records an outbound TCP connection
func (t *TCPTracker) TrackOutbound(srcIP netip.Addr, dstIP netip.Addr, srcPort uint16, dstPort uint16, flags uint8, size int) {
if _, exists := t.updateIfExists(dstIP, srcIP, dstPort, srcPort, flags, nftypes.Egress, size); !exists {
if _, exists := t.updateIfExists(dstIP, srcIP, dstPort, srcPort, flags, 0, 0); !exists {
// if (inverted direction) conn is not tracked, track this direction
t.track(srcIP, dstIP, srcPort, dstPort, flags, nftypes.Egress, nil, size)
}
@@ -210,7 +210,6 @@ func (t *TCPTracker) track(srcIP netip.Addr, dstIP netip.Addr, srcPort uint16, d
t.logger.Trace("New %s TCP connection: %s", direction, key)
t.updateState(key, conn, flags, direction == nftypes.Egress)
conn.UpdateCounters(direction, size)
t.mutex.Lock()
t.connections[key] = conn
@@ -238,7 +237,20 @@ func (t *TCPTracker) IsValidInbound(srcIP netip.Addr, dstIP netip.Addr, srcPort
// Handle RST flag specially - it always causes transition to closed
if flags&TCPRst != 0 {
return t.handleRst(key, conn, size)
if conn.IsTombstone() {
return true
}
conn.Lock()
conn.SetTombstone()
conn.State = TCPStateClosed
conn.SetEstablished(false)
conn.Unlock()
conn.UpdateCounters(nftypes.Ingress, size)
t.logger.Trace("TCP connection reset: %s", key)
t.sendEvent(nftypes.TypeEnd, conn, nil)
return true
}
conn.Lock()
@@ -246,28 +258,10 @@ func (t *TCPTracker) IsValidInbound(srcIP netip.Addr, dstIP netip.Addr, srcPort
isEstablished := conn.IsEstablished()
isValidState := t.isValidStateForFlags(conn.State, flags)
conn.Unlock()
conn.UpdateCounters(nftypes.Ingress, size)
return isEstablished || isValidState
}
func (t *TCPTracker) handleRst(key ConnKey, conn *TCPConnTrack, size int) bool {
if conn.IsTombstone() {
return true
}
conn.Lock()
conn.SetTombstone()
conn.State = TCPStateClosed
conn.SetEstablished(false)
conn.Unlock()
conn.UpdateCounters(nftypes.Ingress, size)
t.logger.Trace("TCP connection reset: %s", key)
t.sendEvent(nftypes.TypeEnd, conn, nil)
return true
}
// updateState updates the TCP connection state based on flags
func (t *TCPTracker) updateState(key ConnKey, conn *TCPConnTrack, flags uint8, isOutbound bool) {
conn.UpdateLastSeen()

View File

@@ -110,7 +110,6 @@ func (t *UDPTracker) track(srcIP netip.Addr, dstIP netip.Addr, srcPort uint16, d
DestPort: dstPort,
}
conn.UpdateLastSeen()
conn.UpdateCounters(direction, size)
t.mutex.Lock()
t.connections[key] = conn

View File

@@ -322,7 +322,7 @@ func (m *Manager) initForwarder() error {
return nil
}
func (m *Manager) Init(*statemanager.Manager) error {
func (m *Manager) Init(statemanager.Manager) error {
return nil
}
@@ -710,18 +710,21 @@ func (m *Manager) handleLocalTraffic(d *decoder, srcIP, dstIP netip.Addr, packet
}
// if running in netstack mode we need to pass this to the forwarder
if m.netstack && m.localForwarding {
if m.netstack {
return m.handleNetstackLocalTraffic(packetData)
}
// track inbound packets to get the correct direction and session id for flows
m.trackInbound(d, srcIP, dstIP, ruleID, size)
// pass to either native or virtual stack (to be picked up by listeners)
return false
}
func (m *Manager) handleNetstackLocalTraffic(packetData []byte) bool {
if !m.localForwarding {
// pass to virtual tcp/ip stack to be picked up by listeners
return false
}
fwd := m.forwarder.Load()
if fwd == nil {

View File

@@ -22,7 +22,7 @@ var (
}
)
type repairConfFn func([]string, string, *resolvConf, *statemanager.Manager) error
type repairConfFn func([]string, string, *resolvConf, statemanager.Manager) error
type repair struct {
operationFile string
@@ -42,7 +42,7 @@ func newRepair(operationFile string, updateFn repairConfFn) *repair {
}
}
func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP string, stateManager *statemanager.Manager) {
func (f *repair) watchFileChanges(nbSearchDomains []string, nbNameserverIP string, stateManager statemanager.Manager) {
if f.inotify != nil {
return
}

View File

@@ -105,7 +105,7 @@ nameserver 8.8.8.8`,
var changed bool
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
updateFn := func([]string, string, *resolvConf, *statemanager.Manager) error {
updateFn := func([]string, string, *resolvConf, statemanager.Manager) error {
changed = true
cancel()
return nil
@@ -152,7 +152,7 @@ searchdomain netbird.cloud something`
var changed bool
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
updateFn := func([]string, string, *resolvConf, *statemanager.Manager) error {
updateFn := func([]string, string, *resolvConf, statemanager.Manager) error {
changed = true
cancel()
return nil

View File

@@ -48,7 +48,7 @@ func (f *fileConfigurator) supportCustomPort() bool {
return false
}
func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
backupFileExist := f.isBackupFileExist()
if !config.RouteAll {
if backupFileExist {
@@ -86,7 +86,7 @@ func (f *fileConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *st
return nil
}
func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP string, cfg *resolvConf, stateManager *statemanager.Manager) error {
func (f *fileConfigurator) updateConfig(nbSearchDomains []string, nbNameserverIP string, cfg *resolvConf, stateManager statemanager.Manager) error {
searchDomainList := mergeSearchDomains(nbSearchDomains, cfg.searchDomains)
nameServers := generateNsList(nbNameserverIP, cfg)

View File

@@ -17,7 +17,7 @@ const (
)
type hostManager interface {
applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error
applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error
restoreHostDNS() error
supportCustomPort() bool
string() string
@@ -43,14 +43,14 @@ type DomainConfig struct {
}
type mockHostConfigurator struct {
applyDNSConfigFunc func(config HostDNSConfig, stateManager *statemanager.Manager) error
applyDNSConfigFunc func(config HostDNSConfig, stateManager statemanager.Manager) error
restoreHostDNSFunc func() error
supportCustomPortFunc func() bool
restoreUncleanShutdownDNSFunc func(*netip.Addr) error
stringFunc func() string
}
func (m *mockHostConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (m *mockHostConfigurator) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
if m.applyDNSConfigFunc != nil {
return m.applyDNSConfigFunc(config, stateManager)
}
@@ -80,7 +80,7 @@ func (m *mockHostConfigurator) string() string {
func newNoopHostMocker() hostManager {
return &mockHostConfigurator{
applyDNSConfigFunc: func(config HostDNSConfig, stateManager *statemanager.Manager) error { return nil },
applyDNSConfigFunc: func(config HostDNSConfig, stateManager statemanager.Manager) error { return nil },
restoreHostDNSFunc: func() error { return nil },
supportCustomPortFunc: func() bool { return true },
restoreUncleanShutdownDNSFunc: func(*netip.Addr) error { return nil },
@@ -122,7 +122,7 @@ func dnsConfigToHostDNSConfig(dnsConfig nbdns.Config, ip string, port int) HostD
type noopHostConfigurator struct{}
func (n noopHostConfigurator) applyDNSConfig(HostDNSConfig, *statemanager.Manager) error {
func (n noopHostConfigurator) applyDNSConfig(HostDNSConfig, statemanager.Manager) error {
return nil
}

View File

@@ -11,7 +11,7 @@ func newHostManager() (*androidHostManager, error) {
return &androidHostManager{}, nil
}
func (a androidHostManager) applyDNSConfig(HostDNSConfig, *statemanager.Manager) error {
func (a androidHostManager) applyDNSConfig(HostDNSConfig, statemanager.Manager) error {
return nil
}

View File

@@ -5,6 +5,7 @@ package dns
import (
"bufio"
"bytes"
"errors"
"fmt"
"io"
"net"
@@ -49,7 +50,7 @@ func (s *systemConfigurator) supportCustomPort() bool {
return true
}
func (s *systemConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (s *systemConfigurator) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
var err error
if err := stateManager.UpdateState(&ShutdownState{}); err != nil {
@@ -200,8 +201,12 @@ func (s *systemConfigurator) recordSystemDNSSettings(force bool) error {
func (s *systemConfigurator) getSystemDNSSettings() (SystemDNSSettings, error) {
primaryServiceKey, _, err := s.getPrimaryService()
if err != nil || primaryServiceKey == "" {
if err == nil {
err = errors.New("primary service key not found")
}
return SystemDNSSettings{}, fmt.Errorf("couldn't find the primary service key: %w", err)
}
dnsServiceKey := getKeyWithInput(primaryServiceStateKeyFormat, primaryServiceKey)
line := buildCommandLine("show", dnsServiceKey, "")
stdinCommands := wrapCommand(line)
@@ -379,7 +384,7 @@ func buildWriteStateOperation(operation, state, commands string) string {
return fmt.Sprintf("d.init\n%s %s\n%s\nset %s\n", operation, state, commands, state)
}
func runSystemConfigCommand(command string) ([]byte, error) {
var runSystemConfigCommand = func(command string) ([]byte, error) {
cmd := exec.Command(scutilPath)
cmd.Stdin = strings.NewReader(command)
out, err := cmd.Output()

View File

@@ -0,0 +1,210 @@
package dns
import (
"errors"
"os/exec"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"go.uber.org/mock/gomock"
"github.com/netbirdio/netbird/client/internal/statemanager/mocks"
)
// MockCommander to mock exec.Command
type MockCommander struct {
mock.Mock
}
func (m *MockCommander) Command(name string, arg ...string) *exec.Cmd {
args := m.Called(name, arg)
return args.Get(0).(*exec.Cmd)
}
func TestNewHostManager(t *testing.T) {
tests := []struct {
name string
wantErr bool
}{
{
name: "successful creation",
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := newHostManager()
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.NotNil(t, got)
assert.NotNil(t, got.createdKeys)
})
}
}
func TestApplyDNSConfig(t *testing.T) {
type mockSetup struct {
stateManagerError error
commandOutput []byte
commandError error
}
tests := []struct {
name string
config HostDNSConfig
mockSetup mockSetup
wantErr bool
}{
{
name: "successful apply with search domains",
config: HostDNSConfig{
RouteAll: true,
Domains: []DomainConfig{
{Domain: "example.com", MatchOnly: false},
{Domain: "test.com", MatchOnly: true},
},
ServerIP: "1.1.1.1",
ServerPort: 53,
},
mockSetup: mockSetup{
stateManagerError: nil,
commandOutput: []byte(`
PrimaryService : ABC123
Router : 192.168.1.1
DomainName : example.com
SearchDomains : <array> {
0 : test.com
}
ServerAddresses : <array> {
0 : 1.1.1.1
}
`),
commandError: nil,
},
wantErr: false,
},
{
name: "state manager error",
config: HostDNSConfig{
ServerIP: "1.1.1.1",
},
mockSetup: mockSetup{
stateManagerError: errors.New("state error"),
},
wantErr: false, // Function does not return an error, it only logs it.
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Setup mocks
s := &systemConfigurator{
createdKeys: make(map[string]struct{}),
}
ctrl := gomock.NewController(t)
defer ctrl.Finish() // Ensures all expectations are met
mockState := mocks.NewMockManager(ctrl)
mockCmd := new(MockCommander)
// Mock UpdateState
mockState.EXPECT().UpdateState(gomock.Any()).Return(tt.mockSetup.stateManagerError).AnyTimes()
// Mock all expected command executions
// mockCmd.On("Command", dscacheutilPath, "-flushcache").Return(&exec.Cmd{}).Once()
// mockCmd.On("Command", "killall", "-HUP", "mDNSResponder").Return(&exec.Cmd{}).Once()
// mockCmd.On("Command", scutilPath).Return(&exec.Cmd{}).Once() // For runSystemConfigCommand
// Mock `runSystemConfigCommand`
originalRunCommand := runSystemConfigCommand
runSystemConfigCommand = func(command string) ([]byte, error) {
return tt.mockSetup.commandOutput, tt.mockSetup.commandError
}
defer func() { runSystemConfigCommand = originalRunCommand }()
err := s.applyDNSConfig(tt.config, mockState)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
mockCmd.AssertExpectations(t) // Ensure Command() is called
})
}
}
func TestGetSystemDNSSettings(t *testing.T) {
tests := []struct {
name string
commandOutput []byte
commandError error
wantSettings SystemDNSSettings
wantErr bool
}{
{
name: "successful retrieval",
commandOutput: []byte(`
PrimaryService : ABC123
Router : 192.168.1.1
---
DomainName : example.com
SearchDomains : <array> {
0 : test.com
}
ServerAddresses : <array> {
0 : 1.1.1.1
}
`),
wantSettings: SystemDNSSettings{
Domains: []string{"example.com", "test.com"},
ServerIP: "1.1.1.1",
ServerPort: 53,
},
wantErr: false,
},
{
name: "command error",
commandError: errors.New("command failed"),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
s := &systemConfigurator{
createdKeys: make(map[string]struct{}),
}
originalRunCommand := runSystemConfigCommand
runSystemConfigCommand = func(command string) ([]byte, error) {
return tt.commandOutput, tt.commandError
}
defer func() { runSystemConfigCommand = originalRunCommand }()
got, err := s.getSystemDNSSettings()
if tt.wantErr {
assert.Error(t, err)
return
}
assert.NoError(t, err)
assert.Equal(t, tt.wantSettings, got)
})
}
}
func TestSupportCustomPort(t *testing.T) {
s := &systemConfigurator{}
assert.True(t, s.supportCustomPort())
}
func TestString(t *testing.T) {
s := &systemConfigurator{}
assert.Equal(t, "scutil", s.string())
}

View File

@@ -20,7 +20,7 @@ func newHostManager(dnsManager IosDnsManager) (*iosHostManager, error) {
}, nil
}
func (a iosHostManager) applyDNSConfig(config HostDNSConfig, _ *statemanager.Manager) error {
func (a iosHostManager) applyDNSConfig(config HostDNSConfig, _ statemanager.Manager) error {
jsonData, err := json.Marshal(config)
if err != nil {
return fmt.Errorf("marshal: %w", err)

View File

@@ -24,8 +24,8 @@ var (
const (
dnsPolicyConfigMatchPath = `SYSTEM\CurrentControlSet\Services\Dnscache\Parameters\DnsPolicyConfig\NetBird-Match`
gpoDnsPolicyRoot = `SOFTWARE\Policies\Microsoft\Windows NT\DNSClient\DnsPolicyConfig`
gpoDnsPolicyConfigMatchPath = gpoDnsPolicyRoot + `\NetBird-Match`
gpoDnsPolicyRoot = `SOFTWARE\Policies\Microsoft\Windows NT\DNSClient`
gpoDnsPolicyConfigMatchPath = gpoDnsPolicyRoot + `\DnsPolicyConfig\NetBird-Match`
dnsPolicyConfigVersionKey = "Version"
dnsPolicyConfigVersionValue = 2
@@ -74,7 +74,7 @@ func (r *registryConfigurator) supportCustomPort() bool {
return false
}
func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (r *registryConfigurator) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
if config.RouteAll {
if err := r.addDNSSetupForAll(config.ServerIP); err != nil {
return fmt.Errorf("add dns setup: %w", err)
@@ -136,6 +136,10 @@ func (r *registryConfigurator) addDNSMatchPolicy(domains []string, ip string) er
return fmt.Errorf("configure GPO DNS policy: %w", err)
}
if err := r.configureDNSPolicy(dnsPolicyConfigMatchPath, domains, ip); err != nil {
return fmt.Errorf("configure local DNS policy: %w", err)
}
if err := refreshGroupPolicy(); err != nil {
log.Warnf("failed to refresh group policy: %v", err)
}

View File

@@ -103,7 +103,7 @@ func (n *networkManagerDbusConfigurator) supportCustomPort() bool {
return false
}
func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (n *networkManagerDbusConfigurator) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
connSettings, configVersion, err := n.getAppliedConnectionSettings()
if err != nil {
return fmt.Errorf("retrieving the applied connection settings, error: %w", err)

View File

@@ -84,7 +84,7 @@ func (r *resolvconf) supportCustomPort() bool {
return false
}
func (r *resolvconf) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (r *resolvconf) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
var err error
if !config.RouteAll {
err = r.restoreHostDNS()

View File

@@ -75,7 +75,7 @@ type DefaultServer struct {
iosDnsManager IosDnsManager
statusRecorder *peer.Status
stateManager *statemanager.Manager
stateManager statemanager.Manager
}
type handlerWithStop interface {
@@ -99,7 +99,7 @@ func NewDefaultServer(
wgInterface WGIface,
customAddress string,
statusRecorder *peer.Status,
stateManager *statemanager.Manager,
stateManager statemanager.Manager,
disableSys bool,
) (*DefaultServer, error) {
var addrPort *netip.AddrPort
@@ -161,7 +161,7 @@ func newDefaultServer(
wgInterface WGIface,
dnsService service,
statusRecorder *peer.Status,
stateManager *statemanager.Manager,
stateManager statemanager.Manager,
disableSys bool,
) *DefaultServer {
ctx, stop := context.WithCancel(ctx)

View File

@@ -647,7 +647,7 @@ func TestDNSServerUpstreamDeactivateCallback(t *testing.T) {
}
var domainsUpdate string
hostManager.applyDNSConfigFunc = func(config HostDNSConfig, statemanager *statemanager.Manager) error {
hostManager.applyDNSConfigFunc = func(config HostDNSConfig, statemanager statemanager.Manager) error {
domains := []string{}
for _, item := range config.Domains {
if item.Disabled {

View File

@@ -87,7 +87,7 @@ func (s *systemdDbusConfigurator) supportCustomPort() bool {
return true
}
func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig, stateManager *statemanager.Manager) error {
func (s *systemdDbusConfigurator) applyDNSConfig(config HostDNSConfig, stateManager statemanager.Manager) error {
parsedIP, err := netip.ParseAddr(config.ServerIP)
if err != nil {
return fmt.Errorf("unable to parse ip address, error: %w", err)

View File

@@ -35,7 +35,7 @@ func (s *ShutdownState) Cleanup() error {
}
// TODO: move file contents to state manager
func createUncleanShutdownIndicator(sourcePath string, dnsAddressStr string, stateManager *statemanager.Manager) error {
func createUncleanShutdownIndicator(sourcePath string, dnsAddressStr string, stateManager statemanager.Manager) error {
dnsAddress, err := netip.ParseAddr(dnsAddressStr)
if err != nil {
return fmt.Errorf("parse dns address %s: %w", dnsAddressStr, err)

View File

@@ -184,7 +184,7 @@ type Engine struct {
checks []*mgmProto.Checks
relayManager *relayClient.Manager
stateManager *statemanager.Manager
stateManager statemanager.Manager
srWatcher *guard.SRWatcher
// Network map persistence

View File

@@ -12,7 +12,6 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/google/uuid"
"github.com/pion/transport/v3/stdnet"
log "github.com/sirupsen/logrus"
@@ -28,8 +27,6 @@ import (
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/client/iface"
"github.com/netbirdio/netbird/client/iface/bind"
"github.com/netbirdio/netbird/client/iface/configurer"
@@ -49,7 +46,6 @@ import (
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -1404,7 +1400,7 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
config := &server.Config{
Stuns: []*server.Host{},
TURNConfig: &server.TURNConfig{},
Relay: &types.Relay{
Relay: &server.Relay{
Addresses: []string{"127.0.0.1:1234"},
CredentialsTTL: util.Duration{Duration: time.Hour},
Secret: "222222222222222222",
@@ -1439,23 +1435,13 @@ func startManagement(t *testing.T, dataDir, testFile string) (*grpc.Server, stri
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.EXPECT().
GetSettings(gomock.Any(), gomock.Any(), gomock.Any()).
Return(&types.Settings{}, nil).
AnyTimes()
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
if err != nil {
return nil, "", err
}
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, nil, nil)
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settings.NewManagerMock(), peersUpdateManager, secretsManager, nil, nil, nil)
if err != nil {
return nil, "", err
}

View File

@@ -88,7 +88,7 @@ func (l *Logger) startReceiver() {
event := types.Event{
ID: id,
EventFields: *eventFields,
Timestamp: time.Now().UTC(),
Timestamp: time.Now(),
}
var isExitNode bool

View File

@@ -70,7 +70,7 @@ func (m *Manager) enableFlow(previous *nftypes.FlowConfig) error {
if m.needsNewClient(previous) {
if m.receiverClient != nil {
if err := m.receiverClient.Close(); err != nil {
log.Warnf("error closing previous flow client: %v", err)
log.Warnf("error closing previous flow client: %s", err)
}
}
@@ -113,19 +113,15 @@ func (m *Manager) disableFlow() error {
// Update applies new flow configuration settings
func (m *Manager) Update(update *nftypes.FlowConfig) error {
if update == nil {
log.Debug("no update provided; skipping update")
return nil
}
log.Tracef("updating flow configuration with new settings: url -> %s, interval -> %s, enabled? %t", update.URL, update.Interval, update.Enabled)
m.mux.Lock()
defer m.mux.Unlock()
previous := m.flowConfig
m.flowConfig = update
// Preserve TokenPayload and TokenSignature if they were set previously
if previous != nil && previous.TokenPayload != "" && m.flowConfig != nil && m.flowConfig.TokenPayload == "" {
m.flowConfig.TokenPayload = previous.TokenPayload
m.flowConfig.TokenSignature = previous.TokenSignature
@@ -134,16 +130,10 @@ func (m *Manager) Update(update *nftypes.FlowConfig) error {
m.logger.UpdateConfig(update.DNSCollection, update.ExitNodeCollection)
if update.Enabled {
log.Infof("netflow manager enabled; starting netflow manager")
return m.enableFlow(previous)
}
log.Infof("netflow manager disabled; stopping netflow manager")
err := m.disableFlow()
if err != nil {
log.Errorf("failed to disable netflow manager: %v", err)
}
return err
return m.disableFlow()
}
// Close cleans up all resources
@@ -157,7 +147,7 @@ func (m *Manager) Close() {
if m.receiverClient != nil {
if err := m.receiverClient.Close(); err != nil {
log.Warnf("failed to close receiver client: %v", err)
log.Warnf("failed to close receiver client: %s", err)
}
}
@@ -181,7 +171,7 @@ func (m *Manager) startSender() {
events := m.logger.GetEvents()
for _, event := range events {
if err := m.send(event); err != nil {
log.Errorf("failed to send flow event to server: %v", err)
log.Errorf("failed to send flow event to server: %s", err)
continue
}
log.Tracef("sent flow event: %s", event.ID)
@@ -194,16 +184,16 @@ func (m *Manager) receiveACKs(client *client.GRPCClient) {
err := client.Receive(m.ctx, m.flowConfig.Interval, func(ack *proto.FlowEventAck) error {
id, err := uuid.FromBytes(ack.EventId)
if err != nil {
log.Warnf("failed to convert ack event id to uuid: %v", err)
log.Warnf("failed to convert ack event id to uuid: %s", err)
return nil
}
log.Tracef("received flow event ack: %s", id)
m.logger.DeleteEvents([]uuid.UUID{id})
m.logger.DeleteEvents([]uuid.UUID{uuid.UUID(ack.EventId)})
return nil
})
if err != nil && !errors.Is(err, context.Canceled) {
log.Errorf("failed to receive flow event ack: %v", err)
log.Errorf("failed to receive flow event ack: %s", err)
}
}

View File

@@ -140,7 +140,7 @@ func NewConn(engineCtx context.Context, config ConnConfig, statusRecorder *Statu
statusRelay: NewAtomicConnStatus(),
statusICE: NewAtomicConnStatus(),
semaphore: semaphore,
dumpState: newStateDump(config.Key, connLog, statusRecorder),
dumpState: newStateDump(connLog),
}
ctrl := isController(config)
@@ -258,7 +258,7 @@ func (conn *Conn) Close() {
// doesn't block, discards the message if connection wasn't ready
func (conn *Conn) OnRemoteAnswer(answer OfferAnswer) bool {
conn.dumpState.RemoteAnswer()
conn.log.Infof("OnRemoteAnswer, priority: %s, status ICE: %s, status relay: %s", conn.currentConnPriority, conn.statusICE, conn.statusRelay)
conn.log.Infof("OnRemoteAnswer, status ICE: %s, status relay: %s", conn.statusICE, conn.statusRelay)
return conn.handshaker.OnRemoteAnswer(answer)
}

View File

@@ -9,9 +9,7 @@ import (
)
type stateDump struct {
log *log.Entry
status *Status
key string
log *log.Entry
sentOffer int
remoteOffer int
@@ -26,11 +24,9 @@ type stateDump struct {
mu sync.Mutex
}
func newStateDump(key string, log *log.Entry, statusRecorder *Status) *stateDump {
func newStateDump(log *log.Entry) *stateDump {
return &stateDump{
log: log,
status: statusRecorder,
key: key,
log: log,
}
}
@@ -70,14 +66,8 @@ func (s *stateDump) dumpState() {
s.mu.Lock()
defer s.mu.Unlock()
status := "unknown"
state, e := s.status.GetPeer(s.key)
if e == nil {
status = state.ConnStatus.String()
}
s.log.Infof("Dump stat: Status: %s, SentOffer: %d, RemoteOffer: %d, RemoteAnswer: %d, RemoteCandidate: %d, P2PConnected: %d, SwitchToRelay: %d, WGCheckSuccess: %d, RelayConnected: %d, LocalProxies: %d",
status, s.sentOffer, s.remoteOffer, s.remoteAnswer, s.remoteCandidate, s.p2pConnected, s.switchToRelay, s.wgCheckSuccess, s.relayConnected, s.localProxies)
s.log.Infof("Dump stat: SentOffer: %d, RemoteOffer: %d, RemoteAnswer: %d, RemoteCandidate: %d, P2PConnected: %d, SwitchToRelay: %d, WGCheckSuccess: %d, RelayConnected: %d, LocalProxies: %d",
s.sentOffer, s.remoteOffer, s.remoteAnswer, s.remoteCandidate, s.p2pConnected, s.switchToRelay, s.wgCheckSuccess, s.relayConnected, s.localProxies)
}
func (s *stateDump) RemoteAnswer() {

View File

@@ -326,7 +326,10 @@ func (d *Status) AddPeerStateRoute(peer string, route string, resourceId string)
d.peers[peer] = peerState
pref, err := netip.ParsePrefix(route)
if err == nil {
if err != nil {
log.Errorf("failed to parse prefix %s: %v", route, err)
} else {
d.routeIDLookup.AddRemoteRouteID(resourceId, pref)
}
@@ -348,7 +351,9 @@ func (d *Status) RemovePeerStateRoute(peer string, route string) error {
d.peers[peer] = peerState
pref, err := netip.ParsePrefix(route)
if err == nil {
if err != nil {
log.Errorf("failed to parse prefix %s: %v", route, err)
} else {
d.routeIDLookup.RemoveRemoteRouteID(pref)
}

View File

@@ -43,7 +43,7 @@ func TestWGWatcher_EnableWgWatcher(t *testing.T) {
mlog := log.WithField("peer", "tet")
mocWgIface := &MocWgIface{}
watcher := NewWGWatcher(mlog, mocWgIface, "", newStateDump("peer", mlog, &Status{}))
watcher := NewWGWatcher(mlog, mocWgIface, "", newStateDump(mlog))
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -72,7 +72,7 @@ func TestWGWatcher_ReEnable(t *testing.T) {
mlog := log.WithField("peer", "tet")
mocWgIface := &MocWgIface{}
watcher := NewWGWatcher(mlog, mocWgIface, "", newStateDump("peer", mlog, &Status{}))
watcher := NewWGWatcher(mlog, mocWgIface, "", newStateDump(mlog))
ctx, cancel := context.WithCancel(context.Background())
defer cancel()

View File

@@ -45,7 +45,7 @@ type Manager interface {
SetRouteChangeListener(listener listener.NetworkChangeListener)
InitialRouteRange() []string
EnableServerRouter(firewall firewall.Manager) error
Stop(stateManager *statemanager.Manager)
Stop(stateManager statemanager.Manager)
}
type ManagerConfig struct {
@@ -56,7 +56,7 @@ type ManagerConfig struct {
StatusRecorder *peer.Status
RelayManager *relayClient.Manager
InitialRoutes []*route.Route
StateManager *statemanager.Manager
StateManager statemanager.Manager
DNSServer dns.Server
PeerStore *peerstore.Store
DisableClientRoutes bool
@@ -80,7 +80,7 @@ type DefaultManager struct {
routeRefCounter *refcounter.RouteRefCounter
allowedIPsRefCounter *refcounter.AllowedIPsRefCounter
dnsRouteInterval time.Duration
stateManager *statemanager.Manager
stateManager statemanager.Manager
// clientRoutes is the most recent list of clientRoutes received from the Management Service
clientRoutes route.HAMap
dnsServer dns.Server
@@ -234,7 +234,7 @@ func (m *DefaultManager) EnableServerRouter(firewall firewall.Manager) error {
}
// Stop stops the manager watchers and clean firewall rules
func (m *DefaultManager) Stop(stateManager *statemanager.Manager) {
func (m *DefaultManager) Stop(stateManager statemanager.Manager) {
m.stop()
if m.serverRouter != nil {
m.serverRouter.cleanUp()

View File

@@ -19,7 +19,7 @@ type MockManager struct {
GetRouteSelectorFunc func() *routeselector.RouteSelector
GetClientRoutesFunc func() route.HAMap
GetClientRoutesWithNetIDFunc func() map[route.NetID][]*route.Route
StopFunc func(manager *statemanager.Manager)
StopFunc func(manager statemanager.Manager)
}
func (m *MockManager) Init() (net.AddHookFunc, net.RemoveHookFunc, error) {
@@ -83,7 +83,7 @@ func (m *MockManager) EnableServerRouter(firewall firewall.Manager) error {
}
// Stop mock implementation of Stop from Manager interface
func (m *MockManager) Stop(stateManager *statemanager.Manager) {
func (m *MockManager) Stop(stateManager statemanager.Manager) {
if m.StopFunc != nil {
m.StopFunc(stateManager)
}

View File

@@ -13,11 +13,11 @@ import (
nbnet "github.com/netbirdio/netbird/util/net"
)
func (r *SysOps) SetupRouting([]net.IP, *statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
func (r *SysOps) SetupRouting([]net.IP, statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
return nil, nil, nil
}
func (r *SysOps) CleanupRouting(*statemanager.Manager) error {
func (r *SysOps) CleanupRouting(statemanager.Manager) error {
return nil
}

View File

@@ -32,7 +32,7 @@ var splitDefaultv6_2 = netip.PrefixFrom(netip.AddrFrom16([16]byte{0x80}), 1)
var ErrRoutingIsSeparate = errors.New("routing is separate")
func (r *SysOps) setupRefCounter(initAddresses []net.IP, stateManager *statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
func (r *SysOps) setupRefCounter(initAddresses []net.IP, stateManager statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
stateManager.RegisterState(&ShutdownState{})
initialNextHopV4, err := GetNextHop(netip.IPv4Unspecified())
@@ -80,13 +80,13 @@ func (r *SysOps) setupRefCounter(initAddresses []net.IP, stateManager *statemana
}
// updateState updates state on every change so it will be persisted regularly
func (r *SysOps) updateState(stateManager *statemanager.Manager) {
func (r *SysOps) updateState(stateManager statemanager.Manager) {
if err := stateManager.UpdateState((*ShutdownState)(r.refCounter)); err != nil {
log.Errorf("failed to update state: %v", err)
}
}
func (r *SysOps) cleanupRefCounter(stateManager *statemanager.Manager) error {
func (r *SysOps) cleanupRefCounter(stateManager statemanager.Manager) error {
if r.refCounter == nil {
return nil
}
@@ -337,7 +337,7 @@ func (r *SysOps) genericRemoveVPNRoute(prefix netip.Prefix, intf *net.Interface)
return r.removeFromRouteTable(prefix, nextHop)
}
func (r *SysOps) setupHooks(initAddresses []net.IP, stateManager *statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
func (r *SysOps) setupHooks(initAddresses []net.IP, stateManager statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
beforeHook := func(connID nbnet.ConnectionID, ip net.IP) error {
prefix, err := util.GetPrefixFromIP(ip)
if err != nil {

View File

@@ -13,14 +13,14 @@ import (
nbnet "github.com/netbirdio/netbird/util/net"
)
func (r *SysOps) SetupRouting([]net.IP, *statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
func (r *SysOps) SetupRouting([]net.IP, statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
r.mu.Lock()
defer r.mu.Unlock()
r.prefixes = make(map[netip.Prefix]struct{})
return nil, nil, nil
}
func (r *SysOps) CleanupRouting(*statemanager.Manager) error {
func (r *SysOps) CleanupRouting(statemanager.Manager) error {
r.mu.Lock()
defer r.mu.Unlock()

View File

@@ -72,7 +72,7 @@ func getSetupRules() []ruleParams {
// Rule 2 (VPN Traffic Routing): Directs all remaining traffic to the 'NetbirdVPNTableID' custom routing table.
// This table is where a default route or other specific routes received from the management server are configured,
// enabling VPN connectivity.
func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager *statemanager.Manager) (_ nbnet.AddHookFunc, _ nbnet.RemoveHookFunc, err error) {
func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager statemanager.Manager) (_ nbnet.AddHookFunc, _ nbnet.RemoveHookFunc, err error) {
if !nbnet.AdvancedRouting() {
log.Infof("Using legacy routing setup")
return r.setupRefCounter(initAddresses, stateManager)
@@ -110,7 +110,7 @@ func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager *statemanager
// CleanupRouting performs a thorough cleanup of the routing configuration established by 'setupRouting'.
// It systematically removes the three rules and any associated routing table entries to ensure a clean state.
// The function uses error aggregation to report any errors encountered during the cleanup process.
func (r *SysOps) CleanupRouting(stateManager *statemanager.Manager) error {
func (r *SysOps) CleanupRouting(stateManager statemanager.Manager) error {
if !nbnet.AdvancedRouting() {
return r.cleanupRefCounter(stateManager)
}

View File

@@ -17,11 +17,11 @@ import (
nbnet "github.com/netbirdio/netbird/util/net"
)
func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager *statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
return r.setupRefCounter(initAddresses, stateManager)
}
func (r *SysOps) CleanupRouting(stateManager *statemanager.Manager) error {
func (r *SysOps) CleanupRouting(stateManager statemanager.Manager) error {
return r.cleanupRefCounter(stateManager)
}

View File

@@ -131,11 +131,11 @@ const (
RouteDeleted
)
func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager *statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
func (r *SysOps) SetupRouting(initAddresses []net.IP, stateManager statemanager.Manager) (nbnet.AddHookFunc, nbnet.RemoveHookFunc, error) {
return r.setupRefCounter(initAddresses, stateManager)
}
func (r *SysOps) CleanupRouting(stateManager *statemanager.Manager) error {
func (r *SysOps) CleanupRouting(stateManager statemanager.Manager) error {
return r.cleanupRefCounter(stateManager)
}

View File

@@ -49,8 +49,25 @@ func (r *RawState) MarshalJSON() ([]byte, error) {
return r.data, nil
}
// Manager is the interface that exposes the persistence and state management methods.
type Manager interface {
Start()
Stop(ctx context.Context) error
RegisterState(state State)
GetState(state State) State
UpdateState(state State) error
DeleteState(state State) error
DeleteStateByName(stateName string) error
DeleteAllStates() (int, error)
PersistState(ctx context.Context) error
LoadState(state State) error
CleanupStateByName(name string) error
PerformCleanup() error
GetSavedStateNames() ([]string, error)
}
// Manager handles the persistence and management of various states
type Manager struct {
type managerImpl struct {
mu sync.Mutex
cancel context.CancelFunc
done chan struct{}
@@ -65,8 +82,8 @@ type Manager struct {
}
// New creates a new Manager instance
func New(filePath string) *Manager {
return &Manager{
func New(filePath string) Manager {
return &managerImpl{
filePath: filePath,
states: make(map[string]State),
dirty: make(map[string]struct{}),
@@ -75,7 +92,7 @@ func New(filePath string) *Manager {
}
// Start starts the state manager periodic save routine
func (m *Manager) Start() {
func (m *managerImpl) Start() {
if m == nil {
return
}
@@ -90,7 +107,7 @@ func (m *Manager) Start() {
go m.periodicStateSave(ctx)
}
func (m *Manager) Stop(ctx context.Context) error {
func (m *managerImpl) Stop(ctx context.Context) error {
if m == nil {
return nil
}
@@ -114,7 +131,7 @@ func (m *Manager) Stop(ctx context.Context) error {
// RegisterState registers a state with the manager but doesn't attempt to persist it.
// Pass an uninitialized state to register it.
func (m *Manager) RegisterState(state State) {
func (m *managerImpl) RegisterState(state State) {
if m == nil {
return
}
@@ -128,7 +145,7 @@ func (m *Manager) RegisterState(state State) {
}
// GetState returns the state for the given type
func (m *Manager) GetState(state State) State {
func (m *managerImpl) GetState(state State) State {
if m == nil {
return nil
}
@@ -141,7 +158,7 @@ func (m *Manager) GetState(state State) State {
// UpdateState updates the state in the manager and marks it as dirty for the next save.
// The state will be replaced with the new one.
func (m *Manager) UpdateState(state State) error {
func (m *managerImpl) UpdateState(state State) error {
if m == nil {
return nil
}
@@ -151,7 +168,7 @@ func (m *Manager) UpdateState(state State) error {
// DeleteState removes the state from the manager and marks it as dirty for the next save.
// Pass an uninitialized state to delete it.
func (m *Manager) DeleteState(state State) error {
func (m *managerImpl) DeleteState(state State) error {
if m == nil {
return nil
}
@@ -159,7 +176,7 @@ func (m *Manager) DeleteState(state State) error {
return m.setState(state.Name(), nil)
}
func (m *Manager) setState(name string, state State) error {
func (m *managerImpl) setState(name string, state State) error {
m.mu.Lock()
defer m.mu.Unlock()
@@ -175,7 +192,7 @@ func (m *Manager) setState(name string, state State) error {
// DeleteStateByName handles deletion of states without cleanup.
// It doesn't require the state to be registered.
func (m *Manager) DeleteStateByName(stateName string) error {
func (m *managerImpl) DeleteStateByName(stateName string) error {
if m == nil {
return nil
}
@@ -203,7 +220,7 @@ func (m *Manager) DeleteStateByName(stateName string) error {
}
// DeleteAllStates removes all states.
func (m *Manager) DeleteAllStates() (int, error) {
func (m *managerImpl) DeleteAllStates() (int, error) {
if m == nil {
return 0, nil
}
@@ -230,7 +247,7 @@ func (m *Manager) DeleteAllStates() (int, error) {
return count, nil
}
func (m *Manager) periodicStateSave(ctx context.Context) {
func (m *managerImpl) periodicStateSave(ctx context.Context) {
ticker := time.NewTicker(10 * time.Second)
defer ticker.Stop()
defer close(m.done)
@@ -248,7 +265,7 @@ func (m *Manager) periodicStateSave(ctx context.Context) {
}
// PersistState persists the states that have been updated since the last save.
func (m *Manager) PersistState(ctx context.Context) error {
func (m *managerImpl) PersistState(ctx context.Context) error {
if m == nil {
return nil
}
@@ -291,7 +308,7 @@ func (m *Manager) PersistState(ctx context.Context) error {
}
// loadStateFile reads and unmarshals the state file into a map of raw JSON messages
func (m *Manager) loadStateFile(deleteCorrupt bool) (map[string]json.RawMessage, error) {
func (m *managerImpl) loadStateFile(deleteCorrupt bool) (map[string]json.RawMessage, error) {
data, err := os.ReadFile(m.filePath)
if err != nil {
if errors.Is(err, fs.ErrNotExist) {
@@ -311,7 +328,7 @@ func (m *Manager) loadStateFile(deleteCorrupt bool) (map[string]json.RawMessage,
}
// handleCorruptedState creates a backup of a corrupted state file by moving it
func (m *Manager) handleCorruptedState(deleteCorrupt bool) {
func (m *managerImpl) handleCorruptedState(deleteCorrupt bool) {
if !deleteCorrupt {
return
}
@@ -327,7 +344,7 @@ func (m *Manager) handleCorruptedState(deleteCorrupt bool) {
}
// loadSingleRawState unmarshals a raw state into a concrete state object
func (m *Manager) loadSingleRawState(name string, rawState json.RawMessage) (State, error) {
func (m *managerImpl) loadSingleRawState(name string, rawState json.RawMessage) (State, error) {
stateType, ok := m.stateTypes[name]
if !ok {
return nil, fmt.Errorf(errStateNotRegistered, name)
@@ -346,7 +363,7 @@ func (m *Manager) loadSingleRawState(name string, rawState json.RawMessage) (Sta
}
// LoadState loads a specific state from the state file
func (m *Manager) LoadState(state State) error {
func (m *managerImpl) LoadState(state State) error {
if m == nil {
return nil
}
@@ -383,7 +400,7 @@ func (m *Manager) LoadState(state State) error {
// cleanupSingleState handles the cleanup of a specific state and returns any error.
// The caller must hold the mutex.
func (m *Manager) cleanupSingleState(name string, rawState json.RawMessage) error {
func (m *managerImpl) cleanupSingleState(name string, rawState json.RawMessage) error {
// For unregistered states, preserve the raw JSON
if _, registered := m.stateTypes[name]; !registered {
m.states[name] = &RawState{data: rawState}
@@ -424,7 +441,7 @@ func (m *Manager) cleanupSingleState(name string, rawState json.RawMessage) erro
// CleanupStateByName loads and cleans up a specific state by name if it implements CleanableState.
// Returns an error if the state doesn't exist, isn't registered, or cleanup fails.
func (m *Manager) CleanupStateByName(name string) error {
func (m *managerImpl) CleanupStateByName(name string) error {
if m == nil {
return nil
}
@@ -461,7 +478,7 @@ func (m *Manager) CleanupStateByName(name string) error {
// PerformCleanup retrieves all states from the state file and calls Cleanup on registered states that support it.
// Unregistered states are preserved in their original state.
func (m *Manager) PerformCleanup() error {
func (m *managerImpl) PerformCleanup() error {
if m == nil {
return nil
}
@@ -491,7 +508,7 @@ func (m *Manager) PerformCleanup() error {
}
// GetSavedStateNames returns all state names that are currently saved in the state file.
func (m *Manager) GetSavedStateNames() ([]string, error) {
func (m *managerImpl) GetSavedStateNames() ([]string, error) {
if m == nil {
return nil, nil
}

View File

@@ -0,0 +1,312 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: client/internal/statemanager/manager.go
//
// Generated by this command:
//
// mockgen -source client/internal/statemanager/manager.go -destination lient/internal/statemanager/manager_mock.go Manager
//
// Package mocks is a generated GoMock package.
package mocks
import (
context "context"
reflect "reflect"
statemanager "github.com/netbirdio/netbird/client/internal/statemanager"
gomock "go.uber.org/mock/gomock"
)
// MockState is a mock of State interface.
type MockState struct {
ctrl *gomock.Controller
recorder *MockStateMockRecorder
isgomock struct{}
}
// MockStateMockRecorder is the mock recorder for MockState.
type MockStateMockRecorder struct {
mock *MockState
}
// NewMockState creates a new mock instance.
func NewMockState(ctrl *gomock.Controller) *MockState {
mock := &MockState{ctrl: ctrl}
mock.recorder = &MockStateMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockState) EXPECT() *MockStateMockRecorder {
return m.recorder
}
// Name mocks base method.
func (m *MockState) Name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name.
func (mr *MockStateMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockState)(nil).Name))
}
// MockCleanableState is a mock of CleanableState interface.
type MockCleanableState struct {
ctrl *gomock.Controller
recorder *MockCleanableStateMockRecorder
isgomock struct{}
}
// MockCleanableStateMockRecorder is the mock recorder for MockCleanableState.
type MockCleanableStateMockRecorder struct {
mock *MockCleanableState
}
// NewMockCleanableState creates a new mock instance.
func NewMockCleanableState(ctrl *gomock.Controller) *MockCleanableState {
mock := &MockCleanableState{ctrl: ctrl}
mock.recorder = &MockCleanableStateMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockCleanableState) EXPECT() *MockCleanableStateMockRecorder {
return m.recorder
}
// Cleanup mocks base method.
func (m *MockCleanableState) Cleanup() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Cleanup")
ret0, _ := ret[0].(error)
return ret0
}
// Cleanup indicates an expected call of Cleanup.
func (mr *MockCleanableStateMockRecorder) Cleanup() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Cleanup", reflect.TypeOf((*MockCleanableState)(nil).Cleanup))
}
// Name mocks base method.
func (m *MockCleanableState) Name() string {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Name")
ret0, _ := ret[0].(string)
return ret0
}
// Name indicates an expected call of Name.
func (mr *MockCleanableStateMockRecorder) Name() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockCleanableState)(nil).Name))
}
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
isgomock struct{}
}
// MockManagerMockRecorder is the mock recorder for MockManager.
type MockManagerMockRecorder struct {
mock *MockManager
}
// NewMockManager creates a new mock instance.
func NewMockManager(ctrl *gomock.Controller) *MockManager {
mock := &MockManager{ctrl: ctrl}
mock.recorder = &MockManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockManager) EXPECT() *MockManagerMockRecorder {
return m.recorder
}
// CleanupStateByName mocks base method.
func (m *MockManager) CleanupStateByName(name string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "CleanupStateByName", name)
ret0, _ := ret[0].(error)
return ret0
}
// CleanupStateByName indicates an expected call of CleanupStateByName.
func (mr *MockManagerMockRecorder) CleanupStateByName(name any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CleanupStateByName", reflect.TypeOf((*MockManager)(nil).CleanupStateByName), name)
}
// DeleteAllStates mocks base method.
func (m *MockManager) DeleteAllStates() (int, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteAllStates")
ret0, _ := ret[0].(int)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// DeleteAllStates indicates an expected call of DeleteAllStates.
func (mr *MockManagerMockRecorder) DeleteAllStates() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteAllStates", reflect.TypeOf((*MockManager)(nil).DeleteAllStates))
}
// DeleteState mocks base method.
func (m *MockManager) DeleteState(state statemanager.State) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteState", state)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteState indicates an expected call of DeleteState.
func (mr *MockManagerMockRecorder) DeleteState(state any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteState", reflect.TypeOf((*MockManager)(nil).DeleteState), state)
}
// DeleteStateByName mocks base method.
func (m *MockManager) DeleteStateByName(stateName string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "DeleteStateByName", stateName)
ret0, _ := ret[0].(error)
return ret0
}
// DeleteStateByName indicates an expected call of DeleteStateByName.
func (mr *MockManagerMockRecorder) DeleteStateByName(stateName any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DeleteStateByName", reflect.TypeOf((*MockManager)(nil).DeleteStateByName), stateName)
}
// GetSavedStateNames mocks base method.
func (m *MockManager) GetSavedStateNames() ([]string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetSavedStateNames")
ret0, _ := ret[0].([]string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetSavedStateNames indicates an expected call of GetSavedStateNames.
func (mr *MockManagerMockRecorder) GetSavedStateNames() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetSavedStateNames", reflect.TypeOf((*MockManager)(nil).GetSavedStateNames))
}
// GetState mocks base method.
func (m *MockManager) GetState(state statemanager.State) statemanager.State {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetState", state)
ret0, _ := ret[0].(statemanager.State)
return ret0
}
// GetState indicates an expected call of GetState.
func (mr *MockManagerMockRecorder) GetState(state any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetState", reflect.TypeOf((*MockManager)(nil).GetState), state)
}
// LoadState mocks base method.
func (m *MockManager) LoadState(state statemanager.State) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "LoadState", state)
ret0, _ := ret[0].(error)
return ret0
}
// LoadState indicates an expected call of LoadState.
func (mr *MockManagerMockRecorder) LoadState(state any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LoadState", reflect.TypeOf((*MockManager)(nil).LoadState), state)
}
// PerformCleanup mocks base method.
func (m *MockManager) PerformCleanup() error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PerformCleanup")
ret0, _ := ret[0].(error)
return ret0
}
// PerformCleanup indicates an expected call of PerformCleanup.
func (mr *MockManagerMockRecorder) PerformCleanup() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PerformCleanup", reflect.TypeOf((*MockManager)(nil).PerformCleanup))
}
// PersistState mocks base method.
func (m *MockManager) PersistState(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PersistState", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// PersistState indicates an expected call of PersistState.
func (mr *MockManagerMockRecorder) PersistState(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PersistState", reflect.TypeOf((*MockManager)(nil).PersistState), ctx)
}
// RegisterState mocks base method.
func (m *MockManager) RegisterState(state statemanager.State) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "RegisterState", state)
}
// RegisterState indicates an expected call of RegisterState.
func (mr *MockManagerMockRecorder) RegisterState(state any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RegisterState", reflect.TypeOf((*MockManager)(nil).RegisterState), state)
}
// Start mocks base method.
func (m *MockManager) Start() {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Start")
}
// Start indicates an expected call of Start.
func (mr *MockManagerMockRecorder) Start() *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockManager)(nil).Start))
}
// Stop mocks base method.
func (m *MockManager) Stop(ctx context.Context) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Stop", ctx)
ret0, _ := ret[0].(error)
return ret0
}
// Stop indicates an expected call of Stop.
func (mr *MockManagerMockRecorder) Stop(ctx any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockManager)(nil).Stop), ctx)
}
// UpdateState mocks base method.
func (m *MockManager) UpdateState(state statemanager.State) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "UpdateState", state)
ret0, _ := ret[0].(error)
return ret0
}
// UpdateState indicates an expected call of UpdateState.
func (mr *MockManagerMockRecorder) UpdateState(state any) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateState", reflect.TypeOf((*MockManager)(nil).UpdateState), state)
}

View File

@@ -17,7 +17,6 @@ import (
"os"
"path/filepath"
"runtime"
"runtime/pprof"
"sort"
"strings"
"time"
@@ -47,9 +46,6 @@ nftables.txt: Anonymized nftables rules with packet counters, if --system-info f
config.txt: Anonymized configuration information of the NetBird client.
network_map.json: Anonymized network map containing peer configurations, routes, DNS settings, and firewall rules.
state.json: Anonymized client state dump containing netbird states.
mutex.prof: Mutex profiling information.
goroutine.prof: Goroutine profiling information.
block.prof: Block profiling information.
Anonymization Process
@@ -92,14 +88,6 @@ The state file follows the same anonymization rules as other files:
- Domain names are consistently anonymized
- Technical identifiers and non-sensitive data remain unchanged
Mutex, Goroutines, and Block Profiling Files
The goroutine, block, and mutex profiling files contains process information that might help the NetBird team diagnose performance issues. The information in these files don't contain personal data.
You can check each using the following go command:
go tool pprof -http=:8088 mutex.prof
This will open a web browser tab with the profiling information.
Routes
For anonymized routes, the IP addresses are replaced as described above. The prefix length remains unchanged. Note that for prefixes, the anonymized IP might not be a network address, but the prefix length is still correct.
@@ -200,10 +188,6 @@ func (s *Server) createArchive(bundlePath *os.File, req *proto.DebugBundleReques
s.addSystemInfo(req, anonymizer, archive)
}
if err := s.addProf(req, anonymizer, archive); err != nil {
log.Errorf("Failed to add goroutines rules to debug bundle: %v", err)
}
if err := s.addNetworkMap(req, anonymizer, archive); err != nil {
return fmt.Errorf("add network map: %w", err)
}
@@ -326,29 +310,6 @@ func (s *Server) addCommonConfigFields(configContent *strings.Builder) {
configContent.WriteString(fmt.Sprintf("BlockLANAccess: %v\n", s.config.BlockLANAccess))
}
func (s *Server) addProf(req *proto.DebugBundleRequest, anonymizer *anonymize.Anonymizer, archive *zip.Writer) error {
runtime.SetBlockProfileRate(1)
_ = runtime.SetMutexProfileFraction(1)
defer runtime.SetBlockProfileRate(0)
defer runtime.SetMutexProfileFraction(0)
time.Sleep(5 * time.Second)
for _, profile := range []string{"goroutine", "block", "mutex"} {
var buff []byte
myBuff := bytes.NewBuffer(buff)
err := pprof.Lookup(profile).WriteTo(myBuff, 0)
if err != nil {
return fmt.Errorf("write %s profile: %w", profile, err)
}
if err := addFileToZip(archive, myBuff, profile+".prof"); err != nil {
return fmt.Errorf("add %s file to zip: %w", profile, err)
}
}
return nil
}
func (s *Server) addRoutes(req *proto.DebugBundleRequest, anonymizer *anonymize.Anonymizer, archive *zip.Writer) error {
routes, err := systemops.GetRoutesFromTable()
if err != nil {

View File

@@ -6,23 +6,20 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"go.opentelemetry.io/otel"
"github.com/netbirdio/management-integrations/integrations"
log "github.com/sirupsen/logrus"
"google.golang.org/grpc"
"google.golang.org/grpc/keepalive"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/internal/peer"
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -131,18 +128,13 @@ func startManagement(t *testing.T, signalAddr string, counter *int) (*grpc.Serve
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
accountManager, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
if err != nil {
return nil, "", err
}
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, nil, nil)
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
mgmtServer, err := server.NewServer(context.Background(), config, accountManager, settings.NewManagerMock(), peersUpdateManager, secretsManager, nil, nil, nil)
if err != nil {
return nil, "", err
}

View File

@@ -8,7 +8,7 @@ import (
"github.com/netbirdio/netbird/client/internal/statemanager"
)
func registerStates(mgr *statemanager.Manager) {
func registerStates(mgr statemanager.Manager) {
mgr.RegisterState(&dns.ShutdownState{})
mgr.RegisterState(&systemops.ShutdownState{})
}

View File

@@ -10,7 +10,7 @@ import (
"github.com/netbirdio/netbird/client/internal/statemanager"
)
func registerStates(mgr *statemanager.Manager) {
func registerStates(mgr statemanager.Manager) {
mgr.RegisterState(&dns.ShutdownState{})
mgr.RegisterState(&systemops.ShutdownState{})
mgr.RegisterState(&nftables.ShutdownState{})

View File

@@ -184,6 +184,7 @@ type serviceClient struct {
mStatus *systray.MenuItem
mUp *systray.MenuItem
mDown *systray.MenuItem
mAdminPanel *systray.MenuItem
mSettings *systray.MenuItem
mAbout *systray.MenuItem
mGitHub *systray.MenuItem
@@ -605,6 +606,7 @@ func (s *serviceClient) onTrayReady() {
s.mUp = systray.AddMenuItem("Connect", "Connect")
s.mDown = systray.AddMenuItem("Disconnect", "Disconnect")
s.mDown.Disable()
s.mAdminPanel = systray.AddMenuItem("Admin Panel", "Netbird Admin Panel")
systray.AddSeparator()
s.mSettings = systray.AddMenuItem("Settings", settingsMenuDescr)
@@ -627,7 +629,7 @@ func (s *serviceClient) onTrayReady() {
s.mAbout = systray.AddMenuItem("About", "About")
s.mAbout.SetIcon(s.icAbout)
s.mGitHub = s.mAbout.AddSubMenuItem("GitHub", "GitHub")
versionString := normalizedVersion(version.NetbirdVersion())
@@ -671,8 +673,11 @@ func (s *serviceClient) onTrayReady() {
go s.eventManager.Start(s.ctx)
go func() {
var err error
for {
select {
case <-s.mAdminPanel.ClickedCh:
err = open.Run(s.adminURL)
case <-s.mUp.ClickedCh:
s.mUp.Disable()
go func() {
@@ -767,6 +772,9 @@ func (s *serviceClient) onTrayReady() {
}
}
if err != nil {
log.Errorf("process connection: %v", err)
}
}
}()
}

View File

@@ -6,7 +6,7 @@ import (
"crypto/x509"
"errors"
"fmt"
"net/url"
"strings"
"sync"
"time"
@@ -31,12 +31,9 @@ type GRPCClient struct {
}
func NewClient(addr, payload, signature string, interval time.Duration) (*GRPCClient, error) {
parsedURL, err := url.Parse(addr)
if err != nil {
return nil, fmt.Errorf("parsing url: %w", err)
}
var opts []grpc.DialOption
if parsedURL.Scheme == "https" {
if strings.Contains(addr, "443") {
certPool, err := x509.SystemCertPool()
if err != nil || certPool == nil {
log.Debugf("System cert pool not available; falling back to embedded cert, error: %v", err)
@@ -61,7 +58,7 @@ func NewClient(addr, payload, signature string, interval time.Duration) (*GRPCCl
grpc.WithDefaultServiceConfig(`{"healthCheckConfig": {"serviceName": ""}}`),
)
conn, err := grpc.NewClient(fmt.Sprintf("%s:%s", parsedURL.Hostname(), parsedURL.Port()), opts...)
conn, err := grpc.NewClient(addr, opts...)
if err != nil {
return nil, fmt.Errorf("creating new grpc client: %w", err)
}
@@ -83,11 +80,7 @@ func (c *GRPCClient) Close() error {
func (c *GRPCClient) Receive(ctx context.Context, interval time.Duration, msgHandler func(msg *proto.FlowEventAck) error) error {
backOff := defaultBackoff(ctx, interval)
operation := func() error {
err := c.establishStreamAndReceive(ctx, msgHandler)
if err != nil {
log.Errorf("receive failed: %v", err)
}
return err
return c.establishStreamAndReceive(ctx, msgHandler)
}
if err := backoff.Retry(operation, backOff); err != nil {
@@ -99,7 +92,7 @@ func (c *GRPCClient) Receive(ctx context.Context, interval time.Duration, msgHan
func (c *GRPCClient) establishStreamAndReceive(ctx context.Context, msgHandler func(msg *proto.FlowEventAck) error) error {
if c.clientConn.GetState() == connectivity.Shutdown {
return errors.New("connection to flow receiver has been shut down")
return backoff.Permanent(errors.New("connection to flow receiver has been shut down"))
}
stream, err := c.realClient.Events(ctx, grpc.WaitForReady(true))
@@ -107,11 +100,6 @@ func (c *GRPCClient) establishStreamAndReceive(ctx context.Context, msgHandler f
return fmt.Errorf("create event stream: %w", err)
}
err = stream.Send(&proto.FlowEvent{IsInitiator: true})
if err != nil {
log.Infof("failed to send initiator message to flow receiver but will attempt to continue. Error: %s", err)
}
if err = checkHeader(stream); err != nil {
return fmt.Errorf("check header: %w", err)
}
@@ -130,11 +118,6 @@ func (c *GRPCClient) receive(stream proto.FlowService_EventsClient, msgHandler f
return fmt.Errorf("receive from stream: %w", err)
}
if msg.IsInitiator {
log.Tracef("received initiator message from flow receiver")
continue
}
if err := msgHandler(msg); err != nil {
return fmt.Errorf("handle message: %w", err)
}

View File

@@ -1,7 +1,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.26.0
// protoc v3.21.9
// protoc v4.24.3
// source: flow.proto
package proto
@@ -134,9 +134,8 @@ type FlowEvent struct {
// When the event occurred
Timestamp *timestamppb.Timestamp `protobuf:"bytes,2,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
// Public key of the sending peer
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
FlowFields *FlowFields `protobuf:"bytes,4,opt,name=flow_fields,json=flowFields,proto3" json:"flow_fields,omitempty"`
IsInitiator bool `protobuf:"varint,5,opt,name=isInitiator,proto3" json:"isInitiator,omitempty"`
PublicKey []byte `protobuf:"bytes,3,opt,name=public_key,json=publicKey,proto3" json:"public_key,omitempty"`
FlowFields *FlowFields `protobuf:"bytes,4,opt,name=flow_fields,json=flowFields,proto3" json:"flow_fields,omitempty"`
}
func (x *FlowEvent) Reset() {
@@ -199,21 +198,13 @@ func (x *FlowEvent) GetFlowFields() *FlowFields {
return nil
}
func (x *FlowEvent) GetIsInitiator() bool {
if x != nil {
return x.IsInitiator
}
return false
}
type FlowEventAck struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
// Unique client event identifier that has been ack'ed
EventId []byte `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"`
IsInitiator bool `protobuf:"varint,2,opt,name=isInitiator,proto3" json:"isInitiator,omitempty"`
EventId []byte `protobuf:"bytes,1,opt,name=event_id,json=eventId,proto3" json:"event_id,omitempty"`
}
func (x *FlowEventAck) Reset() {
@@ -255,13 +246,6 @@ func (x *FlowEventAck) GetEventId() []byte {
return nil
}
func (x *FlowEventAck) GetIsInitiator() bool {
if x != nil {
return x.IsInitiator
}
return false
}
type FlowFields struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -579,7 +563,7 @@ var file_flow_proto_rawDesc = []byte{
0x0a, 0x0a, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x04, 0x66, 0x6c,
0x6f, 0x77, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x2e, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x22, 0xd4, 0x01, 0x0a, 0x09, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e,
0x6f, 0x74, 0x6f, 0x22, 0xb2, 0x01, 0x0a, 0x09, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x76, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x38, 0x0a, 0x09,
0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
@@ -590,70 +574,66 @@ var file_flow_proto_rawDesc = []byte{
0x69, 0x63, 0x4b, 0x65, 0x79, 0x12, 0x31, 0x0a, 0x0b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x66, 0x69,
0x65, 0x6c, 0x64, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x66, 0x6c, 0x6f,
0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x52, 0x0a, 0x66, 0x6c,
0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x49, 0x6e,
0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69,
0x73, 0x49, 0x6e, 0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x4b, 0x0a, 0x0c, 0x46, 0x6c,
0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x76,
0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x20, 0x0a, 0x0b, 0x69, 0x73, 0x49, 0x6e, 0x69, 0x74, 0x69,
0x61, 0x74, 0x6f, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0b, 0x69, 0x73, 0x49, 0x6e,
0x69, 0x74, 0x69, 0x61, 0x74, 0x6f, 0x72, 0x22, 0x9c, 0x04, 0x0a, 0x0a, 0x46, 0x6c, 0x6f, 0x77,
0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69,
0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12,
0x1e, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e,
0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12,
0x17, 0x0a, 0x07, 0x72, 0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c,
0x52, 0x06, 0x72, 0x75, 0x6c, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65,
0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x66, 0x6c,
0x6f, 0x77, 0x2e, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69,
0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x63, 0x6f, 0x6c, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x63, 0x6f, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x70,
0x18, 0x06, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x70,
0x12, 0x17, 0x0a, 0x07, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28,
0x0c, 0x52, 0x06, 0x64, 0x65, 0x73, 0x74, 0x49, 0x70, 0x12, 0x2d, 0x0a, 0x09, 0x70, 0x6f, 0x72,
0x74, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66,
0x6c, 0x6f, 0x77, 0x2e, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08,
0x70, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70,
0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66, 0x6c,
0x6f, 0x77, 0x2e, 0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08, 0x69,
0x63, 0x6d, 0x70, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61,
0x63, 0x6b, 0x65, 0x74, 0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x78, 0x50,
0x61, 0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61, 0x63,
0x6b, 0x65, 0x74, 0x73, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x78, 0x50, 0x61,
0x63, 0x6b, 0x65, 0x74, 0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65,
0x73, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73,
0x12, 0x19, 0x0a, 0x08, 0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01,
0x28, 0x04, 0x52, 0x07, 0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73,
0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69,
0x64, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52,
0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x65, 0x73,
0x74, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0f, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x0e, 0x64, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x49, 0x64, 0x42, 0x11, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x5f, 0x69, 0x6e, 0x66, 0x6f, 0x22, 0x48, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e,
0x66, 0x6f, 0x12, 0x1f, 0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72,
0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50,
0x6f, 0x72, 0x74, 0x12, 0x1b, 0x0a, 0x09, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74,
0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74,
0x22, 0x44, 0x0a, 0x08, 0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09,
0x69, 0x63, 0x6d, 0x70, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52,
0x08, 0x69, 0x63, 0x6d, 0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d,
0x70, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63,
0x6d, 0x70, 0x43, 0x6f, 0x64, 0x65, 0x2a, 0x45, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10,
0x0a, 0x0c, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00,
0x12, 0x0e, 0x0a, 0x0a, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01,
0x12, 0x0c, 0x0a, 0x08, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x0d,
0x0a, 0x09, 0x54, 0x59, 0x50, 0x45, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x03, 0x2a, 0x3b, 0x0a,
0x09, 0x44, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x49,
0x52, 0x45, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10,
0x00, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0a,
0x0a, 0x06, 0x45, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x32, 0x42, 0x0a, 0x0b, 0x46, 0x6c,
0x6f, 0x77, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x45, 0x76, 0x65,
0x6e, 0x74, 0x73, 0x12, 0x0f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x45,
0x76, 0x65, 0x6e, 0x74, 0x1a, 0x12, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08,
0x5a, 0x06, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c, 0x64, 0x73, 0x22, 0x29, 0x0a, 0x0c, 0x46, 0x6c, 0x6f, 0x77,
0x45, 0x76, 0x65, 0x6e, 0x74, 0x41, 0x63, 0x6b, 0x12, 0x19, 0x0a, 0x08, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x65, 0x76, 0x65, 0x6e,
0x74, 0x49, 0x64, 0x22, 0x9c, 0x04, 0x0a, 0x0a, 0x46, 0x6c, 0x6f, 0x77, 0x46, 0x69, 0x65, 0x6c,
0x64, 0x73, 0x12, 0x17, 0x0a, 0x07, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x06, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x1e, 0x0a, 0x04, 0x74,
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0a, 0x2e, 0x66, 0x6c, 0x6f, 0x77,
0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, 0x70, 0x65, 0x12, 0x17, 0x0a, 0x07, 0x72,
0x75, 0x6c, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x72, 0x75,
0x6c, 0x65, 0x49, 0x64, 0x12, 0x2d, 0x0a, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f,
0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x0f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x44,
0x69, 0x72, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x09, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74,
0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18,
0x05, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12,
0x1b, 0x0a, 0x09, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x70, 0x18, 0x06, 0x20, 0x01,
0x28, 0x0c, 0x52, 0x08, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x70, 0x12, 0x17, 0x0a, 0x07,
0x64, 0x65, 0x73, 0x74, 0x5f, 0x69, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x06, 0x64,
0x65, 0x73, 0x74, 0x49, 0x70, 0x12, 0x2d, 0x0a, 0x09, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x69, 0x6e,
0x66, 0x6f, 0x18, 0x08, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e,
0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08, 0x70, 0x6f, 0x72, 0x74,
0x49, 0x6e, 0x66, 0x6f, 0x12, 0x2d, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x69, 0x6e, 0x66,
0x6f, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x49,
0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x48, 0x00, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x49,
0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x72, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74,
0x73, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x72, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65,
0x74, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x74, 0x78, 0x5f, 0x70, 0x61, 0x63, 0x6b, 0x65, 0x74, 0x73,
0x18, 0x0b, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x74, 0x78, 0x50, 0x61, 0x63, 0x6b, 0x65, 0x74,
0x73, 0x12, 0x19, 0x0a, 0x08, 0x72, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0c, 0x20,
0x01, 0x28, 0x04, 0x52, 0x07, 0x72, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x19, 0x0a, 0x08,
0x74, 0x78, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x04, 0x52, 0x07,
0x74, 0x78, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x73, 0x6f, 0x75, 0x72, 0x63,
0x65, 0x5f, 0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0e, 0x20,
0x01, 0x28, 0x0c, 0x52, 0x10, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x73, 0x6f, 0x75,
0x72, 0x63, 0x65, 0x49, 0x64, 0x12, 0x28, 0x0a, 0x10, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x72, 0x65,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x0c, 0x52,
0x0e, 0x64, 0x65, 0x73, 0x74, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x49, 0x64, 0x42,
0x11, 0x0a, 0x0f, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e,
0x66, 0x6f, 0x22, 0x48, 0x0a, 0x08, 0x50, 0x6f, 0x72, 0x74, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1f,
0x0a, 0x0b, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x0a, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x50, 0x6f, 0x72, 0x74, 0x12,
0x1b, 0x0a, 0x09, 0x64, 0x65, 0x73, 0x74, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x02, 0x20, 0x01,
0x28, 0x0d, 0x52, 0x08, 0x64, 0x65, 0x73, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x44, 0x0a, 0x08,
0x49, 0x43, 0x4d, 0x50, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70,
0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d,
0x70, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x69, 0x63, 0x6d, 0x70, 0x5f, 0x63, 0x6f,
0x64, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x69, 0x63, 0x6d, 0x70, 0x43, 0x6f,
0x64, 0x65, 0x2a, 0x45, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x10, 0x0a, 0x0c, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0e, 0x0a, 0x0a,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x53, 0x54, 0x41, 0x52, 0x54, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08,
0x54, 0x59, 0x50, 0x45, 0x5f, 0x45, 0x4e, 0x44, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x54, 0x59,
0x50, 0x45, 0x5f, 0x44, 0x52, 0x4f, 0x50, 0x10, 0x03, 0x2a, 0x3b, 0x0a, 0x09, 0x44, 0x69, 0x72,
0x65, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x15, 0x0a, 0x11, 0x44, 0x49, 0x52, 0x45, 0x43, 0x54,
0x49, 0x4f, 0x4e, 0x5f, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x0b, 0x0a,
0x07, 0x49, 0x4e, 0x47, 0x52, 0x45, 0x53, 0x53, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x45, 0x47,
0x52, 0x45, 0x53, 0x53, 0x10, 0x02, 0x32, 0x42, 0x0a, 0x0b, 0x46, 0x6c, 0x6f, 0x77, 0x53, 0x65,
0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x33, 0x0a, 0x06, 0x45, 0x76, 0x65, 0x6e, 0x74, 0x73, 0x12,
0x0f, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e, 0x74,
0x1a, 0x12, 0x2e, 0x66, 0x6c, 0x6f, 0x77, 0x2e, 0x46, 0x6c, 0x6f, 0x77, 0x45, 0x76, 0x65, 0x6e,
0x74, 0x41, 0x63, 0x6b, 0x22, 0x00, 0x28, 0x01, 0x30, 0x01, 0x42, 0x08, 0x5a, 0x06, 0x2f, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@@ -22,14 +22,11 @@ message FlowEvent {
bytes public_key = 3;
FlowFields flow_fields = 4;
bool isInitiator = 5;
}
message FlowEventAck {
// Unique client event identifier that has been ack'ed
bytes event_id = 1;
bool isInitiator = 2;
}
message FlowFields {

4
go.mod
View File

@@ -62,7 +62,7 @@ require (
github.com/miekg/dns v1.1.59
github.com/mitchellh/hashstructure/v2 v2.0.2
github.com/nadoo/ipset v0.5.0
github.com/netbirdio/management-integrations/integrations v0.0.0-20250320160856-bef8efa19fbd
github.com/netbirdio/management-integrations/integrations v0.0.0-20250314233615-5be625e48b11
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d
github.com/okta/okta-sdk-golang/v2 v2.18.0
github.com/oschwald/maxminddb-golang v1.12.0
@@ -96,6 +96,7 @@ require (
go.opentelemetry.io/otel/exporters/prometheus v0.48.0
go.opentelemetry.io/otel/metric v1.26.0
go.opentelemetry.io/otel/sdk/metric v1.26.0
go.uber.org/mock v0.4.0
go.uber.org/zap v1.27.0
goauthentik.io/api/v3 v3.2023051.3
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
@@ -230,7 +231,6 @@ require (
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.51.0 // indirect
go.opentelemetry.io/otel/sdk v1.26.0 // indirect
go.opentelemetry.io/otel/trace v1.26.0 // indirect
go.uber.org/mock v0.4.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/image v0.18.0 // indirect
golang.org/x/mod v0.17.0 // indirect

4
go.sum
View File

@@ -490,8 +490,8 @@ github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944 h1:TDtJKmM6S
github.com/netbirdio/go-netroute v0.0.0-20240611143515-f59b0e1d3944/go.mod h1:sHA6TRxjQ6RLbnI+3R4DZo2Eseg/iKiPRfNmcuNySVQ=
github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e h1:PURA50S8u4mF6RrkYYCAvvPCixhqqEiEy3Ej6avh04c=
github.com/netbirdio/ice/v3 v3.0.0-20240315174635-e72a50fcb64e/go.mod h1:YMLU7qbKfVjmEv7EoZPIVEI+kNYxWCdPK3VS0BU+U4Q=
github.com/netbirdio/management-integrations/integrations v0.0.0-20250320160856-bef8efa19fbd h1:rL4HSb/QhrEumuWkS4xirtHJgU0DsCbyfQS/MtGecK8=
github.com/netbirdio/management-integrations/integrations v0.0.0-20250320160856-bef8efa19fbd/go.mod h1:xFLzZcnjDssptPZUGj4ux4uIO75xM43GrTUwIKwFJHQ=
github.com/netbirdio/management-integrations/integrations v0.0.0-20250314233615-5be625e48b11 h1:ilgCLSVRiqiFUrhODy/B9qvHtTGcv5f7et0tONSYTz8=
github.com/netbirdio/management-integrations/integrations v0.0.0-20250314233615-5be625e48b11/go.mod h1:3LvBPnW+i06K9fQr1SYwsbhvnxQHtIC8vvO4PjLmmy0=
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502 h1:3tHlFmhTdX9axERMVN63dqyFqnvuD+EMJHzM7mNGON8=
github.com/netbirdio/service v0.0.0-20240911161631-f62744f42502/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
github.com/netbirdio/signal-dispatcher/dispatcher v0.0.0-20241010133937-e0df50df217d h1:bRq5TKgC7Iq20pDiuC54yXaWnAVeS5PdGpSokFTlR28=

View File

@@ -71,6 +71,10 @@ services:
- NB_AUTH_SECRET=$NETBIRD_RELAY_AUTH_SECRET
# ports:
# - $NETBIRD_RELAY_PORT:$NETBIRD_RELAY_PORT
labels:
- traefik.enable=true
- traefik.http.routers.netbird-relay.rule=Host(`$NETBIRD_DOMAIN`) && PathPrefix(`/relay`)
- traefik.http.services.netbird-relay.loadbalancer.server.port=33080
logging:
driver: "json-file"
options:

View File

@@ -8,17 +8,14 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/require"
"github.com/netbirdio/netbird/client/system"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
@@ -75,27 +72,13 @@ func startManagement(t *testing.T) (*grpc.Server, net.Listener) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetSettings(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).
Return(&types.Settings{}, nil).
AnyTimes()
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
accountManager, err := mgmt.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "netbird.selfhosted", eventStore, nil, false, ia, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
if err != nil {
t.Fatal(err)
}
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, nil, nil)
secretsManager := mgmt.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
mgmtServer, err := mgmt.NewServer(context.Background(), config, accountManager, settings.NewManagerMock(), peersUpdateManager, secretsManager, nil, nil, nil)
if err != nil {
t.Fatal(err)
}

View File

@@ -68,42 +68,28 @@ type Client struct {
Events *EventsAPI
}
// New initialize new Client instance using PAT token
// New initialize new Client instance
func New(managementURL, token string) *Client {
client := &Client{
managementURL: managementURL,
authHeader: "Token " + token,
}
client.initialize()
client.Accounts = &AccountsAPI{client}
client.Users = &UsersAPI{client}
client.Tokens = &TokensAPI{client}
client.Peers = &PeersAPI{client}
client.SetupKeys = &SetupKeysAPI{client}
client.Groups = &GroupsAPI{client}
client.Policies = &PoliciesAPI{client}
client.PostureChecks = &PostureChecksAPI{client}
client.Networks = &NetworksAPI{client}
client.Routes = &RoutesAPI{client}
client.DNS = &DNSAPI{client}
client.GeoLocation = &GeoLocationAPI{client}
client.Events = &EventsAPI{client}
return client
}
// NewWithBearerToken initialize new Client instance using Bearer token type
func NewWithBearerToken(managementURL, token string) *Client {
client := &Client{
managementURL: managementURL,
authHeader: "Bearer " + token,
}
client.initialize()
return client
}
func (c *Client) initialize() {
c.Accounts = &AccountsAPI{c}
c.Users = &UsersAPI{c}
c.Tokens = &TokensAPI{c}
c.Peers = &PeersAPI{c}
c.SetupKeys = &SetupKeysAPI{c}
c.Groups = &GroupsAPI{c}
c.Policies = &PoliciesAPI{c}
c.PostureChecks = &PostureChecksAPI{c}
c.Networks = &NetworksAPI{c}
c.Routes = &RoutesAPI{c}
c.DNS = &DNSAPI{c}
c.GeoLocation = &GeoLocationAPI{c}
c.Events = &EventsAPI{c}
}
func (c *Client) newRequest(ctx context.Context, method, path string, body io.Reader) (*http.Response, error) {
req, err := http.NewRequestWithContext(ctx, method, c.managementURL+path, body)
if err != nil {

View File

@@ -34,9 +34,7 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/realip"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/server/peers"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/encryption"
"github.com/netbirdio/netbird/formatter/hook"
@@ -52,6 +50,7 @@ import (
"github.com/netbirdio/netbird/management/server/networks"
"github.com/netbirdio/netbird/management/server/networks/resources"
"github.com/netbirdio/netbird/management/server/networks/routers"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -102,9 +101,9 @@ var (
// detect whether user specified a port
userPort := cmd.Flag("port").Changed
config, err = loadMgmtConfig(ctx, types.MgmtConfigPath)
config, err = loadMgmtConfig(ctx, mgmtConfig)
if err != nil {
return fmt.Errorf("failed reading provided config file: %s: %v", types.MgmtConfigPath, err)
return fmt.Errorf("failed reading provided config file: %s: %v", mgmtConfig, err)
}
if cmd.Flag(idpSignKeyRefreshEnabledFlagName).Changed {
@@ -184,7 +183,7 @@ var (
if config.DataStoreEncryptionKey != key {
log.WithContext(ctx).Infof("update config with activity store key")
config.DataStoreEncryptionKey = key
err := updateMgmtConfig(ctx, types.MgmtConfigPath, config)
err := updateMgmtConfig(ctx, mgmtConfig, config)
if err != nil {
return fmt.Errorf("failed to write out store encryption key: %s", err)
}
@@ -205,16 +204,17 @@ var (
userManager := users.NewManager(store)
extraSettingsManager := integrations.NewManager(eventStore)
settingsManager := settings.NewManager(store, userManager, extraSettingsManager)
permissionsManager := integrations.InitPermissionsManager(userManager, settingsManager)
permissionsManager := permissions.NewManager(userManager, settingsManager)
peersManager := peers.NewManager(store, permissionsManager)
proxyController := integrations.NewController(store)
accountManager, err := server.BuildManager(ctx, store, peersUpdateManager, idpManager, mgmtSingleAccModeDomain,
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController, settingsManager, permissionsManager)
dnsDomain, eventStore, geo, userDeleteFromIDPEnabled, integratedPeerValidator, appMetrics, proxyController, settingsManager)
if err != nil {
return fmt.Errorf("failed to build default manager: %v", err)
}
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsManager)
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
trustedPeers := config.ReverseProxy.TrustedPeers
defaultTrustedPeers := []netip.Prefix{netip.MustParsePrefix("0.0.0.0/0"), netip.MustParsePrefix("::/0")}
@@ -636,7 +636,7 @@ func handleRebrand(cmd *cobra.Command) error {
}
}
}
if types.MgmtConfigPath == defaultMgmtConfig {
if mgmtConfig == defaultMgmtConfig {
if migrateToNetbird(oldDefaultMgmtConfig, defaultMgmtConfig) {
cmd.Printf("will copy Config dir %s and its content to %s\n", oldDefaultMgmtConfigDir, defaultMgmtConfigDir)
err = cpDir(oldDefaultMgmtConfigDir, defaultMgmtConfigDir)

View File

@@ -7,7 +7,6 @@ import (
"github.com/spf13/cobra"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/version"
)
@@ -20,6 +19,7 @@ const (
var (
dnsDomain string
mgmtDataDir string
mgmtConfig string
logLevel string
logFile string
disableMetrics bool
@@ -56,7 +56,7 @@ func init() {
mgmtCmd.Flags().IntVar(&mgmtPort, "port", 80, "server port to listen on (defaults to 443 if TLS is enabled, 80 otherwise")
mgmtCmd.Flags().IntVar(&mgmtMetricsPort, "metrics-port", 9090, "metrics endpoint http port. Metrics are accessible under host:metrics-port/metrics")
mgmtCmd.Flags().StringVar(&mgmtDataDir, "datadir", defaultMgmtDataDir, "server data directory location")
mgmtCmd.Flags().StringVar(&types.MgmtConfigPath, "config", defaultMgmtConfig, "Netbird config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file")
mgmtCmd.Flags().StringVar(&mgmtConfig, "config", defaultMgmtConfig, "Netbird config file location. Config params specified via command line (e.g. datadir) have a precedence over configuration from this file")
mgmtCmd.Flags().StringVar(&mgmtLetsencryptDomain, "letsencrypt-domain", "", "a domain to issue Let's Encrypt certificate for. Enables TLS using Let's Encrypt. Will fetch and renew certificate, and run the server with TLS")
mgmtCmd.Flags().StringVar(&mgmtSingleAccModeDomain, "single-account-mode-domain", defaultSingleAccModeDomain, "Enables single account mode. This means that all the users will be under the same account grouped by the specified domain. If the installation has more than one account, the property is ineffective. Enabled by default with the default domain "+defaultSingleAccModeDomain)
mgmtCmd.Flags().BoolVar(&disableSingleAccMode, "disable-single-account-mode", false, "If set to true, disables single account mode. The --single-account-mode-domain property will be ignored and every new user will have a separate NetBird account.")

View File

@@ -29,7 +29,6 @@ import (
"github.com/netbirdio/netbird/management/server/integrations/integrated_validator"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/posture"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/status"
@@ -90,8 +89,6 @@ type DefaultAccountManager struct {
integratedPeerValidator integrated_validator.IntegratedValidator
metrics telemetry.AppMetrics
permissionsManager permissions.Manager
}
// getJWTGroupsChanges calculates the changes needed to sync a user's JWT groups.
@@ -159,7 +156,6 @@ func BuildManager(
metrics telemetry.AppMetrics,
proxyController port_forwarding.Controller,
settingsManager settings.Manager,
permissionsManager permissions.Manager,
) (*DefaultAccountManager, error) {
start := time.Now()
defer func() {
@@ -184,7 +180,6 @@ func BuildManager(
requestBuffer: NewAccountRequestBuffer(ctx, store),
proxyController: proxyController,
settingsManager: settingsManager,
permissionsManager: permissionsManager,
}
accountsCounter, err := store.GetAccountsCounter(ctx)
if err != nil {
@@ -203,7 +198,7 @@ func BuildManager(
log.WithContext(ctx).Infof("single account mode disabled, accounts number %d", accountsCounter)
}
cacheStore, err := nbcache.NewStore(ctx, nbcache.DefaultIDPCacheExpirationMax, nbcache.DefaultIDPCacheCleanupInterval)
cacheStore, err := nbcache.NewStore(nbcache.DefaultIDPCacheExpirationMax, nbcache.DefaultIDPCacheCleanupInterval)
if err != nil {
return nil, fmt.Errorf("getting cache store: %s", err)
}
@@ -513,6 +508,10 @@ func (am *DefaultAccountManager) DeleteAccount(ctx context.Context, accountID, u
return err
}
if !user.HasAdminPower() {
return status.Errorf(status.PermissionDenied, "user is not allowed to delete account")
}
if user.Role != types.UserRoleOwner {
return status.Errorf(status.PermissionDenied, "user is not allowed to delete account. Only account owner can delete account")
}
@@ -1028,8 +1027,8 @@ func (am *DefaultAccountManager) GetAccountByID(ctx context.Context, accountID s
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.Errorf(status.PermissionDenied, "the user has no permission to access account data")
}
return am.Store.GetAccount(ctx, accountID)
@@ -1062,8 +1061,8 @@ func (am *DefaultAccountManager) GetAccountIDFromUserAuth(ctx context.Context, u
return accountID, user.Id, nil
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return "", "", err
if user.AccountID != accountID {
return "", "", status.Errorf(status.PermissionDenied, "user %s is not part of the account %s", userAuth.UserId, accountID)
}
if !user.IsServiceUser && userAuth.Invited {
@@ -1522,11 +1521,7 @@ func (am *DefaultAccountManager) GetAccountSettings(ctx context.Context, account
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
}
if !user.HasAdminPower() && !user.IsServiceUser {
if user.AccountID != accountID || (!user.HasAdminPower() && !user.IsServiceUser) {
return nil, status.Errorf(status.PermissionDenied, "the user has no permission to access account data")
}
@@ -1611,113 +1606,3 @@ func separateGroups(autoGroups []string, allGroups []*types.Group) ([]string, ma
func (am *DefaultAccountManager) GetStore() store.Store {
return am.Store
}
// Creates account by private domain.
// Expects domain value to be a valid and a private dns domain.
func (am *DefaultAccountManager) CreateAccountByPrivateDomain(ctx context.Context, initiatorId, domain string) (*types.Account, error) {
cancel := am.Store.AcquireGlobalLock(ctx)
defer cancel()
domain = strings.ToLower(domain)
count, err := am.Store.CountAccountsByPrivateDomain(ctx, domain)
if err != nil {
return nil, err
}
if count > 0 {
return nil, status.Errorf(status.InvalidArgument, "account with private domain already exists")
}
// retry twice for new ID clashes
for range 2 {
accountId := xid.New().String()
exists, err := am.Store.AccountExists(ctx, store.LockingStrengthShare, accountId)
if err != nil || exists {
continue
}
network := types.NewNetwork()
peers := make(map[string]*nbpeer.Peer)
users := make(map[string]*types.User)
routes := make(map[route.ID]*route.Route)
setupKeys := map[string]*types.SetupKey{}
nameServersGroups := make(map[string]*nbdns.NameServerGroup)
dnsSettings := types.DNSSettings{
DisabledManagementGroups: make([]string, 0),
}
newAccount := &types.Account{
Id: accountId,
CreatedAt: time.Now().UTC(),
SetupKeys: setupKeys,
Network: network,
Peers: peers,
Users: users,
// @todo check if using the MSP owner id here is ok
CreatedBy: initiatorId,
Domain: domain,
DomainCategory: types.PrivateCategory,
IsDomainPrimaryAccount: false,
Routes: routes,
NameServerGroups: nameServersGroups,
DNSSettings: dnsSettings,
Settings: &types.Settings{
PeerLoginExpirationEnabled: true,
PeerLoginExpiration: types.DefaultPeerLoginExpiration,
GroupsPropagationEnabled: true,
RegularUsersViewBlocked: true,
PeerInactivityExpirationEnabled: false,
PeerInactivityExpiration: types.DefaultPeerInactivityExpiration,
RoutingPeerDNSResolutionEnabled: true,
},
}
if err := newAccount.AddAllGroup(); err != nil {
return nil, status.Errorf(status.Internal, "failed to add all group to new account by private domain")
}
if err := am.Store.SaveAccount(ctx, newAccount); err != nil {
log.WithContext(ctx).Errorf("failed to save new account %s by private domain: %v", newAccount.Id, err)
return nil, err
}
am.StoreEvent(ctx, initiatorId, newAccount.Id, accountId, activity.AccountCreated, nil)
return newAccount, nil
}
return nil, status.Errorf(status.Internal, "failed to create new account by private domain")
}
func (am *DefaultAccountManager) UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error) {
account, err := am.Store.GetAccount(ctx, accountId)
if err != nil {
return nil, err
}
if account.IsDomainPrimaryAccount {
return account, nil
}
// additional check to ensure there is only one account for this domain at the time of update
count, err := am.Store.CountAccountsByPrivateDomain(ctx, account.Domain)
if err != nil {
return nil, err
}
if count > 1 {
return nil, status.Errorf(status.Internal, "more than one account exists with the same private domain")
}
account.IsDomainPrimaryAccount = true
if err := am.Store.SaveAccount(ctx, account); err != nil {
log.WithContext(ctx).Errorf("failed to update primary account %s by private domain: %v", account.Id, err)
return nil, status.Errorf(status.Internal, "failed to update primary account %s by private domain", account.Id)
}
return account, nil
}

View File

@@ -111,6 +111,4 @@ type Manager interface {
BuildUserInfosForAccount(ctx context.Context, accountID, initiatorUserID string, accountUsers []*types.User) (map[string]*types.UserInfo, error)
SyncUserJWTGroups(ctx context.Context, userAuth nbcontext.UserAuth) error
GetStore() store.Store
CreateAccountByPrivateDomain(ctx context.Context, initiatorId, domain string) (*types.Account, error)
UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error)
}

View File

@@ -13,11 +13,8 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
nbAccount "github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/util"
@@ -2798,8 +2795,6 @@ type TB interface {
Cleanup(func())
Helper()
TempDir() string
Errorf(format string, args ...interface{})
Fatalf(format string, args ...interface{})
}
func createManager(t TB) (*DefaultAccountManager, error) {
@@ -2816,22 +2811,7 @@ func createManager(t TB) (*DefaultAccountManager, error) {
return nil, err
}
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.EXPECT().
GetExtraSettings(gomock.Any(), gomock.Any()).
Return(&types.ExtraSettings{}, nil).
AnyTimes()
settingsMockManager.EXPECT().
UpdateExtraSettings(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(false, nil).
AnyTimes()
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
manager, err := BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
if err != nil {
return nil, err
}
@@ -3153,51 +3133,3 @@ func BenchmarkLoginPeer_NewPeer(b *testing.B) {
})
}
}
func Test_CreateAccountByPrivateDomain(t *testing.T) {
manager, err := createManager(t)
if err != nil {
t.Fatal(err)
return
}
ctx := context.Background()
initiatorId := "test-user"
domain := "example.com"
account, err := manager.CreateAccountByPrivateDomain(ctx, initiatorId, domain)
assert.NoError(t, err)
assert.False(t, account.IsDomainPrimaryAccount)
assert.Equal(t, domain, account.Domain)
assert.Equal(t, types.PrivateCategory, account.DomainCategory)
assert.Equal(t, initiatorId, account.CreatedBy)
assert.Equal(t, 1, len(account.Groups))
assert.Equal(t, 0, len(account.Users))
assert.Equal(t, 0, len(account.SetupKeys))
// retry should fail
_, err = manager.CreateAccountByPrivateDomain(ctx, initiatorId, domain)
assert.Error(t, err)
}
func Test_UpdateToPrimaryAccount(t *testing.T) {
manager, err := createManager(t)
if err != nil {
t.Fatal(err)
return
}
ctx := context.Background()
initiatorId := "test-user"
domain := "example.com"
account, err := manager.CreateAccountByPrivateDomain(ctx, initiatorId, domain)
assert.NoError(t, err)
assert.False(t, account.IsDomainPrimaryAccount)
// retry should fail
account, err = manager.UpdateToPrimaryAccount(ctx, account.Id)
assert.NoError(t, err)
assert.True(t, account.IsDomainPrimaryAccount)
}

View File

@@ -44,7 +44,7 @@ func TestNewIDPCacheManagers(t *testing.T) {
t.Setenv(cache.RedisStoreEnvVar, redisURL)
}
cacheStore, err := cache.NewStore(context.Background(), cache.DefaultIDPCacheExpirationMax, cache.DefaultIDPCacheCleanupInterval)
cacheStore, err := cache.NewStore(cache.DefaultIDPCacheExpirationMax, cache.DefaultIDPCacheCleanupInterval)
if err != nil {
t.Fatalf("couldn't create cache store: %s", err)
}

View File

@@ -11,7 +11,6 @@ import (
redis_store "github.com/eko/gocache/store/redis/v4"
gocache "github.com/patrickmn/go-cache"
"github.com/redis/go-redis/v9"
log "github.com/sirupsen/logrus"
)
// RedisStoreEnvVar is the environment variable that determines if a redis store should be used.
@@ -20,16 +19,16 @@ const RedisStoreEnvVar = "NB_IDP_CACHE_REDIS_ADDRESS"
// NewStore creates a new cache store with the given max timeout and cleanup interval. It checks for the environment Variable RedisStoreEnvVar
// to determine if a redis store should be used. If the environment variable is set, it will attempt to connect to the redis store.
func NewStore(ctx context.Context, maxTimeout, cleanupInterval time.Duration) (store.StoreInterface, error) {
func NewStore(maxTimeout, cleanupInterval time.Duration) (store.StoreInterface, error) {
redisAddr := os.Getenv(RedisStoreEnvVar)
if redisAddr != "" {
return getRedisStore(ctx, redisAddr)
return getRedisStore(redisAddr)
}
goc := gocache.New(maxTimeout, cleanupInterval)
return gocache_store.NewGoCache(goc), nil
}
func getRedisStore(ctx context.Context, redisEnvAddr string) (store.StoreInterface, error) {
func getRedisStore(redisEnvAddr string) (store.StoreInterface, error) {
options, err := redis.ParseURL(redisEnvAddr)
if err != nil {
return nil, fmt.Errorf("parsing redis cache url: %s", err)
@@ -39,15 +38,13 @@ func getRedisStore(ctx context.Context, redisEnvAddr string) (store.StoreInterfa
options.MinIdleConns = 3
options.MaxActiveConns = 100
redisClient := redis.NewClient(options)
subCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
_, err = redisClient.Ping(subCtx).Result()
_, err = redisClient.Ping(ctx).Result()
if err != nil {
return nil, err
}
log.WithContext(subCtx).Infof("using redis cache at %s", redisEnvAddr)
return redis_store.NewRedis(redisClient), nil
}

View File

@@ -15,7 +15,7 @@ import (
)
func TestMemoryStore(t *testing.T) {
memStore, err := cache.NewStore(context.Background(), 100*time.Millisecond, 300*time.Millisecond)
memStore, err := cache.NewStore(100*time.Millisecond, 300*time.Millisecond)
if err != nil {
t.Fatalf("couldn't create memory store: %s", err)
}
@@ -42,7 +42,7 @@ func TestMemoryStore(t *testing.T) {
func TestRedisStoreConnectionFailure(t *testing.T) {
t.Setenv(cache.RedisStoreEnvVar, "redis://127.0.0.1:6379")
_, err := cache.NewStore(context.Background(), 10*time.Millisecond, 30*time.Millisecond)
_, err := cache.NewStore(10*time.Millisecond, 30*time.Millisecond)
if err == nil {
t.Fatal("getting redis cache store should return error")
}
@@ -65,7 +65,7 @@ func TestRedisStoreConnectionSuccess(t *testing.T) {
}
t.Setenv(cache.RedisStoreEnvVar, redisURL)
redisStore, err := cache.NewStore(context.Background(), 100*time.Millisecond, 300*time.Millisecond)
redisStore, err := cache.NewStore(100*time.Millisecond, 300*time.Millisecond)
if err != nil {
t.Fatalf("couldn't create redis store: %s", err)
}

View File

@@ -5,7 +5,6 @@ import (
"github.com/netbirdio/netbird/management/server/idp"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/util"
)
@@ -35,7 +34,7 @@ const (
type Config struct {
Stuns []*Host
TURNConfig *TURNConfig
Relay *types.Relay
Relay *Relay
Signal *Host
Datadir string
@@ -77,6 +76,12 @@ type TURNConfig struct {
Turns []*Host
}
type Relay struct {
Addresses []string
CredentialsTTL util.Duration
Secret string
}
// HttpServerConfig is a config of the HTTP Management service server
type HttpServerConfig struct {
LetsEncryptDomain string

View File

@@ -67,8 +67,8 @@ func (am *DefaultAccountManager) GetDNSSettings(ctx context.Context, accountID s
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -89,8 +89,8 @@ func (am *DefaultAccountManager) SaveDNSSettings(ctx context.Context, accountID
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
if !user.HasAdminPower() {

View File

@@ -8,12 +8,10 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
nbdns "github.com/netbirdio/netbird/dns"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -211,14 +209,8 @@ func createDNSManager(t *testing.T) (*DefaultAccountManager, error) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.test", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
}
func createDNSStore(t *testing.T) (store.Store, error) {

View File

@@ -10,8 +10,6 @@ import (
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/types"
)
func isEnabled() bool {
@@ -21,12 +19,16 @@ func isEnabled() bool {
// GetEvents returns a list of activity events of an account
func (am *DefaultAccountManager) GetEvents(ctx context.Context, accountID, userID string) ([]*activity.Event, error) {
user, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, userID)
unlock := am.Store.AcquireWriteLockByUID(ctx, accountID)
defer unlock()
account, err := am.Store.GetAccount(ctx, accountID)
if err != nil {
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
user, err := account.FindUser(userID)
if err != nil {
return nil, err
}
@@ -56,11 +58,6 @@ func (am *DefaultAccountManager) GetEvents(ctx context.Context, accountID, userI
filtered = append(filtered, event)
}
err = am.fillEventsWithUserInfo(ctx, events, accountID, user)
if err != nil {
return nil, err
}
return filtered, nil
}
@@ -82,156 +79,3 @@ func (am *DefaultAccountManager) StoreEvent(ctx context.Context, initiatorID, ta
}()
}
}
type eventUserInfo struct {
email string
name string
accountId string
}
func (am *DefaultAccountManager) fillEventsWithUserInfo(ctx context.Context, events []*activity.Event, accountId string, user *types.User) error {
eventUserInfo, err := am.getEventsUserInfo(ctx, events, accountId, user)
if err != nil {
return err
}
for _, event := range events {
if !fillEventInitiatorInfo(eventUserInfo, event) {
log.WithContext(ctx).Warnf("failed to resolve user info for initiator: %s", event.InitiatorID)
}
fillEventTargetInfo(eventUserInfo, event)
}
return nil
}
func (am *DefaultAccountManager) getEventsUserInfo(ctx context.Context, events []*activity.Event, accountId string, user *types.User) (map[string]eventUserInfo, error) {
accountUsers, err := am.Store.GetAccountUsers(ctx, store.LockingStrengthShare, accountId)
if err != nil {
return nil, err
}
// @note check whether using a external initiator user here is an issue
userInfos, err := am.BuildUserInfosForAccount(ctx, accountId, user.Id, accountUsers)
if err != nil {
return nil, err
}
eventUserInfos := make(map[string]eventUserInfo)
for i, k := range userInfos {
eventUserInfos[i] = eventUserInfo{
email: k.Email,
name: k.Name,
accountId: accountId,
}
}
externalUserIds := []string{}
for _, event := range events {
if _, ok := eventUserInfos[event.InitiatorID]; ok {
continue
}
if event.InitiatorID == activity.SystemInitiator ||
event.InitiatorID == accountId ||
event.Activity == activity.PeerAddedWithSetupKey {
// @todo other events to be excluded if never initiated by a user
continue
}
externalUserIds = append(externalUserIds, event.InitiatorID)
}
if len(externalUserIds) == 0 {
return eventUserInfos, nil
}
return am.getEventsExternalUserInfo(ctx, externalUserIds, eventUserInfos, user)
}
func (am *DefaultAccountManager) getEventsExternalUserInfo(ctx context.Context, externalUserIds []string, eventUserInfos map[string]eventUserInfo, user *types.User) (map[string]eventUserInfo, error) {
externalAccountId := ""
fetched := make(map[string]struct{})
externalUsers := []*types.User{}
for _, id := range externalUserIds {
if _, ok := fetched[id]; ok {
continue
}
externalUser, err := am.Store.GetUserByUserID(ctx, store.LockingStrengthShare, id)
if err != nil {
// @todo consider logging
continue
}
if externalAccountId != "" && externalAccountId != externalUser.AccountID {
return nil, fmt.Errorf("multiple external user accounts in events")
}
if externalAccountId == "" {
externalAccountId = externalUser.AccountID
}
fetched[id] = struct{}{}
externalUsers = append(externalUsers, externalUser)
}
// if we couldn't determine an account, return what we have
if externalAccountId == "" {
log.WithContext(ctx).Warnf("failed to determine external user account from users: %v", externalUserIds)
return eventUserInfos, nil
}
externalUserInfos, err := am.BuildUserInfosForAccount(ctx, externalAccountId, user.Id, externalUsers)
if err != nil {
return nil, err
}
for i, k := range externalUserInfos {
eventUserInfos[i] = eventUserInfo{
email: k.Email,
name: k.Name,
accountId: externalAccountId,
}
}
return eventUserInfos, nil
}
func fillEventTargetInfo(eventUserInfo map[string]eventUserInfo, event *activity.Event) {
userInfo, ok := eventUserInfo[event.TargetID]
if !ok {
return
}
if event.Meta == nil {
event.Meta = make(map[string]any)
}
event.Meta["email"] = userInfo.email
event.Meta["username"] = userInfo.name
}
func fillEventInitiatorInfo(eventUserInfo map[string]eventUserInfo, event *activity.Event) bool {
userInfo, ok := eventUserInfo[event.InitiatorID]
if !ok {
return false
}
if event.InitiatorEmail == "" {
event.InitiatorEmail = userInfo.email
}
if event.InitiatorName == "" {
event.InitiatorName = userInfo.name
}
if event.AccountID != userInfo.accountId {
if event.Meta == nil {
event.Meta = make(map[string]any)
}
event.Meta["external"] = true
}
return true
}

View File

@@ -35,8 +35,8 @@ func (am *DefaultAccountManager) CheckGroupPermissions(ctx context.Context, acco
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -83,8 +83,8 @@ func (am *DefaultAccountManager) SaveGroups(ctx context.Context, accountID, user
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -215,8 +215,8 @@ func (am *DefaultAccountManager) DeleteGroups(ctx context.Context, accountID, us
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {

View File

@@ -187,7 +187,7 @@ func (s *GRPCServer) Sync(req *proto.EncryptedMessage, srv proto.ManagementServi
s.ephemeralManager.OnPeerConnected(ctx, peer)
s.secretsManager.SetupRefresh(ctx, accountID, peer.ID)
s.secretsManager.SetupRefresh(ctx, peer.ID)
if s.appMetrics != nil {
s.appMetrics.GRPCMetrics().CountSyncRequestDuration(time.Since(reqStart))

View File

@@ -107,7 +107,7 @@ components:
type: boolean
example: true
network_traffic_logs_enabled:
description: Enables or disables network traffic logging. If enabled, all network traffic events from peers will be stored.
description: Enables or disables network traffic logs. If enabled, all network traffic logs from peers will be stored.
type: boolean
example: true
network_traffic_packet_counter_enabled:
@@ -179,7 +179,7 @@ components:
type: string
example: api
permissions:
$ref: '#/components/schemas/UserPermissions'
$ref: '#/components/schemas/UserPermissions'
required:
- id
- email
@@ -882,8 +882,8 @@ components:
items:
$ref: '#/components/schemas/GroupMinimum'
sourceResource:
description: Policy rule source resource that the rule is applied to
$ref: '#/components/schemas/Resource'
description: Policy rule source resource that the rule is applied to
$ref: '#/components/schemas/Resource'
destinations:
description: Policy rule destination group IDs
type: array
@@ -1687,20 +1687,21 @@ components:
- connected
- fallback
- region
AvailablePorts:
type: object
properties:
tcp:
description: Number of available TCP ports left on the ingress peer
type: integer
example: 45765
udp:
description: Number of available UDP ports left on the ingress peer
type: integer
example: 50000
required:
- tcp
- udp
type: object
properties:
tcp:
description: Number of available TCP ports left on the ingress peer
type: integer
example: 45765
udp:
description: Number of available UDP ports left on the ingress peer
type: integer
example: 50000
required:
- tcp
- udp
IngressPortAllocationRequest:
type: object
properties:
@@ -1718,8 +1719,8 @@ components:
items:
$ref: '#/components/schemas/IngressPortAllocationRequestPortRange'
direct_port:
description: Direct port allocation
$ref: '#/components/schemas/IngressPortAllocationRequestDirectPort'
description: Direct port allocation
$ref: '#/components/schemas/IngressPortAllocationRequestDirectPort'
required:
- name
- enabled
@@ -1784,7 +1785,7 @@ components:
ingress_ip:
description: Ingress IP address of the ingress peer where the traffic arrives
type: string
example: 192.34.0.123
example:
port_range_mappings:
description: List of port ranges that are allowed to be used by the ingress peer
type: array
@@ -1828,168 +1829,6 @@ components:
- ingress_start
- ingress_end
- protocol
NetworkTrafficLocation:
type: object
properties:
city_name:
type: string
description: "Name of the city (if known)."
example: "Berlin"
country_code:
type: string
description: "ISO country code (if known)."
example: "DE"
required:
- city_name
- country_code
NetworkTrafficEndpoint:
type: object
properties:
id:
type: string
description: "ID of this endpoint (e.g., peer ID or resource ID)."
example: "ch8i4ug6lnn4g9hqv7m0"
type:
type: string
description: "Type of the endpoint object (e.g., UNKNOWN, PEER, HOST_RESOURCE)."
example: "PEER"
name:
type: string
description: "Name is the name of the endpoint object (e.g., a peer name)."
example: "My Peer"
geo_location:
$ref: '#/components/schemas/NetworkTrafficLocation'
os:
type: string
nullable: true
description: "Operating system of the peer, if applicable."
example: "Linux"
address:
type: string
description: "IP address (and possibly port) in string form."
example: "100.64.0.10:51820"
dns_label:
type: string
nullable: true
description: "DNS label/name if available."
example: "*.mydomain.com"
required:
- id
- type
- name
- geo_location
- os
- address
- dns_label
NetworkTrafficEvent:
type: object
properties:
id:
type: string
description: "ID of the event. Unique."
example: "18e204d6-f7c6-405d-8025-70becb216add"
flow_id:
type: string
description: "FlowID is the ID of the connection flow. Not unique because it can be the same for multiple events (e.g., start and end of the connection)."
example: "61092452-b17c-4b14-b7cf-a2158c549826"
reporter_id:
type: string
description: "ID of the reporter of the event (e.g., the peer that reported the event)."
example: "ch8i4ug6lnn4g9hqv7m0"
timestamp:
type: string
format: date-time
description: "Timestamp of the event. Send by the peer."
example: "2025-03-20T16:23:58.125397Z"
receive_timestamp:
type: string
format: date-time
description: "Timestamp when the event was received by our API."
example: "2025-03-20T16:23:58.125397Z"
source:
$ref: '#/components/schemas/NetworkTrafficEndpoint'
user_id:
type: string
nullable: true
description: "UserID is the ID of the user that initiated the event (can be empty as not every event is user-initiated)."
example: "google-oauth2|123456789012345678901"
user_email:
type: string
nullable: true
description: "Email of the user who initiated the event (if any)."
example: "alice@netbird.io"
user_name:
type: string
nullable: true
description: "Name of the user who initiated the event (if any)."
example: "Alice Smith"
destination:
$ref: '#/components/schemas/NetworkTrafficEndpoint'
protocol:
type: integer
description: "Protocol is the protocol of the traffic (e.g. 1 = ICMP, 6 = TCP, 17 = UDP, etc.)."
example: 6
type:
type: string
description: "Type of the event (e.g. TYPE_UNKNOWN, TYPE_START, TYPE_END, TYPE_DROP)."
example: "TYPE_START"
direction:
type: string
description: "Direction of the traffic (e.g. DIRECTION_UNKNOWN, INGRESS, EGRESS)."
example: "INGRESS"
rx_bytes:
type: integer
description: "Number of bytes received."
example: 1234
rx_packets:
type: integer
description: "Number of packets received."
example: 5
tx_bytes:
type: integer
description: "Number of bytes transmitted."
example: 1234
tx_packets:
type: integer
description: "Number of packets transmitted."
example: 5
policy_id:
type: string
description: "ID of the policy that allowed this event."
example: "ch8i4ug6lnn4g9hqv7m0"
policy_name:
type: string
description: "Name of the policy that allowed this event."
example: "All to All"
icmp_type:
type: integer
description: "ICMP type (if applicable)."
example: 8
icmp_code:
type: integer
description: "ICMP code (if applicable)."
example: 0
required:
- id
- flow_id
- reporter_id
- timestamp
- receive_timestamp
- source
- user_id
- user_email
- destination
- protocol
- type
- direction
- rx_bytes
- rx_packets
- tx_bytes
- tx_packets
- policy_id
- policy_name
- icmp_type
- icmp_code
responses:
not_found:
description: Resource not found
@@ -2559,7 +2398,7 @@ paths:
/api/peers/{peerId}/ingress/ports:
get:
x-cloud-only: true
summary: List all Port Allocations
summary: List all Ingress Port Allocations for a Peer
description: Returns a list of all ingress port allocations for a peer
tags: [ Ingress Ports ]
security:
@@ -2596,7 +2435,7 @@ paths:
"$ref": "#/components/responses/internal_error"
post:
x-cloud-only: true
summary: Create a Port Allocation
summary: Create a Ingress Port Allocation
description: Creates a new ingress port allocation for a peer
tags: [ Ingress Ports ]
security:
@@ -2633,7 +2472,7 @@ paths:
/api/peers/{peerId}/ingress/ports/{allocationId}:
get:
x-cloud-only: true
summary: Retrieve a Port Allocation
summary: Retrieve a Ingress Port Allocation
description: Get information about an ingress port allocation
tags: [ Ingress Ports ]
security:
@@ -2669,24 +2508,18 @@ paths:
"$ref": "#/components/responses/internal_error"
put:
x-cloud-only: true
summary: Update a Port Allocation
description: Update information about an ingress port allocation
tags: [ Ingress Ports ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: peerId
required: true
schema:
type: string
type: string
description: The unique identifier of a peer
- in: path
name: allocationId
required: true
schema:
type: string
type: string
description: The unique identifier of an ingress port allocation
requestBody:
description: update an ingress port allocation
@@ -2711,7 +2544,7 @@ paths:
"$ref": "#/components/responses/internal_error"
delete:
x-cloud-only: true
summary: Delete a Port Allocation
summary: Delete a Ingress Port Allocation
description: Delete an ingress port allocation
tags: [ Ingress Ports ]
security:
@@ -2841,7 +2674,7 @@ paths:
name: ingressPeerId
required: true
schema:
type: string
type: string
description: The unique identifier of an ingress peer
requestBody:
description: update an ingress peer
@@ -3928,8 +3761,8 @@ paths:
description: Delete a network router
tags: [ Networks ]
security:
- BearerAuth: [ ]
- TokenAuth: [ ]
- BearerAuth: [ ]
- TokenAuth: [ ]
parameters:
- in: path
name: networkId
@@ -4151,10 +3984,10 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/events/audit:
/api/events:
get:
summary: List all Audit Events
description: Returns a list of all audit events
summary: List all Events
description: Returns a list of all events
tags: [ Events ]
security:
- BearerAuth: [ ]
@@ -4176,30 +4009,6 @@ paths:
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/events/network-traffic:
get:
summary: List all Traffic Events
description: Returns a list of all network traffic events
tags: [ Events ]
x-cloud-only: true
x-experimental: true
responses:
"200":
description: List of network traffic events
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/NetworkTrafficEvent"
'400':
"$ref": "#/components/responses/bad_request"
'401':
"$ref": "#/components/responses/requires_authentication"
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
/api/posture-checks:
get:
summary: List all Posture Checks

View File

@@ -823,100 +823,6 @@ type NetworkRouterRequest struct {
PeerGroups *[]string `json:"peer_groups,omitempty"`
}
// NetworkTrafficEndpoint defines model for NetworkTrafficEndpoint.
type NetworkTrafficEndpoint struct {
// Address IP address (and possibly port) in string form.
Address string `json:"address"`
// DnsLabel DNS label/name if available.
DnsLabel *string `json:"dns_label"`
GeoLocation NetworkTrafficLocation `json:"geo_location"`
// Id ID of this endpoint (e.g., peer ID or resource ID).
Id string `json:"id"`
// Name Name is the name of the endpoint object (e.g., a peer name).
Name string `json:"name"`
// Os Operating system of the peer, if applicable.
Os *string `json:"os"`
// Type Type of the endpoint object (e.g., UNKNOWN, PEER, HOST_RESOURCE).
Type string `json:"type"`
}
// NetworkTrafficEvent defines model for NetworkTrafficEvent.
type NetworkTrafficEvent struct {
Destination NetworkTrafficEndpoint `json:"destination"`
// Direction Direction of the traffic (e.g. DIRECTION_UNKNOWN, INGRESS, EGRESS).
Direction string `json:"direction"`
// FlowId FlowID is the ID of the connection flow. Not unique because it can be the same for multiple events (e.g., start and end of the connection).
FlowId string `json:"flow_id"`
// IcmpCode ICMP code (if applicable).
IcmpCode int `json:"icmp_code"`
// IcmpType ICMP type (if applicable).
IcmpType int `json:"icmp_type"`
// Id ID of the event. Unique.
Id string `json:"id"`
// PolicyId ID of the policy that allowed this event.
PolicyId string `json:"policy_id"`
// PolicyName Name of the policy that allowed this event.
PolicyName string `json:"policy_name"`
// Protocol Protocol is the protocol of the traffic (e.g. 1 = ICMP, 6 = TCP, 17 = UDP, etc.).
Protocol int `json:"protocol"`
// ReceiveTimestamp Timestamp when the event was received by our API.
ReceiveTimestamp time.Time `json:"receive_timestamp"`
// ReporterId ID of the reporter of the event (e.g., the peer that reported the event).
ReporterId string `json:"reporter_id"`
// RxBytes Number of bytes received.
RxBytes int `json:"rx_bytes"`
// RxPackets Number of packets received.
RxPackets int `json:"rx_packets"`
Source NetworkTrafficEndpoint `json:"source"`
// Timestamp Timestamp of the event. Send by the peer.
Timestamp time.Time `json:"timestamp"`
// TxBytes Number of bytes transmitted.
TxBytes int `json:"tx_bytes"`
// TxPackets Number of packets transmitted.
TxPackets int `json:"tx_packets"`
// Type Type of the event (e.g. TYPE_UNKNOWN, TYPE_START, TYPE_END, TYPE_DROP).
Type string `json:"type"`
// UserEmail Email of the user who initiated the event (if any).
UserEmail *string `json:"user_email"`
// UserId UserID is the ID of the user that initiated the event (can be empty as not every event is user-initiated).
UserId *string `json:"user_id"`
// UserName Name of the user who initiated the event (if any).
UserName *string `json:"user_name"`
}
// NetworkTrafficLocation defines model for NetworkTrafficLocation.
type NetworkTrafficLocation struct {
// CityName Name of the city (if known).
CityName string `json:"city_name"`
// CountryCode ISO country code (if known).
CountryCode string `json:"country_code"`
}
// OSVersionCheck Posture check for the version of operating system
type OSVersionCheck struct {
// Android Posture check for the version of operating system

View File

@@ -10,7 +10,6 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/gorilla/mux"
"github.com/stretchr/testify/assert"
@@ -22,15 +21,11 @@ import (
"github.com/netbirdio/netbird/management/server/types"
)
func initAccountsTestData(t *testing.T, account *types.Account) *handler {
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.EXPECT().
GetSettings(gomock.Any(), account.Id, "test_user").
Return(account.Settings, nil).
AnyTimes()
func initAccountsTestData(account *types.Account) *handler {
settingsMock := settings.NewManagerMock()
settingsMock.GetSettingsFunc = func(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
return account.Settings, nil
}
return &handler{
accountManager: &mock_server.MockAccountManager{
GetAccountSettingsFunc: func(ctx context.Context, accountID string, userID string) (*types.Settings, error) {
@@ -51,7 +46,7 @@ func initAccountsTestData(t *testing.T, account *types.Account) *handler {
return accCopy, nil
},
},
settingsManager: settingsMockManager,
settingsManager: settingsMock,
}
}
@@ -62,7 +57,7 @@ func TestAccounts_AccountsHandler(t *testing.T) {
sr := func(v string) *string { return &v }
br := func(v bool) *bool { return &v }
handler := initAccountsTestData(t, &types.Account{
handler := initAccountsTestData(&types.Account{
Id: accountID,
Domain: "hotmail.com",
Network: types.NewNetwork(),

View File

@@ -1,6 +1,7 @@
package events
import (
"context"
"fmt"
"net/http"
@@ -22,7 +23,6 @@ type handler struct {
func AddEndpoints(accountManager account.Manager, router *mux.Router) {
eventsHandler := newHandler(accountManager)
router.HandleFunc("/events", eventsHandler.getAllEvents).Methods("GET", "OPTIONS")
router.HandleFunc("/events/audit", eventsHandler.getAllEvents).Methods("GET", "OPTIONS")
}
// newHandler creates a new events handler
@@ -46,15 +46,66 @@ func (h *handler) getAllEvents(w http.ResponseWriter, r *http.Request) {
util.WriteError(r.Context(), err, w)
return
}
events := make([]*api.Event, len(accountEvents))
for i, e := range accountEvents {
events[i] = toEventResponse(e)
}
err = h.fillEventsWithUserInfo(r.Context(), events, accountID, userID)
if err != nil {
util.WriteError(r.Context(), err, w)
return
}
util.WriteJSONObject(r.Context(), w, events)
}
func (h *handler) fillEventsWithUserInfo(ctx context.Context, events []*api.Event, accountId, userId string) error {
// build email, name maps based on users
userInfos, err := h.accountManager.GetUsersFromAccount(ctx, accountId, userId)
if err != nil {
log.WithContext(ctx).Errorf("failed to get users from account: %s", err)
return err
}
emails := make(map[string]string)
names := make(map[string]string)
for _, ui := range userInfos {
emails[ui.ID] = ui.Email
names[ui.ID] = ui.Name
}
var ok bool
for _, event := range events {
// fill initiator
if event.InitiatorEmail == "" {
event.InitiatorEmail, ok = emails[event.InitiatorId]
if !ok {
log.WithContext(ctx).Warnf("failed to resolve email for initiator: %s", event.InitiatorId)
}
}
if event.InitiatorName == "" {
// here to allowed to be empty because in the first release we did not store the name
event.InitiatorName = names[event.InitiatorId]
}
// fill target meta
email, ok := emails[event.TargetId]
if !ok {
continue
}
event.Meta["email"] = email
username, ok := names[event.TargetId]
if !ok {
continue
}
event.Meta["username"] = username
}
return nil
}
func toEventResponse(event *activity.Event) *api.Event {
meta := make(map[string]string)
if event.Meta != nil {

View File

@@ -118,6 +118,7 @@ func (h *resourceHandler) createResource(w http.ResponseWriter, r *http.Request)
resource.NetworkID = mux.Vars(r)["networkId"]
resource.AccountID = accountID
resource.Enabled = true
resource, err = h.resourceManager.CreateResource(r.Context(), userID, resource)
if err != nil {
util.WriteError(r.Context(), err, w)

View File

@@ -16,18 +16,19 @@ import (
"github.com/golang-jwt/jwt"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/management-integrations/integrations"
"github.com/netbirdio/netbird/management/server/peers"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/users"
"github.com/stretchr/testify/assert"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/management/server/peers"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/account"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/auth"
nbcontext "github.com/netbirdio/netbird/management/server/context"
@@ -124,8 +125,7 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
proxyController := integrations.NewController(store)
userManager := users.NewManager(store)
settingsManager := settings.NewManager(store, userManager, integrations.NewManager(&activity.InMemoryEventStore{}))
permissionsManagerMock := permissions.NewManagerMock()
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager, permissionsManagerMock)
am, err := server.BuildManager(context.Background(), store, peersUpdateManager, nil, "", "", &activity.InMemoryEventStore{}, geoMock, false, validatorMock, metrics, proxyController, settingsManager)
if err != nil {
t.Fatalf("Failed to create manager: %v", err)
}
@@ -143,6 +143,7 @@ func BuildApiBlackBoxWithDBState(t TB, sqlFile string, expectedPeerUpdate *serve
resourcesManagerMock := resources.NewManagerMock()
routersManagerMock := routers.NewManagerMock()
groupsManagerMock := groups.NewManagerMock()
permissionsManagerMock := permissions.NewManagerMock()
peersManager := peers.NewManager(store, permissionsManagerMock)
apiHandler, err := nbhttp.NewAPIHandler(context.Background(), am, networksManagerMock, resourcesManagerMock, routersManagerMock, groupsManagerMock, geoMock, authManagerMock, metrics, validatorMock, proxyController, permissionsManagerMock, peersManager, settingsManager)

View File

@@ -12,7 +12,6 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
@@ -25,7 +24,6 @@ import (
mgmtProto "github.com/netbirdio/netbird/management/proto"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -432,29 +430,18 @@ func startManagementForTest(t *testing.T, testFile string, config *Config) (*grp
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetSettings(gomock.Any(), gomock.Any(), gomock.Any()).
AnyTimes().
Return(&types.Settings{}, nil)
accountManager, err := BuildManager(ctx, store, peersUpdateManager, nil, "", "netbird.selfhosted",
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
if err != nil {
cleanup()
return nil, nil, "", cleanup, err
}
secretsManager := NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
secretsManager := NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
ephemeralMgr := NewEphemeralManager(store, accountManager)
mgmtServer, err := NewServer(context.Background(), config, accountManager, settingsMockManager, peersUpdateManager, secretsManager, nil, ephemeralMgr, nil)
mgmtServer, err := NewServer(context.Background(), config, accountManager, settings.NewManagerMock(), peersUpdateManager, secretsManager, nil, ephemeralMgr, nil)
if err != nil {
return nil, nil, "", cleanup, err
}

View File

@@ -10,7 +10,6 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
pb "github.com/golang/protobuf/proto" //nolint
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
@@ -24,11 +23,9 @@ import (
"github.com/netbirdio/netbird/management/server"
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/util"
)
@@ -181,21 +178,6 @@ func startServer(
t.Fatalf("failed creating metrics: %v", err)
}
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetExtraSettings(gomock.Any(), gomock.Any()).
Return(&types.ExtraSettings{}, nil).
AnyTimes()
settingsMockManager.
EXPECT().
GetSettings(gomock.Any(), gomock.Any(), gomock.Any()).
Return(&types.Settings{}, nil).
AnyTimes()
permissionsManagerMock := permissions.NewManagerMock()
accountManager, err := server.BuildManager(
context.Background(),
str,
@@ -209,19 +191,18 @@ func startServer(
server.MocIntegratedValidator{},
metrics,
port_forwarding.NewControllerMock(),
settingsMockManager,
permissionsManagerMock,
settings.NewManagerMock(),
)
if err != nil {
t.Fatalf("failed creating an account manager: %v", err)
}
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay, settingsMockManager)
secretsManager := server.NewTimeBasedAuthSecretsManager(peersUpdateManager, config.TURNConfig, config.Relay)
mgmtServer, err := server.NewServer(
context.Background(),
config,
accountManager,
settingsMockManager,
settings.NewManagerMock(),
peersUpdateManager,
secretsManager,
nil,

View File

@@ -112,8 +112,6 @@ type MockAccountManager struct {
DeleteSetupKeyFunc func(ctx context.Context, accountID, userID, keyID string) error
BuildUserInfosForAccountFunc func(ctx context.Context, accountID, initiatorUserID string, accountUsers []*types.User) (map[string]*types.UserInfo, error)
GetStoreFunc func() store.Store
CreateAccountByPrivateDomainFunc func(ctx context.Context, initiatorId, domain string) (*types.Account, error)
UpdateToPrimaryAccountFunc func(ctx context.Context, accountId string) (*types.Account, error)
}
func (am *MockAccountManager) UpdateAccountPeers(ctx context.Context, accountID string) {
@@ -849,17 +847,3 @@ func (am *MockAccountManager) GetStore() store.Store {
}
return nil
}
func (am *MockAccountManager) CreateAccountByPrivateDomain(ctx context.Context, initiatorId, domain string) (*types.Account, error) {
if am.CreateAccountByPrivateDomainFunc != nil {
return am.CreateAccountByPrivateDomainFunc(ctx, initiatorId, domain)
}
return nil, status.Errorf(codes.Unimplemented, "method CreateAccountByPrivateDomain is not implemented")
}
func (am *MockAccountManager) UpdateToPrimaryAccount(ctx context.Context, accountId string) (*types.Account, error) {
if am.UpdateToPrimaryAccountFunc != nil {
return am.UpdateToPrimaryAccountFunc(ctx, accountId)
}
return nil, status.Errorf(codes.Unimplemented, "method UpdateToPrimaryAccount is not implemented")
}

View File

@@ -25,8 +25,8 @@ func (am *DefaultAccountManager) GetNameServerGroup(ctx context.Context, account
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -46,8 +46,8 @@ func (am *DefaultAccountManager) CreateNameServerGroup(ctx context.Context, acco
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
newNSGroup := &nbdns.NameServerGroup{
@@ -108,8 +108,8 @@ func (am *DefaultAccountManager) SaveNameServerGroup(ctx context.Context, accoun
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
var updateAccountPeers bool
@@ -159,8 +159,8 @@ func (am *DefaultAccountManager) DeleteNameServerGroup(ctx context.Context, acco
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
var nsGroup *nbdns.NameServerGroup
@@ -203,8 +203,8 @@ func (am *DefaultAccountManager) ListNameServerGroups(ctx context.Context, accou
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {

View File

@@ -6,7 +6,6 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -14,7 +13,6 @@ import (
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -775,12 +773,7 @@ func createNSManager(t *testing.T) (*DefaultAccountManager, error) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
}
func createNSStore(t *testing.T) (store.Store, error) {

View File

@@ -37,8 +37,8 @@ func (am *DefaultAccountManager) GetPeers(ctx context.Context, accountID, userID
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)
@@ -188,8 +188,8 @@ func (am *DefaultAccountManager) UpdatePeer(ctx context.Context, accountID, user
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
var peer *nbpeer.Peer
@@ -321,8 +321,8 @@ func (am *DefaultAccountManager) DeletePeer(ctx context.Context, accountID, peer
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
}
@@ -1099,8 +1099,8 @@ func (am *DefaultAccountManager) GetPeer(ctx context.Context, accountID, peerID,
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
settings, err := am.Store.GetAccountSettings(ctx, store.LockingStrengthShare, accountID)

View File

@@ -13,17 +13,15 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/rs/xid"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.zx2c4.com/wireguard/wgctrl/wgtypes"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/integrations/port_forwarding"
"github.com/netbirdio/netbird/management/server/util"
resourceTypes "github.com/netbirdio/netbird/management/server/networks/resources/types"
@@ -1214,12 +1212,7 @@ func Test_RegisterPeerByUser(t *testing.T) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
assert.NoError(t, err)
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
permissionsManagerMock := permissions.NewManagerMock()
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1283,12 +1276,7 @@ func Test_RegisterPeerBySetupKey(t *testing.T) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
assert.NoError(t, err)
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
permissionsManagerMock := permissions.NewManagerMock()
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"
@@ -1355,12 +1343,7 @@ func Test_RegisterPeerRollbackOnFailure(t *testing.T) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
assert.NoError(t, err)
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
permissionsManagerMock := permissions.NewManagerMock()
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
am, err := BuildManager(context.Background(), s, NewPeersUpdateManager(nil), nil, "", "netbird.cloud", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
assert.NoError(t, err)
existingAccountID := "bf1c8084-ba50-4ce7-9439-34653001fc3b"

View File

@@ -1,7 +1,5 @@
package peers
//go:generate go run github.com/golang/mock/mockgen -package peers -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"
"fmt"
@@ -14,7 +12,6 @@ import (
type Manager interface {
GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error)
GetPeerAccountID(ctx context.Context, peerID string) (string, error)
GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error)
}
@@ -55,7 +52,3 @@ func (m *managerImpl) GetAllPeers(ctx context.Context, accountID, userID string)
return m.store.GetAccountPeers(ctx, store.LockingStrengthShare, accountID, "", "")
}
func (m *managerImpl) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
return m.store.GetAccountIDByPeerID(ctx, store.LockingStrengthShare, peerID)
}

View File

@@ -1,81 +0,0 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: ./manager.go
// Package peers is a generated GoMock package.
package peers
import (
context "context"
reflect "reflect"
gomock "github.com/golang/mock/gomock"
peer "github.com/netbirdio/netbird/management/server/peer"
)
// MockManager is a mock of Manager interface.
type MockManager struct {
ctrl *gomock.Controller
recorder *MockManagerMockRecorder
}
// MockManagerMockRecorder is the mock recorder for MockManager.
type MockManagerMockRecorder struct {
mock *MockManager
}
// NewMockManager creates a new mock instance.
func NewMockManager(ctrl *gomock.Controller) *MockManager {
mock := &MockManager{ctrl: ctrl}
mock.recorder = &MockManagerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockManager) EXPECT() *MockManagerMockRecorder {
return m.recorder
}
// GetAllPeers mocks base method.
func (m *MockManager) GetAllPeers(ctx context.Context, accountID, userID string) ([]*peer.Peer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetAllPeers", ctx, accountID, userID)
ret0, _ := ret[0].([]*peer.Peer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetAllPeers indicates an expected call of GetAllPeers.
func (mr *MockManagerMockRecorder) GetAllPeers(ctx, accountID, userID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetAllPeers", reflect.TypeOf((*MockManager)(nil).GetAllPeers), ctx, accountID, userID)
}
// GetPeer mocks base method.
func (m *MockManager) GetPeer(ctx context.Context, accountID, userID, peerID string) (*peer.Peer, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPeer", ctx, accountID, userID, peerID)
ret0, _ := ret[0].(*peer.Peer)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPeer indicates an expected call of GetPeer.
func (mr *MockManagerMockRecorder) GetPeer(ctx, accountID, userID, peerID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeer", reflect.TypeOf((*MockManager)(nil).GetPeer), ctx, accountID, userID, peerID)
}
// GetPeerAccountID mocks base method.
func (m *MockManager) GetPeerAccountID(ctx context.Context, peerID string) (string, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetPeerAccountID", ctx, peerID)
ret0, _ := ret[0].(string)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetPeerAccountID indicates an expected call of GetPeerAccountID.
func (mr *MockManagerMockRecorder) GetPeerAccountID(ctx, peerID interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetPeerAccountID", reflect.TypeOf((*MockManager)(nil).GetPeerAccountID), ctx, peerID)
}

View File

@@ -7,7 +7,6 @@ import (
"github.com/netbirdio/netbird/management/server/activity"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/status"
"github.com/netbirdio/netbird/management/server/types"
"github.com/netbirdio/netbird/management/server/users"
)
@@ -29,7 +28,6 @@ const (
type Manager interface {
ValidateUserPermissions(ctx context.Context, accountID, userID string, module Module, operation Operation) (bool, error)
ValidateAccountAccess(ctx context.Context, accountID string, user *types.User) error
}
type managerImpl struct {
@@ -54,11 +52,11 @@ func (m *managerImpl) ValidateUserPermissions(ctx context.Context, accountID, us
}
if user == nil {
return false, status.NewUserNotFoundError(userID)
return false, errors.New("user not found")
}
if err := m.ValidateAccountAccess(ctx, accountID, user); err != nil {
return false, err
if user.AccountID != accountID {
return false, errors.New("user does not belong to account")
}
switch user.Role {
@@ -93,13 +91,6 @@ func (m *managerImpl) validateRegularUserPermissions(ctx context.Context, accoun
return false, nil
}
func (m *managerImpl) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User) error {
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
return nil
}
func NewManagerMock() Manager {
return &managerMock{}
}
@@ -110,11 +101,3 @@ func (m *managerMock) ValidateUserPermissions(ctx context.Context, accountID, us
}
return false, nil
}
func (m *managerMock) ValidateAccountAccess(ctx context.Context, accountID string, user *types.User) error {
// @note managers explicitly checked this, so should the mock
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
return nil
}

View File

@@ -22,8 +22,8 @@ func (am *DefaultAccountManager) GetPolicy(ctx context.Context, accountID, polic
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -43,8 +43,8 @@ func (am *DefaultAccountManager) SavePolicy(ctx context.Context, accountID, user
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -100,8 +100,8 @@ func (am *DefaultAccountManager) DeletePolicy(ctx context.Context, accountID, po
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -148,8 +148,8 @@ func (am *DefaultAccountManager) ListPolicies(ctx context.Context, accountID, us
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if user.IsRegularUser() {
@@ -255,24 +255,15 @@ func toProtocolFirewallRules(rules []*types.FirewallRule) []*proto.FirewallRule
for i := range rules {
rule := rules[i]
fwRule := &proto.FirewallRule{
result[i] = &proto.FirewallRule{
PolicyID: []byte(rule.PolicyID),
PeerIP: rule.PeerIP,
Direction: getProtoDirection(rule.Direction),
Action: getProtoAction(rule.Action),
Protocol: getProtoProtocol(rule.Protocol),
Port: rule.Port,
PortInfo: rule.PortRange.ToProto(),
}
if shouldUsePortRange(fwRule) {
fwRule.PortInfo = rule.PortRange.ToProto()
}
result[i] = fwRule
}
return result
}
func shouldUsePortRange(rule *proto.FirewallRule) bool {
return rule.Port == "" && (rule.Protocol == proto.RuleProtocol_UDP || rule.Protocol == proto.RuleProtocol_TCP)
}

View File

@@ -3,7 +3,6 @@ package posture
import (
"context"
"fmt"
"strings"
"github.com/hashicorp/go-version"
log "github.com/sirupsen/logrus"
@@ -17,22 +16,13 @@ type NBVersionCheck struct {
var _ Check = (*NBVersionCheck)(nil)
// sanitizeVersion removes anything after the pre-release tag (e.g., "-dev", "-alpha", etc.)
func sanitizeVersion(version string) string {
parts := strings.Split(version, "-")
return parts[0]
}
func (n *NBVersionCheck) Check(ctx context.Context, peer nbpeer.Peer) (bool, error) {
peerVersion := sanitizeVersion(peer.Meta.WtVersion)
minVersion := sanitizeVersion(n.MinVersion)
peerNBVersion, err := version.NewVersion(peerVersion)
peerNBVersion, err := version.NewVersion(peer.Meta.WtVersion)
if err != nil {
return false, err
}
constraints, err := version.NewConstraint(">= " + minVersion)
constraints, err := version.NewConstraint(">= " + n.MinVersion)
if err != nil {
return false, err
}

View File

@@ -22,8 +22,8 @@ func (am *DefaultAccountManager) GetPostureChecks(ctx context.Context, accountID
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if !user.HasAdminPower() {
@@ -43,8 +43,8 @@ func (am *DefaultAccountManager) SavePostureChecks(ctx context.Context, accountI
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if !user.HasAdminPower() {
@@ -99,8 +99,8 @@ func (am *DefaultAccountManager) DeletePostureChecks(ctx context.Context, accoun
return err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return err
if user.AccountID != accountID {
return status.NewUserNotPartOfAccountError()
}
if !user.HasAdminPower() {
@@ -141,8 +141,8 @@ func (am *DefaultAccountManager) ListPostureChecks(ctx context.Context, accountI
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
if user.AccountID != accountID {
return nil, status.NewUserNotPartOfAccountError()
}
if !user.HasAdminPower() {

View File

@@ -25,11 +25,7 @@ func (am *DefaultAccountManager) GetRoute(ctx context.Context, accountID string,
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
}
if !user.IsAdminOrServiceUser() {
if !user.IsAdminOrServiceUser() || user.AccountID != accountID {
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view Network Routes")
}
@@ -346,11 +342,7 @@ func (am *DefaultAccountManager) ListRoutes(ctx context.Context, accountID, user
return nil, err
}
if err := am.permissionsManager.ValidateAccountAccess(ctx, accountID, user); err != nil {
return nil, err
}
if !user.IsAdminOrServiceUser() {
if !user.IsAdminOrServiceUser() || user.AccountID != accountID {
return nil, status.Errorf(status.PermissionDenied, "only users with admin power can view Network Routes")
}

View File

@@ -9,7 +9,6 @@ import (
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/rs/xid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
@@ -21,7 +20,6 @@ import (
routerTypes "github.com/netbirdio/netbird/management/server/networks/routers/types"
networkTypes "github.com/netbirdio/netbird/management/server/networks/types"
nbpeer "github.com/netbirdio/netbird/management/server/peer"
"github.com/netbirdio/netbird/management/server/permissions"
"github.com/netbirdio/netbird/management/server/settings"
"github.com/netbirdio/netbird/management/server/store"
"github.com/netbirdio/netbird/management/server/telemetry"
@@ -1260,30 +1258,7 @@ func createRouterManager(t *testing.T) (*DefaultAccountManager, error) {
metrics, err := telemetry.NewDefaultAppMetrics(context.Background())
require.NoError(t, err)
permissionsManagerMock := permissions.NewManagerMock()
ctrl := gomock.NewController(t)
t.Cleanup(ctrl.Finish)
settingsMockManager := settings.NewMockManager(ctrl)
settingsMockManager.
EXPECT().
GetSettings(
gomock.Any(),
gomock.Any(),
gomock.Any(),
).
Return(nil, nil).
AnyTimes()
settingsMockManager.
EXPECT().
GetExtraSettings(
gomock.Any(),
gomock.Any(),
).
AnyTimes().
Return(&types.ExtraSettings{}, nil)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settingsMockManager, permissionsManagerMock)
return BuildManager(context.Background(), store, NewPeersUpdateManager(nil), nil, "", "netbird.selfhosted", eventStore, nil, false, MocIntegratedValidator{}, metrics, port_forwarding.NewControllerMock(), settings.NewManagerMock())
}
func createRouterStore(t *testing.T) (store.Store, error) {

View File

@@ -1,7 +1,5 @@
package settings
//go:generate go run github.com/golang/mock/mockgen -package settings -destination=manager_mock.go -source=./manager.go -build_flags=-mod=mod
import (
"context"
"fmt"
@@ -27,6 +25,11 @@ type managerImpl struct {
userManager users.Manager
}
type ManagerMock struct {
GetSettingsFunc func(ctx context.Context, accountID, userID string) (*types.Settings, error)
GetExtraSettingsFunc func(ctx context.Context, accountID string) (*types.ExtraSettings, error)
}
func NewManager(store store.Store, userManager users.Manager, extraSettingsManager extra_settings.Manager) Manager {
return &managerImpl{
store: store,
@@ -96,3 +99,35 @@ func (m *managerImpl) GetExtraSettings(ctx context.Context, accountID string) (*
func (m *managerImpl) UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error) {
return m.extraSettingsManager.UpdateExtraSettings(ctx, accountID, userID, extraSettings)
}
func NewManagerMock() *ManagerMock {
return &ManagerMock{}
}
func (m *ManagerMock) GetExtraSettingsManager() extra_settings.Manager {
return nil
}
func (m *ManagerMock) GetSettings(ctx context.Context, accountID, userID string) (*types.Settings, error) {
if m.GetSettingsFunc != nil {
return m.GetSettingsFunc(ctx, accountID, userID)
}
return &types.Settings{}, nil
}
func (m *ManagerMock) SetGetSettingsFunc(f func(ctx context.Context, accountID, userID string) (*types.Settings, error)) {
m.GetSettingsFunc = f
}
func (m *ManagerMock) GetExtraSettings(ctx context.Context, accountID string) (*types.ExtraSettings, error) {
if m.GetExtraSettingsFunc != nil {
return m.GetExtraSettingsFunc(ctx, accountID)
}
return &types.ExtraSettings{}, nil
}
func (m *ManagerMock) UpdateExtraSettings(ctx context.Context, accountID, userID string, extraSettings *types.ExtraSettings) (bool, error) {
return false, nil
}

Some files were not shown because too many files have changed in this diff Show More