Skip to content

Commit

Permalink
CIDR validation for some server's attributes (#7)
Browse files Browse the repository at this point in the history
Updated validation for server's attribute:
* `network`
* `bind_address`
* `network_wg`
* `route.network`
  • Loading branch information
disc authored Oct 16, 2021
1 parent 816e8f1 commit f0a9567
Show file tree
Hide file tree
Showing 3 changed files with 181 additions and 19 deletions.
100 changes: 82 additions & 18 deletions internal/provider/resource_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
"net"
"strings"
)

Expand Down Expand Up @@ -55,34 +56,94 @@ func resourceServer() *schema.Resource {
Computed: true,
Description: "Network address for the private network that will be created for clients. This network cannot conflict with any existing local networks",

//ValidateFunc: validation.Any(
// // [10,172,192].[0-255,16-31,168].[0-255].0/[8-24]
// func(i interface{}, s string) ([]string, []error) {
// return validation.IsIPv4Address(i.(string), "10.0.0.0/8")
// },
// func(i interface{}, s string) ([]string, []error) {
// return validation.IsIPv4Address(i.(string), "172.16.0.0/11")
// },
// func(i interface{}, s string) ([]string, []error) {
// return validation.IsIPv4Address(i.(string), "192.168.0.0/16")
// },
//),
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
// [10,172,192].[0-255,16-31,168].[0-255].0/[8-24]
// 10.0.0.0/8
// 172.16.0.0/12
// 192.168.0.0/16
warnings := make([]string, 0)
errors := make([]error, 0)

_, actualIpNet, err := net.ParseCIDR(i.(string))
if err != nil {
errors = append(errors, err)

return warnings, errors
}

expectedIpNets := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}

found := false
for _, v := range expectedIpNets {
_, expectedIpNet, _ := net.ParseCIDR(v)
if actualIpNet.Contains(expectedIpNet.IP) || expectedIpNet.Contains(actualIpNet.IP) {
found = true
break
}
}

if !found {
errors = append(errors, fmt.Errorf("provided subnet %s does not belong to expected subnets %s", actualIpNet.String(), strings.Join(expectedIpNets, ", ")))
}

return warnings, errors
},
},
"bind_address": {
Type: schema.TypeString,
Required: false,
Optional: true,
Description: "Network address for the private network that will be created for clients. This network cannot conflict with any existing local networks",
// TODO: Add validation
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
return validation.IsIPAddress(i, s)
},
},
"network_wg": {
Type: schema.TypeString,
Required: false,
Optional: true,
Description: "Network address for the private network that will be created for clients. This network cannot conflict with any existing local networks",
RequiredWith: []string{"port_wg"},
// TODO: Add validation
// [10,172,192].[0-255,16-31,168].[0-255].0/[8-24]
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
// [10,172,192].[0-255,16-31,168].[0-255].0/[8-24]
// 10.0.0.0/8
// 172.16.0.0/12
// 192.168.0.0/16
warnings := make([]string, 0)
errors := make([]error, 0)

_, actualIpNet, err := net.ParseCIDR(i.(string))
if err != nil {
errors = append(errors, err)

return warnings, errors
}

expectedIpNets := []string{
"10.0.0.0/8",
"172.16.0.0/12",
"192.168.0.0/16",
}

found := false
for _, v := range expectedIpNets {
_, expectedIpNet, _ := net.ParseCIDR(v)
if actualIpNet.Contains(expectedIpNet.IP) || expectedIpNet.Contains(actualIpNet.IP) {
found = true
break
}
}

if !found {
errors = append(errors, fmt.Errorf("provided subnet %s does not belong to expected subnets %s", actualIpNet.String(), strings.Join(expectedIpNets, ", ")))
}

return warnings, errors
},
},
"port_wg": {
Type: schema.TypeInt,
Expand Down Expand Up @@ -314,6 +375,9 @@ func resourceServer() *schema.Resource {
Type: schema.TypeString,
Required: true,
Description: "Network address with subnet to route",
ValidateFunc: func(i interface{}, s string) ([]string, []error) {
return validation.IsCIDR(i, s)
},
},
"comment": {
Type: schema.TypeString,
Expand Down Expand Up @@ -446,7 +510,7 @@ func resourceReadServer(ctx context.Context, d *schema.ResourceData, meta interf

declaredOrganizations, ok := d.Get("organization_ids").([]interface{})
if !ok {
return diag.Errorf("failed to parse organization_ids for the server: %d", server.Name)
return diag.Errorf("failed to parse organization_ids for the server: %s", server.Name)
}
d.Set("organization_ids", matchOrganizationWithSchema(organizationsList, declaredOrganizations))
}
Expand All @@ -460,15 +524,15 @@ func resourceReadServer(ctx context.Context, d *schema.ResourceData, meta interf

declaredGroups, ok := d.Get("groups").([]interface{})
if !ok {
return diag.Errorf("failed to parse groups for the server: %d", server.Name)
return diag.Errorf("failed to parse groups for the server: %s", server.Name)
}
d.Set("groups", matchGroupsWithSchema(groupsList, declaredGroups))
}

if len(routes) > 0 {
declaredRoutes, ok := d.Get("route").([]interface{})
if !ok {
return diag.Errorf("failed to parse routes for the server: %d", server.Name)
return diag.Errorf("failed to parse routes for the server: %s", server.Name)
}
d.Set("route", flattenRoutesData(matchRoutesWithSchema(routes, declaredRoutes)))
}
Expand Down
98 changes: 98 additions & 0 deletions internal/provider/resource_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
"regexp"
"testing"
)

Expand Down Expand Up @@ -263,6 +264,91 @@ func TestGetServer_with_a_few_attached_routes(t *testing.T) {
})
}

