Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Geo Location posture check #1500

Merged
merged 10 commits into from
Feb 1, 2024
36 changes: 35 additions & 1 deletion management/server/http/api/openapi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -842,6 +842,8 @@ components:
$ref: '#/components/schemas/NBVersionCheck'
os_version_check:
$ref: '#/components/schemas/OSVersionCheck'
geo_location_check:
$ref: '#/components/schemas/GeoLocationCheck'
NBVersionCheck:
description: Posture check for the version of NetBird
type: object
Expand Down Expand Up @@ -884,6 +886,38 @@ components:
example: "6.6.12"
required:
- min_kernel_version
GeoLocationCheck:
description: Posture check for geo location
type: object
properties:
locations:
description: List of geo locations to which the policy applies
type: array
items:
$ref: '#/components/schemas/Location'
action:
description: Action to take upon policy match
type: string
enum: [ "allow", "deny" ]
example: "allow"
required:
- locations
- action
Location:
description: Describe geographical location information
type: object
properties:
country_code:
description: 2-letter ISO 3166-1 alpha-2 code that represents the country
type: string
example: "DE"
city_name:
description: Commonly used English name of the city
type: string
example: "Berlin"
required:
- country_code
- city_name
PostureCheckUpdate:
type: object
properties:
Expand Down Expand Up @@ -2698,4 +2732,4 @@ paths:
'403':
"$ref": "#/components/responses/forbidden"
'500':
"$ref": "#/components/responses/internal_error"
"$ref": "#/components/responses/internal_error"
30 changes: 30 additions & 0 deletions management/server/http/api/types.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 63 additions & 1 deletion management/server/http/posture_checks_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package http
import (
"encoding/json"
"net/http"
"regexp"

"github.com/gorilla/mux"
"github.com/rs/xid"
Expand All @@ -15,6 +16,10 @@ import (
"github.com/netbirdio/netbird/management/server/status"
)

var (
countryCodeRegex = regexp.MustCompile("^[a-zA-Z]{2}$")
)

// PostureChecksHandler is a handler that returns posture checks of the account.
type PostureChecksHandler struct {
accountManager server.AccountManager
Expand Down Expand Up @@ -195,6 +200,10 @@ func (p *PostureChecksHandler) savePostureChecks(
})
}

if geoLocationCheck := req.Checks.GeoLocationCheck; geoLocationCheck != nil {
postureChecks.Checks = append(postureChecks.Checks, toPostureGeoLocationCheck(geoLocationCheck))
}

if err := p.accountManager.SavePostureChecks(account.Id, user.Id, &postureChecks); err != nil {
util.WriteError(err, w)
return
Expand All @@ -208,7 +217,8 @@ func validatePostureChecksUpdate(req api.PostureCheckUpdate) error {
return status.Errorf(status.InvalidArgument, "posture checks name shouldn't be empty")
}

if req.Checks == nil || (req.Checks.NbVersionCheck == nil && req.Checks.OsVersionCheck == nil) {
if req.Checks == nil || (req.Checks.NbVersionCheck == nil && req.Checks.OsVersionCheck == nil &&
req.Checks.GeoLocationCheck == nil) {
return status.Errorf(status.InvalidArgument, "posture checks shouldn't be empty")
}

Expand All @@ -230,6 +240,25 @@ func validatePostureChecksUpdate(req api.PostureCheckUpdate) error {
}
}

if geoLocationCheck := req.Checks.GeoLocationCheck; geoLocationCheck != nil {
if geoLocationCheck.Action == "" {
return status.Errorf(status.InvalidArgument, "action for geolocation check shouldn't be empty")
}
if len(geoLocationCheck.Locations) == 0 {
return status.Errorf(status.InvalidArgument, "locations for geolocation check shouldn't be empty")
}

for _, loc := range geoLocationCheck.Locations {
if loc.CountryCode == "" {
bcmmbaga marked this conversation as resolved.
Show resolved Hide resolved
return status.Errorf(status.InvalidArgument, "country code for geolocation check shouldn't be empty")
}
if !countryCodeRegex.MatchString(loc.CountryCode) {
return status.Errorf(status.InvalidArgument, "country code must be 2 letters (ISO 3166-1 alpha-2 format)")
}
}

}

return nil
}

Expand All @@ -251,6 +280,9 @@ func toPostureChecksResponse(postureChecks *posture.Checks) *api.PostureCheck {
Linux: (*api.MinKernelVersionCheck)(osCheck.Linux),
Windows: (*api.MinKernelVersionCheck)(osCheck.Windows),
}
case posture.GeoLocationCheckName:
geoLocationCheck := check.(*posture.GeoLocationCheck)
checks.GeoLocationCheck = toGeoLocationCheckResponse(geoLocationCheck)
}
}

Expand All @@ -261,3 +293,33 @@ func toPostureChecksResponse(postureChecks *posture.Checks) *api.PostureCheck {
Checks: checks,
}
}

func toGeoLocationCheckResponse(geoLocationCheck *posture.GeoLocationCheck) *api.GeoLocationCheck {
locations := make([]api.Location, 0, len(geoLocationCheck.Locations))
for _, loc := range geoLocationCheck.Locations {
locations = append(locations, api.Location{
CityName: loc.CityName,
CountryCode: loc.CountryCode,
})
}

return &api.GeoLocationCheck{
Action: api.GeoLocationCheckAction(geoLocationCheck.Action),
Locations: locations,
}
}

func toPostureGeoLocationCheck(apiGeoLocationCheck *api.GeoLocationCheck) *posture.GeoLocationCheck {
locations := make([]posture.Location, 0, len(apiGeoLocationCheck.Locations))
for _, loc := range apiGeoLocationCheck.Locations {
locations = append(locations, posture.Location{
CountryCode: loc.CountryCode,
CityName: loc.CityName,
})
}

return &posture.GeoLocationCheck{
Action: string(apiGeoLocationCheck.Action),
Locations: locations,
}
}
Loading
Loading