mirror of
https://github.com/netbirdio/netbird.git
synced 2026-07-23 00:41:29 +02:00
Compare commits
106 Commits
deploy/per
...
ref/improv
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
092cb33215 | ||
|
|
a01e5abfee | ||
|
|
01d01ac16f | ||
|
|
a7be9fdcfc | ||
|
|
c6f9a36e0e | ||
|
|
1d9fced073 | ||
|
|
0fa65eab5d | ||
|
|
4a5cd74bfe | ||
|
|
f88b93e53f | ||
|
|
40d932e011 | ||
|
|
2e3fde4024 | ||
|
|
7f17cd348a | ||
|
|
4ee8b66c42 | ||
|
|
0051fac5fd | ||
|
|
671e77c359 | ||
|
|
fe16a162e4 | ||
|
|
1a87f6e05e | ||
|
|
17ff31316a | ||
|
|
64f27aee55 | ||
|
|
78b86e0beb | ||
|
|
18871b554f | ||
|
|
76d73548d6 | ||
|
|
11828a064a | ||
|
|
0c2a3dd937 | ||
|
|
47dcf8d68c | ||
|
|
cc8f6bcaf3 | ||
|
|
d8bcf745b0 | ||
|
|
8430139d80 | ||
|
|
a2962b4ce0 | ||
|
|
16fffdb75b | ||
|
|
036cecbf46 | ||
|
|
3482852bb6 | ||
|
|
fd62665b1f | ||
|
|
36da464413 | ||
|
|
86370a0e7b | ||
|
|
cb16d0f45f | ||
|
|
e8d8bd8f18 | ||
|
|
8b07f21c28 | ||
|
|
54be772ffd | ||
|
|
3c3a454e61 | ||
|
|
5ff77b3595 | ||
|
|
b180edbe5c | ||
|
|
0a042ac36d | ||
|
|
e9f11fb11b | ||
|
|
419ed275fa | ||
|
|
2d4fcaf186 | ||
|
|
acf172b52c | ||
|
|
8c81a823fa | ||
|
|
619c549547 | ||
|
|
9a713a0987 | ||
|
|
c4945cd565 | ||
|
|
1e10c17ecb | ||
|
|
96d5190436 | ||
|
|
d19c26df06 | ||
|
|
36e36414d9 | ||
|
|
7e69589e05 | ||
|
|
aa613ab79a | ||
|
|
6ead0ff95e | ||
|
|
0db65a8984 | ||
|
|
c138807e95 | ||
|
|
637c0c8949 | ||
|
|
c72e13d8e6 | ||
|
|
f6d7bccfa0 | ||
|
|
e3ed01cafb | ||
|
|
fa748a7ec2 | ||
|
|
cccc615783 | ||
|
|
2021463ca0 | ||
|
|
f48cfd52e9 | ||
|
|
6838f53f40 | ||
|
|
8276236dfa | ||
|
|
994b923d56 | ||
|
|
59e2432231 | ||
|
|
eee0d123e4 | ||
|
|
e943203ae2 | ||
|
|
6a775217cf | ||
|
|
175674749f | ||
|
|
1e534cecf6 | ||
|
|
aa3aa8c6a8 | ||
|
|
fbdfe45c25 | ||
|
|
81ee172db8 | ||
|
|
f8fd65a65f | ||
|
|
62b978c050 | ||
|
|
4ebf1410c6 | ||
|
|
630edf2480 | ||
|
|
ea469d28d7 | ||
|
|
597f1d47b8 | ||
|
|
fcc96417f9 | ||
|
|
8755211a60 | ||
|
|
e6d4653b08 | ||
|
|
eb69f2de78 | ||
|
|
206420c085 | ||
|
|
88a864c195 | ||
|
|
a789e9e6d8 | ||
|
|
9930913e4e | ||
|
|
48675f579f | ||
|
|
afec455f86 | ||
|
|
035c5d9f23 | ||
|
|
b2a5b29fb2 | ||
|
|
9ec61206c2 | ||
|
|
1b011a2d85 | ||
|
|
a85ea1ddb0 | ||
|
|
829e40d2aa | ||
|
|
6344e34880 | ||
|
|
a76ca8c565 | ||
|
|
26693e4ea8 | ||
|
|
f6a71f4193 |
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -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
|
||||
|
||||
@@ -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 Contributor’s 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 Contributor‘s Contribution(s) alone or by combination of Contributor’s 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 Contributor’s Representations
|
||||
4.1 Contributor represents that Contributor is legally entitled to grant the above license. If Contributor’s employer has IP Rights to Contributor’s 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 Contributor’s 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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
210
client/internal/dns/host_darwin_test.go
Normal file
210
client/internal/dns/host_darwin_test.go
Normal 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())
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
312
client/internal/statemanager/mocks/manager.go
Normal file
312
client/internal/statemanager/mocks/manager.go
Normal 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)
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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{})
|
||||
}
|
||||
|
||||
@@ -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{})
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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
4
go.mod
@@ -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
4
go.sum
@@ -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=
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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.")
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
2
management/server/cache/idp_test.go
vendored
2
management/server/cache/idp_test.go
vendored
@@ -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)
|
||||
}
|
||||
|
||||
13
management/server/cache/store.go
vendored
13
management/server/cache/store.go
vendored
@@ -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
|
||||
}
|
||||
|
||||
6
management/server/cache/store_test.go
vendored
6
management/server/cache/store_test.go
vendored
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user