func TestGetServer_with_invalid_route(t *testing.T) {
invalidRouteNetwork := "10.100.0.2"

resource.Test(t, resource.TestCase{
PreCheck: func() { preCheck(t) },
ProviderFactories: providerFactories,
CheckDestroy: testGetServerDestroy,
Steps: []resource.TestStep{
{
Config: testGetServerSimpleConfigWithAttachedRoute("tfacc-server1", invalidRouteNetwork),
ExpectError: regexp.MustCompile(fmt.Sprintf("invalid CIDR address: %s", invalidRouteNetwork)),
},
},
})
}

func TestCreateServer_with_invalid_network(t *testing.T) {
missedSubnetNetwork := "10.100.0.2"
invalidNetwork := "10.100.0"

resource.Test(t, resource.TestCase{
PreCheck: func() { preCheck(t) },
ProviderFactories: providerFactories,
CheckDestroy: testGetServerDestroy,
Steps: []resource.TestStep{
{
Config: testGetServerConfig("tfacc-server1", missedSubnetNetwork, 11111),
ExpectError: regexp.MustCompile(fmt.Sprintf("invalid CIDR address: %s", missedSubnetNetwork)),
},
{
Config: testGetServerConfig("tfacc-server2", invalidNetwork, 22222),
ExpectError: regexp.MustCompile(fmt.Sprintf("invalid CIDR address: %s", invalidNetwork)),
},
},
})
}

func TestCreateServer_with_unsupported_network(t *testing.T) {
unsupportedNetwork := "172.14.68.0/24"
supportedNetwork := "172.16.68.0/24"

resource.Test(t, resource.TestCase{
PreCheck: func() { preCheck(t) },
ProviderFactories: providerFactories,
CheckDestroy: testGetServerDestroy,
Steps: []resource.TestStep{
{
Config: testGetServerConfig("tfacc-server1", unsupportedNetwork, 11111),
ExpectError: regexp.MustCompile(fmt.Sprintf("provided subnet %s does not belong to expected subnets 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16", unsupportedNetwork)),
},
{
Config: testGetServerConfig("tfacc-server2", supportedNetwork, 22222),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("pritunl_server.test", "name", "tfacc-server2"),
resource.TestCheckResourceAttr("pritunl_server.test", "network", supportedNetwork),
),
},
},
})
}

func TestCreateServer_with_invalid_bind_address(t *testing.T) {
invalidBindAddress := "10.100.0.1/24"
correctBindAddress := "10.100.0.1"

resource.Test(t, resource.TestCase{
PreCheck: func() { preCheck(t) },
ProviderFactories: providerFactories,
CheckDestroy: testGetServerDestroy,
Steps: []resource.TestStep{
{
Config: testGetServerConfigWithBindAddress("tfacc-server1", "172.16.68.0/24", invalidBindAddress, 11111),
ExpectError: regexp.MustCompile(fmt.Sprintf("expected bind_address to contain a valid IP, got: %s", invalidBindAddress)),
},
{
Config: testGetServerConfigWithBindAddress("tfacc-server2", "172.16.68.0/24", correctBindAddress, 22222),
Check: resource.ComposeTestCheckFunc(
resource.TestCheckResourceAttr("pritunl_server.test", "name", "tfacc-server2"),
resource.TestCheckResourceAttr("pritunl_server.test", "bind_address", correctBindAddress),
),
},
},
})
}

func testGetServerSimpleConfig(name string) string {
return fmt.Sprintf(`
resource "pritunl_server" "test" {
Expand Down Expand Up @@ -347,6 +433,18 @@ resource "pritunl_server" "test" {
`, name, network, port)
}

func testGetServerConfigWithBindAddress(name, network, bindAddress string, port int) string {
return fmt.Sprintf(`
resource "pritunl_server" "test" {
name = "%[1]s"
network = "%[2]s"
bind_address = "%[3]s"
port = %[4]d
protocol = "tcp"
}
`, name, network, bindAddress, port)
}

func testGetServerDestroy(s *terraform.State) error {
serverId := s.RootModule().Resources["pritunl_server.test"].Primary.Attributes["id"]

Expand Down
2 changes: 1 addition & 1 deletion internal/provider/resource_user.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ func resourceUserRead(_ context.Context, d *schema.ResourceData, meta interface{

declaredGroups, ok := d.Get("groups").([]interface{})
if !ok {
return diag.Errorf("failed to parse groups for the user: %d", user.Name)
return diag.Errorf("failed to parse groups for the user: %s", user.Name)
}
d.Set("groups", matchGroupsWithSchema(groupsList, declaredGroups))
}
Expand Down

0 comments on commit f0a9567

Please sign in to comment.