mirror of
https://github.com/slackhq/nebula.git
synced 2025-11-10 20:33:58 +01:00
Merge remote-tracking branch 'origin/master' into holepunch-remote-allow-list
This commit is contained in:
commit
0d6b19ee8f
2
.github/workflows/gofmt.yml
vendored
2
.github/workflows/gofmt.yml
vendored
@ -18,7 +18,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Install goimports
|
- name: Install goimports
|
||||||
|
|||||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@ -14,7 +14,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
@ -37,7 +37,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
@ -70,12 +70,12 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Import certificates
|
- name: Import certificates
|
||||||
if: env.HAS_SIGNING_CREDS == 'true'
|
if: env.HAS_SIGNING_CREDS == 'true'
|
||||||
uses: Apple-Actions/import-codesign-certs@v3
|
uses: Apple-Actions/import-codesign-certs@v5
|
||||||
with:
|
with:
|
||||||
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
|
p12-file-base64: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_P12_BASE64 }}
|
||||||
p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
|
p12-password: ${{ secrets.APPLE_DEVELOPER_CERTIFICATE_PASSWORD }}
|
||||||
|
|||||||
2
.github/workflows/smoke.yml
vendored
2
.github/workflows/smoke.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: build
|
- name: build
|
||||||
|
|||||||
14
.github/workflows/test.yml
vendored
14
.github/workflows/test.yml
vendored
@ -22,7 +22,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
@ -32,9 +32,9 @@ jobs:
|
|||||||
run: make vet
|
run: make vet
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v7
|
||||||
with:
|
with:
|
||||||
version: v1.64
|
version: v2.0
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: make test
|
run: make test
|
||||||
@ -60,7 +60,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build
|
- name: Build
|
||||||
@ -102,7 +102,7 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.23'
|
go-version: '1.24'
|
||||||
check-latest: true
|
check-latest: true
|
||||||
|
|
||||||
- name: Build nebula
|
- name: Build nebula
|
||||||
@ -115,9 +115,9 @@ jobs:
|
|||||||
run: make vet
|
run: make vet
|
||||||
|
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v6
|
uses: golangci/golangci-lint-action@v7
|
||||||
with:
|
with:
|
||||||
version: v1.64
|
version: v2.0
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: make test
|
run: make test
|
||||||
|
|||||||
@ -1,9 +1,23 @@
|
|||||||
# yaml-language-server: $schema=https://golangci-lint.run/jsonschema/golangci.jsonschema.json
|
version: "2"
|
||||||
linters:
|
linters:
|
||||||
# Disable all linters.
|
default: none
|
||||||
# Default: false
|
|
||||||
disable-all: true
|
|
||||||
# Enable specific linter
|
|
||||||
# https://golangci-lint.run/usage/linters/#enabled-by-default
|
|
||||||
enable:
|
enable:
|
||||||
- testifylint
|
- testifylint
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
|||||||
@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- `default_local_cidr_any` now defaults to false, meaning that any firewall rule
|
||||||
|
intended to target an `unsafe_routes` entry must explicitly declare it via the
|
||||||
|
`local_cidr` field. This is almost always the intended behavior. This flag is
|
||||||
|
deprecated and will be removed in a future release.
|
||||||
|
|
||||||
## [1.9.4] - 2024-09-09
|
## [1.9.4] - 2024-09-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|||||||
@ -36,7 +36,7 @@ type AllowListNameRule struct {
|
|||||||
|
|
||||||
func NewLocalAllowListFromConfig(c *config.C, k string) (*LocalAllowList, error) {
|
func NewLocalAllowListFromConfig(c *config.C, k string) (*LocalAllowList, error) {
|
||||||
var nameRules []AllowListNameRule
|
var nameRules []AllowListNameRule
|
||||||
handleKey := func(key string, value interface{}) (bool, error) {
|
handleKey := func(key string, value any) (bool, error) {
|
||||||
if key == "interfaces" {
|
if key == "interfaces" {
|
||||||
var err error
|
var err error
|
||||||
nameRules, err = getAllowListInterfaces(k, value)
|
nameRules, err = getAllowListInterfaces(k, value)
|
||||||
@ -70,7 +70,7 @@ func NewRemoteAllowListFromConfig(c *config.C, k, rangesKey string) (*RemoteAllo
|
|||||||
|
|
||||||
// If the handleKey func returns true, the rest of the parsing is skipped
|
// If the handleKey func returns true, the rest of the parsing is skipped
|
||||||
// for this key. This allows parsing of special values like `interfaces`.
|
// for this key. This allows parsing of special values like `interfaces`.
|
||||||
func newAllowListFromConfig(c *config.C, k string, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) {
|
func newAllowListFromConfig(c *config.C, k string, handleKey func(key string, value any) (bool, error)) (*AllowList, error) {
|
||||||
r := c.Get(k)
|
r := c.Get(k)
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@ -81,8 +81,8 @@ func newAllowListFromConfig(c *config.C, k string, handleKey func(key string, va
|
|||||||
|
|
||||||
// If the handleKey func returns true, the rest of the parsing is skipped
|
// If the handleKey func returns true, the rest of the parsing is skipped
|
||||||
// for this key. This allows parsing of special values like `interfaces`.
|
// for this key. This allows parsing of special values like `interfaces`.
|
||||||
func newAllowList(k string, raw interface{}, handleKey func(key string, value interface{}) (bool, error)) (*AllowList, error) {
|
func newAllowList(k string, raw any, handleKey func(key string, value any) (bool, error)) (*AllowList, error) {
|
||||||
rawMap, ok := raw.(map[interface{}]interface{})
|
rawMap, ok := raw.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("config `%s` has invalid type: %T", k, raw)
|
return nil, fmt.Errorf("config `%s` has invalid type: %T", k, raw)
|
||||||
}
|
}
|
||||||
@ -100,12 +100,7 @@ func newAllowList(k string, raw interface{}, handleKey func(key string, value in
|
|||||||
rules4 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false}
|
rules4 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false}
|
||||||
rules6 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false}
|
rules6 := allowListRules{firstValue: true, allValuesMatch: true, defaultSet: false}
|
||||||
|
|
||||||
for rawKey, rawValue := range rawMap {
|
for rawCIDR, rawValue := range rawMap {
|
||||||
rawCIDR, ok := rawKey.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
if handleKey != nil {
|
if handleKey != nil {
|
||||||
handled, err := handleKey(rawCIDR, rawValue)
|
handled, err := handleKey(rawCIDR, rawValue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -116,7 +111,7 @@ func newAllowList(k string, raw interface{}, handleKey func(key string, value in
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
value, ok := rawValue.(bool)
|
value, ok := config.AsBool(rawValue)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("config `%s` has invalid value (type %T): %v", k, rawValue, rawValue)
|
return nil, fmt.Errorf("config `%s` has invalid value (type %T): %v", k, rawValue, rawValue)
|
||||||
}
|
}
|
||||||
@ -173,22 +168,18 @@ func newAllowList(k string, raw interface{}, handleKey func(key string, value in
|
|||||||
return &AllowList{cidrTree: tree}, nil
|
return &AllowList{cidrTree: tree}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllowListInterfaces(k string, v interface{}) ([]AllowListNameRule, error) {
|
func getAllowListInterfaces(k string, v any) ([]AllowListNameRule, error) {
|
||||||
var nameRules []AllowListNameRule
|
var nameRules []AllowListNameRule
|
||||||
|
|
||||||
rawRules, ok := v.(map[interface{}]interface{})
|
rawRules, ok := v.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("config `%s.interfaces` is invalid (type %T): %v", k, v, v)
|
return nil, fmt.Errorf("config `%s.interfaces` is invalid (type %T): %v", k, v, v)
|
||||||
}
|
}
|
||||||
|
|
||||||
firstEntry := true
|
firstEntry := true
|
||||||
var allValues bool
|
var allValues bool
|
||||||
for rawName, rawAllow := range rawRules {
|
for name, rawAllow := range rawRules {
|
||||||
name, ok := rawName.(string)
|
allow, ok := config.AsBool(rawAllow)
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("config `%s.interfaces` has invalid key (type %T): %v", k, rawName, rawName)
|
|
||||||
}
|
|
||||||
allow, ok := rawAllow.(bool)
|
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("config `%s.interfaces` has invalid value (type %T): %v", k, rawAllow, rawAllow)
|
return nil, fmt.Errorf("config `%s.interfaces` has invalid value (type %T): %v", k, rawAllow, rawAllow)
|
||||||
}
|
}
|
||||||
@ -224,16 +215,11 @@ func getRemoteAllowRanges(c *config.C, k string) (*bart.Table[*AllowList], error
|
|||||||
|
|
||||||
remoteAllowRanges := new(bart.Table[*AllowList])
|
remoteAllowRanges := new(bart.Table[*AllowList])
|
||||||
|
|
||||||
rawMap, ok := value.(map[interface{}]interface{})
|
rawMap, ok := value.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value)
|
return nil, fmt.Errorf("config `%s` has invalid type: %T", k, value)
|
||||||
}
|
}
|
||||||
for rawKey, rawValue := range rawMap {
|
for rawCIDR, rawValue := range rawMap {
|
||||||
rawCIDR, ok := rawKey.(string)
|
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("config `%s` has invalid key (type %T): %v", k, rawKey, rawKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
allowList, err := newAllowList(fmt.Sprintf("%s.%s", k, rawCIDR), rawValue, nil)
|
allowList, err := newAllowList(fmt.Sprintf("%s.%s", k, rawCIDR), rawValue, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@ -15,27 +15,27 @@ import (
|
|||||||
func TestNewAllowListFromConfig(t *testing.T) {
|
func TestNewAllowListFromConfig(t *testing.T) {
|
||||||
l := test.NewLogger()
|
l := test.NewLogger()
|
||||||
c := config.NewC(l)
|
c := config.NewC(l)
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"192.168.0.0": true,
|
"192.168.0.0": true,
|
||||||
}
|
}
|
||||||
r, err := newAllowListFromConfig(c, "allowlist", nil)
|
r, err := newAllowListFromConfig(c, "allowlist", nil)
|
||||||
require.EqualError(t, err, "config `allowlist` has invalid CIDR: 192.168.0.0. netip.ParsePrefix(\"192.168.0.0\"): no '/'")
|
require.EqualError(t, err, "config `allowlist` has invalid CIDR: 192.168.0.0. netip.ParsePrefix(\"192.168.0.0\"): no '/'")
|
||||||
assert.Nil(t, r)
|
assert.Nil(t, r)
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"192.168.0.0/16": "abc",
|
"192.168.0.0/16": "abc",
|
||||||
}
|
}
|
||||||
r, err = newAllowListFromConfig(c, "allowlist", nil)
|
r, err = newAllowListFromConfig(c, "allowlist", nil)
|
||||||
require.EqualError(t, err, "config `allowlist` has invalid value (type string): abc")
|
require.EqualError(t, err, "config `allowlist` has invalid value (type string): abc")
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"192.168.0.0/16": true,
|
"192.168.0.0/16": true,
|
||||||
"10.0.0.0/8": false,
|
"10.0.0.0/8": false,
|
||||||
}
|
}
|
||||||
r, err = newAllowListFromConfig(c, "allowlist", nil)
|
r, err = newAllowListFromConfig(c, "allowlist", nil)
|
||||||
require.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for 0.0.0.0/0")
|
require.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for 0.0.0.0/0")
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"0.0.0.0/0": true,
|
"0.0.0.0/0": true,
|
||||||
"10.0.0.0/8": false,
|
"10.0.0.0/8": false,
|
||||||
"10.42.42.0/24": true,
|
"10.42.42.0/24": true,
|
||||||
@ -45,7 +45,7 @@ func TestNewAllowListFromConfig(t *testing.T) {
|
|||||||
r, err = newAllowListFromConfig(c, "allowlist", nil)
|
r, err = newAllowListFromConfig(c, "allowlist", nil)
|
||||||
require.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for ::/0")
|
require.EqualError(t, err, "config `allowlist` contains both true and false rules, but no default set for ::/0")
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"0.0.0.0/0": true,
|
"0.0.0.0/0": true,
|
||||||
"10.0.0.0/8": false,
|
"10.0.0.0/8": false,
|
||||||
"10.42.42.0/24": true,
|
"10.42.42.0/24": true,
|
||||||
@ -55,7 +55,7 @@ func TestNewAllowListFromConfig(t *testing.T) {
|
|||||||
assert.NotNil(t, r)
|
assert.NotNil(t, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"0.0.0.0/0": true,
|
"0.0.0.0/0": true,
|
||||||
"10.0.0.0/8": false,
|
"10.0.0.0/8": false,
|
||||||
"10.42.42.0/24": true,
|
"10.42.42.0/24": true,
|
||||||
@ -70,16 +70,16 @@ func TestNewAllowListFromConfig(t *testing.T) {
|
|||||||
|
|
||||||
// Test interface names
|
// Test interface names
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"interfaces": map[interface{}]interface{}{
|
"interfaces": map[string]any{
|
||||||
`docker.*`: "foo",
|
`docker.*`: "foo",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
lr, err := NewLocalAllowListFromConfig(c, "allowlist")
|
lr, err := NewLocalAllowListFromConfig(c, "allowlist")
|
||||||
require.EqualError(t, err, "config `allowlist.interfaces` has invalid value (type string): foo")
|
require.EqualError(t, err, "config `allowlist.interfaces` has invalid value (type string): foo")
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"interfaces": map[interface{}]interface{}{
|
"interfaces": map[string]any{
|
||||||
`docker.*`: false,
|
`docker.*`: false,
|
||||||
`eth.*`: true,
|
`eth.*`: true,
|
||||||
},
|
},
|
||||||
@ -87,8 +87,8 @@ func TestNewAllowListFromConfig(t *testing.T) {
|
|||||||
lr, err = NewLocalAllowListFromConfig(c, "allowlist")
|
lr, err = NewLocalAllowListFromConfig(c, "allowlist")
|
||||||
require.EqualError(t, err, "config `allowlist.interfaces` values must all be the same true/false value")
|
require.EqualError(t, err, "config `allowlist.interfaces` values must all be the same true/false value")
|
||||||
|
|
||||||
c.Settings["allowlist"] = map[interface{}]interface{}{
|
c.Settings["allowlist"] = map[string]any{
|
||||||
"interfaces": map[interface{}]interface{}{
|
"interfaces": map[string]any{
|
||||||
`docker.*`: false,
|
`docker.*`: false,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -41,7 +41,7 @@ type detailsV1 struct {
|
|||||||
curve Curve
|
curve Curve
|
||||||
}
|
}
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
func (c *certificateV1) Version() Version {
|
func (c *certificateV1) Version() Version {
|
||||||
return Version1
|
return Version1
|
||||||
|
|||||||
@ -10,14 +10,14 @@ import (
|
|||||||
|
|
||||||
func TestNewArgon2Parameters(t *testing.T) {
|
func TestNewArgon2Parameters(t *testing.T) {
|
||||||
p := NewArgon2Parameters(64*1024, 4, 3)
|
p := NewArgon2Parameters(64*1024, 4, 3)
|
||||||
assert.EqualValues(t, &Argon2Parameters{
|
assert.Equal(t, &Argon2Parameters{
|
||||||
version: argon2.Version,
|
version: argon2.Version,
|
||||||
Memory: 64 * 1024,
|
Memory: 64 * 1024,
|
||||||
Parallelism: 4,
|
Parallelism: 4,
|
||||||
Iterations: 3,
|
Iterations: 3,
|
||||||
}, p)
|
}, p)
|
||||||
p = NewArgon2Parameters(2*1024*1024, 2, 1)
|
p = NewArgon2Parameters(2*1024*1024, 2, 1)
|
||||||
assert.EqualValues(t, &Argon2Parameters{
|
assert.Equal(t, &Argon2Parameters{
|
||||||
version: argon2.Version,
|
version: argon2.Version,
|
||||||
Memory: 2 * 1024 * 1024,
|
Memory: 2 * 1024 * 1024,
|
||||||
Parallelism: 2,
|
Parallelism: 2,
|
||||||
|
|||||||
@ -90,26 +90,26 @@ func Test_ca(t *testing.T) {
|
|||||||
assertHelpError(t, ca(
|
assertHelpError(t, ca(
|
||||||
[]string{"-version", "1", "-out-key", "nope", "-out-crt", "nope", "duration", "100m"}, ob, eb, nopw,
|
[]string{"-version", "1", "-out-key", "nope", "-out-crt", "nope", "duration", "100m"}, ob, eb, nopw,
|
||||||
), "-name is required")
|
), "-name is required")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// ipv4 only ips
|
// ipv4 only ips
|
||||||
assertHelpError(t, ca([]string{"-version", "1", "-name", "ipv6", "-ips", "100::100/100"}, ob, eb, nopw), "invalid -networks definition: v1 certificates can only be ipv4, have 100::100/100")
|
assertHelpError(t, ca([]string{"-version", "1", "-name", "ipv6", "-ips", "100::100/100"}, ob, eb, nopw), "invalid -networks definition: v1 certificates can only be ipv4, have 100::100/100")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// ipv4 only subnets
|
// ipv4 only subnets
|
||||||
assertHelpError(t, ca([]string{"-version", "1", "-name", "ipv6", "-subnets", "100::100/100"}, ob, eb, nopw), "invalid -unsafe-networks definition: v1 certificates can only be ipv4, have 100::100/100")
|
assertHelpError(t, ca([]string{"-version", "1", "-name", "ipv6", "-subnets", "100::100/100"}, ob, eb, nopw), "invalid -unsafe-networks definition: v1 certificates can only be ipv4, have 100::100/100")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// failed key write
|
// failed key write
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
eb.Reset()
|
eb.Reset()
|
||||||
args := []string{"-version", "1", "-name", "test", "-duration", "100m", "-out-crt", "/do/not/write/pleasecrt", "-out-key", "/do/not/write/pleasekey"}
|
args := []string{"-version", "1", "-name", "test", "-duration", "100m", "-out-crt", "/do/not/write/pleasecrt", "-out-key", "/do/not/write/pleasekey"}
|
||||||
require.EqualError(t, ca(args, ob, eb, nopw), "error while writing out-key: open /do/not/write/pleasekey: "+NoSuchDirError)
|
require.EqualError(t, ca(args, ob, eb, nopw), "error while writing out-key: open /do/not/write/pleasekey: "+NoSuchDirError)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// create temp key file
|
// create temp key file
|
||||||
keyF, err := os.CreateTemp("", "test.key")
|
keyF, err := os.CreateTemp("", "test.key")
|
||||||
@ -121,8 +121,8 @@ func Test_ca(t *testing.T) {
|
|||||||
eb.Reset()
|
eb.Reset()
|
||||||
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-out-crt", "/do/not/write/pleasecrt", "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-out-crt", "/do/not/write/pleasecrt", "-out-key", keyF.Name()}
|
||||||
require.EqualError(t, ca(args, ob, eb, nopw), "error while writing out-crt: open /do/not/write/pleasecrt: "+NoSuchDirError)
|
require.EqualError(t, ca(args, ob, eb, nopw), "error while writing out-crt: open /do/not/write/pleasecrt: "+NoSuchDirError)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// create temp cert file
|
// create temp cert file
|
||||||
crtF, err := os.CreateTemp("", "test.crt")
|
crtF, err := os.CreateTemp("", "test.crt")
|
||||||
@ -135,8 +135,8 @@ func Test_ca(t *testing.T) {
|
|||||||
eb.Reset()
|
eb.Reset()
|
||||||
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-groups", "1,, 2 , ,,,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-groups", "1,, 2 , ,,,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
||||||
require.NoError(t, ca(args, ob, eb, nopw))
|
require.NoError(t, ca(args, ob, eb, nopw))
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// read cert and key files
|
// read cert and key files
|
||||||
rb, _ := os.ReadFile(keyF.Name())
|
rb, _ := os.ReadFile(keyF.Name())
|
||||||
@ -158,7 +158,7 @@ func Test_ca(t *testing.T) {
|
|||||||
assert.Empty(t, lCrt.UnsafeNetworks())
|
assert.Empty(t, lCrt.UnsafeNetworks())
|
||||||
assert.Len(t, lCrt.PublicKey(), 32)
|
assert.Len(t, lCrt.PublicKey(), 32)
|
||||||
assert.Equal(t, time.Duration(time.Minute*100), lCrt.NotAfter().Sub(lCrt.NotBefore()))
|
assert.Equal(t, time.Duration(time.Minute*100), lCrt.NotAfter().Sub(lCrt.NotBefore()))
|
||||||
assert.Equal(t, "", lCrt.Issuer())
|
assert.Empty(t, lCrt.Issuer())
|
||||||
assert.True(t, lCrt.CheckSignature(lCrt.PublicKey()))
|
assert.True(t, lCrt.CheckSignature(lCrt.PublicKey()))
|
||||||
|
|
||||||
// test encrypted key
|
// test encrypted key
|
||||||
@ -169,7 +169,7 @@ func Test_ca(t *testing.T) {
|
|||||||
args = []string{"-version", "1", "-encrypt", "-name", "test", "-duration", "100m", "-groups", "1,2,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-encrypt", "-name", "test", "-duration", "100m", "-groups", "1,2,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
||||||
require.NoError(t, ca(args, ob, eb, testpw))
|
require.NoError(t, ca(args, ob, eb, testpw))
|
||||||
assert.Equal(t, pwPromptOb, ob.String())
|
assert.Equal(t, pwPromptOb, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// read encrypted key file and verify default params
|
// read encrypted key file and verify default params
|
||||||
rb, _ = os.ReadFile(keyF.Name())
|
rb, _ = os.ReadFile(keyF.Name())
|
||||||
@ -197,7 +197,7 @@ func Test_ca(t *testing.T) {
|
|||||||
args = []string{"-version", "1", "-encrypt", "-name", "test", "-duration", "100m", "-groups", "1,2,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-encrypt", "-name", "test", "-duration", "100m", "-groups", "1,2,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
||||||
require.Error(t, ca(args, ob, eb, errpw))
|
require.Error(t, ca(args, ob, eb, errpw))
|
||||||
assert.Equal(t, pwPromptOb, ob.String())
|
assert.Equal(t, pwPromptOb, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// test when user fails to enter a password
|
// test when user fails to enter a password
|
||||||
os.Remove(keyF.Name())
|
os.Remove(keyF.Name())
|
||||||
@ -207,7 +207,7 @@ func Test_ca(t *testing.T) {
|
|||||||
args = []string{"-version", "1", "-encrypt", "-name", "test", "-duration", "100m", "-groups", "1,2,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-encrypt", "-name", "test", "-duration", "100m", "-groups", "1,2,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
||||||
require.EqualError(t, ca(args, ob, eb, nopw), "no passphrase specified, remove -encrypt flag to write out-key in plaintext")
|
require.EqualError(t, ca(args, ob, eb, nopw), "no passphrase specified, remove -encrypt flag to write out-key in plaintext")
|
||||||
assert.Equal(t, strings.Repeat(pwPromptOb, 5), ob.String()) // prompts 5 times before giving up
|
assert.Equal(t, strings.Repeat(pwPromptOb, 5), ob.String()) // prompts 5 times before giving up
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// create valid cert/key for overwrite tests
|
// create valid cert/key for overwrite tests
|
||||||
os.Remove(keyF.Name())
|
os.Remove(keyF.Name())
|
||||||
@ -222,8 +222,8 @@ func Test_ca(t *testing.T) {
|
|||||||
eb.Reset()
|
eb.Reset()
|
||||||
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-groups", "1,, 2 , ,,,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-groups", "1,, 2 , ,,,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
||||||
require.EqualError(t, ca(args, ob, eb, nopw), "refusing to overwrite existing CA key: "+keyF.Name())
|
require.EqualError(t, ca(args, ob, eb, nopw), "refusing to overwrite existing CA key: "+keyF.Name())
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// test that we won't overwrite existing key file
|
// test that we won't overwrite existing key file
|
||||||
os.Remove(keyF.Name())
|
os.Remove(keyF.Name())
|
||||||
@ -231,8 +231,8 @@ func Test_ca(t *testing.T) {
|
|||||||
eb.Reset()
|
eb.Reset()
|
||||||
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-groups", "1,, 2 , ,,,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-version", "1", "-name", "test", "-duration", "100m", "-groups", "1,, 2 , ,,,3,4,5", "-out-crt", crtF.Name(), "-out-key", keyF.Name()}
|
||||||
require.EqualError(t, ca(args, ob, eb, nopw), "refusing to overwrite existing CA cert: "+crtF.Name())
|
require.EqualError(t, ca(args, ob, eb, nopw), "refusing to overwrite existing CA cert: "+crtF.Name())
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
os.Remove(keyF.Name())
|
os.Remove(keyF.Name())
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,20 +37,20 @@ func Test_keygen(t *testing.T) {
|
|||||||
|
|
||||||
// required args
|
// required args
|
||||||
assertHelpError(t, keygen([]string{"-out-pub", "nope"}, ob, eb), "-out-key is required")
|
assertHelpError(t, keygen([]string{"-out-pub", "nope"}, ob, eb), "-out-key is required")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
assertHelpError(t, keygen([]string{"-out-key", "nope"}, ob, eb), "-out-pub is required")
|
assertHelpError(t, keygen([]string{"-out-key", "nope"}, ob, eb), "-out-pub is required")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// failed key write
|
// failed key write
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
eb.Reset()
|
eb.Reset()
|
||||||
args := []string{"-out-pub", "/do/not/write/pleasepub", "-out-key", "/do/not/write/pleasekey"}
|
args := []string{"-out-pub", "/do/not/write/pleasepub", "-out-key", "/do/not/write/pleasekey"}
|
||||||
require.EqualError(t, keygen(args, ob, eb), "error while writing out-key: open /do/not/write/pleasekey: "+NoSuchDirError)
|
require.EqualError(t, keygen(args, ob, eb), "error while writing out-key: open /do/not/write/pleasekey: "+NoSuchDirError)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// create temp key file
|
// create temp key file
|
||||||
keyF, err := os.CreateTemp("", "test.key")
|
keyF, err := os.CreateTemp("", "test.key")
|
||||||
@ -62,8 +62,8 @@ func Test_keygen(t *testing.T) {
|
|||||||
eb.Reset()
|
eb.Reset()
|
||||||
args = []string{"-out-pub", "/do/not/write/pleasepub", "-out-key", keyF.Name()}
|
args = []string{"-out-pub", "/do/not/write/pleasepub", "-out-key", keyF.Name()}
|
||||||
require.EqualError(t, keygen(args, ob, eb), "error while writing out-pub: open /do/not/write/pleasepub: "+NoSuchDirError)
|
require.EqualError(t, keygen(args, ob, eb), "error while writing out-pub: open /do/not/write/pleasepub: "+NoSuchDirError)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// create temp pub file
|
// create temp pub file
|
||||||
pubF, err := os.CreateTemp("", "test.pub")
|
pubF, err := os.CreateTemp("", "test.pub")
|
||||||
@ -75,8 +75,8 @@ func Test_keygen(t *testing.T) {
|
|||||||
eb.Reset()
|
eb.Reset()
|
||||||
args = []string{"-out-pub", pubF.Name(), "-out-key", keyF.Name()}
|
args = []string{"-out-pub", pubF.Name(), "-out-key", keyF.Name()}
|
||||||
require.NoError(t, keygen(args, ob, eb))
|
require.NoError(t, keygen(args, ob, eb))
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// read cert and key files
|
// read cert and key files
|
||||||
rb, _ := os.ReadFile(keyF.Name())
|
rb, _ := os.ReadFile(keyF.Name())
|
||||||
|
|||||||
@ -17,7 +17,7 @@ func (he *helpError) Error() string {
|
|||||||
return he.s
|
return he.s
|
||||||
}
|
}
|
||||||
|
|
||||||
func newHelpErrorf(s string, v ...interface{}) error {
|
func newHelpErrorf(s string, v ...any) error {
|
||||||
return &helpError{s: fmt.Sprintf(s, v...)}
|
return &helpError{s: fmt.Sprintf(s, v...)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -43,16 +43,16 @@ func Test_printCert(t *testing.T) {
|
|||||||
|
|
||||||
// no path
|
// no path
|
||||||
err := printCert([]string{}, ob, eb)
|
err := printCert([]string{}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
assertHelpError(t, err, "-path is required")
|
assertHelpError(t, err, "-path is required")
|
||||||
|
|
||||||
// no cert at path
|
// no cert at path
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
eb.Reset()
|
eb.Reset()
|
||||||
err = printCert([]string{"-path", "does_not_exist"}, ob, eb)
|
err = printCert([]string{"-path", "does_not_exist"}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.EqualError(t, err, "unable to read cert; open does_not_exist: "+NoSuchFileError)
|
require.EqualError(t, err, "unable to read cert; open does_not_exist: "+NoSuchFileError)
|
||||||
|
|
||||||
// invalid cert at path
|
// invalid cert at path
|
||||||
@ -64,8 +64,8 @@ func Test_printCert(t *testing.T) {
|
|||||||
|
|
||||||
tf.WriteString("-----BEGIN NOPE-----")
|
tf.WriteString("-----BEGIN NOPE-----")
|
||||||
err = printCert([]string{"-path", tf.Name()}, ob, eb)
|
err = printCert([]string{"-path", tf.Name()}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.EqualError(t, err, "error while unmarshaling cert: input did not contain a valid PEM encoded block")
|
require.EqualError(t, err, "error while unmarshaling cert: input did not contain a valid PEM encoded block")
|
||||||
|
|
||||||
// test multiple certs
|
// test multiple certs
|
||||||
@ -155,7 +155,7 @@ func Test_printCert(t *testing.T) {
|
|||||||
`,
|
`,
|
||||||
ob.String(),
|
ob.String(),
|
||||||
)
|
)
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// test json
|
// test json
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
@ -177,7 +177,7 @@ func Test_printCert(t *testing.T) {
|
|||||||
`,
|
`,
|
||||||
ob.String(),
|
ob.String(),
|
||||||
)
|
)
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewTestCaCert will generate a CA cert
|
// NewTestCaCert will generate a CA cert
|
||||||
|
|||||||
@ -38,19 +38,19 @@ func Test_verify(t *testing.T) {
|
|||||||
|
|
||||||
// required args
|
// required args
|
||||||
assertHelpError(t, verify([]string{"-ca", "derp"}, ob, eb), "-crt is required")
|
assertHelpError(t, verify([]string{"-ca", "derp"}, ob, eb), "-crt is required")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
assertHelpError(t, verify([]string{"-crt", "derp"}, ob, eb), "-ca is required")
|
assertHelpError(t, verify([]string{"-crt", "derp"}, ob, eb), "-ca is required")
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
|
|
||||||
// no ca at path
|
// no ca at path
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
eb.Reset()
|
eb.Reset()
|
||||||
err := verify([]string{"-ca", "does_not_exist", "-crt", "does_not_exist"}, ob, eb)
|
err := verify([]string{"-ca", "does_not_exist", "-crt", "does_not_exist"}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.EqualError(t, err, "error while reading ca: open does_not_exist: "+NoSuchFileError)
|
require.EqualError(t, err, "error while reading ca: open does_not_exist: "+NoSuchFileError)
|
||||||
|
|
||||||
// invalid ca at path
|
// invalid ca at path
|
||||||
@ -62,8 +62,8 @@ func Test_verify(t *testing.T) {
|
|||||||
|
|
||||||
caFile.WriteString("-----BEGIN NOPE-----")
|
caFile.WriteString("-----BEGIN NOPE-----")
|
||||||
err = verify([]string{"-ca", caFile.Name(), "-crt", "does_not_exist"}, ob, eb)
|
err = verify([]string{"-ca", caFile.Name(), "-crt", "does_not_exist"}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.EqualError(t, err, "error while adding ca cert to pool: input did not contain a valid PEM encoded block")
|
require.EqualError(t, err, "error while adding ca cert to pool: input did not contain a valid PEM encoded block")
|
||||||
|
|
||||||
// make a ca for later
|
// make a ca for later
|
||||||
@ -76,8 +76,8 @@ func Test_verify(t *testing.T) {
|
|||||||
|
|
||||||
// no crt at path
|
// no crt at path
|
||||||
err = verify([]string{"-ca", caFile.Name(), "-crt", "does_not_exist"}, ob, eb)
|
err = verify([]string{"-ca", caFile.Name(), "-crt", "does_not_exist"}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.EqualError(t, err, "unable to read crt: open does_not_exist: "+NoSuchFileError)
|
require.EqualError(t, err, "unable to read crt: open does_not_exist: "+NoSuchFileError)
|
||||||
|
|
||||||
// invalid crt at path
|
// invalid crt at path
|
||||||
@ -89,8 +89,8 @@ func Test_verify(t *testing.T) {
|
|||||||
|
|
||||||
certFile.WriteString("-----BEGIN NOPE-----")
|
certFile.WriteString("-----BEGIN NOPE-----")
|
||||||
err = verify([]string{"-ca", caFile.Name(), "-crt", certFile.Name()}, ob, eb)
|
err = verify([]string{"-ca", caFile.Name(), "-crt", certFile.Name()}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.EqualError(t, err, "error while parsing crt: input did not contain a valid PEM encoded block")
|
require.EqualError(t, err, "error while parsing crt: input did not contain a valid PEM encoded block")
|
||||||
|
|
||||||
// unverifiable cert at path
|
// unverifiable cert at path
|
||||||
@ -106,8 +106,8 @@ func Test_verify(t *testing.T) {
|
|||||||
certFile.Write(b)
|
certFile.Write(b)
|
||||||
|
|
||||||
err = verify([]string{"-ca", caFile.Name(), "-crt", certFile.Name()}, ob, eb)
|
err = verify([]string{"-ca", caFile.Name(), "-crt", certFile.Name()}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.ErrorIs(t, err, cert.ErrSignatureMismatch)
|
require.ErrorIs(t, err, cert.ErrSignatureMismatch)
|
||||||
|
|
||||||
// verified cert at path
|
// verified cert at path
|
||||||
@ -118,7 +118,7 @@ func Test_verify(t *testing.T) {
|
|||||||
certFile.Write(b)
|
certFile.Write(b)
|
||||||
|
|
||||||
err = verify([]string{"-ca", caFile.Name(), "-crt", certFile.Name()}, ob, eb)
|
err = verify([]string{"-ca", caFile.Name(), "-crt", certFile.Name()}, ob, eb)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
assert.Equal(t, "", eb.String())
|
assert.Empty(t, eb.String())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,14 +17,14 @@ import (
|
|||||||
|
|
||||||
"dario.cat/mergo"
|
"dario.cat/mergo"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type C struct {
|
type C struct {
|
||||||
path string
|
path string
|
||||||
files []string
|
files []string
|
||||||
Settings map[interface{}]interface{}
|
Settings map[string]any
|
||||||
oldSettings map[interface{}]interface{}
|
oldSettings map[string]any
|
||||||
callbacks []func(*C)
|
callbacks []func(*C)
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
reloadLock sync.Mutex
|
reloadLock sync.Mutex
|
||||||
@ -32,7 +32,7 @@ type C struct {
|
|||||||
|
|
||||||
func NewC(l *logrus.Logger) *C {
|
func NewC(l *logrus.Logger) *C {
|
||||||
return &C{
|
return &C{
|
||||||
Settings: make(map[interface{}]interface{}),
|
Settings: make(map[string]any),
|
||||||
l: l,
|
l: l,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,8 +92,8 @@ func (c *C) HasChanged(k string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
nv interface{}
|
nv any
|
||||||
ov interface{}
|
ov any
|
||||||
)
|
)
|
||||||
|
|
||||||
if k == "" {
|
if k == "" {
|
||||||
@ -147,7 +147,7 @@ func (c *C) ReloadConfig() {
|
|||||||
c.reloadLock.Lock()
|
c.reloadLock.Lock()
|
||||||
defer c.reloadLock.Unlock()
|
defer c.reloadLock.Unlock()
|
||||||
|
|
||||||
c.oldSettings = make(map[interface{}]interface{})
|
c.oldSettings = make(map[string]any)
|
||||||
for k, v := range c.Settings {
|
for k, v := range c.Settings {
|
||||||
c.oldSettings[k] = v
|
c.oldSettings[k] = v
|
||||||
}
|
}
|
||||||
@ -167,7 +167,7 @@ func (c *C) ReloadConfigString(raw string) error {
|
|||||||
c.reloadLock.Lock()
|
c.reloadLock.Lock()
|
||||||
defer c.reloadLock.Unlock()
|
defer c.reloadLock.Unlock()
|
||||||
|
|
||||||
c.oldSettings = make(map[interface{}]interface{})
|
c.oldSettings = make(map[string]any)
|
||||||
for k, v := range c.Settings {
|
for k, v := range c.Settings {
|
||||||
c.oldSettings[k] = v
|
c.oldSettings[k] = v
|
||||||
}
|
}
|
||||||
@ -201,7 +201,7 @@ func (c *C) GetStringSlice(k string, d []string) []string {
|
|||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
rv, ok := r.([]interface{})
|
rv, ok := r.([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
@ -215,13 +215,13 @@ func (c *C) GetStringSlice(k string, d []string) []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetMap will get the map for k or return the default d if not found or invalid
|
// GetMap will get the map for k or return the default d if not found or invalid
|
||||||
func (c *C) GetMap(k string, d map[interface{}]interface{}) map[interface{}]interface{} {
|
func (c *C) GetMap(k string, d map[string]any) map[string]any {
|
||||||
r := c.Get(k)
|
r := c.Get(k)
|
||||||
if r == nil {
|
if r == nil {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
v, ok := r.(map[interface{}]interface{})
|
v, ok := r.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return d
|
return d
|
||||||
}
|
}
|
||||||
@ -266,6 +266,22 @@ func (c *C) GetBool(k string, d bool) bool {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AsBool(v any) (value bool, ok bool) {
|
||||||
|
switch x := v.(type) {
|
||||||
|
case bool:
|
||||||
|
return x, true
|
||||||
|
case string:
|
||||||
|
switch x {
|
||||||
|
case "y", "yes":
|
||||||
|
return true, true
|
||||||
|
case "n", "no":
|
||||||
|
return false, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, false
|
||||||
|
}
|
||||||
|
|
||||||
// GetDuration will get the duration for k or return the default d if not found or invalid
|
// GetDuration will get the duration for k or return the default d if not found or invalid
|
||||||
func (c *C) GetDuration(k string, d time.Duration) time.Duration {
|
func (c *C) GetDuration(k string, d time.Duration) time.Duration {
|
||||||
r := c.GetString(k, "")
|
r := c.GetString(k, "")
|
||||||
@ -276,7 +292,7 @@ func (c *C) GetDuration(k string, d time.Duration) time.Duration {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *C) Get(k string) interface{} {
|
func (c *C) Get(k string) any {
|
||||||
return c.get(k, c.Settings)
|
return c.get(k, c.Settings)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -284,10 +300,10 @@ func (c *C) IsSet(k string) bool {
|
|||||||
return c.get(k, c.Settings) != nil
|
return c.get(k, c.Settings) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *C) get(k string, v interface{}) interface{} {
|
func (c *C) get(k string, v any) any {
|
||||||
parts := strings.Split(k, ".")
|
parts := strings.Split(k, ".")
|
||||||
for _, p := range parts {
|
for _, p := range parts {
|
||||||
m, ok := v.(map[interface{}]interface{})
|
m, ok := v.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -346,7 +362,7 @@ func (c *C) addFile(path string, direct bool) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *C) parseRaw(b []byte) error {
|
func (c *C) parseRaw(b []byte) error {
|
||||||
var m map[interface{}]interface{}
|
var m map[string]any
|
||||||
|
|
||||||
err := yaml.Unmarshal(b, &m)
|
err := yaml.Unmarshal(b, &m)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -358,7 +374,7 @@ func (c *C) parseRaw(b []byte) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *C) parse() error {
|
func (c *C) parse() error {
|
||||||
var m map[interface{}]interface{}
|
var m map[string]any
|
||||||
|
|
||||||
for _, path := range c.files {
|
for _, path := range c.files {
|
||||||
b, err := os.ReadFile(path)
|
b, err := os.ReadFile(path)
|
||||||
@ -366,7 +382,7 @@ func (c *C) parse() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var nm map[interface{}]interface{}
|
var nm map[string]any
|
||||||
err = yaml.Unmarshal(b, &nm)
|
err = yaml.Unmarshal(b, &nm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|||||||
@ -10,7 +10,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/test"
|
"github.com/slackhq/nebula/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestConfig_Load(t *testing.T) {
|
func TestConfig_Load(t *testing.T) {
|
||||||
@ -19,7 +19,7 @@ func TestConfig_Load(t *testing.T) {
|
|||||||
// invalid yaml
|
// invalid yaml
|
||||||
c := NewC(l)
|
c := NewC(l)
|
||||||
os.WriteFile(filepath.Join(dir, "01.yaml"), []byte(" invalid yaml"), 0644)
|
os.WriteFile(filepath.Join(dir, "01.yaml"), []byte(" invalid yaml"), 0644)
|
||||||
require.EqualError(t, c.Load(dir), "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `invalid...` into map[interface {}]interface {}")
|
require.EqualError(t, c.Load(dir), "yaml: unmarshal errors:\n line 1: cannot unmarshal !!str `invalid...` into map[string]interface {}")
|
||||||
|
|
||||||
// simple multi config merge
|
// simple multi config merge
|
||||||
c = NewC(l)
|
c = NewC(l)
|
||||||
@ -31,8 +31,8 @@ func TestConfig_Load(t *testing.T) {
|
|||||||
os.WriteFile(filepath.Join(dir, "01.yaml"), []byte("outer:\n inner: hi"), 0644)
|
os.WriteFile(filepath.Join(dir, "01.yaml"), []byte("outer:\n inner: hi"), 0644)
|
||||||
os.WriteFile(filepath.Join(dir, "02.yml"), []byte("outer:\n inner: override\nnew: hi"), 0644)
|
os.WriteFile(filepath.Join(dir, "02.yml"), []byte("outer:\n inner: override\nnew: hi"), 0644)
|
||||||
require.NoError(t, c.Load(dir))
|
require.NoError(t, c.Load(dir))
|
||||||
expected := map[interface{}]interface{}{
|
expected := map[string]any{
|
||||||
"outer": map[interface{}]interface{}{
|
"outer": map[string]any{
|
||||||
"inner": "override",
|
"inner": "override",
|
||||||
},
|
},
|
||||||
"new": "hi",
|
"new": "hi",
|
||||||
@ -44,12 +44,12 @@ func TestConfig_Get(t *testing.T) {
|
|||||||
l := test.NewLogger()
|
l := test.NewLogger()
|
||||||
// test simple type
|
// test simple type
|
||||||
c := NewC(l)
|
c := NewC(l)
|
||||||
c.Settings["firewall"] = map[interface{}]interface{}{"outbound": "hi"}
|
c.Settings["firewall"] = map[string]any{"outbound": "hi"}
|
||||||
assert.Equal(t, "hi", c.Get("firewall.outbound"))
|
assert.Equal(t, "hi", c.Get("firewall.outbound"))
|
||||||
|
|
||||||
// test complex type
|
// test complex type
|
||||||
inner := []map[interface{}]interface{}{{"port": "1", "code": "2"}}
|
inner := []map[string]any{{"port": "1", "code": "2"}}
|
||||||
c.Settings["firewall"] = map[interface{}]interface{}{"outbound": inner}
|
c.Settings["firewall"] = map[string]any{"outbound": inner}
|
||||||
assert.EqualValues(t, inner, c.Get("firewall.outbound"))
|
assert.EqualValues(t, inner, c.Get("firewall.outbound"))
|
||||||
|
|
||||||
// test missing
|
// test missing
|
||||||
@ -59,7 +59,7 @@ func TestConfig_Get(t *testing.T) {
|
|||||||
func TestConfig_GetStringSlice(t *testing.T) {
|
func TestConfig_GetStringSlice(t *testing.T) {
|
||||||
l := test.NewLogger()
|
l := test.NewLogger()
|
||||||
c := NewC(l)
|
c := NewC(l)
|
||||||
c.Settings["slice"] = []interface{}{"one", "two"}
|
c.Settings["slice"] = []any{"one", "two"}
|
||||||
assert.Equal(t, []string{"one", "two"}, c.GetStringSlice("slice", []string{}))
|
assert.Equal(t, []string{"one", "two"}, c.GetStringSlice("slice", []string{}))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -101,14 +101,14 @@ func TestConfig_HasChanged(t *testing.T) {
|
|||||||
// Test key change
|
// Test key change
|
||||||
c = NewC(l)
|
c = NewC(l)
|
||||||
c.Settings["test"] = "hi"
|
c.Settings["test"] = "hi"
|
||||||
c.oldSettings = map[interface{}]interface{}{"test": "no"}
|
c.oldSettings = map[string]any{"test": "no"}
|
||||||
assert.True(t, c.HasChanged("test"))
|
assert.True(t, c.HasChanged("test"))
|
||||||
assert.True(t, c.HasChanged(""))
|
assert.True(t, c.HasChanged(""))
|
||||||
|
|
||||||
// No key change
|
// No key change
|
||||||
c = NewC(l)
|
c = NewC(l)
|
||||||
c.Settings["test"] = "hi"
|
c.Settings["test"] = "hi"
|
||||||
c.oldSettings = map[interface{}]interface{}{"test": "hi"}
|
c.oldSettings = map[string]any{"test": "hi"}
|
||||||
assert.False(t, c.HasChanged("test"))
|
assert.False(t, c.HasChanged("test"))
|
||||||
assert.False(t, c.HasChanged(""))
|
assert.False(t, c.HasChanged(""))
|
||||||
}
|
}
|
||||||
@ -184,11 +184,11 @@ firewall:
|
|||||||
`),
|
`),
|
||||||
}
|
}
|
||||||
|
|
||||||
var m map[any]any
|
var m map[string]any
|
||||||
|
|
||||||
// merge the same way config.parse() merges
|
// merge the same way config.parse() merges
|
||||||
for _, b := range configs {
|
for _, b := range configs {
|
||||||
var nm map[any]any
|
var nm map[string]any
|
||||||
err := yaml.Unmarshal(b, &nm)
|
err := yaml.Unmarshal(b, &nm)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
@ -205,15 +205,15 @@ firewall:
|
|||||||
t.Logf("Merged Config as YAML:\n%s", mYaml)
|
t.Logf("Merged Config as YAML:\n%s", mYaml)
|
||||||
|
|
||||||
// If a bug is present, some items might be replaced instead of merged like we expect
|
// If a bug is present, some items might be replaced instead of merged like we expect
|
||||||
expected := map[any]any{
|
expected := map[string]any{
|
||||||
"firewall": map[any]any{
|
"firewall": map[string]any{
|
||||||
"inbound": []any{
|
"inbound": []any{
|
||||||
map[any]any{"host": "any", "port": "any", "proto": "icmp"},
|
map[string]any{"host": "any", "port": "any", "proto": "icmp"},
|
||||||
map[any]any{"groups": []any{"server"}, "port": 443, "proto": "tcp"},
|
map[string]any{"groups": []any{"server"}, "port": 443, "proto": "tcp"},
|
||||||
map[any]any{"groups": []any{"webapp"}, "port": 443, "proto": "tcp"}},
|
map[string]any{"groups": []any{"webapp"}, "port": 443, "proto": "tcp"}},
|
||||||
"outbound": []any{
|
"outbound": []any{
|
||||||
map[any]any{"host": "any", "port": "any", "proto": "any"}}},
|
map[string]any{"host": "any", "port": "any", "proto": "any"}}},
|
||||||
"listen": map[any]any{
|
"listen": map[string]any{
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": 4242,
|
"port": 4242,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -101,7 +101,7 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) {
|
|||||||
|
|
||||||
// Make sure we don't have any unexpected fields
|
// Make sure we don't have any unexpected fields
|
||||||
assertFields(t, []string{"VpnAddrs", "LocalIndex", "RemoteIndex", "RemoteAddrs", "Cert", "MessageCounter", "CurrentRemote", "CurrentRelaysToMe", "CurrentRelaysThroughMe"}, thi)
|
assertFields(t, []string{"VpnAddrs", "LocalIndex", "RemoteIndex", "RemoteAddrs", "Cert", "MessageCounter", "CurrentRemote", "CurrentRelaysToMe", "CurrentRelaysThroughMe"}, thi)
|
||||||
assert.EqualValues(t, &expectedInfo, thi)
|
assert.Equal(t, &expectedInfo, thi)
|
||||||
test.AssertDeepCopyEqual(t, &expectedInfo, thi)
|
test.AssertDeepCopyEqual(t, &expectedInfo, thi)
|
||||||
|
|
||||||
// Make sure we don't panic if the host info doesn't have a cert yet
|
// Make sure we don't panic if the host info doesn't have a cert yet
|
||||||
@ -110,7 +110,7 @@ func TestControl_GetHostInfoByVpnIp(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func assertFields(t *testing.T, expected []string, actualStruct interface{}) {
|
func assertFields(t *testing.T, expected []string, actualStruct any) {
|
||||||
val := reflect.ValueOf(actualStruct).Elem()
|
val := reflect.ValueOf(actualStruct).Elem()
|
||||||
fields := make([]string, val.NumField())
|
fields := make([]string, val.NumField())
|
||||||
for i := 0; i < val.NumField(); i++ {
|
for i := 0; i < val.NumField(); i++ {
|
||||||
|
|||||||
@ -38,24 +38,24 @@ func TestParsequery(t *testing.T) {
|
|||||||
func Test_getDnsServerAddr(t *testing.T) {
|
func Test_getDnsServerAddr(t *testing.T) {
|
||||||
c := config.NewC(nil)
|
c := config.NewC(nil)
|
||||||
|
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{
|
c.Settings["lighthouse"] = map[string]any{
|
||||||
"dns": map[interface{}]interface{}{
|
"dns": map[string]any{
|
||||||
"host": "0.0.0.0",
|
"host": "0.0.0.0",
|
||||||
"port": "1",
|
"port": "1",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.Equal(t, "0.0.0.0:1", getDnsServerAddr(c))
|
assert.Equal(t, "0.0.0.0:1", getDnsServerAddr(c))
|
||||||
|
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{
|
c.Settings["lighthouse"] = map[string]any{
|
||||||
"dns": map[interface{}]interface{}{
|
"dns": map[string]any{
|
||||||
"host": "::",
|
"host": "::",
|
||||||
"port": "1",
|
"port": "1",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
assert.Equal(t, "[::]:1", getDnsServerAddr(c))
|
assert.Equal(t, "[::]:1", getDnsServerAddr(c))
|
||||||
|
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{
|
c.Settings["lighthouse"] = map[string]any{
|
||||||
"dns": map[interface{}]interface{}{
|
"dns": map[string]any{
|
||||||
"host": "[::]",
|
"host": "[::]",
|
||||||
"port": "1",
|
"port": "1",
|
||||||
},
|
},
|
||||||
@ -63,8 +63,8 @@ func Test_getDnsServerAddr(t *testing.T) {
|
|||||||
assert.Equal(t, "[::]:1", getDnsServerAddr(c))
|
assert.Equal(t, "[::]:1", getDnsServerAddr(c))
|
||||||
|
|
||||||
// Make sure whitespace doesn't mess us up
|
// Make sure whitespace doesn't mess us up
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{
|
c.Settings["lighthouse"] = map[string]any{
|
||||||
"dns": map[interface{}]interface{}{
|
"dns": map[string]any{
|
||||||
"host": "[::] ",
|
"host": "[::] ",
|
||||||
"port": "1",
|
"port": "1",
|
||||||
},
|
},
|
||||||
|
|||||||
@ -20,7 +20,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/udp"
|
"github.com/slackhq/nebula/udp"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BenchmarkHotPath(b *testing.B) {
|
func BenchmarkHotPath(b *testing.B) {
|
||||||
@ -991,7 +991,7 @@ func TestRehandshaking(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var theirNewConfig m
|
var theirNewConfig m
|
||||||
require.NoError(t, yaml.Unmarshal(rc, &theirNewConfig))
|
require.NoError(t, yaml.Unmarshal(rc, &theirNewConfig))
|
||||||
theirFirewall := theirNewConfig["firewall"].(map[interface{}]interface{})
|
theirFirewall := theirNewConfig["firewall"].(map[string]any)
|
||||||
theirFirewall["inbound"] = []m{{
|
theirFirewall["inbound"] = []m{{
|
||||||
"proto": "any",
|
"proto": "any",
|
||||||
"port": "any",
|
"port": "any",
|
||||||
@ -1087,7 +1087,7 @@ func TestRehandshakingLoser(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
var myNewConfig m
|
var myNewConfig m
|
||||||
require.NoError(t, yaml.Unmarshal(rc, &myNewConfig))
|
require.NoError(t, yaml.Unmarshal(rc, &myNewConfig))
|
||||||
theirFirewall := myNewConfig["firewall"].(map[interface{}]interface{})
|
theirFirewall := myNewConfig["firewall"].(map[string]any)
|
||||||
theirFirewall["inbound"] = []m{{
|
theirFirewall["inbound"] = []m{{
|
||||||
"proto": "any",
|
"proto": "any",
|
||||||
"port": "any",
|
"port": "any",
|
||||||
|
|||||||
@ -22,10 +22,10 @@ import (
|
|||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
"github.com/slackhq/nebula/e2e/router"
|
"github.com/slackhq/nebula/e2e/router"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
// newSimpleServer creates a nebula instance with many assumptions
|
// newSimpleServer creates a nebula instance with many assumptions
|
||||||
func newSimpleServer(v cert.Version, caCrt cert.Certificate, caKey []byte, name string, sVpnNetworks string, overrides m) (*nebula.Control, []netip.Prefix, netip.AddrPort, *config.C) {
|
func newSimpleServer(v cert.Version, caCrt cert.Certificate, caKey []byte, name string, sVpnNetworks string, overrides m) (*nebula.Control, []netip.Prefix, netip.AddrPort, *config.C) {
|
||||||
|
|||||||
@ -239,7 +239,28 @@ tun:
|
|||||||
|
|
||||||
# Unsafe routes allows you to route traffic over nebula to non-nebula nodes
|
# Unsafe routes allows you to route traffic over nebula to non-nebula nodes
|
||||||
# Unsafe routes should be avoided unless you have hosts/services that cannot run nebula
|
# Unsafe routes should be avoided unless you have hosts/services that cannot run nebula
|
||||||
# NOTE: The nebula certificate of the "via" node *MUST* have the "route" defined as a subnet in its certificate
|
# Supports weighted ECMP if you define a list of gateways, this can be used for load balancing or redundancy to hosts outside of nebula
|
||||||
|
# NOTES:
|
||||||
|
# * You will only see a single gateway in the routing table if you are not on linux
|
||||||
|
# * If a gateway is not reachable through the overlay another gateway will be selected to send the traffic through, ignoring weights
|
||||||
|
#
|
||||||
|
# unsafe_routes:
|
||||||
|
# # Multiple gateways without defining a weight defaults to a weight of 1, this will balance traffic equally between the three gateways
|
||||||
|
# - route: 192.168.87.0/24
|
||||||
|
# via:
|
||||||
|
# - gateway: 10.0.0.1
|
||||||
|
# - gateway: 10.0.0.2
|
||||||
|
# - gateway: 10.0.0.3
|
||||||
|
# # Multiple gateways with a weight, this will balance traffic accordingly
|
||||||
|
# - route: 192.168.87.0/24
|
||||||
|
# via:
|
||||||
|
# - gateway: 10.0.0.1
|
||||||
|
# weight: 10
|
||||||
|
# - gateway: 10.0.0.2
|
||||||
|
# weight: 5
|
||||||
|
#
|
||||||
|
# NOTE: The nebula certificate of the "via" node(s) *MUST* have the "route" defined as a subnet in its certificate
|
||||||
|
# `via`: single node or list of gateways to use for this route
|
||||||
# `mtu`: will default to tun mtu if this option is not specified
|
# `mtu`: will default to tun mtu if this option is not specified
|
||||||
# `metric`: will default to 0 if this option is not specified
|
# `metric`: will default to 0 if this option is not specified
|
||||||
# `install`: will default to true, controls whether this route is installed in the systems routing table.
|
# `install`: will default to true, controls whether this route is installed in the systems routing table.
|
||||||
@ -325,11 +346,11 @@ firewall:
|
|||||||
outbound_action: drop
|
outbound_action: drop
|
||||||
inbound_action: drop
|
inbound_action: drop
|
||||||
|
|
||||||
# Controls the default value for local_cidr. Default is true, will be deprecated after v1.9 and defaulted to false.
|
# THIS FLAG IS DEPRECATED AND WILL BE REMOVED IN A FUTURE RELEASE. (Defaults to false.)
|
||||||
# This setting only affects nebula hosts with subnets encoded in their certificate. A nebula host acting as an
|
# This setting only affects nebula hosts exposing unsafe_routes. When set to false, each inbound rule must contain a
|
||||||
# unsafe router with `default_local_cidr_any: true` will expose their unsafe routes to every inbound rule regardless
|
# `local_cidr` if the intention is to allow traffic to flow to an unsafe route. When set to true, every firewall rule
|
||||||
# of the actual destination for the packet. Setting this to false requires each inbound rule to contain a `local_cidr`
|
# will apply to all configured unsafe_routes regardless of the actual destination of the packet, unless `local_cidr`
|
||||||
# if the intention is to allow traffic to flow to an unsafe route.
|
# is explicitly defined. This is usually not the desired behavior and should be avoided!
|
||||||
#default_local_cidr_any: false
|
#default_local_cidr_any: false
|
||||||
|
|
||||||
conntrack:
|
conntrack:
|
||||||
@ -347,11 +368,9 @@ firewall:
|
|||||||
# group: `any` or a literal group name, ie `default-group`
|
# group: `any` or a literal group name, ie `default-group`
|
||||||
# groups: Same as group but accepts a list of values. Multiple values are AND'd together and a certificate would have to contain all groups to pass
|
# groups: Same as group but accepts a list of values. Multiple values are AND'd together and a certificate would have to contain all groups to pass
|
||||||
# cidr: a remote CIDR, `0.0.0.0/0` is any ipv4 and `::/0` is any ipv6.
|
# cidr: a remote CIDR, `0.0.0.0/0` is any ipv4 and `::/0` is any ipv6.
|
||||||
# local_cidr: a local CIDR, `0.0.0.0/0` is any ipv4 and `::/0` is any ipv6. This could be used to filter destinations when using unsafe_routes.
|
# local_cidr: a local CIDR, `0.0.0.0/0` is any ipv4 and `::/0` is any ipv6. This can be used to filter destinations when using unsafe_routes.
|
||||||
# If no unsafe networks are present in the certificate(s) or `default_local_cidr_any` is true then the default is any ipv4 or ipv6 network.
|
# By default, this is set to only the VPN (overlay) networks assigned via the certificate networks field unless `default_local_cidr_any` is set to true.
|
||||||
# Otherwise the default is any vpn network assigned to via the certificate.
|
# If there are unsafe_routes present in this config file, `local_cidr` should be set appropriately for the intended us case.
|
||||||
# `default_local_cidr_any` defaults to false and is deprecated, it will be removed in a future release.
|
|
||||||
# If there are unsafe routes present its best to set `local_cidr` to whatever best fits the situation.
|
|
||||||
# ca_name: An issuing CA name
|
# ca_name: An issuing CA name
|
||||||
# ca_sha: An issuing CA shasum
|
# ca_sha: An issuing CA shasum
|
||||||
|
|
||||||
|
|||||||
10
firewall.go
10
firewall.go
@ -331,7 +331,7 @@ func AddFirewallRulesFromConfig(l *logrus.Logger, inbound bool, c *config.C, fw
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rs, ok := r.([]interface{})
|
rs, ok := r.([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("%s failed to parse, should be an array of rules", table)
|
return fmt.Errorf("%s failed to parse, should be an array of rules", table)
|
||||||
}
|
}
|
||||||
@ -918,15 +918,15 @@ type rule struct {
|
|||||||
CASha string
|
CASha string
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertRule(l *logrus.Logger, p interface{}, table string, i int) (rule, error) {
|
func convertRule(l *logrus.Logger, p any, table string, i int) (rule, error) {
|
||||||
r := rule{}
|
r := rule{}
|
||||||
|
|
||||||
m, ok := p.(map[interface{}]interface{})
|
m, ok := p.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return r, errors.New("could not parse rule")
|
return r, errors.New("could not parse rule")
|
||||||
}
|
}
|
||||||
|
|
||||||
toString := func(k string, m map[interface{}]interface{}) string {
|
toString := func(k string, m map[string]any) string {
|
||||||
v, ok := m[k]
|
v, ok := m[k]
|
||||||
if !ok {
|
if !ok {
|
||||||
return ""
|
return ""
|
||||||
@ -944,7 +944,7 @@ func convertRule(l *logrus.Logger, p interface{}, table string, i int) (rule, er
|
|||||||
r.CASha = toString("ca_sha", m)
|
r.CASha = toString("ca_sha", m)
|
||||||
|
|
||||||
// Make sure group isn't an array
|
// Make sure group isn't an array
|
||||||
if v, ok := m["group"].([]interface{}); ok {
|
if v, ok := m["group"].([]any); ok {
|
||||||
if len(v) > 1 {
|
if len(v) > 1 {
|
||||||
return r, errors.New("group should contain a single value, an array with more than one entry was provided")
|
return r, errors.New("group should contain a single value, an array with more than one entry was provided")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@ import (
|
|||||||
"net/netip"
|
"net/netip"
|
||||||
)
|
)
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
const (
|
const (
|
||||||
ProtoAny = 0 // When we want to handle HOPOPT (0) we can change this, if ever
|
ProtoAny = 0 // When we want to handle HOPOPT (0) we can change this, if ever
|
||||||
|
|||||||
@ -631,53 +631,53 @@ func TestNewFirewallFromConfig(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
conf := config.NewC(l)
|
conf := config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": "asdf"}
|
conf.Settings["firewall"] = map[string]any{"outbound": "asdf"}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound failed to parse, should be an array of rules")
|
require.EqualError(t, err, "firewall.outbound failed to parse, should be an array of rules")
|
||||||
|
|
||||||
// Test both port and code
|
// Test both port and code
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"port": "1", "code": "2"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "code": "2"}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; only one of port or code should be provided")
|
require.EqualError(t, err, "firewall.outbound rule #0; only one of port or code should be provided")
|
||||||
|
|
||||||
// Test missing host, group, cidr, ca_name and ca_sha
|
// Test missing host, group, cidr, ca_name and ca_sha
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided")
|
require.EqualError(t, err, "firewall.outbound rule #0; at least one of host, group, cidr, local_cidr, ca_name, or ca_sha must be provided")
|
||||||
|
|
||||||
// Test code/port error
|
// Test code/port error
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"code": "a", "host": "testh"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "a", "host": "testh"}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; code was not a number; `a`")
|
require.EqualError(t, err, "firewall.outbound rule #0; code was not a number; `a`")
|
||||||
|
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"port": "a", "host": "testh"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "a", "host": "testh"}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; port was not a number; `a`")
|
require.EqualError(t, err, "firewall.outbound rule #0; port was not a number; `a`")
|
||||||
|
|
||||||
// Test proto error
|
// Test proto error
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"code": "1", "host": "testh"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "1", "host": "testh"}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; proto was not understood; ``")
|
require.EqualError(t, err, "firewall.outbound rule #0; proto was not understood; ``")
|
||||||
|
|
||||||
// Test cidr parse error
|
// Test cidr parse error
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"code": "1", "cidr": "testh", "proto": "any"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "1", "cidr": "testh", "proto": "any"}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; cidr did not parse; netip.ParsePrefix(\"testh\"): no '/'")
|
require.EqualError(t, err, "firewall.outbound rule #0; cidr did not parse; netip.ParsePrefix(\"testh\"): no '/'")
|
||||||
|
|
||||||
// Test local_cidr parse error
|
// Test local_cidr parse error
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"code": "1", "local_cidr": "testh", "proto": "any"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"code": "1", "local_cidr": "testh", "proto": "any"}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.outbound rule #0; local_cidr did not parse; netip.ParsePrefix(\"testh\"): no '/'")
|
require.EqualError(t, err, "firewall.outbound rule #0; local_cidr did not parse; netip.ParsePrefix(\"testh\"): no '/'")
|
||||||
|
|
||||||
// Test both group and groups
|
// Test both group and groups
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "group": "a", "groups": []string{"b", "c"}}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "group": "a", "groups": []string{"b", "c"}}}}
|
||||||
_, err = NewFirewallFromConfig(l, cs, conf)
|
_, err = NewFirewallFromConfig(l, cs, conf)
|
||||||
require.EqualError(t, err, "firewall.inbound rule #0; only one of group or groups should be defined, both provided")
|
require.EqualError(t, err, "firewall.inbound rule #0; only one of group or groups should be defined, both provided")
|
||||||
}
|
}
|
||||||
@ -687,28 +687,28 @@ func TestAddFirewallRulesFromConfig(t *testing.T) {
|
|||||||
// Test adding tcp rule
|
// Test adding tcp rule
|
||||||
conf := config.NewC(l)
|
conf := config.NewC(l)
|
||||||
mf := &mockFirewall{}
|
mf := &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "tcp", "host": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "proto": "tcp", "host": "a"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoTCP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoTCP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
// Test adding udp rule
|
// Test adding udp rule
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "udp", "host": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "proto": "udp", "host": "a"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoUDP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoUDP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
// Test adding icmp rule
|
// Test adding icmp rule
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"outbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "icmp", "host": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"outbound": []any{map[string]any{"port": "1", "proto": "icmp", "host": "a"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, false, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoICMP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: false, proto: firewall.ProtoICMP, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
// Test adding any rule
|
// Test adding any rule
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "host": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "host": "a"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, host: "a", ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
@ -716,49 +716,49 @@ func TestAddFirewallRulesFromConfig(t *testing.T) {
|
|||||||
cidr := netip.MustParsePrefix("10.0.0.0/8")
|
cidr := netip.MustParsePrefix("10.0.0.0/8")
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "cidr": cidr.String()}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "cidr": cidr.String()}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: cidr, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: cidr, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
// Test adding rule with local_cidr
|
// Test adding rule with local_cidr
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "local_cidr": cidr.String()}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "local_cidr": cidr.String()}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: netip.Prefix{}, localIp: cidr}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: netip.Prefix{}, localIp: cidr}, mf.lastCall)
|
||||||
|
|
||||||
// Test adding rule with ca_sha
|
// Test adding rule with ca_sha
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "ca_sha": "12312313123"}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "ca_sha": "12312313123"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: netip.Prefix{}, localIp: netip.Prefix{}, caSha: "12312313123"}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: netip.Prefix{}, localIp: netip.Prefix{}, caSha: "12312313123"}, mf.lastCall)
|
||||||
|
|
||||||
// Test adding rule with ca_name
|
// Test adding rule with ca_name
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "ca_name": "root01"}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "ca_name": "root01"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: netip.Prefix{}, localIp: netip.Prefix{}, caName: "root01"}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: nil, ip: netip.Prefix{}, localIp: netip.Prefix{}, caName: "root01"}, mf.lastCall)
|
||||||
|
|
||||||
// Test single group
|
// Test single group
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "group": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "group": "a"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a"}, ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a"}, ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
// Test single groups
|
// Test single groups
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "groups": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "groups": "a"}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a"}, ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a"}, ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
// Test multiple AND groups
|
// Test multiple AND groups
|
||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "groups": []string{"a", "b"}}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "groups": []string{"a", "b"}}}}
|
||||||
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
require.NoError(t, AddFirewallRulesFromConfig(l, true, conf, mf))
|
||||||
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a", "b"}, ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
assert.Equal(t, addRuleCall{incoming: true, proto: firewall.ProtoAny, startPort: 1, endPort: 1, groups: []string{"a", "b"}, ip: netip.Prefix{}, localIp: netip.Prefix{}}, mf.lastCall)
|
||||||
|
|
||||||
@ -766,7 +766,7 @@ func TestAddFirewallRulesFromConfig(t *testing.T) {
|
|||||||
conf = config.NewC(l)
|
conf = config.NewC(l)
|
||||||
mf = &mockFirewall{}
|
mf = &mockFirewall{}
|
||||||
mf.nextCallReturn = errors.New("test error")
|
mf.nextCallReturn = errors.New("test error")
|
||||||
conf.Settings["firewall"] = map[interface{}]interface{}{"inbound": []interface{}{map[interface{}]interface{}{"port": "1", "proto": "any", "host": "a"}}}
|
conf.Settings["firewall"] = map[string]any{"inbound": []any{map[string]any{"port": "1", "proto": "any", "host": "a"}}}
|
||||||
require.EqualError(t, AddFirewallRulesFromConfig(l, true, conf, mf), "firewall.inbound rule #0; `test error`")
|
require.EqualError(t, AddFirewallRulesFromConfig(l, true, conf, mf), "firewall.inbound rule #0; `test error`")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -776,8 +776,8 @@ func TestFirewall_convertRule(t *testing.T) {
|
|||||||
l.SetOutput(ob)
|
l.SetOutput(ob)
|
||||||
|
|
||||||
// Ensure group array of 1 is converted and a warning is printed
|
// Ensure group array of 1 is converted and a warning is printed
|
||||||
c := map[interface{}]interface{}{
|
c := map[string]any{
|
||||||
"group": []interface{}{"group1"},
|
"group": []any{"group1"},
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := convertRule(l, c, "test", 1)
|
r, err := convertRule(l, c, "test", 1)
|
||||||
@ -787,17 +787,17 @@ func TestFirewall_convertRule(t *testing.T) {
|
|||||||
|
|
||||||
// Ensure group array of > 1 is errord
|
// Ensure group array of > 1 is errord
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
c = map[interface{}]interface{}{
|
c = map[string]any{
|
||||||
"group": []interface{}{"group1", "group2"},
|
"group": []any{"group1", "group2"},
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err = convertRule(l, c, "test", 1)
|
r, err = convertRule(l, c, "test", 1)
|
||||||
assert.Equal(t, "", ob.String())
|
assert.Empty(t, ob.String())
|
||||||
require.Error(t, err, "group should contain a single value, an array with more than one entry was provided")
|
require.Error(t, err, "group should contain a single value, an array with more than one entry was provided")
|
||||||
|
|
||||||
// Make sure a well formed group is alright
|
// Make sure a well formed group is alright
|
||||||
ob.Reset()
|
ob.Reset()
|
||||||
c = map[interface{}]interface{}{
|
c = map[string]any{
|
||||||
"group": "group1",
|
"group": "group1",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
17
go.mod
17
go.mod
@ -2,7 +2,7 @@ module github.com/slackhq/nebula
|
|||||||
|
|
||||||
go 1.23.6
|
go 1.23.6
|
||||||
|
|
||||||
toolchain go1.23.7
|
toolchain go1.24.1
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.1
|
dario.cat/mergo v1.0.1
|
||||||
@ -10,11 +10,11 @@ require (
|
|||||||
github.com/armon/go-radix v1.0.0
|
github.com/armon/go-radix v1.0.0
|
||||||
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432
|
github.com/cyberdelia/go-metrics-graphite v0.0.0-20161219230853-39f87cc3b432
|
||||||
github.com/flynn/noise v1.1.0
|
github.com/flynn/noise v1.1.0
|
||||||
github.com/gaissmai/bart v0.18.1
|
github.com/gaissmai/bart v0.20.1
|
||||||
github.com/gogo/protobuf v1.3.2
|
github.com/gogo/protobuf v1.3.2
|
||||||
github.com/google/gopacket v1.1.19
|
github.com/google/gopacket v1.1.19
|
||||||
github.com/kardianos/service v1.2.2
|
github.com/kardianos/service v1.2.2
|
||||||
github.com/miekg/dns v1.1.63
|
github.com/miekg/dns v1.1.64
|
||||||
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b
|
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b
|
||||||
github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f
|
github.com/nbrownus/go-metrics-prometheus v0.0.0-20210712211119-974a6260965f
|
||||||
github.com/prometheus/client_golang v1.21.1
|
github.com/prometheus/client_golang v1.21.1
|
||||||
@ -26,15 +26,15 @@ require (
|
|||||||
github.com/vishvananda/netlink v1.3.0
|
github.com/vishvananda/netlink v1.3.0
|
||||||
golang.org/x/crypto v0.36.0
|
golang.org/x/crypto v0.36.0
|
||||||
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
|
golang.org/x/exp v0.0.0-20230725093048-515e97ebf090
|
||||||
golang.org/x/net v0.37.0
|
golang.org/x/net v0.38.0
|
||||||
golang.org/x/sync v0.12.0
|
golang.org/x/sync v0.12.0
|
||||||
golang.org/x/sys v0.31.0
|
golang.org/x/sys v0.31.0
|
||||||
golang.org/x/term v0.30.0
|
golang.org/x/term v0.30.0
|
||||||
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
golang.zx2c4.com/wintun v0.0.0-20230126152724-0fa3db229ce2
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
|
golang.zx2c4.com/wireguard v0.0.0-20230325221338-052af4a8072b
|
||||||
golang.zx2c4.com/wireguard/windows v0.5.3
|
golang.zx2c4.com/wireguard/windows v0.5.3
|
||||||
google.golang.org/protobuf v1.36.5
|
google.golang.org/protobuf v1.36.6
|
||||||
gopkg.in/yaml.v2 v2.4.0
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
gvisor.dev/gvisor v0.0.0-20240423190808-9d7a357edefe
|
gvisor.dev/gvisor v0.0.0-20240423190808-9d7a357edefe
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,8 +50,7 @@ require (
|
|||||||
github.com/prometheus/common v0.62.0 // indirect
|
github.com/prometheus/common v0.62.0 // indirect
|
||||||
github.com/prometheus/procfs v0.15.1 // indirect
|
github.com/prometheus/procfs v0.15.1 // indirect
|
||||||
github.com/vishvananda/netns v0.0.4 // indirect
|
github.com/vishvananda/netns v0.0.4 // indirect
|
||||||
golang.org/x/mod v0.18.0 // indirect
|
golang.org/x/mod v0.23.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
golang.org/x/tools v0.22.0 // indirect
|
golang.org/x/tools v0.30.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
|||||||
26
go.sum
26
go.sum
@ -24,8 +24,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
|
|||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
github.com/flynn/noise v1.1.0 h1:KjPQoQCEFdZDiP03phOvGi11+SVVhBG2wOWAorLsstg=
|
||||||
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
github.com/flynn/noise v1.1.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag=
|
||||||
github.com/gaissmai/bart v0.18.1 h1:bX2j560JC1MJpoEDevBGvXL5OZ1mkls320Vl8Igb5QQ=
|
github.com/gaissmai/bart v0.20.1 h1:igNss0zDsSY8e+ophKgD9KJVPKBOo7uSVjyKCL7nIzo=
|
||||||
github.com/gaissmai/bart v0.18.1/go.mod h1:JJzMAhNF5Rjo4SF4jWBrANuJfqY+FvsFhW7t1UZJ+XY=
|
github.com/gaissmai/bart v0.20.1/go.mod h1:JJzMAhNF5Rjo4SF4jWBrANuJfqY+FvsFhW7t1UZJ+XY=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||||
@ -83,8 +83,8 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|||||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||||
github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=
|
github.com/miekg/dns v1.1.64 h1:wuZgD9wwCE6XMT05UU/mlSko71eRSXEAm2EbjQXLKnQ=
|
||||||
github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=
|
github.com/miekg/dns v1.1.64/go.mod h1:Dzw9769uoKVaLuODMDZz9M6ynFU6Em65csPuoi8G0ck=
|
||||||
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b h1:J/AzCvg5z0Hn1rqZUJjpbzALUmkKX0Zwbc/i4fw7Sfk=
|
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b h1:J/AzCvg5z0Hn1rqZUJjpbzALUmkKX0Zwbc/i4fw7Sfk=
|
||||||
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
github.com/miekg/pkcs11 v1.1.2-0.20231115102856-9078ad6b9d4b/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
@ -164,8 +164,8 @@ golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPI
|
|||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/mod v0.18.0 h1:5+9lSbEzPSdWkH32vYPBwEpX8KwDbM52Ud9xBUvNlb0=
|
golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM=
|
||||||
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||||
@ -176,8 +176,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||||
golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c=
|
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
|
||||||
golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
@ -219,8 +219,8 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn
|
|||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||||
golang.org/x/tools v0.22.0 h1:gqSGLZqv+AI9lIQzniJ0nZDRG5GBPsSi+DRNHWNz6yA=
|
golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY=
|
||||||
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
|
golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
@ -239,8 +239,8 @@ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miE
|
|||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM=
|
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||||
google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
@ -251,8 +251,6 @@ gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|||||||
@ -71,7 +71,8 @@ func ixHandshakeStage0(f *Interface, hh *HandshakeHostInfo) bool {
|
|||||||
|
|
||||||
hsBytes, err := hs.Marshal()
|
hsBytes, err := hs.Marshal()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
f.l.WithError(err).WithField("vpnAddrs", hh.hostinfo.vpnAddrs).WithField("certVersion", v).
|
f.l.WithError(err).WithField("vpnAddrs", hh.hostinfo.vpnAddrs).
|
||||||
|
WithField("certVersion", v).
|
||||||
WithField("handshake", m{"stage": 0, "style": "ix_psk0"}).Error("Failed to marshal handshake message")
|
WithField("handshake", m{"stage": 0, "style": "ix_psk0"}).Error("Failed to marshal handshake message")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -185,6 +186,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
var vpnAddrs []netip.Addr
|
var vpnAddrs []netip.Addr
|
||||||
var filteredNetworks []netip.Prefix
|
var filteredNetworks []netip.Prefix
|
||||||
certName := remoteCert.Certificate.Name()
|
certName := remoteCert.Certificate.Name()
|
||||||
|
certVersion := remoteCert.Certificate.Version()
|
||||||
fingerprint := remoteCert.Fingerprint
|
fingerprint := remoteCert.Fingerprint
|
||||||
issuer := remoteCert.Certificate.Issuer()
|
issuer := remoteCert.Certificate.Issuer()
|
||||||
|
|
||||||
@ -194,6 +196,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if found {
|
if found {
|
||||||
f.l.WithField("vpnAddr", vpnAddr).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddr", vpnAddr).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Refusing to handshake with myself")
|
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Refusing to handshake with myself")
|
||||||
@ -212,6 +215,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if len(vpnAddrs) == 0 {
|
if len(vpnAddrs) == 0 {
|
||||||
f.l.WithError(err).WithField("udpAddr", addr).
|
f.l.WithError(err).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("No usable vpn addresses from host, refusing handshake")
|
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("No usable vpn addresses from host, refusing handshake")
|
||||||
@ -231,6 +235,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
f.l.WithError(err).WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithError(err).WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Failed to generate index")
|
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Failed to generate index")
|
||||||
@ -253,6 +258,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
|
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -264,6 +270,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if hs.Details.Cert == nil {
|
if hs.Details.Cert == nil {
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -281,6 +288,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
f.l.WithError(err).WithField("vpnAddrs", hostinfo.vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithError(err).WithField("vpnAddrs", hostinfo.vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Failed to marshal handshake message")
|
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Failed to marshal handshake message")
|
||||||
@ -292,6 +300,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
f.l.WithError(err).WithField("vpnAddrs", hostinfo.vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithError(err).WithField("vpnAddrs", hostinfo.vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Failed to call noise.WriteMessage")
|
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Failed to call noise.WriteMessage")
|
||||||
@ -299,6 +308,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
} else if dKey == nil || eKey == nil {
|
} else if dKey == nil || eKey == nil {
|
||||||
f.l.WithField("vpnAddrs", hostinfo.vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", hostinfo.vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Noise did not arrive at a key")
|
WithField("handshake", m{"stage": 1, "style": "ix_psk0"}).Error("Noise did not arrive at a key")
|
||||||
@ -366,6 +376,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
// This means there was an existing tunnel and this handshake was older than the one we are currently based on
|
// This means there was an existing tunnel and this handshake was older than the one we are currently based on
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("oldHandshakeTime", existing.lastHandshakeTime).
|
WithField("oldHandshakeTime", existing.lastHandshakeTime).
|
||||||
WithField("newHandshakeTime", hostinfo.lastHandshakeTime).
|
WithField("newHandshakeTime", hostinfo.lastHandshakeTime).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
@ -381,6 +392,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
// This means we failed to insert because of collision on localIndexId. Just let the next handshake packet retry
|
// This means we failed to insert because of collision on localIndexId. Just let the next handshake packet retry
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -393,6 +405,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
// And we forget to update it here
|
// And we forget to update it here
|
||||||
f.l.WithError(err).WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithError(err).WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -409,6 +422,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -417,6 +431,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
} else {
|
} else {
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -435,6 +450,7 @@ func ixHandshakeStage1(f *Interface, addr netip.AddrPort, via *ViaSender, packet
|
|||||||
f.SendVia(via.relayHI, via.relay, msg, make([]byte, 12), make([]byte, mtu), false)
|
f.SendVia(via.relayHI, via.relay, msg, make([]byte, 12), make([]byte, mtu), false)
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("relay", via.relayHI.vpnAddrs[0]).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("relay", via.relayHI.vpnAddrs[0]).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
@ -539,6 +555,7 @@ func ixHandshakeStage2(f *Interface, addr netip.AddrPort, via *ViaSender, hh *Ha
|
|||||||
|
|
||||||
vpnNetworks := remoteCert.Certificate.Networks()
|
vpnNetworks := remoteCert.Certificate.Networks()
|
||||||
certName := remoteCert.Certificate.Name()
|
certName := remoteCert.Certificate.Name()
|
||||||
|
certVersion := remoteCert.Certificate.Version()
|
||||||
fingerprint := remoteCert.Fingerprint
|
fingerprint := remoteCert.Fingerprint
|
||||||
issuer := remoteCert.Certificate.Issuer()
|
issuer := remoteCert.Certificate.Issuer()
|
||||||
|
|
||||||
@ -573,6 +590,7 @@ func ixHandshakeStage2(f *Interface, addr netip.AddrPort, via *ViaSender, hh *Ha
|
|||||||
if len(vpnAddrs) == 0 {
|
if len(vpnAddrs) == 0 {
|
||||||
f.l.WithError(err).WithField("udpAddr", addr).
|
f.l.WithError(err).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("handshake", m{"stage": 2, "style": "ix_psk0"}).Error("No usable vpn addresses from host, refusing handshake")
|
WithField("handshake", m{"stage": 2, "style": "ix_psk0"}).Error("No usable vpn addresses from host, refusing handshake")
|
||||||
@ -582,7 +600,9 @@ func ixHandshakeStage2(f *Interface, addr netip.AddrPort, via *ViaSender, hh *Ha
|
|||||||
// Ensure the right host responded
|
// Ensure the right host responded
|
||||||
if !slices.Contains(vpnAddrs, hostinfo.vpnAddrs[0]) {
|
if !slices.Contains(vpnAddrs, hostinfo.vpnAddrs[0]) {
|
||||||
f.l.WithField("intendedVpnAddrs", hostinfo.vpnAddrs).WithField("haveVpnNetworks", vpnNetworks).
|
f.l.WithField("intendedVpnAddrs", hostinfo.vpnAddrs).WithField("haveVpnNetworks", vpnNetworks).
|
||||||
WithField("udpAddr", addr).WithField("certName", certName).
|
WithField("udpAddr", addr).
|
||||||
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("handshake", m{"stage": 2, "style": "ix_psk0"}).
|
WithField("handshake", m{"stage": 2, "style": "ix_psk0"}).
|
||||||
Info("Incorrect host responded to handshake")
|
Info("Incorrect host responded to handshake")
|
||||||
|
|
||||||
@ -618,6 +638,7 @@ func ixHandshakeStage2(f *Interface, addr netip.AddrPort, via *ViaSender, hh *Ha
|
|||||||
duration := time.Since(hh.startTime).Nanoseconds()
|
duration := time.Since(hh.startTime).Nanoseconds()
|
||||||
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
f.l.WithField("vpnAddrs", vpnAddrs).WithField("udpAddr", addr).
|
||||||
WithField("certName", certName).
|
WithField("certName", certName).
|
||||||
|
WithField("certVersion", certVersion).
|
||||||
WithField("fingerprint", fingerprint).
|
WithField("fingerprint", fingerprint).
|
||||||
WithField("issuer", issuer).
|
WithField("issuer", issuer).
|
||||||
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
WithField("initiatorIndex", hs.Details.InitiatorIndex).WithField("responderIndex", hs.Details.ResponderIndex).
|
||||||
|
|||||||
@ -19,7 +19,7 @@ import (
|
|||||||
// |-----------------------------------------------------------------------|
|
// |-----------------------------------------------------------------------|
|
||||||
// | payload... |
|
// | payload... |
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
const (
|
const (
|
||||||
Version uint8 = 1
|
Version uint8 = 1
|
||||||
|
|||||||
@ -210,8 +210,8 @@ func TestHostMap_reload(t *testing.T) {
|
|||||||
assert.Empty(t, hm.GetPreferredRanges())
|
assert.Empty(t, hm.GetPreferredRanges())
|
||||||
|
|
||||||
c.ReloadConfigString("preferred_ranges: [1.1.1.0/24, 10.1.1.0/24]")
|
c.ReloadConfigString("preferred_ranges: [1.1.1.0/24, 10.1.1.0/24]")
|
||||||
assert.EqualValues(t, []string{"1.1.1.0/24", "10.1.1.0/24"}, toS(hm.GetPreferredRanges()))
|
assert.Equal(t, []string{"1.1.1.0/24", "10.1.1.0/24"}, toS(hm.GetPreferredRanges()))
|
||||||
|
|
||||||
c.ReloadConfigString("preferred_ranges: [1.1.1.1/32]")
|
c.ReloadConfigString("preferred_ranges: [1.1.1.1/32]")
|
||||||
assert.EqualValues(t, []string{"1.1.1.1/32"}, toS(hm.GetPreferredRanges()))
|
assert.Equal(t, []string{"1.1.1.1/32"}, toS(hm.GetPreferredRanges()))
|
||||||
}
|
}
|
||||||
|
|||||||
91
inside.go
91
inside.go
@ -8,6 +8,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/header"
|
"github.com/slackhq/nebula/header"
|
||||||
"github.com/slackhq/nebula/iputil"
|
"github.com/slackhq/nebula/iputil"
|
||||||
"github.com/slackhq/nebula/noiseutil"
|
"github.com/slackhq/nebula/noiseutil"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet, nb, out []byte, q int, localCache firewall.ConntrackCache) {
|
func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet, nb, out []byte, q int, localCache firewall.ConntrackCache) {
|
||||||
@ -49,7 +50,7 @@ func (f *Interface) consumeInsidePacket(packet []byte, fwPacket *firewall.Packet
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
hostinfo, ready := f.getOrHandshake(fwPacket.RemoteAddr, func(hh *HandshakeHostInfo) {
|
hostinfo, ready := f.getOrHandshakeConsiderRouting(fwPacket, func(hh *HandshakeHostInfo) {
|
||||||
hh.cachePacket(f.l, header.Message, 0, packet, f.sendMessageNow, f.cachedPacketMetrics)
|
hh.cachePacket(f.l, header.Message, 0, packet, f.sendMessageNow, f.cachedPacketMetrics)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -121,22 +122,94 @@ func (f *Interface) rejectOutside(packet []byte, ci *ConnectionState, hostinfo *
|
|||||||
f.sendNoMetrics(header.Message, 0, ci, hostinfo, netip.AddrPort{}, out, nb, packet, q)
|
f.sendNoMetrics(header.Message, 0, ci, hostinfo, netip.AddrPort{}, out, nb, packet, q)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handshake will attempt to initiate a tunnel with the provided vpn address if it is within our vpn networks. This is a no-op if the tunnel is already established or being established
|
||||||
func (f *Interface) Handshake(vpnAddr netip.Addr) {
|
func (f *Interface) Handshake(vpnAddr netip.Addr) {
|
||||||
f.getOrHandshake(vpnAddr, nil)
|
f.getOrHandshakeNoRouting(vpnAddr, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
// getOrHandshake returns nil if the vpnAddr is not routable.
|
// getOrHandshakeNoRouting returns nil if the vpnAddr is not routable.
|
||||||
// If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel
|
// If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel
|
||||||
func (f *Interface) getOrHandshake(vpnAddr netip.Addr, cacheCallback func(*HandshakeHostInfo)) (*HostInfo, bool) {
|
func (f *Interface) getOrHandshakeNoRouting(vpnAddr netip.Addr, cacheCallback func(*HandshakeHostInfo)) (*HostInfo, bool) {
|
||||||
_, found := f.myVpnNetworksTable.Lookup(vpnAddr)
|
_, found := f.myVpnNetworksTable.Lookup(vpnAddr)
|
||||||
if !found {
|
if found {
|
||||||
vpnAddr = f.inside.RouteFor(vpnAddr)
|
return f.handshakeManager.GetOrHandshake(vpnAddr, cacheCallback)
|
||||||
if !vpnAddr.IsValid() {
|
}
|
||||||
|
|
||||||
return nil, false
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
// getOrHandshakeConsiderRouting will try to find the HostInfo to handle this packet, starting a handshake if necessary.
|
||||||
|
// If the 2nd return var is false then the hostinfo is not ready to be used in a tunnel.
|
||||||
|
func (f *Interface) getOrHandshakeConsiderRouting(fwPacket *firewall.Packet, cacheCallback func(*HandshakeHostInfo)) (*HostInfo, bool) {
|
||||||
|
|
||||||
|
destinationAddr := fwPacket.RemoteAddr
|
||||||
|
|
||||||
|
hostinfo, ready := f.getOrHandshakeNoRouting(destinationAddr, cacheCallback)
|
||||||
|
|
||||||
|
// Host is inside the mesh, no routing required
|
||||||
|
if hostinfo != nil {
|
||||||
|
return hostinfo, ready
|
||||||
|
}
|
||||||
|
|
||||||
|
gateways := f.inside.RoutesFor(destinationAddr)
|
||||||
|
|
||||||
|
switch len(gateways) {
|
||||||
|
case 0:
|
||||||
|
return nil, false
|
||||||
|
case 1:
|
||||||
|
// Single gateway route
|
||||||
|
return f.handshakeManager.GetOrHandshake(gateways[0].Addr(), cacheCallback)
|
||||||
|
default:
|
||||||
|
// Multi gateway route, perform ECMP categorization
|
||||||
|
gatewayAddr, balancingOk := routing.BalancePacket(fwPacket, gateways)
|
||||||
|
|
||||||
|
if !balancingOk {
|
||||||
|
// This happens if the gateway buckets were not calculated, this _should_ never happen
|
||||||
|
f.l.Error("Gateway buckets not calculated, fallback from ECMP to random routing. Please report this bug.")
|
||||||
|
}
|
||||||
|
|
||||||
|
var handshakeInfoForChosenGateway *HandshakeHostInfo
|
||||||
|
var hhReceiver = func(hh *HandshakeHostInfo) {
|
||||||
|
handshakeInfoForChosenGateway = hh
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the handshakeHostInfo for later.
|
||||||
|
// If this node is not reachable we will attempt other nodes, if none are reachable we will
|
||||||
|
// cache the packet for this gateway.
|
||||||
|
if hostinfo, ready = f.handshakeManager.GetOrHandshake(gatewayAddr, hhReceiver); ready {
|
||||||
|
return hostinfo, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// It appears the selected gateway cannot be reached, find another gateway to fallback on.
|
||||||
|
// The current implementation breaks ECMP but that seems better than no connectivity.
|
||||||
|
// If ECMP is also required when a gateway is down then connectivity status
|
||||||
|
// for each gateway needs to be kept and the weights recalculated when they go up or down.
|
||||||
|
// This would also need to interact with unsafe_route updates through reloading the config or
|
||||||
|
// use of the use_system_route_table option
|
||||||
|
|
||||||
|
if f.l.Level >= logrus.DebugLevel {
|
||||||
|
f.l.WithField("destination", destinationAddr).
|
||||||
|
WithField("originalGateway", gatewayAddr).
|
||||||
|
Debugln("Calculated gateway for ECMP not available, attempting other gateways")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range gateways {
|
||||||
|
// Skip the gateway that failed previously
|
||||||
|
if gateways[i].Addr() == gatewayAddr {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// We do not need the HandshakeHostInfo since we cache the packet in the originally chosen gateway
|
||||||
|
if hostinfo, ready = f.handshakeManager.GetOrHandshake(gateways[i].Addr(), nil); ready {
|
||||||
|
return hostinfo, true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return f.handshakeManager.GetOrHandshake(vpnAddr, cacheCallback)
|
// No gateways reachable, cache the packet in the originally chosen gateway
|
||||||
|
cacheCallback(handshakeInfoForChosenGateway)
|
||||||
|
return hostinfo, false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, nb, out []byte) {
|
func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubType, hostinfo *HostInfo, p, nb, out []byte) {
|
||||||
@ -163,7 +236,7 @@ func (f *Interface) sendMessageNow(t header.MessageType, st header.MessageSubTyp
|
|||||||
|
|
||||||
// SendMessageToVpnAddr handles real addr:port lookup and sends to the current best known address for vpnAddr
|
// SendMessageToVpnAddr handles real addr:port lookup and sends to the current best known address for vpnAddr
|
||||||
func (f *Interface) SendMessageToVpnAddr(t header.MessageType, st header.MessageSubType, vpnAddr netip.Addr, p, nb, out []byte) {
|
func (f *Interface) SendMessageToVpnAddr(t header.MessageType, st header.MessageSubType, vpnAddr netip.Addr, p, nb, out []byte) {
|
||||||
hostInfo, ready := f.getOrHandshake(vpnAddr, func(hh *HandshakeHostInfo) {
|
hostInfo, ready := f.getOrHandshakeNoRouting(vpnAddr, func(hh *HandshakeHostInfo) {
|
||||||
hh.cachePacket(f.l, t, st, p, f.SendMessageToHostInfo, f.cachedPacketMetrics)
|
hh.cachePacket(f.l, t, st, p, f.SendMessageToHostInfo, f.cachedPacketMetrics)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@ -422,7 +422,7 @@ func (lh *LightHouse) loadStaticMap(c *config.C, staticList map[netip.Addr]struc
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
shm := c.GetMap("static_host_map", map[interface{}]interface{}{})
|
shm := c.GetMap("static_host_map", map[string]any{})
|
||||||
i := 0
|
i := 0
|
||||||
|
|
||||||
for k, v := range shm {
|
for k, v := range shm {
|
||||||
@ -436,9 +436,9 @@ func (lh *LightHouse) loadStaticMap(c *config.C, staticList map[netip.Addr]struc
|
|||||||
return util.NewContextualError("static_host_map key is not in our network, invalid", m{"vpnAddr": vpnAddr, "networks": lh.myVpnNetworks, "entry": i + 1}, nil)
|
return util.NewContextualError("static_host_map key is not in our network, invalid", m{"vpnAddr": vpnAddr, "networks": lh.myVpnNetworks, "entry": i + 1}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
vals, ok := v.([]interface{})
|
vals, ok := v.([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
vals = []interface{}{v}
|
vals = []any{v}
|
||||||
}
|
}
|
||||||
remoteAddrs := []string{}
|
remoteAddrs := []string{}
|
||||||
for _, v := range vals {
|
for _, v := range vals {
|
||||||
|
|||||||
@ -14,7 +14,7 @@ import (
|
|||||||
"github.com/slackhq/nebula/test"
|
"github.com/slackhq/nebula/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestOldIPv4Only(t *testing.T) {
|
func TestOldIPv4Only(t *testing.T) {
|
||||||
@ -40,15 +40,15 @@ func Test_lhStaticMapping(t *testing.T) {
|
|||||||
lh1 := "10.128.0.2"
|
lh1 := "10.128.0.2"
|
||||||
|
|
||||||
c := config.NewC(l)
|
c := config.NewC(l)
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{"hosts": []interface{}{lh1}}
|
c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1}}
|
||||||
c.Settings["static_host_map"] = map[interface{}]interface{}{lh1: []interface{}{"1.1.1.1:4242"}}
|
c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
|
||||||
_, err := NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
_, err := NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
lh2 := "10.128.0.3"
|
lh2 := "10.128.0.3"
|
||||||
c = config.NewC(l)
|
c = config.NewC(l)
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{"hosts": []interface{}{lh1, lh2}}
|
c.Settings["lighthouse"] = map[string]any{"hosts": []any{lh1, lh2}}
|
||||||
c.Settings["static_host_map"] = map[interface{}]interface{}{lh1: []interface{}{"100.1.1.1:4242"}}
|
c.Settings["static_host_map"] = map[string]any{lh1: []any{"100.1.1.1:4242"}}
|
||||||
_, err = NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
_, err = NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
||||||
require.EqualError(t, err, "lighthouse 10.128.0.3 does not have a static_host_map entry")
|
require.EqualError(t, err, "lighthouse 10.128.0.3 does not have a static_host_map entry")
|
||||||
}
|
}
|
||||||
@ -65,12 +65,12 @@ func TestReloadLighthouseInterval(t *testing.T) {
|
|||||||
lh1 := "10.128.0.2"
|
lh1 := "10.128.0.2"
|
||||||
|
|
||||||
c := config.NewC(l)
|
c := config.NewC(l)
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{
|
c.Settings["lighthouse"] = map[string]any{
|
||||||
"hosts": []interface{}{lh1},
|
"hosts": []any{lh1},
|
||||||
"interval": "1s",
|
"interval": "1s",
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Settings["static_host_map"] = map[interface{}]interface{}{lh1: []interface{}{"1.1.1.1:4242"}}
|
c.Settings["static_host_map"] = map[string]any{lh1: []any{"1.1.1.1:4242"}}
|
||||||
lh, err := NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
lh, err := NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
lh.ifce = &mockEncWriter{}
|
lh.ifce = &mockEncWriter{}
|
||||||
@ -192,8 +192,8 @@ func TestLighthouse_Memory(t *testing.T) {
|
|||||||
theirVpnIp := netip.MustParseAddr("10.128.0.3")
|
theirVpnIp := netip.MustParseAddr("10.128.0.3")
|
||||||
|
|
||||||
c := config.NewC(l)
|
c := config.NewC(l)
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{"am_lighthouse": true}
|
c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
|
||||||
c.Settings["listen"] = map[interface{}]interface{}{"port": 4242}
|
c.Settings["listen"] = map[string]any{"port": 4242}
|
||||||
|
|
||||||
myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
|
myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
|
||||||
nt := new(bart.Table[struct{}])
|
nt := new(bart.Table[struct{}])
|
||||||
@ -277,8 +277,8 @@ func TestLighthouse_Memory(t *testing.T) {
|
|||||||
func TestLighthouse_reload(t *testing.T) {
|
func TestLighthouse_reload(t *testing.T) {
|
||||||
l := test.NewLogger()
|
l := test.NewLogger()
|
||||||
c := config.NewC(l)
|
c := config.NewC(l)
|
||||||
c.Settings["lighthouse"] = map[interface{}]interface{}{"am_lighthouse": true}
|
c.Settings["lighthouse"] = map[string]any{"am_lighthouse": true}
|
||||||
c.Settings["listen"] = map[interface{}]interface{}{"port": 4242}
|
c.Settings["listen"] = map[string]any{"port": 4242}
|
||||||
|
|
||||||
myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
|
myVpnNet := netip.MustParsePrefix("10.128.0.1/24")
|
||||||
nt := new(bart.Table[struct{}])
|
nt := new(bart.Table[struct{}])
|
||||||
@ -291,9 +291,9 @@ func TestLighthouse_reload(t *testing.T) {
|
|||||||
lh, err := NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
lh, err := NewLightHouseFromConfig(context.Background(), l, c, cs, nil, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
nc := map[interface{}]interface{}{
|
nc := map[string]any{
|
||||||
"static_host_map": map[interface{}]interface{}{
|
"static_host_map": map[string]any{
|
||||||
"10.128.0.2": []interface{}{"1.1.1.1:4242"},
|
"10.128.0.2": []any{"1.1.1.1:4242"},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
rc, err := yaml.Marshal(nc)
|
rc, err := yaml.Marshal(nc)
|
||||||
|
|||||||
4
main.go
4
main.go
@ -13,10 +13,10 @@ import (
|
|||||||
"github.com/slackhq/nebula/sshd"
|
"github.com/slackhq/nebula/sshd"
|
||||||
"github.com/slackhq/nebula/udp"
|
"github.com/slackhq/nebula/udp"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logger, deviceFactory overlay.DeviceFactory) (retcon *Control, reterr error) {
|
func Main(c *config.C, configTest bool, buildVersion string, logger *logrus.Logger, deviceFactory overlay.DeviceFactory) (retcon *Control, reterr error) {
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|||||||
@ -3,6 +3,8 @@ package overlay
|
|||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Device interface {
|
type Device interface {
|
||||||
@ -10,6 +12,6 @@ type Device interface {
|
|||||||
Activate() error
|
Activate() error
|
||||||
Networks() []netip.Prefix
|
Networks() []netip.Prefix
|
||||||
Name() string
|
Name() string
|
||||||
RouteFor(netip.Addr) netip.Addr
|
RoutesFor(netip.Addr) routing.Gateways
|
||||||
NewMultiQueueReader() (io.ReadWriteCloser, error)
|
NewMultiQueueReader() (io.ReadWriteCloser, error)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,13 +11,14 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Route struct {
|
type Route struct {
|
||||||
MTU int
|
MTU int
|
||||||
Metric int
|
Metric int
|
||||||
Cidr netip.Prefix
|
Cidr netip.Prefix
|
||||||
Via netip.Addr
|
Via routing.Gateways
|
||||||
Install bool
|
Install bool
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,15 +48,17 @@ func (r Route) String() string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRouteTree(l *logrus.Logger, routes []Route, allowMTU bool) (*bart.Table[netip.Addr], error) {
|
func makeRouteTree(l *logrus.Logger, routes []Route, allowMTU bool) (*bart.Table[routing.Gateways], error) {
|
||||||
routeTree := new(bart.Table[netip.Addr])
|
routeTree := new(bart.Table[routing.Gateways])
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if !allowMTU && r.MTU > 0 {
|
if !allowMTU && r.MTU > 0 {
|
||||||
l.WithField("route", r).Warnf("route MTU is not supported in %s", runtime.GOOS)
|
l.WithField("route", r).Warnf("route MTU is not supported in %s", runtime.GOOS)
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Via.IsValid() {
|
gateways := r.Via
|
||||||
routeTree.Insert(r.Cidr, r.Via)
|
if len(gateways) > 0 {
|
||||||
|
routing.CalculateBucketsForGateways(gateways)
|
||||||
|
routeTree.Insert(r.Cidr, gateways)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return routeTree, nil
|
return routeTree, nil
|
||||||
@ -69,7 +72,7 @@ func parseRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
|
|||||||
return []Route{}, nil
|
return []Route{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rawRoutes, ok := r.([]interface{})
|
rawRoutes, ok := r.([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("tun.routes is not an array")
|
return nil, fmt.Errorf("tun.routes is not an array")
|
||||||
}
|
}
|
||||||
@ -80,7 +83,7 @@ func parseRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
|
|||||||
|
|
||||||
routes := make([]Route, len(rawRoutes))
|
routes := make([]Route, len(rawRoutes))
|
||||||
for i, r := range rawRoutes {
|
for i, r := range rawRoutes {
|
||||||
m, ok := r.(map[interface{}]interface{})
|
m, ok := r.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("entry %v in tun.routes is invalid", i+1)
|
return nil, fmt.Errorf("entry %v in tun.routes is invalid", i+1)
|
||||||
}
|
}
|
||||||
@ -148,7 +151,7 @@ func parseUnsafeRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
|
|||||||
return []Route{}, nil
|
return []Route{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
rawRoutes, ok := r.([]interface{})
|
rawRoutes, ok := r.([]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("tun.unsafe_routes is not an array")
|
return nil, fmt.Errorf("tun.unsafe_routes is not an array")
|
||||||
}
|
}
|
||||||
@ -159,7 +162,7 @@ func parseUnsafeRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
|
|||||||
|
|
||||||
routes := make([]Route, len(rawRoutes))
|
routes := make([]Route, len(rawRoutes))
|
||||||
for i, r := range rawRoutes {
|
for i, r := range rawRoutes {
|
||||||
m, ok := r.(map[interface{}]interface{})
|
m, ok := r.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("entry %v in tun.unsafe_routes is invalid", i+1)
|
return nil, fmt.Errorf("entry %v in tun.unsafe_routes is invalid", i+1)
|
||||||
}
|
}
|
||||||
@ -201,16 +204,65 @@ func parseUnsafeRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
|
|||||||
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not present", i+1)
|
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not present", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
via, ok := rVia.(string)
|
var gateways routing.Gateways
|
||||||
if !ok {
|
|
||||||
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not a string: found %T", i+1, rVia)
|
|
||||||
}
|
|
||||||
|
|
||||||
viaVpnIp, err := netip.ParseAddr(via)
|
switch via := rVia.(type) {
|
||||||
|
case string:
|
||||||
|
viaIp, err := netip.ParseAddr(via)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes failed to parse address: %v", i+1, err)
|
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes failed to parse address: %v", i+1, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gateways = routing.Gateways{routing.NewGateway(viaIp, 1)}
|
||||||
|
|
||||||
|
case []any:
|
||||||
|
gateways = make(routing.Gateways, len(via))
|
||||||
|
for ig, v := range via {
|
||||||
|
gatewayMap, ok := v.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("entry %v in tun.unsafe_routes[%v].via is invalid", i+1, ig+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
rGateway, ok := gatewayMap["gateway"]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("entry .gateway in tun.unsafe_routes[%v].via[%v] is not present", i+1, ig+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedGateway, ok := rGateway.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("entry .gateway in tun.unsafe_routes[%v].via[%v] is not a string", i+1, ig+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayIp, err := netip.ParseAddr(parsedGateway)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("entry .gateway in tun.unsafe_routes[%v].via[%v] failed to parse address: %v", i+1, ig+1, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
rGatewayWeight, ok := gatewayMap["weight"]
|
||||||
|
if !ok {
|
||||||
|
rGatewayWeight = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
gatewayWeight, ok := rGatewayWeight.(int)
|
||||||
|
if !ok {
|
||||||
|
_, err = strconv.ParseInt(rGatewayWeight.(string), 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("entry .weight in tun.unsafe_routes[%v].via[%v] is not an integer", i+1, ig+1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if gatewayWeight < 1 || gatewayWeight > math.MaxInt32 {
|
||||||
|
return nil, fmt.Errorf("entry .weight in tun.unsafe_routes[%v].via[%v] is not in range (1-%d) : %v", i+1, ig+1, math.MaxInt32, gatewayWeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
gateways[ig] = routing.NewGateway(gatewayIp, gatewayWeight)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("entry %v.via in tun.unsafe_routes is not a string or list of gateways: found %T", i+1, rVia)
|
||||||
|
}
|
||||||
|
|
||||||
rRoute, ok := m["route"]
|
rRoute, ok := m["route"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("entry %v.route in tun.unsafe_routes is not present", i+1)
|
return nil, fmt.Errorf("entry %v.route in tun.unsafe_routes is not present", i+1)
|
||||||
@ -226,7 +278,7 @@ func parseUnsafeRoutes(c *config.C, networks []netip.Prefix) ([]Route, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
r := Route{
|
r := Route{
|
||||||
Via: viaVpnIp,
|
Via: gateways,
|
||||||
MTU: mtu,
|
MTU: mtu,
|
||||||
Metric: metric,
|
Metric: metric,
|
||||||
Install: install,
|
Install: install,
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/test"
|
"github.com/slackhq/nebula/test"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
@ -23,75 +24,75 @@ func Test_parseRoutes(t *testing.T) {
|
|||||||
assert.Empty(t, routes)
|
assert.Empty(t, routes)
|
||||||
|
|
||||||
// not an array
|
// not an array
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": "hi"}
|
c.Settings["tun"] = map[string]any{"routes": "hi"}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "tun.routes is not an array")
|
require.EqualError(t, err, "tun.routes is not an array")
|
||||||
|
|
||||||
// no routes
|
// no routes
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{}}
|
c.Settings["tun"] = map[string]any{"routes": []any{}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, routes)
|
assert.Empty(t, routes)
|
||||||
|
|
||||||
// weird route
|
// weird route
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{"asdf"}}
|
c.Settings["tun"] = map[string]any{"routes": []any{"asdf"}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1 in tun.routes is invalid")
|
require.EqualError(t, err, "entry 1 in tun.routes is invalid")
|
||||||
|
|
||||||
// no mtu
|
// no mtu
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.mtu in tun.routes is not present")
|
require.EqualError(t, err, "entry 1.mtu in tun.routes is not present")
|
||||||
|
|
||||||
// bad mtu
|
// bad mtu
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "nope"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "nope"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.mtu in tun.routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
|
require.EqualError(t, err, "entry 1.mtu in tun.routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
|
||||||
|
|
||||||
// low mtu
|
// low mtu
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "499"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "499"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.mtu in tun.routes is below 500: 499")
|
require.EqualError(t, err, "entry 1.mtu in tun.routes is below 500: 499")
|
||||||
|
|
||||||
// missing route
|
// missing route
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.routes is not present")
|
require.EqualError(t, err, "entry 1.route in tun.routes is not present")
|
||||||
|
|
||||||
// unparsable route
|
// unparsable route
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "route": "nope"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "nope"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
|
require.EqualError(t, err, "entry 1.route in tun.routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
|
||||||
|
|
||||||
// below network range
|
// below network range
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "route": "1.0.0.0/8"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "1.0.0.0/8"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 1.0.0.0/8, networks: [10.0.0.0/24]")
|
require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 1.0.0.0/8, networks: [10.0.0.0/24]")
|
||||||
|
|
||||||
// above network range
|
// above network range
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "route": "10.0.1.0/24"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "10.0.1.0/24"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 10.0.1.0/24, networks: [10.0.0.0/24]")
|
require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 10.0.1.0/24, networks: [10.0.0.0/24]")
|
||||||
|
|
||||||
// Not in multiple ranges
|
// Not in multiple ranges
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "route": "192.0.0.0/24"}}}
|
c.Settings["tun"] = map[string]any{"routes": []any{map[string]any{"mtu": "500", "route": "192.0.0.0/24"}}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n, netip.MustParsePrefix("192.1.0.0/24")})
|
routes, err = parseRoutes(c, []netip.Prefix{n, netip.MustParsePrefix("192.1.0.0/24")})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 192.0.0.0/24, networks: [10.0.0.0/24 192.1.0.0/24]")
|
require.EqualError(t, err, "entry 1.route in tun.routes is not contained within the configured vpn networks; route: 192.0.0.0/24, networks: [10.0.0.0/24 192.1.0.0/24]")
|
||||||
|
|
||||||
// happy case
|
// happy case
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"routes": []interface{}{
|
c.Settings["tun"] = map[string]any{"routes": []any{
|
||||||
map[interface{}]interface{}{"mtu": "9000", "route": "10.0.0.0/29"},
|
map[string]any{"mtu": "9000", "route": "10.0.0.0/29"},
|
||||||
map[interface{}]interface{}{"mtu": "8000", "route": "10.0.0.1/32"},
|
map[string]any{"mtu": "8000", "route": "10.0.0.1/32"},
|
||||||
}}
|
}}
|
||||||
routes, err = parseRoutes(c, []netip.Prefix{n})
|
routes, err = parseRoutes(c, []netip.Prefix{n})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -128,105 +129,129 @@ func Test_parseUnsafeRoutes(t *testing.T) {
|
|||||||
assert.Empty(t, routes)
|
assert.Empty(t, routes)
|
||||||
|
|
||||||
// not an array
|
// not an array
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": "hi"}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": "hi"}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "tun.unsafe_routes is not an array")
|
require.EqualError(t, err, "tun.unsafe_routes is not an array")
|
||||||
|
|
||||||
// no routes
|
// no routes
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Empty(t, routes)
|
assert.Empty(t, routes)
|
||||||
|
|
||||||
// weird route
|
// weird route
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{"asdf"}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{"asdf"}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1 in tun.unsafe_routes is invalid")
|
require.EqualError(t, err, "entry 1 in tun.unsafe_routes is invalid")
|
||||||
|
|
||||||
// no via
|
// no via
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not present")
|
require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not present")
|
||||||
|
|
||||||
// invalid via
|
// invalid via
|
||||||
for _, invalidValue := range []interface{}{
|
for _, invalidValue := range []any{
|
||||||
127, false, nil, 1.0, []string{"1", "2"},
|
127, false, nil, 1.0, []string{"1", "2"},
|
||||||
} {
|
} {
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": invalidValue}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": invalidValue}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, fmt.Sprintf("entry 1.via in tun.unsafe_routes is not a string: found %T", invalidValue))
|
require.EqualError(t, err, fmt.Sprintf("entry 1.via in tun.unsafe_routes is not a string or list of gateways: found %T", invalidValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Unparsable list of via
|
||||||
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": []string{"1", "2"}}}}
|
||||||
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
|
assert.Nil(t, routes)
|
||||||
|
require.EqualError(t, err, "entry 1.via in tun.unsafe_routes is not a string or list of gateways: found []string")
|
||||||
|
|
||||||
// unparsable via
|
// unparsable via
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"mtu": "500", "via": "nope"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": "nope"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.via in tun.unsafe_routes failed to parse address: ParseAddr(\"nope\"): unable to parse IP")
|
require.EqualError(t, err, "entry 1.via in tun.unsafe_routes failed to parse address: ParseAddr(\"nope\"): unable to parse IP")
|
||||||
|
|
||||||
|
// unparsable gateway
|
||||||
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"gateway": "1"}}}}}
|
||||||
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
|
assert.Nil(t, routes)
|
||||||
|
require.EqualError(t, err, "entry .gateway in tun.unsafe_routes[1].via[1] failed to parse address: ParseAddr(\"1\"): unable to parse IP")
|
||||||
|
|
||||||
|
// missing gateway element
|
||||||
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"weight": "1"}}}}}
|
||||||
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
|
assert.Nil(t, routes)
|
||||||
|
require.EqualError(t, err, "entry .gateway in tun.unsafe_routes[1].via[1] is not present")
|
||||||
|
|
||||||
|
// unparsable weight element
|
||||||
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"mtu": "500", "via": []any{map[string]any{"gateway": "10.0.0.1", "weight": "a"}}}}}
|
||||||
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
|
assert.Nil(t, routes)
|
||||||
|
require.EqualError(t, err, "entry .weight in tun.unsafe_routes[1].via[1] is not an integer")
|
||||||
|
|
||||||
// missing route
|
// missing route
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "500"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "500"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is not present")
|
require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is not present")
|
||||||
|
|
||||||
// unparsable route
|
// unparsable route
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "500", "route": "nope"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "500", "route": "nope"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.unsafe_routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
|
require.EqualError(t, err, "entry 1.route in tun.unsafe_routes failed to parse: netip.ParsePrefix(\"nope\"): no '/'")
|
||||||
|
|
||||||
// within network range
|
// within network range
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "route": "10.0.0.0/24"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "10.0.0.0/24"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is contained within the configured vpn networks; route: 10.0.0.0/24, network: 10.0.0.0/24")
|
require.EqualError(t, err, "entry 1.route in tun.unsafe_routes is contained within the configured vpn networks; route: 10.0.0.0/24, network: 10.0.0.0/24")
|
||||||
|
|
||||||
// below network range
|
// below network range
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Len(t, routes, 1)
|
assert.Len(t, routes, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// above network range
|
// above network range
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "route": "10.0.1.0/24"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "10.0.1.0/24"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Len(t, routes, 1)
|
assert.Len(t, routes, 1)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// no mtu
|
// no mtu
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "route": "1.0.0.0/8"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Len(t, routes, 1)
|
assert.Len(t, routes, 1)
|
||||||
assert.Equal(t, 0, routes[0].MTU)
|
assert.Equal(t, 0, routes[0].MTU)
|
||||||
|
|
||||||
// bad mtu
|
// bad mtu
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "nope"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "nope"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
|
require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is not an integer: strconv.Atoi: parsing \"nope\": invalid syntax")
|
||||||
|
|
||||||
// low mtu
|
// low mtu
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "499"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "499"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is below 500: 499")
|
require.EqualError(t, err, "entry 1.mtu in tun.unsafe_routes is below 500: 499")
|
||||||
|
|
||||||
// bad install
|
// bad install
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "nope"}}}
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "nope"}}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
assert.Nil(t, routes)
|
assert.Nil(t, routes)
|
||||||
require.EqualError(t, err, "entry 1.install in tun.unsafe_routes is not a boolean: strconv.ParseBool: parsing \"nope\": invalid syntax")
|
require.EqualError(t, err, "entry 1.install in tun.unsafe_routes is not a boolean: strconv.ParseBool: parsing \"nope\": invalid syntax")
|
||||||
|
|
||||||
// happy case
|
// happy case
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
|
||||||
map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "t"},
|
map[string]any{"via": "127.0.0.1", "mtu": "9000", "route": "1.0.0.0/29", "install": "t"},
|
||||||
map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "8000", "route": "1.0.0.1/32", "install": 0},
|
map[string]any{"via": "127.0.0.1", "mtu": "8000", "route": "1.0.0.1/32", "install": 0},
|
||||||
map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32", "install": 1},
|
map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32", "install": 1},
|
||||||
map[interface{}]interface{}{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32"},
|
map[string]any{"via": "127.0.0.1", "mtu": "1500", "metric": 1234, "route": "1.0.0.2/32"},
|
||||||
}}
|
}}
|
||||||
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err = parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -263,9 +288,9 @@ func Test_makeRouteTree(t *testing.T) {
|
|||||||
n, err := netip.ParsePrefix("10.0.0.0/24")
|
n, err := netip.ParsePrefix("10.0.0.0/24")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c.Settings["tun"] = map[interface{}]interface{}{"unsafe_routes": []interface{}{
|
c.Settings["tun"] = map[string]any{"unsafe_routes": []any{
|
||||||
map[interface{}]interface{}{"via": "192.168.0.1", "route": "1.0.0.0/28"},
|
map[string]any{"via": "192.168.0.1", "route": "1.0.0.0/28"},
|
||||||
map[interface{}]interface{}{"via": "192.168.0.2", "route": "1.0.0.1/32"},
|
map[string]any{"via": "192.168.0.2", "route": "1.0.0.1/32"},
|
||||||
}}
|
}}
|
||||||
routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
|
routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -280,7 +305,7 @@ func Test_makeRouteTree(t *testing.T) {
|
|||||||
|
|
||||||
nip, err := netip.ParseAddr("192.168.0.1")
|
nip, err := netip.ParseAddr("192.168.0.1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, nip, r)
|
assert.Equal(t, nip, r[0].Addr())
|
||||||
|
|
||||||
ip, err = netip.ParseAddr("1.0.0.1")
|
ip, err = netip.ParseAddr("1.0.0.1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@ -289,10 +314,91 @@ func Test_makeRouteTree(t *testing.T) {
|
|||||||
|
|
||||||
nip, err = netip.ParseAddr("192.168.0.2")
|
nip, err = netip.ParseAddr("192.168.0.2")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
assert.Equal(t, nip, r)
|
assert.Equal(t, nip, r[0].Addr())
|
||||||
|
|
||||||
ip, err = netip.ParseAddr("1.1.0.1")
|
ip, err = netip.ParseAddr("1.1.0.1")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
r, ok = routeTree.Lookup(ip)
|
r, ok = routeTree.Lookup(ip)
|
||||||
assert.False(t, ok)
|
assert.False(t, ok)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_makeMultipathUnsafeRouteTree(t *testing.T) {
|
||||||
|
l := test.NewLogger()
|
||||||
|
c := config.NewC(l)
|
||||||
|
n, err := netip.ParsePrefix("10.0.0.0/24")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
c.Settings["tun"] = map[string]any{
|
||||||
|
"unsafe_routes": []any{
|
||||||
|
map[string]any{
|
||||||
|
"route": "192.168.86.0/24",
|
||||||
|
"via": "192.168.100.10",
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"route": "192.168.87.0/24",
|
||||||
|
"via": []any{
|
||||||
|
map[string]any{
|
||||||
|
"gateway": "10.0.0.1",
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"gateway": "10.0.0.2",
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"gateway": "10.0.0.3",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"route": "192.168.89.0/24",
|
||||||
|
"via": []any{
|
||||||
|
map[string]any{
|
||||||
|
"gateway": "10.0.0.1",
|
||||||
|
"weight": 10,
|
||||||
|
},
|
||||||
|
map[string]any{
|
||||||
|
"gateway": "10.0.0.2",
|
||||||
|
"weight": 5,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
routes, err := parseUnsafeRoutes(c, []netip.Prefix{n})
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Len(t, routes, 3)
|
||||||
|
routeTree, err := makeRouteTree(l, routes, true)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
ip, err := netip.ParseAddr("192.168.86.1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
r, ok := routeTree.Lookup(ip)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
nip, err := netip.ParseAddr("192.168.100.10")
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, nip, r[0].Addr())
|
||||||
|
|
||||||
|
ip, err = netip.ParseAddr("192.168.87.1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
r, ok = routeTree.Lookup(ip)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
expectedGateways := routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 1),
|
||||||
|
routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 1),
|
||||||
|
routing.NewGateway(netip.MustParseAddr("10.0.0.3"), 1)}
|
||||||
|
|
||||||
|
routing.CalculateBucketsForGateways(expectedGateways)
|
||||||
|
assert.ElementsMatch(t, expectedGateways, r)
|
||||||
|
|
||||||
|
ip, err = netip.ParseAddr("192.168.89.1")
|
||||||
|
require.NoError(t, err)
|
||||||
|
r, ok = routeTree.Lookup(ip)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
expectedGateways = routing.Gateways{routing.NewGateway(netip.MustParseAddr("10.0.0.1"), 10),
|
||||||
|
routing.NewGateway(netip.MustParseAddr("10.0.0.2"), 5)}
|
||||||
|
|
||||||
|
routing.CalculateBucketsForGateways(expectedGateways)
|
||||||
|
assert.ElementsMatch(t, expectedGateways, r)
|
||||||
|
}
|
||||||
|
|||||||
@ -13,6 +13,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ type tun struct {
|
|||||||
fd int
|
fd int
|
||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,7 +57,7 @@ func newTun(_ *config.C, _ *logrus.Logger, _ []netip.Prefix, _ bool) (*tun, erro
|
|||||||
return nil, fmt.Errorf("newTun not supported in Android")
|
return nil, fmt.Errorf("newTun not supported in Android")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
netroute "golang.org/x/net/route"
|
netroute "golang.org/x/net/route"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@ -28,7 +29,7 @@ type tun struct {
|
|||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
DefaultMTU int
|
DefaultMTU int
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
linkAddr *netroute.LinkAddr
|
linkAddr *netroute.LinkAddr
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
|
|
||||||
@ -342,12 +343,12 @@ func (t *tun) reload(c *config.C, initial bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, ok := t.routeTree.Load().Lookup(ip)
|
r, ok := t.routeTree.Load().Lookup(ip)
|
||||||
if ok {
|
if ok {
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
return netip.Addr{}
|
return routing.Gateways{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the LinkAddr for the interface of the given name
|
// Get the LinkAddr for the interface of the given name
|
||||||
@ -382,7 +383,7 @@ func (t *tun) addRoutes(logErrors bool) error {
|
|||||||
routes := *t.Routes.Load()
|
routes := *t.Routes.Load()
|
||||||
|
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if !r.Via.IsValid() || !r.Install {
|
if len(r.Via) == 0 || !r.Install {
|
||||||
// We don't allow route MTUs so only install routes with a via
|
// We don't allow route MTUs so only install routes with a via
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -393,7 +394,7 @@ func (t *tun) addRoutes(logErrors bool) error {
|
|||||||
t.l.WithField("route", r.Cidr).
|
t.l.WithField("route", r.Cidr).
|
||||||
Warnf("unable to add unsafe_route, identical route already exists")
|
Warnf("unable to add unsafe_route, identical route already exists")
|
||||||
} else {
|
} else {
|
||||||
retErr := util.NewContextualError("Failed to add route", map[string]interface{}{"route": r}, err)
|
retErr := util.NewContextualError("Failed to add route", map[string]any{"route": r}, err)
|
||||||
if logErrors {
|
if logErrors {
|
||||||
retErr.Log(t.l)
|
retErr.Log(t.l)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/rcrowley/go-metrics"
|
"github.com/rcrowley/go-metrics"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/iputil"
|
"github.com/slackhq/nebula/iputil"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type disabledTun struct {
|
type disabledTun struct {
|
||||||
@ -43,8 +44,8 @@ func (*disabledTun) Activate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*disabledTun) RouteFor(addr netip.Addr) netip.Addr {
|
func (*disabledTun) RoutesFor(addr netip.Addr) routing.Gateways {
|
||||||
return netip.Addr{}
|
return routing.Gateways{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *disabledTun) Networks() []netip.Prefix {
|
func (t *disabledTun) Networks() []netip.Prefix {
|
||||||
|
|||||||
@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -50,7 +51,7 @@ type tun struct {
|
|||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
MTU int
|
MTU int
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
|
|
||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
@ -242,7 +243,7 @@ func (t *tun) reload(c *config.C, initial bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@ -262,7 +263,7 @@ func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
|
|||||||
func (t *tun) addRoutes(logErrors bool) error {
|
func (t *tun) addRoutes(logErrors bool) error {
|
||||||
routes := *t.Routes.Load()
|
routes := *t.Routes.Load()
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if !r.Via.IsValid() || !r.Install {
|
if len(r.Via) == 0 || !r.Install {
|
||||||
// We don't allow route MTUs so only install routes with a via
|
// We don't allow route MTUs so only install routes with a via
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -270,7 +271,7 @@ func (t *tun) addRoutes(logErrors bool) error {
|
|||||||
cmd := exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), "-interface", t.Device)
|
cmd := exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), "-interface", t.Device)
|
||||||
t.l.Debug("command: ", cmd.String())
|
t.l.Debug("command: ", cmd.String())
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]interface{}{"route": r}, err)
|
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]any{"route": r}, err)
|
||||||
if logErrors {
|
if logErrors {
|
||||||
retErr.Log(t.l)
|
retErr.Log(t.l)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -23,7 +24,7 @@ type tun struct {
|
|||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -79,7 +80,7 @@ func (t *tun) reload(c *config.C, initial bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
"github.com/vishvananda/netlink"
|
"github.com/vishvananda/netlink"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
@ -34,7 +35,7 @@ type tun struct {
|
|||||||
ioctlFd uintptr
|
ioctlFd uintptr
|
||||||
|
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
routeChan chan struct{}
|
routeChan chan struct{}
|
||||||
useSystemRoutes bool
|
useSystemRoutes bool
|
||||||
|
|
||||||
@ -231,7 +232,7 @@ func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
|
|||||||
return file, nil
|
return file, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@ -463,7 +464,7 @@ func (t *tun) addRoutes(logErrors bool) error {
|
|||||||
|
|
||||||
err := netlink.RouteReplace(&nr)
|
err := netlink.RouteReplace(&nr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
retErr := util.NewContextualError("Failed to add route", map[string]interface{}{"route": r}, err)
|
retErr := util.NewContextualError("Failed to add route", map[string]any{"route": r}, err)
|
||||||
if logErrors {
|
if logErrors {
|
||||||
retErr.Log(t.l)
|
retErr.Log(t.l)
|
||||||
} else {
|
} else {
|
||||||
@ -550,20 +551,7 @@ func (t *tun) watchRoutes() {
|
|||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) updateRoutes(r netlink.RouteUpdate) {
|
func (t *tun) isGatewayInVpnNetworks(gwAddr netip.Addr) bool {
|
||||||
if r.Gw == nil {
|
|
||||||
// Not a gateway route, ignore
|
|
||||||
t.l.WithField("route", r).Debug("Ignoring route update, not a gateway route")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gwAddr, ok := netip.AddrFromSlice(r.Gw)
|
|
||||||
if !ok {
|
|
||||||
t.l.WithField("route", r).Debug("Ignoring route update, invalid gateway address")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
gwAddr = gwAddr.Unmap()
|
|
||||||
withinNetworks := false
|
withinNetworks := false
|
||||||
for i := range t.vpnNetworks {
|
for i := range t.vpnNetworks {
|
||||||
if t.vpnNetworks[i].Contains(gwAddr) {
|
if t.vpnNetworks[i].Contains(gwAddr) {
|
||||||
@ -571,9 +559,68 @@ func (t *tun) updateRoutes(r netlink.RouteUpdate) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !withinNetworks {
|
|
||||||
|
return withinNetworks
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tun) getGatewaysFromRoute(r *netlink.Route) routing.Gateways {
|
||||||
|
|
||||||
|
var gateways routing.Gateways
|
||||||
|
|
||||||
|
link, err := netlink.LinkByName(t.Device)
|
||||||
|
if err != nil {
|
||||||
|
t.l.WithField("Devicename", t.Device).Error("Ignoring route update: failed to get link by name")
|
||||||
|
return gateways
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this route is relevant to our interface and there is a gateway then add it
|
||||||
|
if r.LinkIndex == link.Attrs().Index && len(r.Gw) > 0 {
|
||||||
|
gwAddr, ok := netip.AddrFromSlice(r.Gw)
|
||||||
|
if !ok {
|
||||||
|
t.l.WithField("route", r).Debug("Ignoring route update, invalid gateway address")
|
||||||
|
} else {
|
||||||
|
gwAddr = gwAddr.Unmap()
|
||||||
|
|
||||||
|
if !t.isGatewayInVpnNetworks(gwAddr) {
|
||||||
// Gateway isn't in our overlay network, ignore
|
// Gateway isn't in our overlay network, ignore
|
||||||
t.l.WithField("route", r).Debug("Ignoring route update, not in our networks")
|
t.l.WithField("route", r).Debug("Ignoring route update, not in our network")
|
||||||
|
} else {
|
||||||
|
gateways = append(gateways, routing.NewGateway(gwAddr, 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range r.MultiPath {
|
||||||
|
// If this route is relevant to our interface and there is a gateway then add it
|
||||||
|
if p.LinkIndex == link.Attrs().Index && len(p.Gw) > 0 {
|
||||||
|
gwAddr, ok := netip.AddrFromSlice(p.Gw)
|
||||||
|
if !ok {
|
||||||
|
t.l.WithField("route", r).Debug("Ignoring multipath route update, invalid gateway address")
|
||||||
|
} else {
|
||||||
|
gwAddr = gwAddr.Unmap()
|
||||||
|
|
||||||
|
if !t.isGatewayInVpnNetworks(gwAddr) {
|
||||||
|
// Gateway isn't in our overlay network, ignore
|
||||||
|
t.l.WithField("route", r).Debug("Ignoring route update, not in our network")
|
||||||
|
} else {
|
||||||
|
// p.Hops+1 = weight of the route
|
||||||
|
gateways = append(gateways, routing.NewGateway(gwAddr, p.Hops+1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
routing.CalculateBucketsForGateways(gateways)
|
||||||
|
return gateways
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *tun) updateRoutes(r netlink.RouteUpdate) {
|
||||||
|
|
||||||
|
gateways := t.getGatewaysFromRoute(&r.Route)
|
||||||
|
|
||||||
|
if len(gateways) == 0 {
|
||||||
|
// No gateways relevant to our network, no routing changes required.
|
||||||
|
t.l.WithField("route", r).Debug("Ignoring route update, no gateways")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -589,12 +636,12 @@ func (t *tun) updateRoutes(r netlink.RouteUpdate) {
|
|||||||
newTree := t.routeTree.Load().Clone()
|
newTree := t.routeTree.Load().Clone()
|
||||||
|
|
||||||
if r.Type == unix.RTM_NEWROUTE {
|
if r.Type == unix.RTM_NEWROUTE {
|
||||||
t.l.WithField("destination", r.Dst).WithField("via", r.Gw).Info("Adding route")
|
t.l.WithField("destination", dst).WithField("via", gateways).Info("Adding route")
|
||||||
newTree.Insert(dst, gwAddr)
|
newTree.Insert(dst, gateways)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
t.l.WithField("destination", dst).WithField("via", gateways).Info("Removing route")
|
||||||
newTree.Delete(dst)
|
newTree.Delete(dst)
|
||||||
t.l.WithField("destination", r.Dst).WithField("via", r.Gw).Info("Removing route")
|
|
||||||
}
|
}
|
||||||
t.routeTree.Store(newTree)
|
t.routeTree.Store(newTree)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ type tun struct {
|
|||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
MTU int
|
MTU int
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
|
|
||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
@ -177,7 +178,7 @@ func (t *tun) reload(c *config.C, initial bool) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@ -197,7 +198,7 @@ func (t *tun) NewMultiQueueReader() (io.ReadWriteCloser, error) {
|
|||||||
func (t *tun) addRoutes(logErrors bool) error {
|
func (t *tun) addRoutes(logErrors bool) error {
|
||||||
routes := *t.Routes.Load()
|
routes := *t.Routes.Load()
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if !r.Via.IsValid() || !r.Install {
|
if len(r.Via) == 0 || !r.Install {
|
||||||
// We don't allow route MTUs so only install routes with a via
|
// We don't allow route MTUs so only install routes with a via
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -205,7 +206,7 @@ func (t *tun) addRoutes(logErrors bool) error {
|
|||||||
cmd := exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.vpnNetworks[0].Addr().String())
|
cmd := exec.Command("/sbin/route", "-n", "add", "-net", r.Cidr.String(), t.vpnNetworks[0].Addr().String())
|
||||||
t.l.Debug("command: ", cmd.String())
|
t.l.Debug("command: ", cmd.String())
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]interface{}{"route": r}, err)
|
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]any{"route": r}, err)
|
||||||
if logErrors {
|
if logErrors {
|
||||||
retErr.Log(t.l)
|
retErr.Log(t.l)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -25,7 +26,7 @@ type tun struct {
|
|||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
MTU int
|
MTU int
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
|
|
||||||
io.ReadWriteCloser
|
io.ReadWriteCloser
|
||||||
@ -158,7 +159,7 @@ func (t *tun) Activate() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *tun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
@ -166,7 +167,7 @@ func (t *tun) RouteFor(ip netip.Addr) netip.Addr {
|
|||||||
func (t *tun) addRoutes(logErrors bool) error {
|
func (t *tun) addRoutes(logErrors bool) error {
|
||||||
routes := *t.Routes.Load()
|
routes := *t.Routes.Load()
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if !r.Via.IsValid() || !r.Install {
|
if len(r.Via) == 0 || !r.Install {
|
||||||
// We don't allow route MTUs so only install routes with a via
|
// We don't allow route MTUs so only install routes with a via
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -174,7 +175,7 @@ func (t *tun) addRoutes(logErrors bool) error {
|
|||||||
cmd := exec.Command("/sbin/route", "-n", "add", "-inet", r.Cidr.String(), t.vpnNetworks[0].Addr().String())
|
cmd := exec.Command("/sbin/route", "-n", "add", "-inet", r.Cidr.String(), t.vpnNetworks[0].Addr().String())
|
||||||
t.l.Debug("command: ", cmd.String())
|
t.l.Debug("command: ", cmd.String())
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]interface{}{"route": r}, err)
|
retErr := util.NewContextualError("failed to run 'route add' for unsafe_route", map[string]any{"route": r}, err)
|
||||||
if logErrors {
|
if logErrors {
|
||||||
retErr.Log(t.l)
|
retErr.Log(t.l)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -13,13 +13,14 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TestTun struct {
|
type TestTun struct {
|
||||||
Device string
|
Device string
|
||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
Routes []Route
|
Routes []Route
|
||||||
routeTree *bart.Table[netip.Addr]
|
routeTree *bart.Table[routing.Gateways]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
|
|
||||||
closed atomic.Bool
|
closed atomic.Bool
|
||||||
@ -86,7 +87,7 @@ func (t *TestTun) Get(block bool) []byte {
|
|||||||
// Below this is boilerplate implementation to make nebula actually work
|
// Below this is boilerplate implementation to make nebula actually work
|
||||||
//********************************************************************************************************************//
|
//********************************************************************************************************************//
|
||||||
|
|
||||||
func (t *TestTun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *TestTun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Lookup(ip)
|
r, _ := t.routeTree.Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/gaissmai/bart"
|
"github.com/gaissmai/bart"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
"github.com/slackhq/nebula/util"
|
"github.com/slackhq/nebula/util"
|
||||||
"github.com/slackhq/nebula/wintun"
|
"github.com/slackhq/nebula/wintun"
|
||||||
"golang.org/x/sys/windows"
|
"golang.org/x/sys/windows"
|
||||||
@ -31,7 +32,7 @@ type winTun struct {
|
|||||||
vpnNetworks []netip.Prefix
|
vpnNetworks []netip.Prefix
|
||||||
MTU int
|
MTU int
|
||||||
Routes atomic.Pointer[[]Route]
|
Routes atomic.Pointer[[]Route]
|
||||||
routeTree atomic.Pointer[bart.Table[netip.Addr]]
|
routeTree atomic.Pointer[bart.Table[routing.Gateways]]
|
||||||
l *logrus.Logger
|
l *logrus.Logger
|
||||||
|
|
||||||
tun *wintun.NativeTun
|
tun *wintun.NativeTun
|
||||||
@ -147,15 +148,18 @@ func (t *winTun) addRoutes(logErrors bool) error {
|
|||||||
foundDefault4 := false
|
foundDefault4 := false
|
||||||
|
|
||||||
for _, r := range routes {
|
for _, r := range routes {
|
||||||
if !r.Via.IsValid() || !r.Install {
|
if len(r.Via) == 0 || !r.Install {
|
||||||
// We don't allow route MTUs so only install routes with a via
|
// We don't allow route MTUs so only install routes with a via
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add our unsafe route
|
// Add our unsafe route
|
||||||
err := luid.AddRoute(r.Cidr, r.Via, uint32(r.Metric))
|
// Windows does not support multipath routes natively, so we install only a single route.
|
||||||
|
// This is not a problem as traffic will always be sent to Nebula which handles the multipath routing internally.
|
||||||
|
// In effect this provides multipath routing support to windows supporting loadbalancing and redundancy.
|
||||||
|
err := luid.AddRoute(r.Cidr, r.Via[0].Addr(), uint32(r.Metric))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
retErr := util.NewContextualError("Failed to add route", map[string]interface{}{"route": r}, err)
|
retErr := util.NewContextualError("Failed to add route", map[string]any{"route": r}, err)
|
||||||
if logErrors {
|
if logErrors {
|
||||||
retErr.Log(t.l)
|
retErr.Log(t.l)
|
||||||
continue
|
continue
|
||||||
@ -198,7 +202,8 @@ func (t *winTun) removeRoutes(routes []Route) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
err := luid.DeleteRoute(r.Cidr, r.Via)
|
// See comment on luid.AddRoute
|
||||||
|
err := luid.DeleteRoute(r.Cidr, r.Via[0].Addr())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.l.WithError(err).WithField("route", r).Error("Failed to remove route")
|
t.l.WithError(err).WithField("route", r).Error("Failed to remove route")
|
||||||
} else {
|
} else {
|
||||||
@ -208,7 +213,7 @@ func (t *winTun) removeRoutes(routes []Route) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *winTun) RouteFor(ip netip.Addr) netip.Addr {
|
func (t *winTun) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
r, _ := t.routeTree.Load().Lookup(ip)
|
r, _ := t.routeTree.Load().Lookup(ip)
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func NewUserDeviceFromConfig(c *config.C, l *logrus.Logger, vpnNetworks []netip.Prefix, routines int) (Device, error) {
|
func NewUserDeviceFromConfig(c *config.C, l *logrus.Logger, vpnNetworks []netip.Prefix, routines int) (Device, error) {
|
||||||
@ -38,9 +39,13 @@ type UserDevice struct {
|
|||||||
func (d *UserDevice) Activate() error {
|
func (d *UserDevice) Activate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *UserDevice) Networks() []netip.Prefix { return d.vpnNetworks }
|
func (d *UserDevice) Networks() []netip.Prefix { return d.vpnNetworks }
|
||||||
func (d *UserDevice) Name() string { return "faketun0" }
|
func (d *UserDevice) Name() string { return "faketun0" }
|
||||||
func (d *UserDevice) RouteFor(ip netip.Addr) netip.Addr { return ip }
|
func (d *UserDevice) RoutesFor(ip netip.Addr) routing.Gateways {
|
||||||
|
return routing.Gateways{routing.NewGateway(ip, 1)}
|
||||||
|
}
|
||||||
|
|
||||||
func (d *UserDevice) NewMultiQueueReader() (io.ReadWriteCloser, error) {
|
func (d *UserDevice) NewMultiQueueReader() (io.ReadWriteCloser, error) {
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ func TestNewPunchyFromConfig(t *testing.T) {
|
|||||||
assert.True(t, p.GetPunch())
|
assert.True(t, p.GetPunch())
|
||||||
|
|
||||||
// punchy.punch
|
// punchy.punch
|
||||||
c.Settings["punchy"] = map[interface{}]interface{}{"punch": true}
|
c.Settings["punchy"] = map[string]any{"punch": true}
|
||||||
p = NewPunchyFromConfig(l, c)
|
p = NewPunchyFromConfig(l, c)
|
||||||
assert.True(t, p.GetPunch())
|
assert.True(t, p.GetPunch())
|
||||||
|
|
||||||
@ -37,18 +37,18 @@ func TestNewPunchyFromConfig(t *testing.T) {
|
|||||||
assert.True(t, p.GetRespond())
|
assert.True(t, p.GetRespond())
|
||||||
|
|
||||||
// punchy.respond
|
// punchy.respond
|
||||||
c.Settings["punchy"] = map[interface{}]interface{}{"respond": true}
|
c.Settings["punchy"] = map[string]any{"respond": true}
|
||||||
c.Settings["punch_back"] = false
|
c.Settings["punch_back"] = false
|
||||||
p = NewPunchyFromConfig(l, c)
|
p = NewPunchyFromConfig(l, c)
|
||||||
assert.True(t, p.GetRespond())
|
assert.True(t, p.GetRespond())
|
||||||
|
|
||||||
// punchy.delay
|
// punchy.delay
|
||||||
c.Settings["punchy"] = map[interface{}]interface{}{"delay": "1m"}
|
c.Settings["punchy"] = map[string]any{"delay": "1m"}
|
||||||
p = NewPunchyFromConfig(l, c)
|
p = NewPunchyFromConfig(l, c)
|
||||||
assert.Equal(t, time.Minute, p.GetDelay())
|
assert.Equal(t, time.Minute, p.GetDelay())
|
||||||
|
|
||||||
// punchy.respond_delay
|
// punchy.respond_delay
|
||||||
c.Settings["punchy"] = map[interface{}]interface{}{"respond_delay": "1m"}
|
c.Settings["punchy"] = map[string]any{"respond_delay": "1m"}
|
||||||
p = NewPunchyFromConfig(l, c)
|
p = NewPunchyFromConfig(l, c)
|
||||||
assert.Equal(t, time.Minute, p.GetRespondDelay())
|
assert.Equal(t, time.Minute, p.GetRespondDelay())
|
||||||
}
|
}
|
||||||
|
|||||||
39
routing/balance.go
Normal file
39
routing/balance.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/firewall"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Hashes the packet source and destination port and always returns a positive integer
|
||||||
|
// Based on 'Prospecting for Hash Functions'
|
||||||
|
// - https://nullprogram.com/blog/2018/07/31/
|
||||||
|
// - https://github.com/skeeto/hash-prospector
|
||||||
|
// [16 21f0aaad 15 d35a2d97 15] = 0.10760229515479501
|
||||||
|
func hashPacket(p *firewall.Packet) int {
|
||||||
|
x := (uint32(p.LocalPort) << 16) | uint32(p.RemotePort)
|
||||||
|
x ^= x >> 16
|
||||||
|
x *= 0x21f0aaad
|
||||||
|
x ^= x >> 15
|
||||||
|
x *= 0xd35a2d97
|
||||||
|
x ^= x >> 15
|
||||||
|
|
||||||
|
return int(x) & 0x7FFFFFFF
|
||||||
|
}
|
||||||
|
|
||||||
|
// For this function to work correctly it requires that the buckets for the gateways have been calculated
|
||||||
|
// If the contract is violated balancing will not work properly and the second return value will return false
|
||||||
|
func BalancePacket(fwPacket *firewall.Packet, gateways []Gateway) (netip.Addr, bool) {
|
||||||
|
hash := hashPacket(fwPacket)
|
||||||
|
|
||||||
|
for i := range gateways {
|
||||||
|
if hash <= gateways[i].BucketUpperBound() {
|
||||||
|
return gateways[i].Addr(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If you land here then the buckets for the gateways are not properly calculated
|
||||||
|
// Fallback to random routing and let the caller know
|
||||||
|
return gateways[hash%len(gateways)].Addr(), false
|
||||||
|
}
|
||||||
144
routing/balance_test.go
Normal file
144
routing/balance_test.go
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/firewall"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPacketsAreBalancedEqually(t *testing.T) {
|
||||||
|
|
||||||
|
gateways := []Gateway{}
|
||||||
|
|
||||||
|
gw1Addr := netip.MustParseAddr("1.0.0.1")
|
||||||
|
gw2Addr := netip.MustParseAddr("1.0.0.2")
|
||||||
|
gw3Addr := netip.MustParseAddr("1.0.0.3")
|
||||||
|
|
||||||
|
gateways = append(gateways, NewGateway(gw1Addr, 1))
|
||||||
|
gateways = append(gateways, NewGateway(gw2Addr, 1))
|
||||||
|
gateways = append(gateways, NewGateway(gw3Addr, 1))
|
||||||
|
|
||||||
|
CalculateBucketsForGateways(gateways)
|
||||||
|
|
||||||
|
gw1count := 0
|
||||||
|
gw2count := 0
|
||||||
|
gw3count := 0
|
||||||
|
|
||||||
|
iterationCount := uint16(65535)
|
||||||
|
for i := uint16(0); i < iterationCount; i++ {
|
||||||
|
packet := firewall.Packet{
|
||||||
|
LocalAddr: netip.MustParseAddr("192.168.1.1"),
|
||||||
|
RemoteAddr: netip.MustParseAddr("10.0.0.1"),
|
||||||
|
LocalPort: i,
|
||||||
|
RemotePort: 65535 - i,
|
||||||
|
Protocol: 6, // TCP
|
||||||
|
Fragment: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedGw, ok := BalancePacket(&packet, gateways)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
switch selectedGw {
|
||||||
|
case gw1Addr:
|
||||||
|
gw1count += 1
|
||||||
|
case gw2Addr:
|
||||||
|
gw2count += 1
|
||||||
|
case gw3Addr:
|
||||||
|
gw3count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Assert packets are balanced, allow variation of up to 100 packets per gateway
|
||||||
|
assert.InDeltaf(t, iterationCount/3, gw1count, 100, "Expected %d +/- 100, but got %d", iterationCount/3, gw1count)
|
||||||
|
assert.InDeltaf(t, iterationCount/3, gw2count, 100, "Expected %d +/- 100, but got %d", iterationCount/3, gw1count)
|
||||||
|
assert.InDeltaf(t, iterationCount/3, gw3count, 100, "Expected %d +/- 100, but got %d", iterationCount/3, gw1count)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPacketsAreBalancedByPriority(t *testing.T) {
|
||||||
|
|
||||||
|
gateways := []Gateway{}
|
||||||
|
|
||||||
|
gw1Addr := netip.MustParseAddr("1.0.0.1")
|
||||||
|
gw2Addr := netip.MustParseAddr("1.0.0.2")
|
||||||
|
|
||||||
|
gateways = append(gateways, NewGateway(gw1Addr, 10))
|
||||||
|
gateways = append(gateways, NewGateway(gw2Addr, 5))
|
||||||
|
|
||||||
|
CalculateBucketsForGateways(gateways)
|
||||||
|
|
||||||
|
gw1count := 0
|
||||||
|
gw2count := 0
|
||||||
|
|
||||||
|
iterationCount := uint16(65535)
|
||||||
|
for i := uint16(0); i < iterationCount; i++ {
|
||||||
|
packet := firewall.Packet{
|
||||||
|
LocalAddr: netip.MustParseAddr("192.168.1.1"),
|
||||||
|
RemoteAddr: netip.MustParseAddr("10.0.0.1"),
|
||||||
|
LocalPort: i,
|
||||||
|
RemotePort: 65535 - i,
|
||||||
|
Protocol: 6, // TCP
|
||||||
|
Fragment: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedGw, ok := BalancePacket(&packet, gateways)
|
||||||
|
assert.True(t, ok)
|
||||||
|
|
||||||
|
switch selectedGw {
|
||||||
|
case gw1Addr:
|
||||||
|
gw1count += 1
|
||||||
|
case gw2Addr:
|
||||||
|
gw2count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
iterationCountAsFloat := float32(iterationCount)
|
||||||
|
|
||||||
|
assert.InDeltaf(t, iterationCountAsFloat*(2.0/3.0), gw1count, 100, "Expected %d +/- 100, but got %d", iterationCountAsFloat*(2.0/3.0), gw1count)
|
||||||
|
assert.InDeltaf(t, iterationCountAsFloat*(1.0/3.0), gw2count, 100, "Expected %d +/- 100, but got %d", iterationCountAsFloat*(1.0/3.0), gw2count)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBalancePacketDistributsRandomlyAndReturnsFalseIfBucketsNotCalculated(t *testing.T) {
|
||||||
|
gateways := []Gateway{}
|
||||||
|
|
||||||
|
gw1Addr := netip.MustParseAddr("1.0.0.1")
|
||||||
|
gw2Addr := netip.MustParseAddr("1.0.0.2")
|
||||||
|
|
||||||
|
gateways = append(gateways, NewGateway(gw1Addr, 10))
|
||||||
|
gateways = append(gateways, NewGateway(gw2Addr, 5))
|
||||||
|
|
||||||
|
iterationCount := uint16(65535)
|
||||||
|
gw1count := 0
|
||||||
|
gw2count := 0
|
||||||
|
|
||||||
|
for i := uint16(0); i < iterationCount; i++ {
|
||||||
|
packet := firewall.Packet{
|
||||||
|
LocalAddr: netip.MustParseAddr("192.168.1.1"),
|
||||||
|
RemoteAddr: netip.MustParseAddr("10.0.0.1"),
|
||||||
|
LocalPort: i,
|
||||||
|
RemotePort: 65535 - i,
|
||||||
|
Protocol: 6, // TCP
|
||||||
|
Fragment: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedGw, ok := BalancePacket(&packet, gateways)
|
||||||
|
assert.False(t, ok)
|
||||||
|
|
||||||
|
switch selectedGw {
|
||||||
|
case gw1Addr:
|
||||||
|
gw1count += 1
|
||||||
|
case gw2Addr:
|
||||||
|
gw2count += 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, int(iterationCount), (gw1count + gw2count))
|
||||||
|
assert.NotEqual(t, 0, gw1count)
|
||||||
|
assert.NotEqual(t, 0, gw2count)
|
||||||
|
|
||||||
|
}
|
||||||
70
routing/gateway.go
Normal file
70
routing/gateway.go
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/netip"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Sentinal value
|
||||||
|
BucketNotCalculated = -1
|
||||||
|
)
|
||||||
|
|
||||||
|
type Gateways []Gateway
|
||||||
|
|
||||||
|
func (g Gateways) String() string {
|
||||||
|
str := ""
|
||||||
|
for i, gw := range g {
|
||||||
|
str += gw.String()
|
||||||
|
if i < len(g)-1 {
|
||||||
|
str += ", "
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return str
|
||||||
|
}
|
||||||
|
|
||||||
|
type Gateway struct {
|
||||||
|
addr netip.Addr
|
||||||
|
weight int
|
||||||
|
bucketUpperBound int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGateway(addr netip.Addr, weight int) Gateway {
|
||||||
|
return Gateway{addr: addr, weight: weight, bucketUpperBound: BucketNotCalculated}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) BucketUpperBound() int {
|
||||||
|
return g.bucketUpperBound
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) Addr() netip.Addr {
|
||||||
|
return g.addr
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Gateway) String() string {
|
||||||
|
return fmt.Sprintf("{addr: %s, weight: %d}", g.addr, g.weight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Divide and round to nearest integer
|
||||||
|
func divideAndRound(v uint64, d uint64) uint64 {
|
||||||
|
var tmp uint64 = v + d/2
|
||||||
|
return tmp / d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Implements Hash-Threshold mapping, equivalent to the implementation in the linux kernel.
|
||||||
|
// After this function returns each gateway will have a
|
||||||
|
// positive bucketUpperBound with a maximum value of 2147483647 (INT_MAX)
|
||||||
|
func CalculateBucketsForGateways(gateways []Gateway) {
|
||||||
|
|
||||||
|
var totalWeight int = 0
|
||||||
|
for i := range gateways {
|
||||||
|
totalWeight += gateways[i].weight
|
||||||
|
}
|
||||||
|
|
||||||
|
var loopWeight int = 0
|
||||||
|
for i := range gateways {
|
||||||
|
loopWeight += gateways[i].weight
|
||||||
|
gateways[i].bucketUpperBound = int(divideAndRound(uint64(loopWeight)<<31, uint64(totalWeight))) - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
34
routing/gateway_test.go
Normal file
34
routing/gateway_test.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
package routing
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/netip"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRebalance3_2Split(t *testing.T) {
|
||||||
|
gateways := []Gateway{}
|
||||||
|
|
||||||
|
gateways = append(gateways, Gateway{addr: netip.Addr{}, weight: 10})
|
||||||
|
gateways = append(gateways, Gateway{addr: netip.Addr{}, weight: 5})
|
||||||
|
|
||||||
|
CalculateBucketsForGateways(gateways)
|
||||||
|
|
||||||
|
assert.Equal(t, 1431655764, gateways[0].bucketUpperBound) // INT_MAX/3*2
|
||||||
|
assert.Equal(t, 2147483647, gateways[1].bucketUpperBound) // INT_MAX
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRebalanceEqualSplit(t *testing.T) {
|
||||||
|
gateways := []Gateway{}
|
||||||
|
|
||||||
|
gateways = append(gateways, Gateway{addr: netip.Addr{}, weight: 1})
|
||||||
|
gateways = append(gateways, Gateway{addr: netip.Addr{}, weight: 1})
|
||||||
|
gateways = append(gateways, Gateway{addr: netip.Addr{}, weight: 1})
|
||||||
|
|
||||||
|
CalculateBucketsForGateways(gateways)
|
||||||
|
|
||||||
|
assert.Equal(t, 715827882, gateways[0].bucketUpperBound) // INT_MAX/3
|
||||||
|
assert.Equal(t, 1431655764, gateways[1].bucketUpperBound) // INT_MAX/3*2
|
||||||
|
assert.Equal(t, 2147483647, gateways[2].bucketUpperBound) // INT_MAX
|
||||||
|
}
|
||||||
@ -13,10 +13,10 @@ import (
|
|||||||
"github.com/slackhq/nebula/cert_test"
|
"github.com/slackhq/nebula/cert_test"
|
||||||
"github.com/slackhq/nebula/config"
|
"github.com/slackhq/nebula/config"
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
"gopkg.in/yaml.v2"
|
"gopkg.in/yaml.v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
func newSimpleService(caCrt cert.Certificate, caKey []byte, name string, udpIp netip.Addr, overrides m) *Service {
|
func newSimpleService(caCrt cert.Certificate, caKey []byte, name string, udpIp netip.Addr, overrides m) *Service {
|
||||||
_, _, myPrivKey, myPEM := cert_test.NewTestCert(cert.Version2, cert.Curve_CURVE25519, caCrt, caKey, "a", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{netip.PrefixFrom(udpIp, 24)}, nil, []string{})
|
_, _, myPrivKey, myPEM := cert_test.NewTestCert(cert.Version2, cert.Curve_CURVE25519, caCrt, caKey, "a", time.Now(), time.Now().Add(5*time.Minute), []netip.Prefix{netip.PrefixFrom(udpIp, 24)}, nil, []string{})
|
||||||
|
|||||||
92
ssh.go
92
ssh.go
@ -124,10 +124,10 @@ func configSSH(l *logrus.Logger, ssh *sshd.SSHServer, c *config.C) (func(), erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
rawKeys := c.Get("sshd.authorized_users")
|
rawKeys := c.Get("sshd.authorized_users")
|
||||||
keys, ok := rawKeys.([]interface{})
|
keys, ok := rawKeys.([]any)
|
||||||
if ok {
|
if ok {
|
||||||
for _, rk := range keys {
|
for _, rk := range keys {
|
||||||
kDef, ok := rk.(map[interface{}]interface{})
|
kDef, ok := rk.(map[string]any)
|
||||||
if !ok {
|
if !ok {
|
||||||
l.WithField("sshKeyConfig", rk).Warn("Authorized user had an error, ignoring")
|
l.WithField("sshKeyConfig", rk).Warn("Authorized user had an error, ignoring")
|
||||||
continue
|
continue
|
||||||
@ -148,7 +148,7 @@ func configSSH(l *logrus.Logger, ssh *sshd.SSHServer, c *config.C) (func(), erro
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
case []interface{}:
|
case []any:
|
||||||
for _, subK := range v {
|
for _, subK := range v {
|
||||||
sk, ok := subK.(string)
|
sk, ok := subK.(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -190,7 +190,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "list-hostmap",
|
Name: "list-hostmap",
|
||||||
ShortDescription: "List all known previously connected hosts",
|
ShortDescription: "List all known previously connected hosts",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshListHostMapFlags{}
|
s := sshListHostMapFlags{}
|
||||||
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
||||||
@ -198,7 +198,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
fl.BoolVar(&s.ByIndex, "by-index", false, "gets all hosts in the hostmap from the index table")
|
fl.BoolVar(&s.ByIndex, "by-index", false, "gets all hosts in the hostmap from the index table")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshListHostMap(f.hostMap, fs, w)
|
return sshListHostMap(f.hostMap, fs, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -206,7 +206,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "list-pending-hostmap",
|
Name: "list-pending-hostmap",
|
||||||
ShortDescription: "List all handshaking hosts",
|
ShortDescription: "List all handshaking hosts",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshListHostMapFlags{}
|
s := sshListHostMapFlags{}
|
||||||
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
||||||
@ -214,7 +214,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
fl.BoolVar(&s.ByIndex, "by-index", false, "gets all hosts in the hostmap from the index table")
|
fl.BoolVar(&s.ByIndex, "by-index", false, "gets all hosts in the hostmap from the index table")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshListHostMap(f.handshakeManager, fs, w)
|
return sshListHostMap(f.handshakeManager, fs, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -222,14 +222,14 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "list-lighthouse-addrmap",
|
Name: "list-lighthouse-addrmap",
|
||||||
ShortDescription: "List all lighthouse map entries",
|
ShortDescription: "List all lighthouse map entries",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshListHostMapFlags{}
|
s := sshListHostMapFlags{}
|
||||||
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
||||||
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
|
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshListLighthouseMap(f.lightHouse, fs, w)
|
return sshListLighthouseMap(f.lightHouse, fs, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -237,7 +237,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "reload",
|
Name: "reload",
|
||||||
ShortDescription: "Reloads configuration from disk, same as sending HUP to the process",
|
ShortDescription: "Reloads configuration from disk, same as sending HUP to the process",
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshReload(c, w)
|
return sshReload(c, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -251,7 +251,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "stop-cpu-profile",
|
Name: "stop-cpu-profile",
|
||||||
ShortDescription: "Stops a cpu profile and writes output to the previously provided file",
|
ShortDescription: "Stops a cpu profile and writes output to the previously provided file",
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
pprof.StopCPUProfile()
|
pprof.StopCPUProfile()
|
||||||
return w.WriteLine("If a CPU profile was running it is now stopped")
|
return w.WriteLine("If a CPU profile was running it is now stopped")
|
||||||
},
|
},
|
||||||
@ -278,7 +278,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "log-level",
|
Name: "log-level",
|
||||||
ShortDescription: "Gets or sets the current log level",
|
ShortDescription: "Gets or sets the current log level",
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshLogLevel(l, fs, a, w)
|
return sshLogLevel(l, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -286,7 +286,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "log-format",
|
Name: "log-format",
|
||||||
ShortDescription: "Gets or sets the current log format",
|
ShortDescription: "Gets or sets the current log format",
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshLogFormat(l, fs, a, w)
|
return sshLogFormat(l, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -294,7 +294,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "version",
|
Name: "version",
|
||||||
ShortDescription: "Prints the currently running version of nebula",
|
ShortDescription: "Prints the currently running version of nebula",
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshVersion(f, fs, a, w)
|
return sshVersion(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -302,14 +302,14 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "device-info",
|
Name: "device-info",
|
||||||
ShortDescription: "Prints information about the network device.",
|
ShortDescription: "Prints information about the network device.",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshDeviceInfoFlags{}
|
s := sshDeviceInfoFlags{}
|
||||||
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
fl.BoolVar(&s.Json, "json", false, "outputs as json with more information")
|
||||||
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
|
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json, assumes -json")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshDeviceInfo(f, fs, w)
|
return sshDeviceInfo(f, fs, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -317,7 +317,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "print-cert",
|
Name: "print-cert",
|
||||||
ShortDescription: "Prints the current certificate being used or the certificate for the provided vpn addr",
|
ShortDescription: "Prints the current certificate being used or the certificate for the provided vpn addr",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshPrintCertFlags{}
|
s := sshPrintCertFlags{}
|
||||||
fl.BoolVar(&s.Json, "json", false, "outputs as json")
|
fl.BoolVar(&s.Json, "json", false, "outputs as json")
|
||||||
@ -325,7 +325,7 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
fl.BoolVar(&s.Raw, "raw", false, "raw prints the PEM encoded certificate, not compatible with -json or -pretty")
|
fl.BoolVar(&s.Raw, "raw", false, "raw prints the PEM encoded certificate, not compatible with -json or -pretty")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshPrintCert(f, fs, a, w)
|
return sshPrintCert(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -333,13 +333,13 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "print-tunnel",
|
Name: "print-tunnel",
|
||||||
ShortDescription: "Prints json details about a tunnel for the provided vpn addr",
|
ShortDescription: "Prints json details about a tunnel for the provided vpn addr",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshPrintTunnelFlags{}
|
s := sshPrintTunnelFlags{}
|
||||||
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json")
|
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshPrintTunnel(f, fs, a, w)
|
return sshPrintTunnel(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -347,13 +347,13 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "print-relays",
|
Name: "print-relays",
|
||||||
ShortDescription: "Prints json details about all relay info",
|
ShortDescription: "Prints json details about all relay info",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshPrintTunnelFlags{}
|
s := sshPrintTunnelFlags{}
|
||||||
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json")
|
fl.BoolVar(&s.Pretty, "pretty", false, "pretty prints json")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshPrintRelays(f, fs, a, w)
|
return sshPrintRelays(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -361,13 +361,13 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "change-remote",
|
Name: "change-remote",
|
||||||
ShortDescription: "Changes the remote address used in the tunnel for the provided vpn addr",
|
ShortDescription: "Changes the remote address used in the tunnel for the provided vpn addr",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshChangeRemoteFlags{}
|
s := sshChangeRemoteFlags{}
|
||||||
fl.StringVar(&s.Address, "address", "", "The new remote address, ip:port")
|
fl.StringVar(&s.Address, "address", "", "The new remote address, ip:port")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshChangeRemote(f, fs, a, w)
|
return sshChangeRemote(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -375,13 +375,13 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
ssh.RegisterCommand(&sshd.Command{
|
ssh.RegisterCommand(&sshd.Command{
|
||||||
Name: "close-tunnel",
|
Name: "close-tunnel",
|
||||||
ShortDescription: "Closes a tunnel for the provided vpn addr",
|
ShortDescription: "Closes a tunnel for the provided vpn addr",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshCloseTunnelFlags{}
|
s := sshCloseTunnelFlags{}
|
||||||
fl.BoolVar(&s.LocalOnly, "local-only", false, "Disables notifying the remote that the tunnel is shutting down")
|
fl.BoolVar(&s.LocalOnly, "local-only", false, "Disables notifying the remote that the tunnel is shutting down")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshCloseTunnel(f, fs, a, w)
|
return sshCloseTunnel(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -390,13 +390,13 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
Name: "create-tunnel",
|
Name: "create-tunnel",
|
||||||
ShortDescription: "Creates a tunnel for the provided vpn address",
|
ShortDescription: "Creates a tunnel for the provided vpn address",
|
||||||
Help: "The lighthouses will be queried for real addresses but you can provide one as well.",
|
Help: "The lighthouses will be queried for real addresses but you can provide one as well.",
|
||||||
Flags: func() (*flag.FlagSet, interface{}) {
|
Flags: func() (*flag.FlagSet, any) {
|
||||||
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
fl := flag.NewFlagSet("", flag.ContinueOnError)
|
||||||
s := sshCreateTunnelFlags{}
|
s := sshCreateTunnelFlags{}
|
||||||
fl.StringVar(&s.Address, "address", "", "Optionally provide a real remote address, ip:port ")
|
fl.StringVar(&s.Address, "address", "", "Optionally provide a real remote address, ip:port ")
|
||||||
return fl, &s
|
return fl, &s
|
||||||
},
|
},
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshCreateTunnel(f, fs, a, w)
|
return sshCreateTunnel(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@ -405,13 +405,13 @@ func attachCommands(l *logrus.Logger, c *config.C, ssh *sshd.SSHServer, f *Inter
|
|||||||
Name: "query-lighthouse",
|
Name: "query-lighthouse",
|
||||||
ShortDescription: "Query the lighthouses for the provided vpn address",
|
ShortDescription: "Query the lighthouses for the provided vpn address",
|
||||||
Help: "This command is asynchronous. Only currently known udp addresses will be printed.",
|
Help: "This command is asynchronous. Only currently known udp addresses will be printed.",
|
||||||
Callback: func(fs interface{}, a []string, w sshd.StringWriter) error {
|
Callback: func(fs any, a []string, w sshd.StringWriter) error {
|
||||||
return sshQueryLighthouse(f, fs, a, w)
|
return sshQueryLighthouse(f, fs, a, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshListHostMap(hl controlHostLister, a interface{}, w sshd.StringWriter) error {
|
func sshListHostMap(hl controlHostLister, a any, w sshd.StringWriter) error {
|
||||||
fs, ok := a.(*sshListHostMapFlags)
|
fs, ok := a.(*sshListHostMapFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -451,7 +451,7 @@ func sshListHostMap(hl controlHostLister, a interface{}, w sshd.StringWriter) er
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshListLighthouseMap(lightHouse *LightHouse, a interface{}, w sshd.StringWriter) error {
|
func sshListLighthouseMap(lightHouse *LightHouse, a any, w sshd.StringWriter) error {
|
||||||
fs, ok := a.(*sshListHostMapFlags)
|
fs, ok := a.(*sshListHostMapFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -505,7 +505,7 @@ func sshListLighthouseMap(lightHouse *LightHouse, a interface{}, w sshd.StringWr
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshStartCpuProfile(fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshStartCpuProfile(fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
err := w.WriteLine("No path to write profile provided")
|
err := w.WriteLine("No path to write profile provided")
|
||||||
return err
|
return err
|
||||||
@ -527,11 +527,11 @@ func sshStartCpuProfile(fs interface{}, a []string, w sshd.StringWriter) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshVersion(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshVersion(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
return w.WriteLine(fmt.Sprintf("%s", ifce.version))
|
return w.WriteLine(fmt.Sprintf("%s", ifce.version))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshQueryLighthouse(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshQueryLighthouse(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return w.WriteLine("No vpn address was provided")
|
return w.WriteLine("No vpn address was provided")
|
||||||
}
|
}
|
||||||
@ -553,7 +553,7 @@ func sshQueryLighthouse(ifce *Interface, fs interface{}, a []string, w sshd.Stri
|
|||||||
return json.NewEncoder(w.GetWriter()).Encode(cm)
|
return json.NewEncoder(w.GetWriter()).Encode(cm)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshCloseTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshCloseTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
flags, ok := fs.(*sshCloseTunnelFlags)
|
flags, ok := fs.(*sshCloseTunnelFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -593,7 +593,7 @@ func sshCloseTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWr
|
|||||||
return w.WriteLine("Closed")
|
return w.WriteLine("Closed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshCreateTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshCreateTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
flags, ok := fs.(*sshCreateTunnelFlags)
|
flags, ok := fs.(*sshCreateTunnelFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -638,7 +638,7 @@ func sshCreateTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringW
|
|||||||
return w.WriteLine("Created")
|
return w.WriteLine("Created")
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshChangeRemote(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshChangeRemote(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
flags, ok := fs.(*sshChangeRemoteFlags)
|
flags, ok := fs.(*sshChangeRemoteFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -675,7 +675,7 @@ func sshChangeRemote(ifce *Interface, fs interface{}, a []string, w sshd.StringW
|
|||||||
return w.WriteLine("Changed")
|
return w.WriteLine("Changed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshGetHeapProfile(fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshGetHeapProfile(fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return w.WriteLine("No path to write profile provided")
|
return w.WriteLine("No path to write profile provided")
|
||||||
}
|
}
|
||||||
@ -696,7 +696,7 @@ func sshGetHeapProfile(fs interface{}, a []string, w sshd.StringWriter) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshMutexProfileFraction(fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshMutexProfileFraction(fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
rate := runtime.SetMutexProfileFraction(-1)
|
rate := runtime.SetMutexProfileFraction(-1)
|
||||||
return w.WriteLine(fmt.Sprintf("Current value: %d", rate))
|
return w.WriteLine(fmt.Sprintf("Current value: %d", rate))
|
||||||
@ -711,7 +711,7 @@ func sshMutexProfileFraction(fs interface{}, a []string, w sshd.StringWriter) er
|
|||||||
return w.WriteLine(fmt.Sprintf("New value: %d. Old value: %d", newRate, oldRate))
|
return w.WriteLine(fmt.Sprintf("New value: %d. Old value: %d", newRate, oldRate))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshGetMutexProfile(fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshGetMutexProfile(fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return w.WriteLine("No path to write profile provided")
|
return w.WriteLine("No path to write profile provided")
|
||||||
}
|
}
|
||||||
@ -735,7 +735,7 @@ func sshGetMutexProfile(fs interface{}, a []string, w sshd.StringWriter) error {
|
|||||||
return w.WriteLine(fmt.Sprintf("Mutex profile created at %s", a))
|
return w.WriteLine(fmt.Sprintf("Mutex profile created at %s", a))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshLogLevel(l *logrus.Logger, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshLogLevel(l *logrus.Logger, fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return w.WriteLine(fmt.Sprintf("Log level is: %s", l.Level))
|
return w.WriteLine(fmt.Sprintf("Log level is: %s", l.Level))
|
||||||
}
|
}
|
||||||
@ -749,7 +749,7 @@ func sshLogLevel(l *logrus.Logger, fs interface{}, a []string, w sshd.StringWrit
|
|||||||
return w.WriteLine(fmt.Sprintf("Log level is: %s", l.Level))
|
return w.WriteLine(fmt.Sprintf("Log level is: %s", l.Level))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshLogFormat(l *logrus.Logger, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshLogFormat(l *logrus.Logger, fs any, a []string, w sshd.StringWriter) error {
|
||||||
if len(a) == 0 {
|
if len(a) == 0 {
|
||||||
return w.WriteLine(fmt.Sprintf("Log format is: %s", reflect.TypeOf(l.Formatter)))
|
return w.WriteLine(fmt.Sprintf("Log format is: %s", reflect.TypeOf(l.Formatter)))
|
||||||
}
|
}
|
||||||
@ -767,7 +767,7 @@ func sshLogFormat(l *logrus.Logger, fs interface{}, a []string, w sshd.StringWri
|
|||||||
return w.WriteLine(fmt.Sprintf("Log format is: %s", reflect.TypeOf(l.Formatter)))
|
return w.WriteLine(fmt.Sprintf("Log format is: %s", reflect.TypeOf(l.Formatter)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshPrintCert(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshPrintCert(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
args, ok := fs.(*sshPrintCertFlags)
|
args, ok := fs.(*sshPrintCertFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -822,7 +822,7 @@ func sshPrintCert(ifce *Interface, fs interface{}, a []string, w sshd.StringWrit
|
|||||||
return w.WriteLine(cert.String())
|
return w.WriteLine(cert.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshPrintRelays(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshPrintRelays(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
args, ok := fs.(*sshPrintTunnelFlags)
|
args, ok := fs.(*sshPrintTunnelFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
w.WriteLine(fmt.Sprintf("sshPrintRelays failed to convert args type"))
|
w.WriteLine(fmt.Sprintf("sshPrintRelays failed to convert args type"))
|
||||||
@ -919,7 +919,7 @@ func sshPrintRelays(ifce *Interface, fs interface{}, a []string, w sshd.StringWr
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshPrintTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWriter) error {
|
func sshPrintTunnel(ifce *Interface, fs any, a []string, w sshd.StringWriter) error {
|
||||||
args, ok := fs.(*sshPrintTunnelFlags)
|
args, ok := fs.(*sshPrintTunnelFlags)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
@ -951,7 +951,7 @@ func sshPrintTunnel(ifce *Interface, fs interface{}, a []string, w sshd.StringWr
|
|||||||
return enc.Encode(copyHostInfo(hostInfo, ifce.hostMap.GetPreferredRanges()))
|
return enc.Encode(copyHostInfo(hostInfo, ifce.hostMap.GetPreferredRanges()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func sshDeviceInfo(ifce *Interface, fs interface{}, w sshd.StringWriter) error {
|
func sshDeviceInfo(ifce *Interface, fs any, w sshd.StringWriter) error {
|
||||||
|
|
||||||
data := struct {
|
data := struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
// CommandFlags is a function called before help or command execution to parse command line flags
|
// CommandFlags is a function called before help or command execution to parse command line flags
|
||||||
// It should return a flag.FlagSet instance and a pointer to the struct that will contain parsed flags
|
// It should return a flag.FlagSet instance and a pointer to the struct that will contain parsed flags
|
||||||
type CommandFlags func() (*flag.FlagSet, interface{})
|
type CommandFlags func() (*flag.FlagSet, any)
|
||||||
|
|
||||||
// CommandCallback is the function called when your command should execute.
|
// CommandCallback is the function called when your command should execute.
|
||||||
// fs will be a a pointer to the struct provided by Command.Flags callback, if there was one. -h and -help are reserved
|
// fs will be a a pointer to the struct provided by Command.Flags callback, if there was one. -h and -help are reserved
|
||||||
@ -21,7 +21,7 @@ type CommandFlags func() (*flag.FlagSet, interface{})
|
|||||||
// w is the writer to use when sending messages back to the client.
|
// w is the writer to use when sending messages back to the client.
|
||||||
// If an error is returned by the callback it is logged locally, the callback should handle messaging errors to the user
|
// If an error is returned by the callback it is logged locally, the callback should handle messaging errors to the user
|
||||||
// where appropriate
|
// where appropriate
|
||||||
type CommandCallback func(fs interface{}, a []string, w StringWriter) error
|
type CommandCallback func(fs any, a []string, w StringWriter) error
|
||||||
|
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Name string
|
Name string
|
||||||
@ -34,7 +34,7 @@ type Command struct {
|
|||||||
func execCommand(c *Command, args []string, w StringWriter) error {
|
func execCommand(c *Command, args []string, w StringWriter) error {
|
||||||
var (
|
var (
|
||||||
fl *flag.FlagSet
|
fl *flag.FlagSet
|
||||||
fs interface{}
|
fs any
|
||||||
)
|
)
|
||||||
|
|
||||||
if c.Flags != nil {
|
if c.Flags != nil {
|
||||||
@ -85,7 +85,7 @@ func lookupCommand(c *radix.Tree, sCmd string) (*Command, error) {
|
|||||||
|
|
||||||
func matchCommand(c *radix.Tree, cmd string) []string {
|
func matchCommand(c *radix.Tree, cmd string) []string {
|
||||||
cmds := make([]string, 0)
|
cmds := make([]string, 0)
|
||||||
c.WalkPrefix(cmd, func(found string, v interface{}) bool {
|
c.WalkPrefix(cmd, func(found string, v any) bool {
|
||||||
cmds = append(cmds, found)
|
cmds = append(cmds, found)
|
||||||
return false
|
return false
|
||||||
})
|
})
|
||||||
@ -95,7 +95,7 @@ func matchCommand(c *radix.Tree, cmd string) []string {
|
|||||||
|
|
||||||
func allCommands(c *radix.Tree) []*Command {
|
func allCommands(c *radix.Tree) []*Command {
|
||||||
cmds := make([]*Command, 0)
|
cmds := make([]*Command, 0)
|
||||||
c.WalkPrefix("", func(found string, v interface{}) bool {
|
c.WalkPrefix("", func(found string, v any) bool {
|
||||||
cmd, ok := v.(*Command)
|
cmd, ok := v.(*Command)
|
||||||
if ok {
|
if ok {
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
|||||||
@ -86,7 +86,7 @@ func NewSSHServer(l *logrus.Entry) (*SSHServer, error) {
|
|||||||
s.RegisterCommand(&Command{
|
s.RegisterCommand(&Command{
|
||||||
Name: "help",
|
Name: "help",
|
||||||
ShortDescription: "prints available commands or help <command> for specific usage info",
|
ShortDescription: "prints available commands or help <command> for specific usage info",
|
||||||
Callback: func(a interface{}, args []string, w StringWriter) error {
|
Callback: func(a any, args []string, w StringWriter) error {
|
||||||
return helpCallback(s.commands, args, w)
|
return helpCallback(s.commands, args, w)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@ -9,13 +9,13 @@ import (
|
|||||||
"github.com/armon/go-radix"
|
"github.com/armon/go-radix"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
"golang.org/x/crypto/ssh/terminal"
|
"golang.org/x/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
type session struct {
|
type session struct {
|
||||||
l *logrus.Entry
|
l *logrus.Entry
|
||||||
c *ssh.ServerConn
|
c *ssh.ServerConn
|
||||||
term *terminal.Terminal
|
term *term.Terminal
|
||||||
commands *radix.Tree
|
commands *radix.Tree
|
||||||
exitChan chan bool
|
exitChan chan bool
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ func NewSession(commands *radix.Tree, conn *ssh.ServerConn, chans <-chan ssh.New
|
|||||||
s.commands.Insert("logout", &Command{
|
s.commands.Insert("logout", &Command{
|
||||||
Name: "logout",
|
Name: "logout",
|
||||||
ShortDescription: "Ends the current session",
|
ShortDescription: "Ends the current session",
|
||||||
Callback: func(a interface{}, args []string, w StringWriter) error {
|
Callback: func(a any, args []string, w StringWriter) error {
|
||||||
s.Close()
|
s.Close()
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
@ -106,8 +106,8 @@ func (s *session) handleRequests(in <-chan *ssh.Request, channel ssh.Channel) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *session) createTerm(channel ssh.Channel) *terminal.Terminal {
|
func (s *session) createTerm(channel ssh.Channel) *term.Terminal {
|
||||||
term := terminal.NewTerminal(channel, s.c.User()+"@nebula > ")
|
term := term.NewTerminal(channel, s.c.User()+"@nebula > ")
|
||||||
term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
|
term.AutoCompleteCallback = func(line string, pos int, key rune) (newLine string, newPos int, ok bool) {
|
||||||
// key 9 is tab
|
// key 9 is tab
|
||||||
if key == 9 {
|
if key == 9 {
|
||||||
|
|||||||
@ -13,7 +13,7 @@ import (
|
|||||||
|
|
||||||
// AssertDeepCopyEqual checks to see if two variables have the same values but DO NOT share any memory
|
// AssertDeepCopyEqual checks to see if two variables have the same values but DO NOT share any memory
|
||||||
// There is currently a special case for `time.loc` (as this code traverses into unexported fields)
|
// There is currently a special case for `time.loc` (as this code traverses into unexported fields)
|
||||||
func AssertDeepCopyEqual(t *testing.T, a interface{}, b interface{}) {
|
func AssertDeepCopyEqual(t *testing.T, a any, b any) {
|
||||||
v1 := reflect.ValueOf(a)
|
v1 := reflect.ValueOf(a)
|
||||||
v2 := reflect.ValueOf(b)
|
v2 := reflect.ValueOf(b)
|
||||||
|
|
||||||
|
|||||||
@ -4,12 +4,14 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"net/netip"
|
"net/netip"
|
||||||
|
|
||||||
|
"github.com/slackhq/nebula/routing"
|
||||||
)
|
)
|
||||||
|
|
||||||
type NoopTun struct{}
|
type NoopTun struct{}
|
||||||
|
|
||||||
func (NoopTun) RouteFor(addr netip.Addr) netip.Addr {
|
func (NoopTun) RoutesFor(addr netip.Addr) routing.Gateways {
|
||||||
return netip.Addr{}
|
return routing.Gateways{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (NoopTun) Activate() error {
|
func (NoopTun) Activate() error {
|
||||||
|
|||||||
@ -9,11 +9,11 @@ import (
|
|||||||
|
|
||||||
type ContextualError struct {
|
type ContextualError struct {
|
||||||
RealError error
|
RealError error
|
||||||
Fields map[string]interface{}
|
Fields map[string]any
|
||||||
Context string
|
Context string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewContextualError(msg string, fields map[string]interface{}, realError error) *ContextualError {
|
func NewContextualError(msg string, fields map[string]any, realError error) *ContextualError {
|
||||||
return &ContextualError{Context: msg, Fields: fields, RealError: realError}
|
return &ContextualError{Context: msg, Fields: fields, RealError: realError}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
type m map[string]interface{}
|
type m = map[string]any
|
||||||
|
|
||||||
type TestLogWriter struct {
|
type TestLogWriter struct {
|
||||||
Logs []string
|
Logs []string
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user