From a28f34d9ee300fc8a126c265cad221fc44359d9e Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Wed, 24 Apr 2024 20:26:55 +0530 Subject: [PATCH 01/31] one click access for rdp changes (#168) * one click access for rdp changes --- banyan/resource_service_rdp.go | 18 ++ banyan/resource_service_rdp_test.go | 157 +++++++++++++++++- .../specs/service_infra/rdp-collection.json | 3 +- banyan/specs/service_infra/rdp-conn.json | 3 +- client/service/model.go | 2 + docs/resources/service_rdp.md | 1 + 6 files changed, 181 insertions(+), 3 deletions(-) diff --git a/banyan/resource_service_rdp.go b/banyan/resource_service_rdp.go index 890f9a03..b0c17e0b 100644 --- a/banyan/resource_service_rdp.go +++ b/banyan/resource_service_rdp.go @@ -203,6 +203,14 @@ func RdpSchema() map[string]*schema.Schema { Default: true, Description: "Allow the end user to override the backend_port for this service", }, + "rdp_settings": { + Type: schema.TypeSet, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + Description: "allow admin to add custom rdp settings which app will add in rdp file", + }, } } @@ -254,6 +262,12 @@ func resourceServiceInfraRdpRead(ctx context.Context, d *schema.ResourceData, m return diag.FromErr(err) } } + + rdpSettings := svc.CreateServiceSpec.Metadata.Tags.RDPSettings + if rdpSettings != nil { + d.Set("rdp_settings", rdpSettings) + } + return resourceServiceInfraCommonRead(svc, d, m) } @@ -302,6 +316,9 @@ func expandRDPMetatdataTags(d *schema.ResourceData) (metadatatags service.Tags) if alp != nil { appListenPort = alp.(string) } + + rdpSettings := convertSchemaSetToStringSlice(d.Get("rdp_settings").(*schema.Set)) + metadatatags = service.Tags{ Template: &template, UserFacing: &userFacing, @@ -314,6 +331,7 @@ func expandRDPMetatdataTags(d *schema.ResourceData) (metadatatags service.Tags) AppListenPort: &appListenPort, AllowUserOverride: &allowUserOverride, DescriptionLink: &descriptionLink, + RDPSettings: &rdpSettings, } return } diff --git a/banyan/resource_service_rdp_test.go b/banyan/resource_service_rdp_test.go index 11b48a08..037d1f83 100644 --- a/banyan/resource_service_rdp_test.go +++ b/banyan/resource_service_rdp_test.go @@ -22,6 +22,7 @@ func TestSchemaServiceInfraRdp_rdp_conn(t *testing.T) { "backend_domain": "10.10.2.1", "backend_port": 3309, "client_banyanproxy_listen_port": 9109, + "rdp_settings": []interface{}{"devicestoredirect:s:*"}, } d := schema.TestResourceDataRaw(t, RdpSchema(), svc_rdp_conn) @@ -43,6 +44,7 @@ func TestSchemaServiceInfraRdp_rdp_collection(t *testing.T) { "domain": "test-rdp-collection.tdupnsan.getbnn.com", "http_connect": true, "client_banyanproxy_listen_port": 9108, + "rdp_settings": []interface{}{"devicestoredirect:s:*"}, } d := schema.TestResourceDataRaw(t, RdpSchema(), svc_rdp_collection) @@ -78,6 +80,29 @@ func TestAccService_infra_rdp(t *testing.T) { }) } +func TestAccService_infra_rdp_without_rdp_settings(t *testing.T) { + var bnnService service.GetServiceSpec + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckServiceDestroy(t, &bnnService.ServiceID), + Steps: []resource.TestStep{ + { + Config: testAccService_infra_rdp_create_without_rdp_settings(rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_rdp.example_without_rdp", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_infra_rdp_create_without_rdp_settings_json(rName), &bnnService.ServiceID), + ), + }, + { + ResourceName: "banyan_service_rdp.example_without_rdp", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + // Returns terraform configuration for a typical rdp service func testAccService_infra_rdp_create(name string) string { return fmt.Sprintf(` @@ -88,12 +113,142 @@ resource "banyan_service_rdp" "example" { domain = "%s-rdp.corp.com" backend_domain = "%s-rdp.internal" backend_port = 3389 + rdp_settings = ["devicestoredirect:s:*"] } `, name, name, name) } func testAccService_infra_rdp_create_json(name string) string { return fmt.Sprintf(` +{ + "kind": "BanyanService", + "apiVersion": "rbac.banyanops.com/v1", + "type": "origin", + "metadata": { + "name": "%s-rdp", + "description": "some RDP service description", + "cluster": "cluster1", + "tags": { + "template": "TCP_USER", + "user_facing": "true", + "protocol": "tcp", + "domain": "%s-rdp.corp.com", + "port": "8443", + "icon": "", + "service_app_type": "RDP", + "banyanproxy_mode": "TCP", + "app_listen_port": "", + "allow_user_override": true, + "description_link": "", + "rdp_settings": ["devicestoredirect:s:*"] + } + }, + "spec": { + "attributes": { + "tls_sni": [ + "%s-rdp.corp.com" + ], + "frontend_addresses": [ + { + "cidr": "", + "port": "8443" + } + ], + "host_tag_selector": [ + { + "com.banyanops.hosttag.site_name": "us-west1" + } + ], + "disable_private_dns": false + }, + "backend": { + "target": { + "name": "%s-rdp.internal", + "port": "3389", + "tls": false, + "tls_insecure": false, + "client_certificate": false + }, + "dns_overrides": {}, + "whitelist": [], + "connector_name": "" + }, + "cert_settings": { + "dns_names": [ + "%s-rdp.corp.com" + ], + "custom_tls_cert": { + "enabled": false, + "cert_file": "", + "key_file": "" + }, + "letsencrypt": false + }, + "http_settings": { + "enabled": false, + "oidc_settings": { + "enabled": false, + "service_domain_name": "", + "post_auth_redirect_path": "", + "api_path": "", + "trust_callbacks": null, + "suppress_device_trust_verification": false + }, + "http_health_check": { + "enabled": false, + "addresses": null, + "method": "", + "path": "", + "user_agent": "", + "from_address": [], + "https": false + }, + "http_redirect": { + "enabled": false, + "addresses": null, + "from_address": null, + "url": "", + "status_code": 0 + }, + "exempted_paths": { + "enabled": false, + "patterns": [ + { + "hosts": [ + { + "origin_header": [], + "target": [] + } + ], + "methods": [], + "paths": [], + "mandatory_headers": [] + } + ] + }, + "headers": {} + }, + "client_cidrs": [] + } +} +`, name, name, name, name, name) +} + +func testAccService_infra_rdp_create_without_rdp_settings(name string) string { + return fmt.Sprintf(` +resource "banyan_service_rdp" "example_without_rdp" { + name = "%s-rdp" + description = "some RDP service description" + access_tier = "us-west1" + domain = "%s-rdp.corp.com" + backend_domain = "%s-rdp.internal" + backend_port = 3389 +} +`, name, name, name) +} + +func testAccService_infra_rdp_create_without_rdp_settings_json(name string) string { + return fmt.Sprintf(` { "kind": "BanyanService", "apiVersion": "rbac.banyanops.com/v1", @@ -200,7 +355,7 @@ func testAccService_infra_rdp_create_json(name string) string { ] }, "headers": {} - }, + }, "client_cidrs": [] } } diff --git a/banyan/specs/service_infra/rdp-collection.json b/banyan/specs/service_infra/rdp-collection.json index 4adc93ac..2c1d7282 100644 --- a/banyan/specs/service_infra/rdp-collection.json +++ b/banyan/specs/service_infra/rdp-collection.json @@ -17,7 +17,8 @@ "banyanproxy_mode": "RDPGATEWAY", "app_listen_port": "9108", "allow_user_override": true, - "description_link": "" + "description_link": "", + "rdp_settings": ["devicestoredirect:s:*"] }, "autorun": false }, diff --git a/banyan/specs/service_infra/rdp-conn.json b/banyan/specs/service_infra/rdp-conn.json index c8c07cfc..32f14587 100644 --- a/banyan/specs/service_infra/rdp-conn.json +++ b/banyan/specs/service_infra/rdp-conn.json @@ -17,7 +17,8 @@ "banyanproxy_mode": "TCP", "app_listen_port": "9109", "allow_user_override": true, - "description_link": "" + "description_link": "", + "rdp_settings": ["devicestoredirect:s:*"] }, "autorun": false }, diff --git a/client/service/model.go b/client/service/model.go index b2527efa..34f779b5 100644 --- a/client/service/model.go +++ b/client/service/model.go @@ -72,6 +72,8 @@ type Tags struct { DescriptionLink *string `json:"description_link,omitempty" toml:"description_link,omitempty"` IncludeDomains *[]string `json:"include_domains,omitempty" toml:"include_domains,omitempty"` + RDPSettings *[]string `json:"rdp_settings,omitempty" toml:"rdp_settings,omitempty"` + RegisteredDomainID *string `json:"registered_domain_id,omitempty" toml:"registered_domain_id,omitempty"` } diff --git a/docs/resources/service_rdp.md b/docs/resources/service_rdp.md index c159322a..56aa6b99 100644 --- a/docs/resources/service_rdp.md +++ b/docs/resources/service_rdp.md @@ -52,6 +52,7 @@ resource "banyan_service_rdp" "example" { - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `policy` (String) Policy ID to be attached to this service - `port` (Number) The external-facing port for this service +- `rdp_settings` (Set of String) - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true ### Read-Only From d598cae626944ef93d1bb95db66d3b754ea086d7 Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Wed, 24 Apr 2024 21:20:05 +0530 Subject: [PATCH 02/31] tunnel_policy changes add new fields name and description (#169) * tunnel_policy changes --- banyan/resource_policy_tunnel.go | 68 ++++++++++--- banyan/resource_policy_tunnel_test.go | 140 ++++++++++++++++++++++++-- banyan/specs/policy/l4.json | 6 +- client/policy/types.go | 14 ++- 4 files changed, 201 insertions(+), 27 deletions(-) diff --git a/banyan/resource_policy_tunnel.go b/banyan/resource_policy_tunnel.go index 6ce6e2ae..ada053eb 100644 --- a/banyan/resource_policy_tunnel.go +++ b/banyan/resource_policy_tunnel.go @@ -51,6 +51,16 @@ func PolicyTunnelSchema() (s map[string]*schema.Schema) { Description: "Access describes the access rights for a set of roles", Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Optional: true, + Description: "access group name", + }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "access group description description", + }, "roles": { Type: schema.TypeSet, Description: "Role names to include ", @@ -79,6 +89,11 @@ func PolicyTunnelSchema() (s map[string]*schema.Schema) { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Optional: true, + Description: "l4 policy description", + }, "cidrs": { Type: schema.TypeSet, Description: "Allowed CIDRs through the service tunnel", @@ -121,6 +136,11 @@ func PolicyTunnelSchema() (s map[string]*schema.Schema) { Optional: true, Elem: &schema.Resource{ Schema: map[string]*schema.Schema{ + "description": { + Type: schema.TypeString, + Optional: true, + Description: "l4 policy description", + }, "cidrs": { Type: schema.TypeSet, Description: "Denied CIDRs through the service tunnel", @@ -279,12 +299,25 @@ func expandPolicyTunnelAccess(m []interface{}) (access []policy.Access) { data := raw.(map[string]interface{}) a := policy.Access{ - Roles: convertSchemaSetToStringSlice(data["roles"].(*schema.Set)), + Name: data["name"].(string), + Description: data["description"].(string), + Roles: convertSchemaSetToStringSlice(data["roles"].(*schema.Set)), Rules: policy.Rules{ L4Access: expandL4Access(data["l4_access"].([]interface{})), }, } a.Rules.Conditions.TrustLevel = data["trust_level"].(string) + + name := data["name"] + if name != nil { + a.Name = name.(string) + } + + description := data["description"] + if description != nil { + a.Description = description.(string) + } + access = append(access, a) } return @@ -295,9 +328,10 @@ func expandL4Access(m []interface{}) *policy.L4Access { var allow []policy.L4Rule var deny []policy.L4Rule allow = append(allow, policy.L4Rule{ - CIDRs: []string{"*"}, - Protocols: []string{"ALL"}, - Ports: []string{"*"}, + Description: "", + CIDRs: []string{"*"}, + Protocols: []string{"ALL"}, + Ports: []string{"*"}, }) p := policy.L4Access{ Allow: allow, @@ -334,12 +368,19 @@ func expandL4Rules(m interface{}) (l4Rules []policy.L4Rule) { ports = []string{"*"} } + description := "" + if rule["description"] != nil { + description = rule["description"].(string) + } + l4Rules = append(l4Rules, policy.L4Rule{ - CIDRs: cidrs, - Protocols: protocols, - Ports: ports, - FQDNs: fqdns, + Description: description, + CIDRs: cidrs, + Protocols: protocols, + Ports: ports, + FQDNs: fqdns, }) + } return } @@ -348,6 +389,8 @@ func flattenPolicyTunnelAccess(toFlatten []policy.Access) (flattened []interface flattened = make([]interface{}, len(toFlatten)) for idx, accessItem := range toFlatten { ai := make(map[string]interface{}) + ai["name"] = accessItem.Name + ai["description"] = accessItem.Description ai["roles"] = accessItem.Roles ai["trust_level"] = accessItem.Rules.Conditions.TrustLevel ai["l4_access"] = flattenL4Access(accessItem.L4Access) @@ -372,10 +415,11 @@ func flattenL4Access(l4Access *policy.L4Access) (flattened []interface{}) { func flattenL4Rules(l4Rules []policy.L4Rule) (flattened []interface{}) { for _, rule := range l4Rules { flattened = append(flattened, map[string]interface{}{ - "cidrs": rule.CIDRs, - "protocols": rule.Protocols, - "ports": rule.Ports, - "fqdns": rule.FQDNs, + "description": rule.Description, + "cidrs": rule.CIDRs, + "protocols": rule.Protocols, + "ports": rule.Ports, + "fqdns": rule.FQDNs, }) } return diff --git a/banyan/resource_policy_tunnel_test.go b/banyan/resource_policy_tunnel_test.go index a23f1434..7a7cf323 100644 --- a/banyan/resource_policy_tunnel_test.go +++ b/banyan/resource_policy_tunnel_test.go @@ -14,24 +14,28 @@ import ( func TestSchemaPolicyTunnel_l4(t *testing.T) { access1 := map[string]interface{}{ + "name": "test-name", + "description": "test-desc", "roles": []interface{}{"UsersRegisteredDevice"}, "trust_level": "Low", "l4_access": []interface{}{ map[string]interface{}{ "allow": []interface{}{ map[string]interface{}{ - "cidrs": []interface{}{"10.138.0.14/32", "10.138.0.11/32", "10.10.0.0/16"}, - "protocols": []interface{}{"ALL"}, - "ports": []interface{}{"*"}, - "fqdns": []interface{}{}, + "description": "test-description", + "cidrs": []interface{}{"10.138.0.14/32", "10.138.0.11/32", "10.10.0.0/16"}, + "protocols": []interface{}{"ALL"}, + "ports": []interface{}{"*"}, + "fqdns": []interface{}{}, }, }, "deny": []interface{}{ map[string]interface{}{ - "cidrs": []interface{}{"10.10.1.0/24", "10.10.2.0/24"}, - "protocols": []interface{}{"TCP"}, - "ports": []interface{}{"22"}, - "fqdns": []interface{}{}, + "description": "test-description", + "cidrs": []interface{}{"10.10.1.0/24", "10.10.2.0/24"}, + "protocols": []interface{}{"TCP"}, + "ports": []interface{}{"22"}, + "fqdns": []interface{}{}, }, }, }, @@ -174,7 +178,7 @@ func testAccPolicy_tunnel_l4_create_json(name string) string { "UDP" ], "fqdns": [ - "www.example.com" + "www.example.com" ] } ], @@ -285,7 +289,7 @@ func testAccPolicy_tunnel_l4_create_json_fqdn(name string) string { "UDP" ], "fqdns": [ - "www.example.com" + "www.example.com" ] } ], @@ -391,3 +395,119 @@ func testAccPolicy_tunnel_any_create_json(name string) string { } `, name) } + +func TestAccPolicy_tunnel_l4_optional_field(t *testing.T) { + var bnnPolicy policy.GetPolicy + + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + // CheckDestroy: testAccCheckPolicy_destroy(t, &bnnPolicy.ID), + Steps: []resource.TestStep{ + // Create the policy using terraform config and check that it exists + { + Config: fmt.Sprintf(` + resource "banyan_policy_tunnel" "example" { + name = "%s" + description = "some tunnel policy description" + access { + name = "grp-1" + description = "some tunnel policy grp description" + roles = ["Everyone"] + trust_level = "High" + l4_access { + allow { + description = "l4 policy" + cidrs = [] + protocols = ["UDP"] + ports = ["80"] + fqdns = ["www.example.com"] + } + deny { + description = "l4 policy" + cidrs = [] + protocols = ["TCP"] + ports = ["80"] + fqdns = ["www.deny.com"] + } + } + } + } + `, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingPolicy("banyan_policy_tunnel.example", &bnnPolicy), + testAccCheckPolicyAgainstJson(t, testAccPolicy_tunnel_l4_create_json_name_description(rName), &bnnPolicy.ID), + ), + }, + { + ResourceName: "banyan_policy_tunnel.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccPolicy_tunnel_l4_create_json_name_description(name string) string { + return fmt.Sprintf(` +{ + "kind": "BanyanPolicy", + "apiVersion": "rbac.banyanops.com/v1", + "metadata": { + "name": "%s", + "description": "some tunnel policy description", + "tags": { + "template": "USER" + } + }, + "type": "USER", + "spec": { + "access": [ + { + "name" : "grp-1", + "description" : "some tunnel policy grp description", + "roles": [ + "Everyone" + ], + "rules": { + "conditions": { + "trust_level": "High" + }, + "l4_access": { + "allow": [ + { + "description" : "l4 policy", + "ports": [ + "80" + ], + "protocols": [ + "UDP" + ], + "fqdns": [ + "www.example.com" + ] + } + ], + "deny": [ + { + "description" : "l4 policy", + "ports": [ + "80" + ], + "protocols": [ + "TCP" + ], + "fqdns": [ + "www.deny.com" + ] + } + ] + } + } + } + ] + } +} +`, name) +} diff --git a/banyan/specs/policy/l4.json b/banyan/specs/policy/l4.json index c778268a..16624862 100644 --- a/banyan/specs/policy/l4.json +++ b/banyan/specs/policy/l4.json @@ -16,6 +16,8 @@ }, "access": [ { + "name" : "test-name", + "description" : "test-desc", "roles": [ "UsersRegisteredDevice" ], @@ -24,6 +26,7 @@ "l4_access": { "allow": [ { + "description": "test-description", "cidrs": [ "10.138.0.14/32", "10.138.0.11/32", @@ -39,6 +42,7 @@ ], "deny": [ { + "description": "test-description", "cidrs": [ "10.10.1.0/24", "10.10.2.0/24" @@ -50,7 +54,7 @@ "22" ] } - ] + ] }, "conditions": { "trust_level": "Low" diff --git a/client/policy/types.go b/client/policy/types.go index a504a9ea..c31b5bd2 100644 --- a/client/policy/types.go +++ b/client/policy/types.go @@ -60,6 +60,10 @@ type Options struct { // Access describes the access rights for a set of roles. type Access struct { + // Name is an optional name for this access group. + Name string `json:"name,omitempty"` + // Description is an optional description for this access group. + Description string `json:"description,omitempty"` // Roles is a list of Role names to include . Roles []string `json:"roles"` // Rules lists the access rights given to principals/subjects that have any of the corresponding Roles. @@ -106,10 +110,12 @@ type L4Access struct { } type L4Rule struct { - CIDRs []string `json:"cidrs,omitempty"` - Protocols []string `json:"protocols,omitempty"` - Ports []string `json:"ports,omitempty"` - FQDNs []string `json:"fqdns,omitempty"` + // Description is an optional description for this L4 rule. + Description string `json:"description,omitempty"` + CIDRs []string `json:"cidrs,omitempty"` + Protocols []string `json:"protocols,omitempty"` + Ports []string `json:"ports,omitempty"` + FQDNs []string `json:"fqdns,omitempty"` } // Exception describes exceptional cases that bypass regular policy enforcement. From 62136a6856cdd58d81e97592b1e254920672fe40 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Wed, 24 Apr 2024 14:48:51 -0700 Subject: [PATCH 03/31] remove default l4 policy checks --- .github/workflows/run_tests.yml | 3 +-- banyan/resource_policy_tunnel.go | 35 --------------------------- client/accesstiergroup/client_test.go | 6 ++--- 3 files changed, 4 insertions(+), 40 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 3d6f8eaf..cf2da9cb 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -9,7 +9,6 @@ on: required: true default: 'https://net.banyanops.com' type: string - secrets: api_key: description: 'API Key' required: true @@ -42,7 +41,7 @@ jobs: run: go test ./banyan/... -v env: TF_ACC: true - BANYAN_API_KEY: ${{ secrets.api_key }} + BANYAN_API_KEY: ::add-mask::${{ inputs.api_key }} BANYAN_HOST: ${{ inputs.command_center_url }} - name: Generate Documentation diff --git a/banyan/resource_policy_tunnel.go b/banyan/resource_policy_tunnel.go index ada053eb..bf4c6141 100644 --- a/banyan/resource_policy_tunnel.go +++ b/banyan/resource_policy_tunnel.go @@ -210,13 +210,6 @@ func policyTunnelFromState(d *schema.ResourceData) (pol policy.Object) { func resourcePolicyTunnelCreate(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { c := m.(*client.Holder) - - // ValidateFunc is not supported on lists or sets, so use this method instead - err := invalidL4AccessRules(d) - if err != nil { - return diag.FromErr(errors.WithMessage(err, "invalid l4_access block")) - } - createdPolicy, err := c.Policy.Create(policyTunnelFromState(d)) if err != nil { return diag.FromErr(errors.WithMessage(err, "couldn't create new tunnel policy")) @@ -228,13 +221,6 @@ func resourcePolicyTunnelCreate(ctx context.Context, d *schema.ResourceData, m i func resourcePolicyTunnelUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { c := m.(*client.Holder) - - // ValidateFunc is not supported on lists or sets, so use this method instead - err := invalidL4AccessRules(d) - if err != nil { - return diag.FromErr(errors.WithMessage(err, "invalid l4_access block")) - } - createdPolicy, err := c.Policy.Update(policyTunnelFromState(d)) if err != nil { return diag.FromErr(errors.WithMessage(err, "couldn't create new tunnel policy")) @@ -273,27 +259,6 @@ func resourcePolicyTunnelDelete(ctx context.Context, d *schema.ResourceData, m i return } -func invalidL4AccessRules(d *schema.ResourceData) error { - allowAll := []policy.L4Rule{{CIDRs: []string{"*"}, Protocols: []string{"ALL"}, Ports: []string{"*"}}} - - m := d.Get("access").([]interface{}) - for _, raw := range m { - data := raw.(map[string]interface{}) - l4Access := data["l4_access"].([]interface{}) - if len(l4Access) == 0 || l4Access[0] == nil { - continue - } - l4Rules := l4Access[0].(map[string]interface{}) - allowRule := expandL4Rules(l4Rules["allow"].([]interface{})) - denyRule := expandL4Rules(l4Rules["deny"].([]interface{})) - if reflect.DeepEqual(allowRule, allowAll) && denyRule == nil { - return errors.New("redundant l4_access block with allow_all rules; remove l4_access block entirely") - } - } - - return nil -} - func expandPolicyTunnelAccess(m []interface{}) (access []policy.Access) { for _, raw := range m { data := raw.(map[string]interface{}) diff --git a/client/accesstiergroup/client_test.go b/client/accesstiergroup/client_test.go index 9903cebf..2099c322 100644 --- a/client/accesstiergroup/client_test.go +++ b/client/accesstiergroup/client_test.go @@ -21,10 +21,10 @@ var want = accesstiergroup.AccessTierGroupPost{ }, TunnelConfig: &accesstier.AccessTierTunnelInfoPost{ DNSSearchDomains: "", - Domains: []string{"test-1.com"}, - CIDRs: []string{"198.169.0.1/24"}, + Domains: []string{"test-2.com"}, + CIDRs: []string{"198.170.0.1/24"}, DNSEnabled: false, - UDPPortNumber: 16578, + UDPPortNumber: 16579, }, SharedFQDN: "testing.com", } From 7e2d2b00867f3b20f5e01cebef4cf459604ad1c6 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Wed, 24 Apr 2024 17:09:46 -0700 Subject: [PATCH 04/31] remove default l4 policy checks --- .github/workflows/run_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index cf2da9cb..b74125c5 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -41,7 +41,7 @@ jobs: run: go test ./banyan/... -v env: TF_ACC: true - BANYAN_API_KEY: ::add-mask::${{ inputs.api_key }} + BANYAN_API_KEY: ${{ inputs.api_key }} BANYAN_HOST: ${{ inputs.command_center_url }} - name: Generate Documentation From aa02875c3a105f1a65f575611ee3f99aeca122ce Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Wed, 24 Apr 2024 18:41:40 -0700 Subject: [PATCH 05/31] mask inputs --- .github/workflows/run_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index b74125c5..9d16fcd8 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -41,8 +41,8 @@ jobs: run: go test ./banyan/... -v env: TF_ACC: true - BANYAN_API_KEY: ${{ inputs.api_key }} - BANYAN_HOST: ${{ inputs.command_center_url }} + BANYAN_API_KEY: "::add-mask::${{ inputs.api_key }}" + BANYAN_HOST: "::add-mask::${{ inputs.command_center_url }}" - name: Generate Documentation run: go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name terraform-provider-banyan --examples-dir ./examples/ From be73b4a131e14a130b566800d13b19c36428ece6 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Thu, 25 Apr 2024 09:51:37 -0700 Subject: [PATCH 06/31] revert masking --- .github/workflows/run_tests.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index 9d16fcd8..b74125c5 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -41,8 +41,8 @@ jobs: run: go test ./banyan/... -v env: TF_ACC: true - BANYAN_API_KEY: "::add-mask::${{ inputs.api_key }}" - BANYAN_HOST: "::add-mask::${{ inputs.command_center_url }}" + BANYAN_API_KEY: ${{ inputs.api_key }} + BANYAN_HOST: ${{ inputs.command_center_url }} - name: Generate Documentation run: go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name terraform-provider-banyan --examples-dir ./examples/ From ba26a646536b7cfeab5edfc96f3dcdb03acd4e6a Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:25:12 +0530 Subject: [PATCH 07/31] fix ATG test case (#171) --- client/accesstiergroup/accesstiergroup.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/accesstiergroup/accesstiergroup.go b/client/accesstiergroup/accesstiergroup.go index 60de77c3..45fda622 100644 --- a/client/accesstiergroup/accesstiergroup.go +++ b/client/accesstiergroup/accesstiergroup.go @@ -39,7 +39,7 @@ type AccessTierGroupResponse struct { ClusterName string `json:"cluster_name"` CreatedAt int64 `json:"created_at"` UpdatedAt int64 `json:"updated_at"` - TunnelConfig TunnelConfigInfo `json:"tunnel_config"` + TunnelConfig TunnelConfigInfo `json:"tunnel_enduser"` } type TunnelConfigInfo struct { From 06c5513d5abbeaae3e049af1b150977251fffb90 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Tue, 30 Apr 2024 07:05:27 -0700 Subject: [PATCH 08/31] update documentation --- docs/resources/policy_tunnel.md | 4 ++++ docs/resources/service_rdp.md | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/resources/policy_tunnel.md b/docs/resources/policy_tunnel.md index 889be856..3c5e2343 100644 --- a/docs/resources/policy_tunnel.md +++ b/docs/resources/policy_tunnel.md @@ -121,7 +121,9 @@ Required: Optional: +- `description` (String) access group description description - `l4_access` (Block List, Max: 1) L4 access rules (see [below for nested schema](#nestedblock--access--l4_access)) +- `name` (String) access group name ### Nested Schema for `access.l4_access` @@ -137,6 +139,7 @@ Optional: Optional: - `cidrs` (Set of String) Allowed CIDRs through the service tunnel +- `description` (String) l4 policy description - `fqdns` (Set of String) Allowed FQDNs through the service tunnel - `ports` (Set of String) Allowed ports through the service tunnel - `protocols` (Set of String) Allowed protocols through the service tunnel. Set to "TCP", "UDP", "ICMP", or "ALL" @@ -148,6 +151,7 @@ Optional: Optional: - `cidrs` (Set of String) Denied CIDRs through the service tunnel +- `description` (String) l4 policy description - `fqdns` (Set of String) Allowed FQDNs through the service tunnel - `ports` (Set of String) Denied ports through the service tunnel - `protocols` (Set of String) Denied protocols through the service tunnel. Set to "TCP", "UDP", "ICMP", or "ALL" diff --git a/docs/resources/service_rdp.md b/docs/resources/service_rdp.md index 56aa6b99..b1ab2c18 100644 --- a/docs/resources/service_rdp.md +++ b/docs/resources/service_rdp.md @@ -52,7 +52,7 @@ resource "banyan_service_rdp" "example" { - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `policy` (String) Policy ID to be attached to this service - `port` (Number) The external-facing port for this service -- `rdp_settings` (Set of String) +- `rdp_settings` (Set of String) allow admin to add custom rdp settings which app will add in rdp file - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true ### Read-Only From a9ed3d8e2ff00b5a7b125cf810bf38bc39419cf8 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Fri, 3 May 2024 11:24:47 -0700 Subject: [PATCH 09/31] address review comment --- .github/workflows/run_tests.yml | 20 ++++++-------------- banyan/resource_policy_tunnel_test.go | 4 ++-- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml index b74125c5..bda95d64 100644 --- a/.github/workflows/run_tests.yml +++ b/.github/workflows/run_tests.yml @@ -3,16 +3,8 @@ name: run_tests on: workflow_dispatch: branches: [ "dev" ] - inputs: - command_center_url: - description: 'Command Center URL' - required: true - default: 'https://net.banyanops.com' - type: string - api_key: - description: 'API Key' - required: true - type: string + environment: + name: 'Dev' jobs: run_tests: @@ -41,16 +33,16 @@ jobs: run: go test ./banyan/... -v env: TF_ACC: true - BANYAN_API_KEY: ${{ inputs.api_key }} - BANYAN_HOST: ${{ inputs.command_center_url }} + BANYAN_API_KEY: ${{ secrets.BANYAN_API_KEY }} + BANYAN_HOST: ${{ secrets.BANYAN_HOST }} - name: Generate Documentation run: go run github.com/hashicorp/terraform-plugin-docs/cmd/tfplugindocs generate --provider-name terraform-provider-banyan --examples-dir ./examples/ - - name: commit documentation changes if any + name: Check if Documentation changes are required run: | if [[ $(if [[ -n "$(git status --porcelain)" ]]; then echo "true"; else echo "false"; fi) == "true" ]]; then - echo "\n ERROR: check in documentation changes" + echo "\n ERROR: Documentation changes are required, please update documentation to reflect the changes." exit 1 else echo "\n++No Changes to Documentation++\n" diff --git a/banyan/resource_policy_tunnel_test.go b/banyan/resource_policy_tunnel_test.go index 7a7cf323..3e72452a 100644 --- a/banyan/resource_policy_tunnel_test.go +++ b/banyan/resource_policy_tunnel_test.go @@ -213,8 +213,8 @@ func TestAccPolicy_tunnel_l4_fqdn(t *testing.T) { rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) resource.Test(t, resource.TestCase{ - Providers: testAccProviders, - // CheckDestroy: testAccCheckPolicy_destroy(t, &bnnPolicy.ID), + Providers: testAccProviders, + CheckDestroy: testAccCheckPolicy_destroy(t, &bnnPolicy.ID), Steps: []resource.TestStep{ // Create the policy using terraform config and check that it exists { From 517922e38b6ea872a337201dafad42018514abfa Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Tue, 4 Jun 2024 13:44:42 -0700 Subject: [PATCH 10/31] Service tunnel policy and rdp setting feature addition. (#170) (#173) * one click access for rdp changes (#168) * tunnel_policy changes add new fields name and description (#169) * remove default l4 policy checks * fix AccessTierGroup changes (#171) --------- Co-authored-by: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> From 08dae679cd8c6a1c36e2ea9a0c67df31f58b6858 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Tue, 4 Jun 2024 13:44:42 -0700 Subject: [PATCH 11/31] Service tunnel policy and rdp setting feature addition. (#170) (#173) * one click access for rdp changes (#168) * tunnel_policy changes add new fields name and description (#169) * remove default l4 policy checks * fix AccessTierGroup changes (#171) --------- Co-authored-by: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> From f92f33e8b6b7b2872b6fbe7300e09a346aa8cab0 Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Fri, 11 Oct 2024 01:44:18 +0530 Subject: [PATCH 12/31] add description field to applicable resources (#184) * add description field to resource access tier * add description support in connetor --- banyan/resource_accesstier.go | 12 ++++++++++++ banyan/resource_connector.go | 11 +++++++++++ client/satellite/model.go | 2 ++ 3 files changed, 25 insertions(+) diff --git a/banyan/resource_accesstier.go b/banyan/resource_accesstier.go index 3050c577..90a5bc36 100644 --- a/banyan/resource_accesstier.go +++ b/banyan/resource_accesstier.go @@ -293,6 +293,11 @@ func AccessTierSchema() map[string]*schema.Schema { Optional: true, Description: "Timeout value for service discovery batch processing", }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "description of an access tier", + }, } return s } @@ -383,6 +388,12 @@ func resourceAccessTierRead(ctx context.Context, d *schema.ResourceData, m inter if err != nil { return diag.FromErr(err) } + + err = d.Set("description", at.Description) + if err != nil { + return diag.FromErr(err) + } + err = flattenTunnelConfigSatellite(d, &at) if err != nil { return diag.FromErr(err) @@ -431,6 +442,7 @@ func atFromState(d *schema.ResourceData, clusterName string) (accessTier accesst DisableSnat: d.Get("disable_snat").(bool), SrcNATCIDRRange: d.Get("src_nat_cidr_range").(string), ApiKeyId: d.Get("api_key_id").(string), + Description: d.Get("description").(string), } return at } diff --git a/banyan/resource_connector.go b/banyan/resource_connector.go index 5292239a..94f33e82 100644 --- a/banyan/resource_connector.go +++ b/banyan/resource_connector.go @@ -68,6 +68,11 @@ func resourceConnector() *schema.Resource { Type: schema.TypeString, }, }, + "description": { + Type: schema.TypeString, + Optional: true, + Description: "description of connector", + }, }, } } @@ -85,6 +90,7 @@ func connectorFromState(d *schema.ResourceData) (info satellite.Info) { Metadata: satellite.Metadata{ Name: d.Get("name").(string), DisplayName: d.Get("name").(string), + Description: d.Get("description").(string), }, Spec: satellite.Spec{ APIKeyID: d.Get("api_key_id").(string), @@ -140,6 +146,11 @@ func resourceConnectorRead(ctx context.Context, d *schema.ResourceData, m interf if err != nil { return diag.FromErr(err) } + + err = d.Set("description", sat.Description) + if err != nil { + return diag.FromErr(err) + } return } diff --git a/client/satellite/model.go b/client/satellite/model.go index feae361c..36b41cde 100644 --- a/client/satellite/model.go +++ b/client/satellite/model.go @@ -45,6 +45,7 @@ type SatelliteTunnelConfig struct { Spec string `json:"spec"` IpTables string `json:"ip_tables,omitempty"` Domains []string `json:"domains"` + Description string `json:"description"` } type AccessTier struct { @@ -93,6 +94,7 @@ type Info struct { type Metadata struct { Name string `json:"name"` DisplayName string `json:"display_name"` + Description string `description` } type Spec struct { From d12d4d555b05cdadd9bc391e6bb19cf60967f80b Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Fri, 11 Oct 2024 01:45:05 +0530 Subject: [PATCH 13/31] set tls insecure value correctly (#185) --- banyan/resource_service_web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index e596854f..0a5e3d49 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -316,7 +316,7 @@ func resourceServiceWebRead(ctx context.Context, d *schema.ResourceData, m inter if err != nil { return diag.FromErr(err) } - err = d.Set("backend_tls_insecure", svc.CreateServiceSpec.Spec.BackendTarget.TLS) + err = d.Set("backend_tls_insecure", svc.CreateServiceSpec.Spec.BackendTarget.TLSInsecure) if err != nil { return diag.FromErr(err) } From 6529f30f1dd01ba2d2be3f6fc2927512b3315254 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Thu, 18 Jul 2024 08:48:22 -0700 Subject: [PATCH 14/31] Terraform changes to support NRPT option at ORG level (#175) * Terraform changes to support configure by NRPT option at ORG level (#172) * adding app config (nrpt configuration) * add documentation * add comment * fix tests --------- Co-authored-by: sparsh-arora-josh --- Makefile | 2 +- banyan/provider.go | 29 +++--- banyan/resource_accesstier_group_test.go | 2 +- banyan/resource_app_config.go | 94 +++++++++++++++++ banyan/resource_app_config_test.go | 124 +++++++++++++++++++++++ client/appconfig/appconfig.go | 20 ++++ client/appconfig/client.go | 74 ++++++++++++++ client/appconfig/client_test.go | 44 ++++++++ client/client_holder.go | 3 + docs/resources/app_config.md | 24 +++++ 10 files changed, 400 insertions(+), 16 deletions(-) create mode 100644 banyan/resource_app_config.go create mode 100644 banyan/resource_app_config_test.go create mode 100644 client/appconfig/appconfig.go create mode 100644 client/appconfig/client.go create mode 100644 client/appconfig/client_test.go create mode 100644 docs/resources/app_config.md diff --git a/Makefile b/Makefile index e3fe8dda..c4b719ec 100644 --- a/Makefile +++ b/Makefile @@ -29,4 +29,4 @@ build: install: build mkdir -p ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} mv $(ARTIFACT_NAME) ~/.terraform.d/plugins/${HOSTNAME}/${NAMESPACE}/${NAME}/${VERSION}/${OS_ARCH} -.PHONY: install +.PHONY: install \ No newline at end of file diff --git a/banyan/provider.go b/banyan/provider.go index 8e61b73e..909c7f05 100644 --- a/banyan/provider.go +++ b/banyan/provider.go @@ -28,22 +28,23 @@ func Provider() *schema.Provider { }, }, ResourcesMap: map[string]*schema.Resource{ - "banyan_service_ssh": resourceServiceSsh(), - "banyan_service_rdp": resourceServiceRdp(), - "banyan_service_tcp": resourceServiceTcp(), - "banyan_service_k8s": resourceServiceK8s(), - "banyan_service_db": resourceServiceDb(), - "banyan_service_web": resourceServiceWeb(), - "banyan_service_tunnel": resourceServiceTunnel(), - "banyan_policy_web": resourcePolicyWeb(), - "banyan_policy_infra": resourcePolicyInfra(), - "banyan_policy_tunnel": resourcePolicyTunnel(), - "banyan_role": resourceRole(), - "banyan_api_key": resourceApiKey(), - "banyan_connector": resourceConnector(), - "banyan_accesstier": resourceAccessTier(), + "banyan_service_ssh": resourceServiceSsh(), + "banyan_service_rdp": resourceServiceRdp(), + "banyan_service_tcp": resourceServiceTcp(), + "banyan_service_k8s": resourceServiceK8s(), + "banyan_service_db": resourceServiceDb(), + "banyan_service_web": resourceServiceWeb(), + "banyan_service_tunnel": resourceServiceTunnel(), + "banyan_policy_web": resourcePolicyWeb(), + "banyan_policy_infra": resourcePolicyInfra(), + "banyan_policy_tunnel": resourcePolicyTunnel(), + "banyan_role": resourceRole(), + "banyan_api_key": resourceApiKey(), + "banyan_connector": resourceConnector(), + "banyan_accesstier": resourceAccessTier(), "banyan_accesstier_group": resourceAccessTierGroup(), "banyan_scim": resourceSCIM(), + "banyan_app_config": resourceAppConfig(), }, DataSourcesMap: map[string]*schema.Resource{ "banyan_oidc_settings": dataSourceOidcSettings(), diff --git a/banyan/resource_accesstier_group_test.go b/banyan/resource_accesstier_group_test.go index 581fadbd..2f8a08fd 100644 --- a/banyan/resource_accesstier_group_test.go +++ b/banyan/resource_accesstier_group_test.go @@ -37,7 +37,7 @@ resource "banyan_accesstier_group" "example" { statsd_address = "192.168.0.1:8090" domains = ["test-1.com"] cidrs = ["198.169.0.1/24"] - dns_enabled = false + dns_enabled = true shared_fqdn = "testing.com" udp_port_number = 16580 keepalive = 30 diff --git a/banyan/resource_app_config.go b/banyan/resource_app_config.go new file mode 100644 index 00000000..ffafb5f3 --- /dev/null +++ b/banyan/resource_app_config.go @@ -0,0 +1,94 @@ +package banyan + +import ( + "context" + + "github.com/banyansecurity/terraform-banyan-provider/client" + "github.com/banyansecurity/terraform-banyan-provider/client/appconfig" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" +) + +func resourceAppConfig() *schema.Resource { + return &schema.Resource{ + Description: "The app config resource allows for creating and updating configuration of app for an org. ", + CreateContext: resourceAppConfigCreate, + ReadContext: resourceAppConfigRead, + UpdateContext: resourceAppConfigUpdate, + DeleteContext: resourceAppConfigDelete, + Schema: AppConfigSchema(), + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + } +} + +func AppConfigSchema() map[string]*schema.Schema { + s := map[string]*schema.Schema{ + "id": { + Type: schema.TypeString, + Computed: true, + Description: "ID of the app config in Banyan", + ForceNew: true, + }, + "nrpt_config": { + Type: schema.TypeBool, + Optional: true, + Description: "Enable/Disable nrpt config for app", + }, + } + return s +} + +func resourceAppConfigCreate(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { + c := m.(*client.Holder) + appConfig, err := c.AppConfig.Create(appConfigFromState(d)) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(appConfig.Data.ID) + + return +} + +func resourceAppConfigRead(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { + c := m.(*client.Holder) + key, err := c.AppConfig.Get(d.Id()) + if err != nil { + handleNotFoundError(d, err) + return + } + + d.SetId(key.Data.ID) + + err = d.Set("nrpt_config", key.Data.NRPTConfig) + if err != nil { + return diag.FromErr(err) + } + + return +} + +func resourceAppConfigUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { + c := m.(*client.Holder) + _, err := c.AppConfig.Update(appConfigFromState(d)) + if err != nil { + return diag.FromErr(err) + } + + return +} + +func resourceAppConfigDelete(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { + return +} + +// creates an app config from the terraform state +func appConfigFromState(d *schema.ResourceData) appconfig.AppConfigRequest { + nrptConfig := d.Get("nrpt_config").(bool) + ac := appconfig.AppConfigRequest{ + NRPTConfig: &nrptConfig, + } + return ac +} \ No newline at end of file diff --git a/banyan/resource_app_config_test.go b/banyan/resource_app_config_test.go new file mode 100644 index 00000000..810de6f4 --- /dev/null +++ b/banyan/resource_app_config_test.go @@ -0,0 +1,124 @@ +package banyan + +// Commented the tests because this is a one time setting, create can be done only once. +// import ( +// "encoding/json" +// "fmt" +// "os/exec" +// "testing" + +// "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" +// "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" +// ) + +// var existingResourceID string + +// func TestAccAppConfig_update(t *testing.T) { +// resource.Test(t, resource.TestCase{ +// PreCheck: func() { +// fmt.Println("Executing PreConfig...") +// existingResourceID = testAccCheckBanyanConfig() +// }, +// Providers: testAccProviders, +// CheckDestroy: nil, +// Steps: []resource.TestStep{ +// { +// PreConfig: func() { +// fmt.Println("Executing PreConfig...") +// existingResourceID = testAccCheckBanyanConfig() +// }, +// Config: testAccAppConfig_conditionalCreate(existingResourceID), +// Check: resource.ComposeAggregateTestCheckFunc( +// func(s *terraform.State) error { +// if existingResourceID == "" { +// rs, ok := s.RootModule().Resources["banyan_app_config.example"] +// if !ok { +// return fmt.Errorf("Not found: %s", "banyan_app_config.example") +// } +// if rs.Primary.ID == "" { +// return fmt.Errorf("No ID is set") +// } +// existingResourceID = rs.Primary.ID +// } +// return nil +// }, +// ), +// }, +// { +// ResourceName: "banyan_app_config.example", +// ImportState: true, +// ImportStateVerify: true, +// ImportStateIdFunc: func(*terraform.State) (string, error) { +// fmt.Println("ImportStateIdFunc called") +// return existingResourceID, nil +// }, +// }, +// { +// Config: testAccAppConfig_basic_update(), +// Check: resource.ComposeAggregateTestCheckFunc( +// resource.TestCheckResourceAttr("banyan_app_config.example", "nrpt_config", "false"), +// ), +// }, +// }, +// }) +// } + +// func testAccAppConfig_basic_create() string { +// return fmt.Sprintf(` +// resource "banyan_app_config" "example" { +// nrpt_config = true +// } +// `) +// } + +// func testAccAppConfig_basic_update() string { +// return fmt.Sprintf(` +// resource "banyan_app_config" "example" { +// nrpt_config = false +// } +// `) +// } + +// func testAccAppConfig_conditionalCreate(existingResourceID string) string { +// if existingResourceID == "" { +// return testAccAppConfig_basic_create() +// } +// return `` +// } + +// func testAccCheckBanyanConfig() string { +// fmt.Println("Executing function...") +// client := NewAccClient() + +// present, err := client.AppConfig.Get("random") +// if err != nil { +// fmt.Println("Error checking nrpt_config presence: ", err) +// return "" +// } + +// return present.Data.ID +// } + +// func testAccCheckBanyanConfigExists(resourceName string, existingResourceID *string) error { +// cmd := exec.Command("terraform", "state", "pull") +// output, err := cmd.Output() +// if err != nil { +// return fmt.Errorf("error running terraform state pull: %v", err) +// } + +// var state terraform.State +// if err := json.Unmarshal(output, &state); err != nil { +// return fmt.Errorf("error unmarshalling state: %v", err) +// } + +// rs, ok := state.RootModule().Resources[resourceName] +// if !ok { +// // Resource not found in state +// return nil +// } + +// if rs.Primary.ID != "" { +// *existingResourceID = rs.Primary.ID +// } +// return nil +// } diff --git a/client/appconfig/appconfig.go b/client/appconfig/appconfig.go new file mode 100644 index 00000000..8f9483f8 --- /dev/null +++ b/client/appconfig/appconfig.go @@ -0,0 +1,20 @@ +package appconfig + +type AppConfigRequest struct { + NRPTConfig *bool `json:"nrpt_config"` +} + +type AppConfigRecord struct { + ID string `json:"id"` + OrgID string `json:"org_id"` + NRPTConfig bool `json:"nrpt_config"` + CreatedAt int64 `json:"created_at"` + UpdatedAt int64 `json:"updated_at"` +} + +type AppConfigResponse struct { + RequestId string `json:"request_id"` + ErrorCode int `json:"error_code"` + ErrorDescription string `json:"error_description"` + Data AppConfigRecord `json:"data"` +} diff --git a/client/appconfig/client.go b/client/appconfig/client.go new file mode 100644 index 00000000..072aac36 --- /dev/null +++ b/client/appconfig/client.go @@ -0,0 +1,74 @@ +package appconfig + +import ( + "encoding/json" + + "github.com/banyansecurity/terraform-banyan-provider/client/restclient" +) + +const apiVersion = "api/v2" +const component = "/org/app_config" +const path = apiVersion + component + +type AppConfig struct { + restClient *restclient.Client +} + +// NewClient returns a new client for interacting with the app config resource +func NewClient(restClient *restclient.Client) Client { + client := AppConfig{ + restClient: restClient, + } + return &client +} + +type Client interface { + Create(appConfig AppConfigRequest) (resp AppConfigResponse, err error) + Get(id string) (resp AppConfigResponse, err error) + Update(appConfig AppConfigRequest) (resp AppConfigResponse, err error) +} + +func (a *AppConfig) Create(appConfig AppConfigRequest) (created AppConfigResponse, err error) { + body, err := json.Marshal(appConfig) + if err != nil { + return + } + resp, err := a.restClient.Create(apiVersion, component, body, path) + if err != nil { + return + } + + err = json.Unmarshal(resp, &created) + if err != nil { + return + } + + return +} + +func (a *AppConfig) Get(id string) (get AppConfigResponse, err error) { + resp, err := a.restClient.Read(apiVersion, component, id, path) + if err != nil { + return + } + + err = json.Unmarshal(resp, &get) + if err != nil { + return + } + + return +} + +func (a *AppConfig) Update(appConfig AppConfigRequest) (updated AppConfigResponse, err error) { + body, err := json.Marshal(appConfig) + if err != nil { + return + } + resp, err := a.restClient.Update(apiVersion, component, "", body, path) + if err != nil { + return + } + err = json.Unmarshal(resp, &updated) + return +} diff --git a/client/appconfig/client_test.go b/client/appconfig/client_test.go new file mode 100644 index 00000000..cf8c1d3e --- /dev/null +++ b/client/appconfig/client_test.go @@ -0,0 +1,44 @@ +package appconfig_test + +import ( + "testing" + + "github.com/banyansecurity/terraform-banyan-provider/client/appconfig" + "github.com/banyansecurity/terraform-banyan-provider/client/testutil" + "github.com/stretchr/testify/assert" +) + +var trueValue = true + +var want = appconfig.AppConfigRecord{ + NRPTConfig: true, +} + +var req = appconfig.AppConfigRequest{ + NRPTConfig: &trueValue, +} + +func Test_Create(t *testing.T) { + + client, err := testutil.GetClientHolderForTest() + + assert.NoError(t, err, "Expected to not get an error here") + + got, err := client.AppConfig.Create(req) + if err != nil { + t.Fatal(err) + } + + assert.Equal(t, got.Data.NRPTConfig, want.NRPTConfig) +} + +func Test_Get(t *testing.T) { + client, err := testutil.GetClientHolderForTest() + + assert.NoError(t, err, "Expected to not get an error here") + + got, err := client.AppConfig.Get("") + + assert.NoError(t, err, "expected no error here") + assert.Equal(t, got.Data.NRPTConfig, want.NRPTConfig) +} \ No newline at end of file diff --git a/client/client_holder.go b/client/client_holder.go index 57a09d1c..97488767 100644 --- a/client/client_holder.go +++ b/client/client_holder.go @@ -7,6 +7,7 @@ import ( "github.com/banyansecurity/terraform-banyan-provider/client/accesstiergroup" admin "github.com/banyansecurity/terraform-banyan-provider/client/admin" "github.com/banyansecurity/terraform-banyan-provider/client/apikey" + "github.com/banyansecurity/terraform-banyan-provider/client/appconfig" "github.com/banyansecurity/terraform-banyan-provider/client/policy" "github.com/banyansecurity/terraform-banyan-provider/client/policyattachment" "github.com/banyansecurity/terraform-banyan-provider/client/restclient" @@ -32,6 +33,7 @@ type Holder struct { RestClient *restclient.Client AccessTierGroup accesstiergroup.Client SCIM scim.Client + AppConfig appconfig.Client } // NewClientHolder returns a new client which is used to perform operations on all Banyan resources. @@ -54,6 +56,7 @@ func NewClientHolder(hostUrl string, apiKey string) (client *Holder, err error) RestClient: restClient, AccessTierGroup: accesstiergroup.NewClient(restClient), SCIM: scim.NewClient(restClient), + AppConfig: appconfig.NewClient(restClient), } return &c, err } diff --git a/docs/resources/app_config.md b/docs/resources/app_config.md new file mode 100644 index 00000000..6f1db05f --- /dev/null +++ b/docs/resources/app_config.md @@ -0,0 +1,24 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "banyan_app_config Resource - terraform-provider-banyan" +subcategory: "" +description: |- + The app config resource allows for creating and updating configuration of app for an org. +--- + +# banyan_app_config (Resource) + +The app config resource allows for creating and updating configuration of app for an org. + + + + +## Schema + +### Optional + +- `nrpt_config` (Boolean) Enable/Disable nrpt config for app + +### Read-Only + +- `id` (String) ID of the app config in Banyan From 0ee041a761ebf6c8367cec3531a6ac2f32433de7 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Mon, 26 Aug 2024 14:24:23 -0700 Subject: [PATCH 15/31] BC-13824: Add deployment property to connector spec (#177) (#178) * BC-13824: Add deployment property to connector spec (#177) * feat: Added new fields to connector * updated the description of platform and method --------- Co-authored-by: Suhaan-Bhandary * bump releaser version * Documentation generated --------- Co-authored-by: Suhaan-Bhandary-Josh Co-authored-by: Suhaan-Bhandary Co-authored-by: github-actions --- .github/workflows/release.yml | 2 +- banyan/resource_connector.go | 14 ++++++++++++++ client/satellite/model.go | 6 ++++++ client/satellite/satellite_test.go | 7 ++++++- docs/resources/connector.md | 2 ++ 5 files changed, 29 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1c818a8a..1bb41dc6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,7 +45,7 @@ jobs: PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v5 + uses: goreleaser/goreleaser-action@v6 with: version: latest args: release --rm-dist diff --git a/banyan/resource_connector.go b/banyan/resource_connector.go index 94f33e82..a6b98d7f 100644 --- a/banyan/resource_connector.go +++ b/banyan/resource_connector.go @@ -68,6 +68,16 @@ func resourceConnector() *schema.Resource { Type: schema.TypeString, }, }, + "platform": { + Type: schema.TypeString, + Optional: true, + Description: "The platform from which the satellite is deployed.", + }, + "method": { + Type: schema.TypeString, + Optional: true, + Description: "The method used for the deployment of the satellite.", + }, "description": { Type: schema.TypeString, Optional: true, @@ -102,6 +112,10 @@ func connectorFromState(d *schema.ResourceData) (info satellite.Info) { }, CIDRs: convertSchemaSetToStringSlice(d.Get("cidrs").(*schema.Set)), Domains: convertSchemaSetToStringSlice(d.Get("domains").(*schema.Set)), + Deployment: &satellite.Deployment{ + Platform: d.Get("platform").(string), + Method: d.Get("method").(string), + }, }, } return spec diff --git a/client/satellite/model.go b/client/satellite/model.go index 36b41cde..42dd5500 100644 --- a/client/satellite/model.go +++ b/client/satellite/model.go @@ -104,6 +104,12 @@ type Spec struct { PeerAccessTiers []PeerAccessTier `json:"peer_access_tiers"` DisableSnat bool `json:"disable_snat"` Domains []string `json:"domains,omitempty"` + Deployment *Deployment `json:"deployment,omitempty"` +} + +type Deployment struct { + Platform string `json:"platform"` // Windows, Linux, sonicOS, other + Method string `json:"method"` // app, tar, docker, firmware, terraform, other } type PeerAccessTier struct { diff --git a/client/satellite/satellite_test.go b/client/satellite/satellite_test.go index 06f77157..32cd80df 100644 --- a/client/satellite/satellite_test.go +++ b/client/satellite/satellite_test.go @@ -1,11 +1,12 @@ package satellite_test import ( + "testing" + "github.com/banyansecurity/terraform-banyan-provider/client/satellite" "github.com/banyansecurity/terraform-banyan-provider/client/testutil" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" - "testing" ) func Test_CreatSatellite(t *testing.T) { @@ -29,6 +30,10 @@ func Test_CreatSatellite(t *testing.T) { AccessTiers: []string{"us-west1"}, }, }, + Deployment: &satellite.Deployment{ + Platform: "Linux", + Method: "docker", + }, }, } got, err := client.Satellite.Create(testSatellite) diff --git a/docs/resources/connector.md b/docs/resources/connector.md index 1184ec63..9faed6f5 100644 --- a/docs/resources/connector.md +++ b/docs/resources/connector.md @@ -69,6 +69,8 @@ resource "banyan_service_tunnel" "example" { - `cidrs` (Set of String) Specifies the IPv4 address ranges of your private network in CIDR notation, ex: 192.168.1.0/24. Note that you can only specify private IP address ranges as defined in RFC-1918. - `cluster` (String) Cluster / shield name in Banyan. If not provided then the cluster will be set automatically - `domains` (Set of String) Specifies the domains that should resolve at a DNS server in your private network, ex: mycompany.local. +- `method` (String) The method used for the deployment of the satellite. +- `platform` (String) The platform from which the satellite is deployed. ### Read-Only From f6abbd0c3bc98e5819322fc6c8a8a27933ab457e Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Tue, 27 Aug 2024 21:12:09 -0700 Subject: [PATCH 16/31] fix releaser args to --clean (#179) * fix releaser args to --clean * fix releaser args to --clean --- .github/workflows/release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1bb41dc6..72b8f95f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -47,8 +47,8 @@ jobs: name: Run GoReleaser uses: goreleaser/goreleaser-action@v6 with: - version: latest - args: release --rm-dist + version: '~> v2' + args: release --clean env: GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 4796bcf22a1347846ee3c754deaae0b3f52ada34 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Thu, 5 Sep 2024 16:52:28 -0700 Subject: [PATCH 17/31] revert go releaser to v5 (#180) --- .github/workflows/release.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 72b8f95f..1c818a8a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,10 +45,10 @@ jobs: PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} - name: Run GoReleaser - uses: goreleaser/goreleaser-action@v6 + uses: goreleaser/goreleaser-action@v5 with: - version: '~> v2' - args: release --clean + version: latest + args: release --rm-dist env: GPG_FINGERPRINT: ${{ steps.import_gpg.outputs.fingerprint }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file From 97f68add7f0f60ea7b490f78c2cb1ea71d2a3c7d Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Sun, 15 Sep 2024 11:43:47 -0700 Subject: [PATCH 18/31] Service tunnel configuration support (#181) * service tunnel delta implementation with api * service tunnel delta implementation with api * fix tests * add nameserver configuration * fix tests to determine defaults for access_tiers and cluster name * Documentation generated * add documentation for service tunnel resource usage * Documentation generated * add documentation for service tunnel resource usage --------- Co-authored-by: github-actions --- banyan/resource_connector_test.go | 6 +- banyan/resource_service_tunnel.go | 621 +++++++++++------- banyan/resource_service_tunnel_test.go | 174 +++-- banyan/specs/service_tunnel/tunnel-at.json | 7 - ...ic-multiple-at-multiple-configuration.json | 111 ++++ .../tunnel-public-multiple-at.json | 7 - client/dns/nameresolution.go | 6 + client/servicetunnel/client.go | 16 +- client/servicetunnel/service_tunnel.go | 35 +- docs/resources/service_tunnel.md | 144 +++- .../banyan_service_tunnel/resource.tf | 71 +- .../resource.tf | 11 +- 12 files changed, 886 insertions(+), 323 deletions(-) create mode 100644 banyan/specs/service_tunnel/tunnel-public-multiple-at-multiple-configuration.json create mode 100644 client/dns/nameresolution.go diff --git a/banyan/resource_connector_test.go b/banyan/resource_connector_test.go index 8b58aa38..cba88b91 100644 --- a/banyan/resource_connector_test.go +++ b/banyan/resource_connector_test.go @@ -76,8 +76,10 @@ func TestAccConnector_tunnel(t *testing.T) { resource "banyan_service_tunnel" "example" { name = "%s" description = "realdescription" - connectors = [banyan_connector.example.name] - policy = banyan_policy_tunnel.example.id + network_settings { + connectors = [banyan_connector.example.name] + } + policy = banyan_policy_tunnel.example.id } `, rName, rName, rName, rName), Check: resource.ComposeTestCheckFunc( diff --git a/banyan/resource_service_tunnel.go b/banyan/resource_service_tunnel.go index ccc1d6f6..bf720a95 100644 --- a/banyan/resource_service_tunnel.go +++ b/banyan/resource_service_tunnel.go @@ -3,6 +3,7 @@ package banyan import ( "context" "fmt" + "github.com/banyansecurity/terraform-banyan-provider/client/dns" "strings" "github.com/banyansecurity/terraform-banyan-provider/client" @@ -54,108 +55,161 @@ func TunnelSchema() (s map[string]*schema.Schema) { Optional: true, Description: "Autorun for the service, if set true service would autorun on the app", }, - "connectors": { - Type: schema.TypeSet, - Optional: true, - Description: "Names of the connectors which the service tunnel should be associated with", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - ConflictsWith: []string{"access_tiers"}, - }, - "access_tiers": { - Type: schema.TypeSet, - Optional: true, - Description: "Names of the access_tiers which the service tunnel should be associated with", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - ConflictsWith: []string{"connectors"}, - }, - "public_cidrs_include": { - Type: schema.TypeSet, - Optional: true, - Description: "Specifies public IP addresses in CIDR notation that should be included in the tunnel, ex: 8.8.0.0/16.", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "public_cidrs_exclude": { - Type: schema.TypeSet, + "lock_autorun": { + Type: schema.TypeBool, Optional: true, - Description: "Specifies public IP addresses in CIDR notation that should be excluded from the tunnel, ex: 8.8.12.0/24.", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, + Description: "Lock autorun for the service, if set true service tunnel will be always autorun. end user cannot set it off", }, - "public_domains_include": { + + "network_settings": { Type: schema.TypeSet, Optional: true, - Description: "Specifies the domains that should be that should be included in the tunnel, ex: cnn.com", - Elem: &schema.Schema{ - Type: schema.TypeString, + Description: "Add a network that will be accessible via this Service Tunnel.", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "cluster": { + Type: schema.TypeString, + Optional: true, + Description: "cluster name where access-tier belongs to", + }, + "access_tiers": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "connectors": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "public_cidrs": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "include": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "exclude": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "public_domains": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "include": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "exclude": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "applications": { + Type: schema.TypeSet, + Optional: true, + MaxItems: 1, + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "include": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "exclude": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, + }, + }, + "access_tier_group": { + Type: schema.TypeString, + Optional: true, + Default: "", + Description: "AccessTier group name", + }, + }, }, }, - "public_domains_exclude": { + "name_resolution": { Type: schema.TypeSet, Optional: true, - Description: "Specifies the domains that should be that should be excluded from the tunnel, ex: zoom.us", - Elem: &schema.Schema{ - Type: schema.TypeString, + MaxItems: 1, + Description: "Private Search Domains", + Elem: &schema.Resource{ + Schema: map[string]*schema.Schema{ + "name_servers": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + "dns_search_domains": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, + }, + }, }, }, - "public_traffic_tunnel_via_access_tier": { - Type: schema.TypeString, - Optional: true, - Description: "Access Tier to be used to tunnel through public traffic", - }, "policy": { Type: schema.TypeString, Required: true, Description: "Policy ID to be attached to this service tunnel", }, - "cluster": { - Type: schema.TypeString, - Description: "(Depreciated) Sets the cluster / shield for the service", - Computed: true, - Optional: true, - Deprecated: "This attribute is now configured automatically. This attribute will be removed in a future release of the provider.", - ForceNew: true, - }, - "applications_include": { - Type: schema.TypeSet, - Optional: true, - Description: "Specifies the applications ids that should be included in the tunnel, ex: 905a72d3-6216-4ffc-ad18-db1593782915", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "applications_exclude": { - Type: schema.TypeSet, - Optional: true, - Description: "Specifies the applications ids that should be excluded in the tunnel, ex: 633301ab-fd20-439b-b5ae-47153ec7fbf2", - Elem: &schema.Schema{ - Type: schema.TypeString, - }, - }, - "access_tier_group": { - Type: schema.TypeString, - Optional: true, - Description: "Name of the access_tier group which the service tunnel should be associated with", - }, - "lock_autorun": { + "policy_enforcing": { Type: schema.TypeBool, Optional: true, - Description: "Lock autorun for the service, if set true service tunnel will be always autorun. end user cannot set it off", + Default: true, + Description: "Policy Enforcing / Permissive", }, } return } -func TunFromState(d *schema.ResourceData) (tun servicetunnel.Info) { +func TunFromState(d *schema.ResourceData) (tun servicetunnel.Info, err error) { icon := "" descriptionLink := "" - + spec, err := expandServiceTunnelSpec(d) + if err != nil { + return + } tun = servicetunnel.Info{ Kind: "BanyanServiceTunnel", APIVersion: "rbac.banyanops.com/v1", @@ -171,17 +225,17 @@ func TunFromState(d *schema.ResourceData) (tun servicetunnel.Info) { Autorun: expandAutorun(d), LockAutoRun: expandLockAutorun(d), }, - Spec: expandServiceTunnelSpec(d), + Spec: spec, } return } func resourceServiceTunnelCreate(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { - err := setCluster(d, m) + c := m.(*client.Holder) + state, err := TunFromState(d) if err != nil { return diag.FromErr(err) } - c := m.(*client.Holder) - tun, err := c.ServiceTunnel.Create(TunFromState(d)) + tun, err := c.ServiceTunnel.Create(state) if err != nil { return diag.FromErr(err) } @@ -195,7 +249,11 @@ func resourceServiceTunnelCreate(ctx context.Context, d *schema.ResourceData, m func resourceServiceTunnelUpdate(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { c := m.(*client.Holder) - tun, err := c.ServiceTunnel.Update(d.Id(), TunFromState(d)) + state, err := TunFromState(d) + if err != nil { + return diag.FromErr(err) + } + tun, err := c.ServiceTunnel.Update(d.Id(), state) if err != nil { return diag.FromErr(err) } @@ -212,9 +270,14 @@ func attachPolicy(c *client.Holder, d *schema.ResourceData) (err error) { if policy == nil { return } + policyEnforcing := d.Get("policy_enforcing") + if policyEnforcing == nil { + return + } + _, err = c.ServiceTunnel.AttachPolicy(d.Id(), servicetunnel.PolicyAttachmentPost{ PolicyID: policy.(string), - Enabled: true, + Enabled: policyEnforcing.(bool), }) if err != nil { return fmt.Errorf("failed to attach policy to service tunnel: %s", err) @@ -238,7 +301,7 @@ func resourceServiceTunnelRead(ctx context.Context, d *schema.ResourceData, m in if err != nil { return diag.FromErr(err) } - err = flattenServiceTunnelSpec(d, tun) + err = flattenServiceTunnelSpec(d, tun.Spec.Spec) if err != nil { return diag.FromErr(err) } @@ -250,6 +313,15 @@ func resourceServiceTunnelRead(ctx context.Context, d *schema.ResourceData, m in if err != nil { return diag.FromErr(err) } + + policyEnforcing := false + if strings.EqualFold("TRUE", policy.Enabled) { + policyEnforcing = true + } + err = d.Set("policy_enforcing", policyEnforcing) + if err != nil { + return diag.FromErr(err) + } return } @@ -280,7 +352,6 @@ func resourceServiceTunnelDetachPolicy(d *schema.ResourceData, c *client.Holder) if err != nil { return } - // This may not be necessary after policy refactor err = c.PolicyAttachment.Delete(attachedPolicy.PolicyID) if err != nil { return @@ -288,171 +359,271 @@ func resourceServiceTunnelDetachPolicy(d *schema.ResourceData, c *client.Holder) return } -func expandServiceTunnelSpec(d *schema.ResourceData) (expanded servicetunnel.Spec) { - ats := convertSchemaSetToStringSlice(d.Get("access_tiers").(*schema.Set)) - conns := convertSchemaSetToStringSlice(d.Get("connectors").(*schema.Set)) - inclCidrs := convertSchemaSetToStringSlice(d.Get("public_cidrs_include").(*schema.Set)) - exclCidrs := convertSchemaSetToStringSlice(d.Get("public_cidrs_exclude").(*schema.Set)) - inclDomains := convertSchemaSetToStringSlice(d.Get("public_domains_include").(*schema.Set)) - exclDomains := convertSchemaSetToStringSlice(d.Get("public_domains_exclude").(*schema.Set)) - inclApplications := convertSchemaSetToStringSlice(d.Get("applications_include").(*schema.Set)) - exclApplications := convertSchemaSetToStringSlice(d.Get("applications_exclude").(*schema.Set)) - - var peers []servicetunnel.PeerAccessTier - accessTierGroup := d.Get("access_tier_group").(string) - // if access_tiers not set and access tier group is empty => global-edge, use ["*"] - if len(ats) == 0 { - peer := servicetunnel.PeerAccessTier{ - Cluster: d.Get("cluster").(string), - AccessTiers: []string{"*"}, - Connectors: conns, +func expandServiceTunnelSpec(d *schema.ResourceData) (expanded servicetunnel.Spec, err error) { + peers, err := expandPeerAccessTiers(d) + if err != nil { + return + } + + expanded = servicetunnel.Spec{ + PeerAccessTiers: peers, + } + nameResolution, err := expandNameResolution(d) + if err != nil { + return + } + if nameResolution != nil { + expanded.NameResolution = nameResolution + } + return +} + +func expandNameResolution(d *schema.ResourceData) (nameResolutionRef *dns.NameResolutionInfo, err error) { + nameResolutionSet := d.Get("name_resolution").(*schema.Set) + var nameResolution dns.NameResolutionInfo + nameResolution.NameServers = make([]string, 0) + nameResolution.DnsSearchDomains = make([]string, 0) + for _, eachNameResolution := range nameResolutionSet.List() { + eachNameResolutionItem, ok := eachNameResolution.(map[string]interface{}) + if !ok { + err = fmt.Errorf("unable to read name_resolution") + return + } + nameServer, ok := eachNameResolutionItem["name_servers"].([]interface{}) + if !ok { + err = fmt.Errorf("unable to read name_servers") + return } + for _, eachNameServer := range nameServer { + eachNameServerString, ok := eachNameServer.(string) + if ok { + nameResolution.NameServers = append(nameResolution.NameServers, eachNameServerString) + } + } + dnsSearchDomains, ok := eachNameResolutionItem["dns_search_domains"].([]interface{}) + if !ok { + err = fmt.Errorf("unable to read dns_search_domains") + return + } + for _, eachDnsSearchDomains := range dnsSearchDomains { + eachDnsSearchDomainsString, ok := eachDnsSearchDomains.(string) + if ok { + nameResolution.DnsSearchDomains = append(nameResolution.DnsSearchDomains, eachDnsSearchDomainsString) + } + } + } + if len(nameResolution.NameServers) > 0 || len(nameResolution.DnsSearchDomains) > 0 { + nameResolutionRef = &nameResolution + } + return +} - if accessTierGroup != "" { - peer.AccessTiers = nil - peer.Connectors = nil - peer.AccessTierGroup = accessTierGroup +func expandPeerAccessTiers(d *schema.ResourceData) (peers []servicetunnel.PeerAccessTier, err error) { + peers = make([]servicetunnel.PeerAccessTier, 0) + peerAccessTierConfigs := d.Get("network_settings").(*schema.Set) + for _, eachPeer := range peerAccessTierConfigs.List() { + var peer servicetunnel.PeerAccessTier + eachPeerAccessTier, ok := eachPeer.(map[string]interface{}) + if !ok { + err = fmt.Errorf("unable to parse PeerAccessTier") + return + } + if len(eachPeerAccessTier) == 0 { + continue } - peers = append(peers, peer) - } else { - // If multiple accessTiers are set create peer foreach. - for i, eachAts := range ats { - peer := servicetunnel.PeerAccessTier{ - Cluster: d.Get("cluster").(string), - AccessTiers: []string{eachAts}, - Connectors: nil, + if connectorsRaw, ok := eachPeerAccessTier["connectors"]; ok { + connectors, ok := connectorsRaw.([]interface{}) + if !ok { + err = fmt.Errorf("unable to parse connectors") + return + } + + for _, eachConnector := range connectors { + connectorString, ok := eachConnector.(string) + if ok { + peer.Connectors = append(peer.Connectors, connectorString) + } + } + + } + + atsRaw, ok := eachPeerAccessTier["access_tiers"] + // Ignore access_tier if set if there is connector set and set as {*} as it would be a global edge access_tier + if ok && len(peer.Connectors) == 0 { + ats, ok := atsRaw.([]interface{}) + if !ok { + err = fmt.Errorf("unable to parse access_tiers") + return } - if (inclCidrs != nil) || (exclCidrs != nil) || (inclDomains != nil) || (exclDomains != nil) || (inclApplications != nil) || (exclApplications != nil) { - publicTrafficAccessTier, ok := d.GetOk("public_traffic_tunnel_via_access_tier") - - if strings.EqualFold(publicTrafficAccessTier.(string), eachAts) || - /* if only one access tier */ len(ats) == 1 || - /* backward compatibility */ (!ok && i == 0) { - peer.PublicCIDRs = &servicetunnel.IncludeExclude{ - Include: inclCidrs, - Exclude: exclCidrs, - } - peer.PublicDomains = &servicetunnel.IncludeExclude{ - Include: inclDomains, - Exclude: exclDomains, - } - peer.Applications = &servicetunnel.IncludeExclude{ - Include: inclApplications, - Exclude: exclApplications, - } + for _, eachAt := range ats { + eachAtString, ok := eachAt.(string) + if ok { + peer.AccessTiers = append(peer.AccessTiers, eachAtString) } } - peers = append(peers, peer) + } else if len(peer.Connectors) > 0 { + peer.AccessTiers = []string{"*"} } - } - expanded = servicetunnel.Spec{ - PeerAccessTiers: peers, + if atGroupRaw, ok := eachPeerAccessTier["access_tier_group"]; ok { + atGroup, ok := atGroupRaw.(string) + if !ok { + err = fmt.Errorf("unable to parse access_tier_group") + return + } + if atGroup != "" && len(peer.AccessTiers) > 0 { + err = fmt.Errorf("invalid configuration cannot set both access_tier_group and access_tiers") + return + } + if atGroup != "" { + peer.AccessTierGroup = atGroup + } + } + + if clusterNameRaw, ok := eachPeerAccessTier["cluster"]; ok { + clusterName, ok := clusterNameRaw.(string) + if !ok { + err = fmt.Errorf("unable to parse cluster") + return + } + if clusterName != "" { + peer.Cluster = clusterName + } + } + + if len(peer.Connectors) > 0 && peer.Cluster == "" { + peer.Cluster = "global-edge" + } + + if publicCIDRsRaw, ok := eachPeerAccessTier["public_cidrs"]; ok { + publicCIDRs, myErr := extractIncludeExclude("public_cidrs", publicCIDRsRaw) + if myErr != nil { + err = myErr + return + } + peer.PublicCIDRs = publicCIDRs + } + + if publicDomainsRaw, ok := eachPeerAccessTier["public_domains"]; ok { + publicDomains, myErr := extractIncludeExclude("public_domains", publicDomainsRaw) + if myErr != nil { + err = myErr + return + } + peer.PublicDomains = publicDomains + } + + if applicationsRaw, ok := eachPeerAccessTier["applications"]; ok { + applications, myErr := extractIncludeExclude("applications", applicationsRaw) + if myErr != nil { + err = myErr + return + } + peer.Applications = applications + } + peers = append(peers, peer) } return } -func flattenServiceTunnelSpec(d *schema.ResourceData, tun servicetunnel.ServiceTunnelInfo) (err error) { - if len(tun.Spec.PeerAccessTiers) == 0 { +func extractIncludeExclude(key string, inputRaw interface{}) (extracted *servicetunnel.IncludeExclude, err error) { + var inputBlock servicetunnel.IncludeExclude + inputRawSet, ok := inputRaw.(*schema.Set) + if !ok { + err = fmt.Errorf("unable to parse " + key) return } - // set common parameters using first peer. - p1 := tun.Spec.PeerAccessTiers[0] - err = d.Set("cluster", p1.Cluster) - if err != nil { - return err + inputList := inputRawSet.List() + if len(inputList) > 1 { + err = fmt.Errorf("max length is 1 for " + key) + return } - // if connectors set => global-edge - if len(p1.Connectors) > 0 { - err = d.Set("connectors", p1.Connectors) - if err != nil { - return err + for _, eachInput := range inputList { + input, ok := eachInput.(map[string]interface{}) + if !ok { + err = fmt.Errorf("unable to read " + key + " block") + return } - err = d.Set("access_tiers", nil) - if err != nil { - return err - } - } else { - var ats []string - err = d.Set("connectors", nil) - if err != nil { - return err - } - for _, eachPeer := range tun.Spec.PeerAccessTiers { - ats = append(ats, eachPeer.AccessTiers...) - if eachPeer.PublicCIDRs != nil { - if len(eachPeer.PublicCIDRs.Include) > 0 { - err = d.Set("public_cidrs_include", eachPeer.PublicCIDRs.Include) - if err != nil { - return err - } - } - if len(eachPeer.PublicCIDRs.Exclude) > 0 { - err = d.Set("public_cidrs_exclude", eachPeer.PublicCIDRs.Exclude) - if err != nil { - return err - } - } - if len(eachPeer.AccessTiers) > 0 { - err = d.Set("public_traffic_tunnel_via_access_tier", eachPeer.AccessTiers[0]) - if err != nil { - return err - } - } - + if inputInclude, ok := input["include"]; ok { + inputIncludeList, ok := inputInclude.([]interface{}) + if !ok { + err = fmt.Errorf("unable to read " + key + "inlude ist") } - if eachPeer.PublicDomains != nil { - if len(eachPeer.PublicDomains.Include) > 0 { - err = d.Set("public_domains_include", eachPeer.PublicDomains.Include) - if err != nil { - return err - } - } - if len(eachPeer.PublicDomains.Exclude) > 0 { - err = d.Set("public_domains_exclude", eachPeer.PublicDomains.Exclude) - if err != nil { - return err - } - } - if len(eachPeer.AccessTiers) > 0 { - err = d.Set("public_traffic_tunnel_via_access_tier", eachPeer.AccessTiers[0]) - if err != nil { - return err - } + for _, eachInputInclude := range inputIncludeList { + eachInputIncludeString, ok := eachInputInclude.(string) + if ok { + inputBlock.Include = append(inputBlock.Include, eachInputIncludeString) } } - if eachPeer.Applications != nil { - if len(eachPeer.Applications.Include) > 0 { - err = d.Set("applications_include", eachPeer.Applications.Include) - if err != nil { - return err - } - } - if len(eachPeer.Applications.Exclude) > 0 { - err = d.Set("applications_exclude", eachPeer.Applications.Exclude) - if err != nil { - return err - } - } - if len(eachPeer.AccessTiers) > 0 { - err = d.Set("public_traffic_tunnel_via_access_tier", eachPeer.AccessTiers[0]) - if err != nil { - return err - } - } + } + if inputExclude, ok := input["exclude"]; ok { + inputExcludeList, ok := inputExclude.([]interface{}) + if !ok { + err = fmt.Errorf("unable to read " + key + "exclude ist") } - err = d.Set("access_tier_group", eachPeer.AccessTierGroup) - if err != nil { - return err + for _, eachInputExclude := range inputExcludeList { + eachInputExcludeString, ok := eachInputExclude.(string) + if ok { + inputBlock.Exclude = append(inputBlock.Exclude, eachInputExcludeString) + } } + } + extracted = &inputBlock + } + return +} + +func flattenServiceTunnelSpec(d *schema.ResourceData, spec servicetunnel.Spec) (err error) { + if len(spec.PeerAccessTiers) == 0 { + return + } + + flattened := make([]interface{}, 0) + for _, eachPeerAccessTier := range spec.PeerAccessTiers { + eachPeerAccessTierMap := make(map[string]interface{}) + // if connectors are set access_tiers are inferred to be * + if len(eachPeerAccessTier.AccessTiers) > 0 && eachPeerAccessTier.AccessTiers[0] != "*" { + eachPeerAccessTierMap["access_tiers"] = eachPeerAccessTier.AccessTiers + } + if len(eachPeerAccessTier.Connectors) > 0 { + eachPeerAccessTierMap["connectors"] = eachPeerAccessTier.Connectors + } + if eachPeerAccessTier.AccessTierGroup != "" { + eachPeerAccessTierMap["access_tier_group"] = eachPeerAccessTier.AccessTierGroup + } + // set cluster only if not global-edge as global edge is auto set. + if eachPeerAccessTier.Cluster != "" && eachPeerAccessTier.Cluster != "global-edge" { + eachPeerAccessTierMap["cluster"] = eachPeerAccessTier.Cluster + } + if eachPeerAccessTier.PublicCIDRs != nil { + publicCIDRs := make(map[string]interface{}) + publicCIDRs["include"] = eachPeerAccessTier.PublicCIDRs.Include + publicCIDRs["exclude"] = eachPeerAccessTier.PublicCIDRs.Exclude + eachPeerAccessTierMap["public_cidrs"] = []map[string]interface{}{publicCIDRs} + } + if eachPeerAccessTier.PublicDomains != nil { + publicDomains := make(map[string]interface{}) + publicDomains["include"] = eachPeerAccessTier.PublicDomains.Include + publicDomains["exclude"] = eachPeerAccessTier.PublicDomains.Exclude + eachPeerAccessTierMap["public_domains"] = []map[string]interface{}{publicDomains} } - err = d.Set("access_tiers", ats) - if err != nil { - return err + + if eachPeerAccessTier.Applications != nil { + applications := make(map[string]interface{}) + applications["include"] = eachPeerAccessTier.Applications.Include + applications["exclude"] = eachPeerAccessTier.Applications.Exclude + eachPeerAccessTierMap["applications"] = applications } + if len(eachPeerAccessTierMap) > 0 { + flattened = append(flattened, eachPeerAccessTierMap) + } + } + err = d.Set("network_settings", flattened) + if err != nil { + return err } return } diff --git a/banyan/resource_service_tunnel_test.go b/banyan/resource_service_tunnel_test.go index 3dc80483..97d73a86 100644 --- a/banyan/resource_service_tunnel_test.go +++ b/banyan/resource_service_tunnel_test.go @@ -17,12 +17,16 @@ func TestSchemaServiceTunnel_tunnel_at(t *testing.T) { "name": "tunnel-at", "description": "describe tunnel-at", "autorun": true, - "cluster": "cluster1", - "access_tiers": []interface{}{"gcp-tdnovpn-v1", "gcp-tdnovpn-v2"}, "lock_autorun": true, + "network_settings": []interface{}{ + map[string]interface{}{ + "cluster": "cluster1", + "access_tiers": []interface{}{"gcp-tdnovpn-v1"}, + }, + }, } d := schema.TestResourceDataRaw(t, TunnelSchema(), svc_tunnel_at) - svc_obj := TunFromState(d) + svc_obj, _ := TunFromState(d) json_spec, _ := os.ReadFile("./specs/service_tunnel/tunnel-at.json") var ref_obj servicetunnel.Info @@ -35,11 +39,16 @@ func TestSchemaServiceTunnel_tunnel_conn(t *testing.T) { svc_tunnel_conn := map[string]interface{}{ "name": "global-edge-tunnel", "description": "Geo DNS to multiple ATs", - "cluster": "managed-cl-edge1", - "connectors": []interface{}{"gcp-test-drive", "td-gcp-tdnovpn"}, + "network_settings": []interface{}{ + map[string]interface{}{ + "cluster": "managed-cl-edge1", + "access_tiers": []interface{}{"*"}, + "connectors": []interface{}{"gcp-test-drive", "td-gcp-tdnovpn"}, + }, + }, } d := schema.TestResourceDataRaw(t, TunnelSchema(), svc_tunnel_conn) - svc_obj := TunFromState(d) + svc_obj, _ := TunFromState(d) json_spec, _ := os.ReadFile("./specs/service_tunnel/tunnel-conn.json") var ref_obj servicetunnel.Info @@ -50,18 +59,33 @@ func TestSchemaServiceTunnel_tunnel_conn(t *testing.T) { func TestSchemaServiceTunnel_tunnel_public(t *testing.T) { svc_tunnel_public := map[string]interface{}{ - "name": "tunnel-domains", - "description": "describe tunnel-domains", - "cluster": "cluster1", - "access_tiers": []interface{}{"gcp-tdnovpn-v2"}, - "public_cidrs_include": []interface{}{"8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"}, - "public_domains_include": []interface{}{"cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"}, - "public_traffic_tunnel_via_access_tier": "gcp-tdnovpn-v2", - "applications_include": []interface{}{"067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"}, - "access_tier_group": "", + "name": "tunnel-domains", + "description": "describe tunnel-domains", + "network_settings": []interface{}{ + map[string]interface{}{ + "cluster": "cluster1", + "access_tiers": []interface{}{"gcp-tdnovpn-v2"}, + "public_cidrs": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"}, + }, + }, + "public_domains": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"}, + }, + }, + "applications": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"}, + }, + }, + }, + }, } + d := schema.TestResourceDataRaw(t, TunnelSchema(), svc_tunnel_public) - svc_obj := TunFromState(d) + svc_obj, _ := TunFromState(d) json_spec, _ := os.ReadFile("./specs/service_tunnel/tunnel-public.json") var ref_obj servicetunnel.Info @@ -72,18 +96,32 @@ func TestSchemaServiceTunnel_tunnel_public(t *testing.T) { func TestSchemaServiceTunnel_tunnel_public_one_at(t *testing.T) { svc_tunnel_public := map[string]interface{}{ - "name": "tunnel-domains", - "description": "describe tunnel-domains", - "cluster": "cluster1", - "access_tiers": []interface{}{"gcp-tdnovpn-v2"}, - "public_cidrs_include": []interface{}{"8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"}, - "public_domains_include": []interface{}{"cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"}, - - "applications_include": []interface{}{"067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"}, - "access_tier_group": "", + "name": "tunnel-domains", + "description": "describe tunnel-domains", + "network_settings": []interface{}{ + map[string]interface{}{ + "cluster": "cluster1", + "access_tiers": []interface{}{"gcp-tdnovpn-v2"}, + "public_cidrs": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"}, + }, + }, + "public_domains": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"}, + }, + }, + "applications": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"}, + }, + }, + }, + }, } d := schema.TestResourceDataRaw(t, TunnelSchema(), svc_tunnel_public) - svc_obj := TunFromState(d) + svc_obj, _ := TunFromState(d) json_spec, _ := os.ReadFile("./specs/service_tunnel/tunnel-public.json") var ref_obj servicetunnel.Info @@ -94,18 +132,32 @@ func TestSchemaServiceTunnel_tunnel_public_one_at(t *testing.T) { func TestSchemaServiceTunnel_tunnel_public_select_at_from_multiple(t *testing.T) { svc_tunnel_public := map[string]interface{}{ - "name": "tunnel-domains", - "description": "describe tunnel-domains", - "cluster": "cluster1", - "access_tiers": []interface{}{"gcp-tdnovpn-v1", "gcp-tdnovpn-v2"}, - "public_cidrs_include": []interface{}{"8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"}, - "public_domains_include": []interface{}{"cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"}, - "applications_include": []interface{}{"067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"}, - "public_traffic_tunnel_via_access_tier": "gcp-tdnovpn-v2", - "access_tier_group": "", + "name": "tunnel-domains", + "description": "describe tunnel-domains", + "network_settings": []interface{}{ + map[string]interface{}{ + "cluster": "cluster1", + "access_tiers": []interface{}{"gcp-tdnovpn-v2"}, + "public_cidrs": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"}, + }, + }, + "public_domains": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"}, + }, + }, + "applications": []interface{}{ + map[string]interface{}{ + "include": []interface{}{"067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"}, + }, + }, + }, + }, } d := schema.TestResourceDataRaw(t, TunnelSchema(), svc_tunnel_public) - svc_obj := TunFromState(d) + svc_obj, _ := TunFromState(d) json_spec, _ := os.ReadFile("./specs/service_tunnel/tunnel-public-multiple-at.json") var ref_obj servicetunnel.Info @@ -150,8 +202,12 @@ func TestAccServiceTunnel_basic(t *testing.T) { resource "banyan_service_tunnel" "example" { name = "%s" description = "realdescription" - access_tiers = [banyan_accesstier.example.name] - policy = banyan_policy_tunnel.example.id + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + policy = banyan_policy_tunnel.example.id + policy_enforcing = false } `, rName, rName, rName, rName), Check: resource.ComposeTestCheckFunc( @@ -189,8 +245,11 @@ func TestAccServiceTunnel_basic(t *testing.T) { resource "banyan_service_tunnel" "example" { name = "%s" description = "some description" - access_tiers = [banyan_accesstier.example.name] - policy = banyan_policy_tunnel.example.id + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + policy = banyan_policy_tunnel.example.id } `, rName, rName, rName, rName), Check: resource.ComposeTestCheckFunc( @@ -241,8 +300,11 @@ func TestAccServiceTunnel_change_policy(t *testing.T) { resource "banyan_service_tunnel" "example" { name = "%s" description = "realdescription" - access_tiers = [banyan_accesstier.example.name] - policy = banyan_policy_tunnel.example.id + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + policy = banyan_policy_tunnel.example.id } `, rName, rName, rName, rName), Check: resource.ComposeTestCheckFunc( @@ -284,8 +346,11 @@ func TestAccServiceTunnel_change_policy(t *testing.T) { resource "banyan_service_tunnel" "example" { name = "%s" description = "some description" - access_tiers = [banyan_accesstier.example.name] - policy = banyan_policy_tunnel.new.id + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + policy = banyan_policy_tunnel.new.id } `, rName, rName, rName, rName, rName), Check: resource.ComposeTestCheckFunc( @@ -298,13 +363,17 @@ func TestAccServiceTunnel_change_policy(t *testing.T) { func TestSchemaServiceTunnel_with_access_tier_group(t *testing.T) { svc_tunnel_public := map[string]interface{}{ - "name": "tunnel-domains", - "description": "describe tunnel-domains", - "cluster": "cluster1", - "access_tier_group": "atg-1", + "name": "tunnel-domains", + "description": "describe tunnel-domains", + "network_settings": []interface{}{ + map[string]interface{}{ + "cluster": "cluster1", + "access_tier_group": "atg-1", + }, + }, } d := schema.TestResourceDataRaw(t, TunnelSchema(), svc_tunnel_public) - svc_obj := TunFromState(d) + svc_obj, _ := TunFromState(d) json_spec := []byte(`{ "kind": "BanyanServiceTunnel", @@ -364,8 +433,11 @@ func TestAccServiceTunnel_with_access_tier_group(t *testing.T) { resource "banyan_service_tunnel" "example" { name = "%s" description = "realdescription" - access_tier_group = "new-grp-1" - policy = banyan_policy_tunnel.example.id + network_settings { + cluster = "cluster1" + access_tier_group = "new-grp-1" + } + policy = banyan_policy_tunnel.example.id } `, rName, rName), Check: resource.ComposeTestCheckFunc( diff --git a/banyan/specs/service_tunnel/tunnel-at.json b/banyan/specs/service_tunnel/tunnel-at.json index 6596ea7d..af53e969 100644 --- a/banyan/specs/service_tunnel/tunnel-at.json +++ b/banyan/specs/service_tunnel/tunnel-at.json @@ -25,13 +25,6 @@ [ "gcp-tdnovpn-v1" ] - }, - { - "cluster": "cluster1", - "access_tiers": - [ - "gcp-tdnovpn-v2" - ] } ] } diff --git a/banyan/specs/service_tunnel/tunnel-public-multiple-at-multiple-configuration.json b/banyan/specs/service_tunnel/tunnel-public-multiple-at-multiple-configuration.json new file mode 100644 index 00000000..e4f00a03 --- /dev/null +++ b/banyan/specs/service_tunnel/tunnel-public-multiple-at-multiple-configuration.json @@ -0,0 +1,111 @@ +{ + "kind": "BanyanServiceTunnel", + "api_version": "rbac.banyanops.com", + "type": "origin", + "metadata": { + "name": "MultipleATMultipleConfig", + "friendly_name": "MultipleATMultipleConfig", + "description": "Access to resources", + "autorun": false, + "lock_autorun": false, + "tags": { + "icon": "", + "description_link": "" + } + }, + "spec": { + "peer_access_tiers": [ + { + "cluster": "cluster1", + "access_tiers": [ + "my-accesstier-1" + ], + "public_cidrs": { + "include": [], + "exclude": [] + }, + "public_domains": { + "include": [ + "my-domain-1.dev", + "my-domain-2.dev", + "my-domain-3.dev", + "my-domain-4.dev" + ], + "exclude": [] + }, + "applications": { + "include": [], + "exclude": [] + }, + "access_tier_group": "" + }, + { + "cluster": "cluster1", + "access_tiers": [ + "my-accesstier-2" + ], + "public_cidrs": { + "include": [], + "exclude": [] + }, + "public_domains": { + "include": [ + "my-domain.com" + ], + "exclude": [] + }, + "applications": { + "include": [], + "exclude": [] + }, + "access_tier_group": "" + }, + { + "cluster": "cluster1", + "access_tiers": [ + "my-accesstier-3" + ], + "public_cidrs": { + "include": [], + "exclude": [] + }, + "public_domains": { + "include": [ + "my-domain.org" + ], + "exclude": [] + }, + "applications": { + "include": [], + "exclude": [] + }, + "access_tier_group": "" + }, + { + "cluster": "cluster1", + "access_tiers": [ + "my-accesstier-4" + ], + "public_cidrs": { + "include": [], + "exclude": [] + }, + "public_domains": { + "include": [ + "my-domain-1.net", + "my-domain-2.net" + ], + "exclude": [ + "my-domain-3.net", + "my-domain-4.net" + ] + }, + "applications": { + "include": [], + "exclude": [] + }, + "access_tier_group": "" + } + ] + } +} \ No newline at end of file diff --git a/banyan/specs/service_tunnel/tunnel-public-multiple-at.json b/banyan/specs/service_tunnel/tunnel-public-multiple-at.json index deb9f780..354b0395 100644 --- a/banyan/specs/service_tunnel/tunnel-public-multiple-at.json +++ b/banyan/specs/service_tunnel/tunnel-public-multiple-at.json @@ -19,13 +19,6 @@ { "peer_access_tiers": [ - { - "cluster": "cluster1", - "access_tiers": - [ - "gcp-tdnovpn-v1" - ] - }, { "cluster": "cluster1", "access_tiers": diff --git a/client/dns/nameresolution.go b/client/dns/nameresolution.go new file mode 100644 index 00000000..0d2f0242 --- /dev/null +++ b/client/dns/nameresolution.go @@ -0,0 +1,6 @@ +package dns + +type NameResolutionInfo struct { + NameServers []string `json:"name_servers,omitempty"` + DnsSearchDomains []string `json:"dns_search_domains,omitempty"` +} diff --git a/client/servicetunnel/client.go b/client/servicetunnel/client.go index 46910ad6..1ae2c11b 100644 --- a/client/servicetunnel/client.go +++ b/client/servicetunnel/client.go @@ -31,7 +31,7 @@ type Client interface { Delete(id string) (err error) AttachPolicy(id string, post PolicyAttachmentPost) (created PolicyAttachmentInfo, err error) DeletePolicy(tunID string, policyID string) (err error) - GetPolicy(id string) (policy PolicyAttachmentInfo, err error) + GetPolicy(id string) (policy GetPolicyAttachmentInfo, err error) } func (a *ServiceTunnel) Get(id string) (spec ServiceTunnelInfo, err error) { @@ -52,8 +52,9 @@ func (a *ServiceTunnel) Create(spec Info) (created ServiceTunnelInfo, err error) Name: spec.Metadata.Name, FriendlyName: spec.Metadata.FriendlyName, Description: spec.Metadata.Description, - Autorun: spec.Autorun, - LockAutoRun: spec.LockAutoRun, + Autorun: spec.Metadata.Autorun, + LockAutoRun: spec.Metadata.LockAutoRun, + Tags: spec.Metadata.Tags, }, Spec: spec.Spec, }) @@ -77,6 +78,9 @@ func (a *ServiceTunnel) Update(id string, spec Info) (updated ServiceTunnelInfo, Name: spec.Metadata.Name, FriendlyName: spec.Metadata.FriendlyName, Description: spec.Metadata.Description, + Autorun: spec.Metadata.Autorun, + LockAutoRun: spec.Metadata.LockAutoRun, + Tags: spec.Metadata.Tags, }, Spec: spec.Spec, }) @@ -97,9 +101,9 @@ func (a *ServiceTunnel) Delete(id string) (err error) { } // GetPolicy returns the policy attached to the service tunnel -func (a *ServiceTunnel) GetPolicy(id string) (policy PolicyAttachmentInfo, err error) { +func (a *ServiceTunnel) GetPolicy(id string) (policy GetPolicyAttachmentInfo, err error) { path := fmt.Sprintf("%s/%s/%s/security_policy", apiVersion, component, id) - var j PolicyResponse + var j GetPolicyResponse resp, err := a.restClient.Read(apiVersion, component, id, path) if err != nil { return policy, nil @@ -173,7 +177,7 @@ func specFromResponse(respData []byte) (created ServiceTunnelInfo, err error) { FriendlyName: spec.FriendlyName, Description: spec.Description, Enabled: spec.Enabled, - Spec: jSpec.Spec, + Spec: jSpec, CreatedAt: spec.CreatedAt, CreatedBy: spec.CreatedBy, UpdatedAt: spec.UpdatedAt, diff --git a/client/servicetunnel/service_tunnel.go b/client/servicetunnel/service_tunnel.go index 666d160b..1771b25f 100644 --- a/client/servicetunnel/service_tunnel.go +++ b/client/servicetunnel/service_tunnel.go @@ -1,13 +1,25 @@ package servicetunnel +import "github.com/banyansecurity/terraform-banyan-provider/client/dns" + +type GetPolicyAttachmentInfo struct { + ID string `json:"id"` + PolicyID string `json:"policy_id"` + PolicyName string `json:"policy_name"` + AttachedToID string `json:"attached_to_id"` + AttachedToType string `json:"attached_to_type"` + AttachedBy string `json:"attached_by"` + AttachedAt int64 `json:"Attached_at"` + Enabled string `json:"enabled"` +} type PolicyAttachmentInfo struct { ID string `json:"id"` PolicyID string `json:"policy_id"` - PolicyVersion int `json:"policy_version"` + PolicyVersion int64 `json:"policy_version"` ServiceTunnelID string `json:"service_tunnel_id"` AttachedBy string `json:"attached_by"` AttachedAt int64 `json:"Attached_at"` - // BROKEN Enabled bool `json:"enabled"` //true/false: true => Enforced; false => Permissive mode + Enabled bool `json:"enabled"` } type PolicyAttachmentPost struct { @@ -20,7 +32,13 @@ type PolicyResponse struct { ErrorCode int `json:"error_code"` ErrorDescription string `json:"error_description"` Data PolicyAttachmentInfo `json:"data"` - Count int `json:"count"` +} + +type GetPolicyResponse struct { + RequestId string `json:"request_id"` + ErrorCode int `json:"error_code"` + ErrorDescription string `json:"error_description"` + Data GetPolicyAttachmentInfo `json:"data"` } // ServiceTunnelInfo used to send data to shield over websocket from restapi @@ -31,11 +49,13 @@ type ServiceTunnelInfo struct { FriendlyName string `json:"friendly_name"` Description string `json:"description"` Enabled bool `json:"enabled"` - Spec Spec `json:"spec"` + Spec Info `json:"spec"` CreatedAt int64 `json:"created_at"` CreatedBy string `json:"created_by"` UpdatedAt int64 `json:"updated_at"` UpdatedBy string `json:"updated_by"` + + ActiveConnectionsCount int64 `json:"active_connections_count"` } // Contains the spec string from the api response @@ -67,22 +87,21 @@ type Metadata struct { Name string `json:"name,omitempty"` FriendlyName string `json:"friendly_name,omitempty"` Description string `json:"description,omitempty"` - Tags Tags `json:"tags"` Autorun bool `json:"autorun"` LockAutoRun bool `json:"lock_autorun"` + Tags Tags `json:"tags"` } // Tags represents the metadata tags type Tags struct { - Template *string `json:"template,omitempty"` - UserFacing *string `json:"user_facing,omitempty"` Icon *string `json:"icon,omitempty"` DescriptionLink *string `json:"description_link,omitempty"` } // Spec represents the attributes stanza of a Info. type Spec struct { - PeerAccessTiers []PeerAccessTier `json:"peer_access_tiers"` + PeerAccessTiers []PeerAccessTier `json:"peer_access_tiers"` + NameResolution *dns.NameResolutionInfo `json:"name_resolution,omitempty"` } type PeerAccessTier struct { diff --git a/docs/resources/service_tunnel.md b/docs/resources/service_tunnel.md index 55e17081..504a7a08 100644 --- a/docs/resources/service_tunnel.md +++ b/docs/resources/service_tunnel.md @@ -27,10 +27,79 @@ resource "banyan_accesstier" "example" { resource "banyan_service_tunnel" "example" { name = "example-anyone-high" description = "tunnel allowing anyone with a high trust level" - access_tiers = [banyan_accesstier.example.name] + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + policy = banyan_policy_tunnel.anyone-high.id + policy_enforcing = true +} + +resource "banyan_service_tunnel" "example1" { + name = "example-anyone-high" + description = "tunnel allowing anyone with a high trust level" + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + network_settings { + connectors = ["myconnector"] + public_cidrs { + include = ["8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"] + exclude = ["99.99.99.99/32"] + } + public_domains { + include = ["cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"] + exclude = ["excluded.com"] + } + applications { + include = ["067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"] + exclude = ["067c3a25-8271-4764-89dd-c3543ac99a5c"] + } + } + network_settings { + cluster = "cluster1" + access_tiers = ["myaccesstier1"] + public_cidrs { + include = ["8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"] + exclude = ["99.99.99.99/32"] + } + public_domains { + include = ["cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"] + exclude = ["excluded.com"] + } + applications { + include = ["067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"] + exclude = ["067c3a25-8271-4764-89dd-c3543ac99a5c"] + } + } + + network_settings { + cluster = "cluster1" + access_tier_group = "atg" + public_cidrs { + include = ["8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"] + exclude = ["99.99.99.99/32"] + } + public_domains { + include = ["cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"] + exclude = ["excluded.com"] + } + applications { + include = ["067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"] + exclude = ["067c3a25-8271-4764-89dd-c3543ac99a5c"] + } + } + name_resolution { + name_servers = ["8.8.8.8"] + dns_search_domains = ["mylocal.local"] + } policy = banyan_policy_tunnel.anyone-high.id + policy_enforcing = true } + + resource "banyan_policy_tunnel" "anyone-high" { name = "allow anyone" description = "${banyan_accesstier.example.name} allow" @@ -72,14 +141,21 @@ resource "banyan_accesstier" "example" { resource "banyan_service_tunnel" "users" { name = "corporate network" description = "tunnel allowing anyone with a high trust level access to 443" - access_tiers = [banyan_accesstier.example.name] + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } policy = banyan_policy_tunnel.anyone-high.id + polict_enforcing = true } resource "banyan_service_tunnel" "administrators" { name = "corporate network admin" description = "tunnel allowing administrators access to the networks" - access_tiers = [banyan_accesstier.example.name] + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } policy = banyan_policy_tunnel.administrators.id } @@ -127,25 +203,65 @@ In this example an access tier is configured to tunnel `10.10.0.0/16`. A service ### Optional -- `access_tier_group` (String) Name of the access_tier group which the service tunnel should be associated with -- `access_tiers` (Set of String) Names of the access_tiers which the service tunnel should be associated with -- `applications_exclude` (Set of String) Specifies the applications ids that should be excluded in the tunnel, ex: 633301ab-fd20-439b-b5ae-47153ec7fbf2 -- `applications_include` (Set of String) Specifies the applications ids that should be included in the tunnel, ex: 905a72d3-6216-4ffc-ad18-db1593782915 - `autorun` (Boolean) Autorun for the service, if set true service would autorun on the app -- `cluster` (String, Deprecated) (Depreciated) Sets the cluster / shield for the service -- `connectors` (Set of String) Names of the connectors which the service tunnel should be associated with - `description` (String) Description of the service tunnel - `description_link` (String) Link shown to the end user of the banyan app for this service - `lock_autorun` (Boolean) Lock autorun for the service, if set true service tunnel will be always autorun. end user cannot set it off -- `public_cidrs_exclude` (Set of String) Specifies public IP addresses in CIDR notation that should be excluded from the tunnel, ex: 8.8.12.0/24. -- `public_cidrs_include` (Set of String) Specifies public IP addresses in CIDR notation that should be included in the tunnel, ex: 8.8.0.0/16. -- `public_domains_exclude` (Set of String) Specifies the domains that should be that should be excluded from the tunnel, ex: zoom.us -- `public_domains_include` (Set of String) Specifies the domains that should be that should be included in the tunnel, ex: cnn.com -- `public_traffic_tunnel_via_access_tier` (String) Access Tier to be used to tunnel through public traffic +- `name_resolution` (Block Set, Max: 1) Private Search Domains (see [below for nested schema](#nestedblock--name_resolution)) +- `network_settings` (Block Set) Add a network that will be accessible via this Service Tunnel. (see [below for nested schema](#nestedblock--network_settings)) +- `policy_enforcing` (Boolean) Policy Enforcing / Permissive ### Read-Only - `id` (String) ID of the service tunnel key in Banyan + + +### Nested Schema for `name_resolution` + +Optional: + +- `dns_search_domains` (List of String) +- `name_servers` (List of String) + + + +### Nested Schema for `network_settings` + +Optional: + +- `access_tier_group` (String) AccessTier group name +- `access_tiers` (List of String) +- `applications` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--network_settings--applications)) +- `cluster` (String) cluster name where access-tier belongs to +- `connectors` (List of String) +- `public_cidrs` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--network_settings--public_cidrs)) +- `public_domains` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--network_settings--public_domains)) + + +### Nested Schema for `network_settings.applications` + +Optional: + +- `exclude` (List of String) +- `include` (List of String) + + + +### Nested Schema for `network_settings.public_cidrs` + +Optional: + +- `exclude` (List of String) +- `include` (List of String) + + + +### Nested Schema for `network_settings.public_domains` + +Optional: + +- `exclude` (List of String) +- `include` (List of String) ## Import Import is supported using the following syntax: ```shell diff --git a/examples/resources/banyan_service_tunnel/resource.tf b/examples/resources/banyan_service_tunnel/resource.tf index 293d8548..c208cbdc 100644 --- a/examples/resources/banyan_service_tunnel/resource.tf +++ b/examples/resources/banyan_service_tunnel/resource.tf @@ -14,10 +14,79 @@ resource "banyan_accesstier" "example" { resource "banyan_service_tunnel" "example" { name = "example-anyone-high" description = "tunnel allowing anyone with a high trust level" - access_tiers = [banyan_accesstier.example.name] + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + policy = banyan_policy_tunnel.anyone-high.id + policy_enforcing = true +} + +resource "banyan_service_tunnel" "example1" { + name = "example-anyone-high" + description = "tunnel allowing anyone with a high trust level" + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } + network_settings { + connectors = ["myconnector"] + public_cidrs { + include = ["8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"] + exclude = ["99.99.99.99/32"] + } + public_domains { + include = ["cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"] + exclude = ["excluded.com"] + } + applications { + include = ["067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"] + exclude = ["067c3a25-8271-4764-89dd-c3543ac99a5c"] + } + } + network_settings { + cluster = "cluster1" + access_tiers = ["myaccesstier1"] + public_cidrs { + include = ["8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"] + exclude = ["99.99.99.99/32"] + } + public_domains { + include = ["cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"] + exclude = ["excluded.com"] + } + applications { + include = ["067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"] + exclude = ["067c3a25-8271-4764-89dd-c3543ac99a5c"] + } + } + + network_settings { + cluster = "cluster1" + access_tier_group = "atg" + public_cidrs { + include = ["8.8.8.8/32", "75.75.75.75/32", "75.75.76.76/32"] + exclude = ["99.99.99.99/32"] + } + public_domains { + include = ["cnn.com", "icanhazip.com", "fast.com", "yahoo.com", "banyansecurity.io"] + exclude = ["excluded.com"] + } + applications { + include = ["067c3a25-8271-4764-89dd-c3543ac99a5a", "0b90e7d0-e8fc-43fb-95b7-4ad5d6881bb8"] + exclude = ["067c3a25-8271-4764-89dd-c3543ac99a5c"] + } + } + name_resolution { + name_servers = ["8.8.8.8"] + dns_search_domains = ["mylocal.local"] + } policy = banyan_policy_tunnel.anyone-high.id + policy_enforcing = true } + + resource "banyan_policy_tunnel" "anyone-high" { name = "allow anyone" description = "${banyan_accesstier.example.name} allow" diff --git a/examples/resources/banyan_service_tunnel_l4_policy/resource.tf b/examples/resources/banyan_service_tunnel_l4_policy/resource.tf index aa012c09..0af15ea3 100644 --- a/examples/resources/banyan_service_tunnel_l4_policy/resource.tf +++ b/examples/resources/banyan_service_tunnel_l4_policy/resource.tf @@ -27,14 +27,21 @@ resource "banyan_accesstier" "example" { resource "banyan_service_tunnel" "users" { name = "corporate network" description = "tunnel allowing anyone with a high trust level access to 443" - access_tiers = [banyan_accesstier.example.name] + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } policy = banyan_policy_tunnel.anyone-high.id + polict_enforcing = true } resource "banyan_service_tunnel" "administrators" { name = "corporate network admin" description = "tunnel allowing administrators access to the networks" - access_tiers = [banyan_accesstier.example.name] + network_settings { + cluster = "cluster1" + access_tiers = [banyan_accesstier.example.name] + } policy = banyan_policy_tunnel.administrators.id } From b12d16bcf189c747a7226d6577b9a7aee6ac5ff9 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Mon, 16 Sep 2024 15:25:27 -0700 Subject: [PATCH 19/31] fix applications block conversion (#182) --- banyan/resource_service_tunnel.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/banyan/resource_service_tunnel.go b/banyan/resource_service_tunnel.go index bf720a95..f625d257 100644 --- a/banyan/resource_service_tunnel.go +++ b/banyan/resource_service_tunnel.go @@ -615,12 +615,13 @@ func flattenServiceTunnelSpec(d *schema.ResourceData, spec servicetunnel.Spec) ( applications := make(map[string]interface{}) applications["include"] = eachPeerAccessTier.Applications.Include applications["exclude"] = eachPeerAccessTier.Applications.Exclude - eachPeerAccessTierMap["applications"] = applications + eachPeerAccessTierMap["applications"] = []map[string]interface{}{applications} } if len(eachPeerAccessTierMap) > 0 { flattened = append(flattened, eachPeerAccessTierMap) } } + err = d.Set("network_settings", flattened) if err != nil { return err From 1742f47b221c9297c8d1dfd0dcec86f39b15a6fc Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Thu, 10 Oct 2024 13:29:06 -0700 Subject: [PATCH 20/31] fix description tag --- client/satellite/model.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/satellite/model.go b/client/satellite/model.go index 42dd5500..5e56814a 100644 --- a/client/satellite/model.go +++ b/client/satellite/model.go @@ -94,7 +94,7 @@ type Info struct { type Metadata struct { Name string `json:"name"` DisplayName string `json:"display_name"` - Description string `description` + Description string `json:"description"` } type Spec struct { From 6a30b36a64a13b1f14e72d57e62861215fa07099 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 10 Oct 2024 20:34:42 +0000 Subject: [PATCH 21/31] Documentation generated --- docs/resources/accesstier.md | 1 + docs/resources/connector.md | 1 + 2 files changed, 2 insertions(+) diff --git a/docs/resources/accesstier.md b/docs/resources/accesstier.md index 67b9bc7d..bf617e6d 100644 --- a/docs/resources/accesstier.md +++ b/docs/resources/accesstier.md @@ -92,6 +92,7 @@ resource "banyan_policy_tunnel" "anyone-high" { - `debug_shield_timeout` (Number) If Shield is not available, policies will be treated as if they are permissive. Zero means this is disabled. - `debug_use_rsa` (Boolean) Netagent will generate RSA instead of ECDSA keys - `debug_visibility_only` (Boolean) Enable or disable visibility mode. If on, Netagent will not do policy enforcement on inbound traffic +- `description` (String) description of an access tier - `disable_snat` (Boolean) Disable Source Network Address Translation (SNAT) - `enable_hsts` (Boolean) If enabled, Banyan will send the HTTP Strict-Transport-Security response header - `event_key_rate_limiting` (Boolean) Enable rate limiting of Access Event generation based on a credit-based rate control mechanism diff --git a/docs/resources/connector.md b/docs/resources/connector.md index 9faed6f5..7d9ffd7f 100644 --- a/docs/resources/connector.md +++ b/docs/resources/connector.md @@ -68,6 +68,7 @@ resource "banyan_service_tunnel" "example" { - `access_tiers` (Set of String) Name of the access tiers the connector will use to establish a secure dial-out connection. Will be set automatically if omitted. - `cidrs` (Set of String) Specifies the IPv4 address ranges of your private network in CIDR notation, ex: 192.168.1.0/24. Note that you can only specify private IP address ranges as defined in RFC-1918. - `cluster` (String) Cluster / shield name in Banyan. If not provided then the cluster will be set automatically +- `description` (String) description of connector - `domains` (Set of String) Specifies the domains that should resolve at a DNS server in your private network, ex: mycompany.local. - `method` (String) The method used for the deployment of the satellite. - `platform` (String) The platform from which the satellite is deployed. From 7df24012700e9c1f72aa22f3da2765e0d47dc3ab Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:43:33 +0530 Subject: [PATCH 22/31] allow setting post redirect url in webservice (#187) * allow setting post redirect url in webservice --- banyan/resource_service_web.go | 13 +- banyan/resource_service_web_test.go | 170 +++++++++++++++++++++++- banyan/specs/service_web/web-at.json | 2 +- banyan/specs/service_web/web-certs.json | 2 +- banyan/specs/service_web/web-conn.json | 2 +- 5 files changed, 183 insertions(+), 6 deletions(-) diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index 0a5e3d49..21611483 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -286,6 +286,12 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Description: "access tier group which is associated with service", }, + "post_auth_redirect_path": { + Type: schema.TypeString, + Optional: true, + Description: "redirect the user to the following path after authentication", + Default: "/", + }, } return } @@ -374,6 +380,11 @@ func resourceServiceWebRead(ctx context.Context, d *schema.ResourceData, m inter return diag.FromErr(err) } } + + err = d.Set("post_auth_redirect_path", svc.CreateServiceSpec.Spec.HTTPSettings.OIDCSettings.PostAuthRedirectPath) + if err != nil { + return diag.FromErr(err) + } return } @@ -602,7 +613,7 @@ func expandWebOIDCSettings(d *schema.ResourceData) (oidcSettings service.OIDCSet Enabled: true, ServiceDomainName: fmt.Sprintf("https://%s", d.Get("domain").(string)), APIPath: "", - PostAuthRedirectPath: "", + PostAuthRedirectPath: d.Get("post_auth_redirect_path").(string), SuppressDeviceTrustVerification: d.Get("suppress_device_trust_verification").(bool), } return diff --git a/banyan/resource_service_web_test.go b/banyan/resource_service_web_test.go index fbe67318..e4896ed4 100644 --- a/banyan/resource_service_web_test.go +++ b/banyan/resource_service_web_test.go @@ -340,7 +340,7 @@ func testAccService_basic_web_create_json(name string) string { "oidc_settings": { "enabled": true, "service_domain_name": "https://%s-web.corp.com", - "post_auth_redirect_path": "", + "post_auth_redirect_path": "/", "api_path": "", "trust_callbacks": null, "suppress_device_trust_verification": false @@ -440,7 +440,173 @@ func testAccService_basic_web_update_json(name string) string { "oidc_settings": { "enabled": true, "service_domain_name": "https://%s-web-updated.corp.com", - "post_auth_redirect_path": "", + "post_auth_redirect_path": "/", + "api_path": "", + "trust_callbacks": null, + "suppress_device_trust_verification": false + }, + "http_health_check": { + "enabled": false, + "addresses": null, + "method": "", + "path": "", + "user_agent": "", + "from_address": [], + "https": false + }, + "http_redirect": { + "enabled": false, + "addresses": null, + "from_address": null, + "url": "", + "status_code": 0 + }, + "exempted_paths": { + "enabled": false + }, + "headers": {} + }, + "client_cidrs": [] + } +} +`, name, name, name, name, name, name) +} + +func TestAccService_post_auth_redirect_path(t *testing.T) { + var bnnService service.GetServiceSpec + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckServiceDestroy(t, &bnnService.ServiceID), + Steps: []resource.TestStep{ + // Create the service using terraform config and check that it exists + { + Config: fmt.Sprintf(` + resource "banyan_policy_web" "example" { + name = "%s-pol" + description = "some web policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + resource "banyan_service_web" "example" { + name = "%s-web" + access_tier = "us-west1" + domain = "%s-web.corp.com" + backend_domain = "%s-web.internal" + backend_port = 8443 + policy = banyan_policy_web.example.id + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_web.example", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_basic_web_create_json(rName), &bnnService.ServiceID), + ), + }, + { + Config: fmt.Sprintf(` + resource "banyan_policy_web" "example" { + name = "%s-pol" + description = "some web policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + resource "banyan_service_web" "example" { + name = "%s-web" + access_tier = "us-west1" + domain = "%s-web-updated.corp.com" + backend_domain = "%s-web-updated.internal" + backend_port = 8444 + policy = banyan_policy_web.example.id + post_auth_redirect_path = "new-redirect-url" + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_web.example", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_basic_web_with_post_redirect_url(rName), &bnnService.ServiceID), + ), + }, + { + ResourceName: "banyan_service_web.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccService_basic_web_with_post_redirect_url(name string) string { + return fmt.Sprintf(` +{ + "kind": "BanyanService", + "apiVersion": "rbac.banyanops.com/v1", + "type": "origin", + "metadata": { + "name": "%s-web", + "description": "", + "cluster": "cluster1", + "tags": { + "template": "WEB_USER", + "user_facing": "true", + "protocol": "https", + "domain": "%s-web-updated.corp.com", + "port": "443", + "icon": "", + "service_app_type": "WEB", + "description_link": "" + } + }, + "spec": { + "attributes": { + "tls_sni": [ + "%s-web-updated.corp.com" + ], + "frontend_addresses": [ + { + "cidr": "", + "port": "443" + } + ], + "host_tag_selector": [ + { + "com.banyanops.hosttag.access_tier_group": "", + "com.banyanops.hosttag.site_name": "us-west1" + } + ], + "disable_private_dns": false + }, + "backend": { + "target": { + "name": "%s-web-updated.internal", + "port": "8444", + "tls": false, + "tls_insecure": false, + "client_certificate": false + }, + "dns_overrides": {}, + "whitelist": [], + "connector_name": "" + }, + "cert_settings": { + "dns_names": [ + "%s-web-updated.corp.com" + ], + "custom_tls_cert": { + "enabled": false, + "cert_file": "", + "key_file": "" + }, + "letsencrypt": false + }, + "http_settings": { + "enabled": true, + "oidc_settings": { + "enabled": true, + "service_domain_name": "https://%s-web-updated.corp.com", + "post_auth_redirect_path": "new-redirect-url", "api_path": "", "trust_callbacks": null, "suppress_device_trust_verification": false diff --git a/banyan/specs/service_web/web-at.json b/banyan/specs/service_web/web-at.json index f20e16dc..e35ea7b1 100644 --- a/banyan/specs/service_web/web-at.json +++ b/banyan/specs/service_web/web-at.json @@ -64,7 +64,7 @@ "oidc_settings": { "enabled": true, "service_domain_name": "https://test-web-at.bar.com", - "post_auth_redirect_path": "", + "post_auth_redirect_path": "/", "api_path": "", "trust_callbacks": null, "suppress_device_trust_verification": false diff --git a/banyan/specs/service_web/web-certs.json b/banyan/specs/service_web/web-certs.json index b3756174..cd92ce54 100644 --- a/banyan/specs/service_web/web-certs.json +++ b/banyan/specs/service_web/web-certs.json @@ -64,7 +64,7 @@ "oidc_settings": { "enabled": true, "service_domain_name": "https://test-web-certs.tdupnsan.getbnn.com", - "post_auth_redirect_path": "", + "post_auth_redirect_path": "/", "api_path": "", "trust_callbacks": null, "suppress_device_trust_verification": false diff --git a/banyan/specs/service_web/web-conn.json b/banyan/specs/service_web/web-conn.json index bbb96cd7..16ea02dc 100644 --- a/banyan/specs/service_web/web-conn.json +++ b/banyan/specs/service_web/web-conn.json @@ -64,7 +64,7 @@ "oidc_settings": { "enabled": true, "service_domain_name": "https://test-web-conn.tdupnsan.getbnn.com", - "post_auth_redirect_path": "", + "post_auth_redirect_path": "/", "api_path": "", "trust_callbacks": null, "suppress_device_trust_verification": false From 2e9e10b37d7a53c6c0a965c3d77e3ed3ec8f3920 Mon Sep 17 00:00:00 2001 From: github-actions Date: Wed, 16 Oct 2024 16:15:22 +0000 Subject: [PATCH 23/31] Documentation generated --- docs/resources/service_web.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/resources/service_web.md b/docs/resources/service_web.md index 4ab799ed..097b2ee4 100644 --- a/docs/resources/service_web.md +++ b/docs/resources/service_web.md @@ -117,6 +117,7 @@ resource "banyan_service_web" "example-service" { - `letsencrypt` (Boolean) Use a Public CA-issued server certificate instead of a Private CA-issued one - `policy` (String) Policy ID to be attached to this service - `port` (Number) The external-facing port for this service +- `post_auth_redirect_path` (String) redirect the user to the following path after authentication - `service_account_access` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--service_account_access)) - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true - `whitelist` (List of String) whitelist is an optional section that indicates the allowed names for the backend workload instance. If this field is populated, then the backend name must match at least one entry in this field list to establish connection with the backend service.The names in this list are allowed to start with the wildcard character "*" to match more than one backend name. This field is used generally with http_connect=false. For all http_connect=true cases, or where more advanced backend defining patterns are required, use allow_patterns. From 35fb8157492707580c946b1b71164ba8e7069fe0 Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Wed, 16 Oct 2024 21:47:37 +0530 Subject: [PATCH 24/31] add support to set tls_sni in web service (#188) * add support to set tls_sni in web service * add test cases for newly added field --------- Co-authored-by: Naresh Kakubal --- banyan/resource_service_web.go | 39 +++++++- banyan/resource_service_web_test.go | 143 ++++++++++++++++++++++++++++ 2 files changed, 180 insertions(+), 2 deletions(-) diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index 21611483..6fbf59ef 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -286,12 +286,18 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Description: "access tier group which is associated with service", }, +"tls_sni": { + Type: schema.TypeList, + Optional: true, + Elem: &schema.Schema{ + Type: schema.TypeString, + }, "post_auth_redirect_path": { Type: schema.TypeString, Optional: true, Description: "redirect the user to the following path after authentication", Default: "/", - }, + }, } return } @@ -381,10 +387,19 @@ func resourceServiceWebRead(ctx context.Context, d *schema.ResourceData, m inter } } + TLSSNI := d.Get("tls_sni") + if TLSSNI != nil { + err = d.Set("tls_sni", expandTLSSNIs(d)) + if err != nil { + return diag.FromErr(err) + } + } + err = d.Set("post_auth_redirect_path", svc.CreateServiceSpec.Spec.HTTPSettings.OIDCSettings.PostAuthRedirectPath) if err != nil { return diag.FromErr(err) } + return } @@ -458,8 +473,19 @@ func expandWebAttributes(d *schema.ResourceData) (attributes service.Attributes, if err != nil { return } + + TLSSNI := expandTLSSNIs(d) + if len(TLSSNI) != 0 { + err = d.Set("tls_sni", TLSSNI) + if err != nil { + return + } + } else { + TLSSNI = append(TLSSNI, d.Get("domain").(string)) + } + attributes = service.Attributes{ - TLSSNI: []string{d.Get("domain").(string)}, + TLSSNI: TLSSNI, FrontendAddresses: expandWebFrontendAddresses(d), HostTagSelector: hostTagSelector, DisablePrivateDns: d.Get("disable_private_dns").(bool), @@ -497,6 +523,15 @@ func expandBackendWhitelist(d *schema.ResourceData) []string { return items } +func expandTLSSNIs(d *schema.ResourceData) []string { + itemsRaw := d.Get("tls_sni").([]interface{}) + items := make([]string, len(itemsRaw)) + for i, raw := range itemsRaw { + items[i] = raw.(string) + } + return items +} + func expandBackendDNSOverrides(d *schema.ResourceData) map[string]string { dnsOverrides := make(map[string]string) v, ok := d.GetOk("dns_overrides") diff --git a/banyan/resource_service_web_test.go b/banyan/resource_service_web_test.go index e4896ed4..773bc1e2 100644 --- a/banyan/resource_service_web_test.go +++ b/banyan/resource_service_web_test.go @@ -637,3 +637,146 @@ func testAccService_basic_web_with_post_redirect_url(name string) string { } `, name, name, name, name, name, name) } + +func TestAccService_custom_tls_sni(t *testing.T) { + var bnnService service.GetServiceSpec + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckServiceDestroy(t, &bnnService.ServiceID), + Steps: []resource.TestStep{ + // Create the service using terraform config and check that it exists + { + Config: fmt.Sprintf(` + resource "banyan_policy_web" "example" { + name = "%s-pol" + description = "some web policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + resource "banyan_service_web" "example" { + name = "%s-web" + access_tier = "us-west1" + domain = "%s-web.corp.com" + backend_domain = "%s-web.internal" + backend_port = 8443 + policy = banyan_policy_web.example.id + tls_sni = ["newtlssni.test.com","newtlssni2.test.com"] + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_web.example", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_basic_web_create_with_tls_sni(rName), &bnnService.ServiceID), + ), + }, + { + ResourceName: "banyan_service_web.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} + +func testAccService_basic_web_create_with_tls_sni(name string) string { + return fmt.Sprintf(` +{ + "kind": "BanyanService", + "apiVersion": "rbac.banyanops.com/v1", + "type": "origin", + "metadata": { + "name": "%s-web", + "description": "", + "cluster": "cluster1", + "tags": { + "template": "WEB_USER", + "user_facing": "true", + "protocol": "https", + "domain": "%s-web.corp.com", + "port": "443", + "icon": "", + "service_app_type": "WEB", + "description_link": "" + } + }, + "spec": { + "attributes": { + "tls_sni": [ + "newtlssni.test.com", + "newtlssni2.test.com" + ], + "frontend_addresses": [ + { + "cidr": "", + "port": "443" + } + ], + "host_tag_selector": [ + { + "com.banyanops.hosttag.access_tier_group": "", + "com.banyanops.hosttag.site_name": "us-west1" + } + ], + "disable_private_dns": false + }, + "backend": { + "target": { + "name": "%s-web.internal", + "port": "8443", + "tls": false, + "tls_insecure": false, + "client_certificate": false + }, + "dns_overrides": {}, + "whitelist": [], + "connector_name": "" + }, + "cert_settings": { + "dns_names": [ + "%s-web.corp.com" + ], + "custom_tls_cert": { + "enabled": false, + "cert_file": "", + "key_file": "" + }, + "letsencrypt": false + }, + "http_settings": { + "enabled": true, + "oidc_settings": { + "enabled": true, + "service_domain_name": "https://%s-web.corp.com", + "post_auth_redirect_path": "", + "api_path": "", + "trust_callbacks": null, + "suppress_device_trust_verification": false + }, + "http_health_check": { + "enabled": false, + "addresses": null, + "method": "", + "path": "", + "user_agent": "", + "from_address": [], + "https": false + }, + "http_redirect": { + "enabled": false, + "addresses": null, + "from_address": null, + "url": "", + "status_code": 0 + }, + "exempted_paths": { + "enabled": false + }, + "headers": {} + }, + "client_cidrs": [] + } +} +`, name, name, name, name, name) +} From ceca312f12a9de7a1c49c65e36c8b3f9e46438b3 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Wed, 16 Oct 2024 10:09:01 -0700 Subject: [PATCH 25/31] fix merge conflict --- banyan/resource_service_web.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index 6fbf59ef..0cb77b39 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -286,18 +286,19 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Description: "access tier group which is associated with service", }, -"tls_sni": { + "tls_sni": { Type: schema.TypeList, Optional: true, Elem: &schema.Schema{ Type: schema.TypeString, }, + }, "post_auth_redirect_path": { Type: schema.TypeString, Optional: true, Description: "redirect the user to the following path after authentication", Default: "/", - }, + }, } return } From 86195074c62db3433cbb9863ca5ddbe9c7416c1d Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Thu, 17 Oct 2024 20:27:28 +0530 Subject: [PATCH 26/31] add support to set enforcing and permissive mode for services (#189) * add support to set enforcing and permissive mode for services --- banyan/resource_service_db.go | 6 ++ banyan/resource_service_k8s.go | 6 ++ banyan/resource_service_k8s_test.go | 104 ++++++++++++++++++++++++++++ banyan/resource_service_rdp.go | 6 ++ banyan/resource_service_rdp_test.go | 100 ++++++++++++++++++++++++++ banyan/resource_service_ssh.go | 6 ++ banyan/resource_service_ssh_test.go | 101 ++++++++++++++++++++++++++- banyan/resource_service_tcp.go | 6 ++ banyan/resource_service_tcp_test.go | 104 +++++++++++++++++++++++++++- banyan/resource_service_web.go | 6 ++ banyan/resource_service_web_test.go | 1 + banyan/service_helpers.go | 17 ++++- banyan/service_infra_helpers.go | 20 ++++-- 13 files changed, 474 insertions(+), 9 deletions(-) diff --git a/banyan/resource_service_db.go b/banyan/resource_service_db.go index b05d842f..0fb8312c 100644 --- a/banyan/resource_service_db.go +++ b/banyan/resource_service_db.go @@ -211,6 +211,12 @@ func DbSchema() map[string]*schema.Schema { Default: true, Description: "Allow the end user to override the backend_port for this service", }, + "policy_enforcing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", + }, } } diff --git a/banyan/resource_service_k8s.go b/banyan/resource_service_k8s.go index af8b5809..32557c6d 100644 --- a/banyan/resource_service_k8s.go +++ b/banyan/resource_service_k8s.go @@ -142,6 +142,12 @@ func K8sSchema() map[string]*schema.Schema { Default: true, Description: "Allow the end user to override the backend_port for this service", }, + "policy_enforcing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", + }, } } diff --git a/banyan/resource_service_k8s_test.go b/banyan/resource_service_k8s_test.go index 18476c31..9a795c70 100644 --- a/banyan/resource_service_k8s_test.go +++ b/banyan/resource_service_k8s_test.go @@ -53,6 +53,7 @@ func TestAccService_k8s(t *testing.T) { client_kube_cluster_name = "k8s-cluster" client_kube_ca_key = "k8scAk3yH3re" client_banyanproxy_listen_port = "9119" + policy_enforcing = false } `, rName, rName, rName), Check: resource.ComposeTestCheckFunc( @@ -67,3 +68,106 @@ func TestAccService_k8s(t *testing.T) { }, }) } + +func TestAccK8Service_basic(t *testing.T) { + + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + //test case with policy enforce + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_k8s" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + policy_enforcing = false + backend_dns_override_for_domain = "test-k8s.service" + client_kube_cluster_name = "k8s-cluster" + client_kube_ca_key = "k8scAk3yH3re" + client_banyanproxy_listen_port = "9119" + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_k8s.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_k8s.example", + ImportState: true, + ImportStateVerify: true, + }, + // test case without policy enforcing + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_k8s" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_dns_override_for_domain = "test-k8s.service" + client_kube_cluster_name = "k8s-cluster" + client_kube_ca_key = "k8scAk3yH3re" + client_banyanproxy_listen_port = "9119" + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_k8s.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_k8s.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/banyan/resource_service_rdp.go b/banyan/resource_service_rdp.go index b0c17e0b..6f0ea741 100644 --- a/banyan/resource_service_rdp.go +++ b/banyan/resource_service_rdp.go @@ -211,6 +211,12 @@ func RdpSchema() map[string]*schema.Schema { }, Description: "allow admin to add custom rdp settings which app will add in rdp file", }, + "policy_enforcing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", + }, } } diff --git a/banyan/resource_service_rdp_test.go b/banyan/resource_service_rdp_test.go index 037d1f83..74f1dbc0 100644 --- a/banyan/resource_service_rdp_test.go +++ b/banyan/resource_service_rdp_test.go @@ -114,6 +114,7 @@ resource "banyan_service_rdp" "example" { backend_domain = "%s-rdp.internal" backend_port = 3389 rdp_settings = ["devicestoredirect:s:*"] + policy_enforcing = false } `, name, name, name) } @@ -243,6 +244,7 @@ resource "banyan_service_rdp" "example_without_rdp" { domain = "%s-rdp.corp.com" backend_domain = "%s-rdp.internal" backend_port = 3389 + policy_enforcing = false } `, name, name, name) } @@ -361,3 +363,101 @@ func testAccService_infra_rdp_create_without_rdp_settings_json(name string) stri } `, name, name, name, name, name) } + +func TestAccRDPService_basic(t *testing.T) { + + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + //test case with policy enforce + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_rdp" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_domain = "10.1.34.54" + backend_port = 3389 + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_rdp.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_rdp.example", + ImportState: true, + ImportStateVerify: true, + }, + // test case without policy enforcing + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_rdp" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_domain = "10.1.34.54" + backend_port = 3389 + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_rdp.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_rdp.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/banyan/resource_service_ssh.go b/banyan/resource_service_ssh.go index e19346ec..8efa03f3 100644 --- a/banyan/resource_service_ssh.go +++ b/banyan/resource_service_ssh.go @@ -152,6 +152,12 @@ func SshSchema() map[string]*schema.Schema { Optional: true, Default: false, }, + "policy_enforcing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", + }, "allow_patterns": { Type: schema.TypeSet, MaxItems: 1, diff --git a/banyan/resource_service_ssh_test.go b/banyan/resource_service_ssh_test.go index eba4bc65..4943bd7c 100644 --- a/banyan/resource_service_ssh_test.go +++ b/banyan/resource_service_ssh_test.go @@ -85,6 +85,7 @@ resource "banyan_service_ssh" "example" { domain = "%s-ssh.corp.com" backend_domain = "%s-ssh.internal" backend_port = 22 + policy_enforcing = false } `, name, name, name) } @@ -198,9 +199,107 @@ func testAccService_ssh_create_json(name string) string { ] }, "headers": {} - }, + }, "client_cidrs": [] } } `, name, name, name, name, name) } + +func TestAccSSHService_basic(t *testing.T) { + + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + //test case with policy enforce + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_ssh" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_domain = "10.1.34.54" + backend_port = 3389 + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_ssh.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_ssh.example", + ImportState: true, + ImportStateVerify: true, + }, + // test case without policy enforcing + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_ssh" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_domain = "10.1.34.54" + backend_port = 3389 + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_ssh.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_ssh.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/banyan/resource_service_tcp.go b/banyan/resource_service_tcp.go index 501329d9..066caee7 100644 --- a/banyan/resource_service_tcp.go +++ b/banyan/resource_service_tcp.go @@ -211,6 +211,12 @@ func TcpSchema() map[string]*schema.Schema { Default: true, Description: "Allow the end user to override the backend_port for this service", }, + "policy_enforcing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", + }, } } diff --git a/banyan/resource_service_tcp_test.go b/banyan/resource_service_tcp_test.go index 621f9bf0..fddee6af 100644 --- a/banyan/resource_service_tcp_test.go +++ b/banyan/resource_service_tcp_test.go @@ -87,6 +87,7 @@ resource "banyan_service_tcp" "example" { domain = "%s-tcp.corp.com" backend_domain = "%s-tcp.internal" backend_port = 5673 + policy_enforcing = false } `, name, name, name) } @@ -197,7 +198,7 @@ func testAccService_tcp_create_json(name string) string { "paths": [], "mandatory_headers": [] } - ] + ] }, "headers": {} }, @@ -240,6 +241,7 @@ resource "banyan_service_tcp" "example" { backend_domain = "" backend_port = 0 http_connect = true + policy_enforcing = false allow_patterns { ports { port_list = ["8443", "8444", "8445"] @@ -375,7 +377,7 @@ func testAccService_tcp_httpconn_create_json(name string) string { "paths": [], "mandatory_headers": [] } - ] + ] }, "headers": {} }, @@ -384,3 +386,101 @@ func testAccService_tcp_httpconn_create_json(name string) string { } `, name, name, name, name) } + +func TestAccTCPService_basic(t *testing.T) { + + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: nil, + Steps: []resource.TestStep{ + //test case with policy enforce + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_tcp" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_domain = "10.1.34.54" + backend_port = 3389 + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_tcp.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_tcp.example", + ImportState: true, + ImportStateVerify: true, + }, + // test case without policy enforcing + { + Config: fmt.Sprintf(` + resource "banyan_api_key" "example" { + name = "%s" + description = "realdescription" + scope = "access_tier" + } + + resource banyan_accesstier "example" { + name = "%s" + address = "*.example.com" + api_key_id = banyan_api_key.example.id + } + + resource "banyan_policy_infra" "example" { + name = "%s" + description = "some tunnel policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + + resource "banyan_service_tcp" "example" { + name = "%s" + description = "realdescription" + access_tier = banyan_accesstier.example.name + domain = "test-k8s.corp.com" + policy = banyan_policy_infra.example.id + backend_domain = "10.1.34.54" + backend_port = 3389 + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + resource.TestCheckResourceAttr("banyan_service_tcp.example", "name", rName), + ), + }, + { + ResourceName: "banyan_service_tcp.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index 0cb77b39..ed3abc99 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -286,6 +286,11 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Description: "access tier group which is associated with service", }, + "policy_enforcing": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", "tls_sni": { Type: schema.TypeList, Optional: true, @@ -298,6 +303,7 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Description: "redirect the user to the following path after authentication", Default: "/", + }, } return diff --git a/banyan/resource_service_web_test.go b/banyan/resource_service_web_test.go index 773bc1e2..5aeab85d 100644 --- a/banyan/resource_service_web_test.go +++ b/banyan/resource_service_web_test.go @@ -102,6 +102,7 @@ func TestAccService_required_web(t *testing.T) { backend_domain = "%s-web.internal" backend_port = 8443 policy = banyan_policy_web.example.id + policy_enforcing = false } `, rName, rName, rName, rName), Check: resource.ComposeTestCheckFunc( diff --git a/banyan/service_helpers.go b/banyan/service_helpers.go index a4ed8832..7701e2e2 100644 --- a/banyan/service_helpers.go +++ b/banyan/service_helpers.go @@ -3,12 +3,13 @@ package banyan import ( "context" "fmt" + "log" + "github.com/banyansecurity/terraform-banyan-provider/client" "github.com/banyansecurity/terraform-banyan-provider/client/policyattachment" "github.com/banyansecurity/terraform-banyan-provider/client/service" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "log" ) func resourceServiceDelete(ctx context.Context, d *schema.ResourceData, m interface{}) (diagnostics diag.Diagnostics) { @@ -68,8 +69,13 @@ func attachPolicyToService(d *schema.ResourceData, c *client.Holder) (err error) AttachedToID: d.Get("id").(string), AttachedToType: "service", IsEnabled: true, - Enabled: "TRUE", } + + policyEnforcing := d.Get("policy_enforcing") + if policyEnforcing != nil { + body.Enabled = boolToString(policyEnforcing.(bool)) + } + pa, err := c.PolicyAttachment.Create(policyID, body) if err != nil { return @@ -94,3 +100,10 @@ func resourceServiceUpdate(svc service.CreateService, d *schema.ResourceData, m } return } + +func boolToString(boolValue bool) string { + if boolValue { + return "TRUE" + } + return "FALSE" +} diff --git a/banyan/service_infra_helpers.go b/banyan/service_infra_helpers.go index 30e3530a..b4ec06cd 100644 --- a/banyan/service_infra_helpers.go +++ b/banyan/service_infra_helpers.go @@ -2,12 +2,13 @@ package banyan import ( "fmt" - "github.com/banyansecurity/terraform-banyan-provider/client" "log" "reflect" "strconv" "strings" + "github.com/banyansecurity/terraform-banyan-provider/client" + "github.com/banyansecurity/terraform-banyan-provider/client/service" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -112,15 +113,26 @@ func resourceServiceInfraCommonRead(svc service.GetServiceSpec, d *schema.Resour return diag.FromErr(err) } - // set policy for service - policy, err := c.Service.GetPolicyForService(svc.ServiceID) + policyInfo, err := c.PolicyAttachment.Get(svc.ServiceID, "service") + if err != nil { + return + } + + err = d.Set("policy", policyInfo.PolicyID) if err != nil { return diag.FromErr(err) } - err = d.Set("policy", policy.ID) + + policyEnforcing := false + if strings.EqualFold("TRUE", policyInfo.Enabled) { + policyEnforcing = true + } + + err = d.Set("policy_enforcing", policyEnforcing) if err != nil { return diag.FromErr(err) } + d.SetId(d.Id()) log.Printf("[INFO] Read service %s", d.Id()) return From 2c5c2650d3f9016ea5f73470da84ca89e851abf5 Mon Sep 17 00:00:00 2001 From: Naresh Kakubal Date: Thu, 17 Oct 2024 10:59:40 -0700 Subject: [PATCH 27/31] fix merge conflict --- banyan/resource_service_web.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index ed3abc99..971d7140 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -291,6 +291,7 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Default: true, Description: "mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode", + }, "tls_sni": { Type: schema.TypeList, Optional: true, @@ -303,7 +304,6 @@ func WebSchema() (s map[string]*schema.Schema) { Optional: true, Description: "redirect the user to the following path after authentication", Default: "/", - }, } return From 883bdda97ecff15e784f03c3a52067aaacda2287 Mon Sep 17 00:00:00 2001 From: rohitSangamnerkar <148537411+rohitSangamnerkar@users.noreply.github.com> Date: Fri, 18 Oct 2024 02:50:14 +0530 Subject: [PATCH 28/31] fix test case (#191) --- banyan/resource_service_web_test.go | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/banyan/resource_service_web_test.go b/banyan/resource_service_web_test.go index 5aeab85d..f76309e9 100644 --- a/banyan/resource_service_web_test.go +++ b/banyan/resource_service_web_test.go @@ -672,11 +672,6 @@ func TestAccService_custom_tls_sni(t *testing.T) { testAccCheckServiceAgainstJson(t, testAccService_basic_web_create_with_tls_sni(rName), &bnnService.ServiceID), ), }, - { - ResourceName: "banyan_service_web.example", - ImportState: true, - ImportStateVerify: true, - }, }, }) } @@ -750,7 +745,7 @@ func testAccService_basic_web_create_with_tls_sni(name string) string { "oidc_settings": { "enabled": true, "service_domain_name": "https://%s-web.corp.com", - "post_auth_redirect_path": "", + "post_auth_redirect_path": "/", "api_path": "", "trust_callbacks": null, "suppress_device_trust_verification": false From b95a311b42c45e068d6959798ef6eac86d1e2304 Mon Sep 17 00:00:00 2001 From: github-actions Date: Thu, 17 Oct 2024 21:24:48 +0000 Subject: [PATCH 29/31] Documentation generated --- docs/resources/service_db.md | 1 + docs/resources/service_k8s.md | 1 + docs/resources/service_rdp.md | 1 + docs/resources/service_ssh.md | 1 + docs/resources/service_tcp.md | 1 + docs/resources/service_web.md | 2 ++ 6 files changed, 7 insertions(+) diff --git a/docs/resources/service_db.md b/docs/resources/service_db.md index e23dee4f..cf8296d3 100644 --- a/docs/resources/service_db.md +++ b/docs/resources/service_db.md @@ -52,6 +52,7 @@ resource "banyan_service_db" "example" { - `end_user_override` (Boolean) Allow the end user to override the backend_port for this service - `http_connect` (Boolean) Indicates to use HTTP Connect request to derive the backend target address. - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config +- `policy_enforcing` (Boolean) mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode - `port` (Number) The external-facing port for this service - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true diff --git a/docs/resources/service_k8s.md b/docs/resources/service_k8s.md index 5fb8a870..b178f212 100644 --- a/docs/resources/service_k8s.md +++ b/docs/resources/service_k8s.md @@ -51,6 +51,7 @@ resource "banyan_service_k8s" "example" { - `end_user_override` (Boolean) Allow the end user to override the backend_port for this service - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `policy` (String) Policy ID to be attached to this service +- `policy_enforcing` (Boolean) mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode - `port` (Number) The external-facing port for this service - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true diff --git a/docs/resources/service_rdp.md b/docs/resources/service_rdp.md index b1ab2c18..5f0442c5 100644 --- a/docs/resources/service_rdp.md +++ b/docs/resources/service_rdp.md @@ -51,6 +51,7 @@ resource "banyan_service_rdp" "example" { - `http_connect` (Boolean) Indicates whether to use HTTP Connect request to derive the backend target address. Set to true for an RDP gateway - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `policy` (String) Policy ID to be attached to this service +- `policy_enforcing` (Boolean) mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode - `port` (Number) The external-facing port for this service - `rdp_settings` (Set of String) allow admin to add custom rdp settings which app will add in rdp file - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true diff --git a/docs/resources/service_ssh.md b/docs/resources/service_ssh.md index f2a12983..7e27be7d 100644 --- a/docs/resources/service_ssh.md +++ b/docs/resources/service_ssh.md @@ -51,6 +51,7 @@ resource "banyan_service_ssh" "example" { - `http_connect` (Boolean) Indicates to use HTTP Connect request to derive the backend target address. - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `policy` (String) Policy ID to be attached to this service +- `policy_enforcing` (Boolean) mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode - `port` (Number) The external-facing port for this service - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true diff --git a/docs/resources/service_tcp.md b/docs/resources/service_tcp.md index bdefddf0..0ec8c1af 100644 --- a/docs/resources/service_tcp.md +++ b/docs/resources/service_tcp.md @@ -52,6 +52,7 @@ resource "banyan_service_tcp" "example" { - `http_connect` (Boolean) Indicates to use HTTP Connect request to derive the backend target address. - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `policy` (String) Policy ID to be attached to this service +- `policy_enforcing` (Boolean) mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode - `port` (Number) The external-facing port for this service - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true diff --git a/docs/resources/service_web.md b/docs/resources/service_web.md index 097b2ee4..a6c326cc 100644 --- a/docs/resources/service_web.md +++ b/docs/resources/service_web.md @@ -116,10 +116,12 @@ resource "banyan_service_web" "example-service" { - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `letsencrypt` (Boolean) Use a Public CA-issued server certificate instead of a Private CA-issued one - `policy` (String) Policy ID to be attached to this service +- `policy_enforcing` (Boolean) mode in which policy should be. If this is true policy is in enforcing mode else policy is in Permissive mode - `port` (Number) The external-facing port for this service - `post_auth_redirect_path` (String) redirect the user to the following path after authentication - `service_account_access` (Block Set, Max: 1) (see [below for nested schema](#nestedblock--service_account_access)) - `suppress_device_trust_verification` (Boolean) suppress_device_trust_verification disables Device Trust Verification for a service if set to true +- `tls_sni` (List of String) - `whitelist` (List of String) whitelist is an optional section that indicates the allowed names for the backend workload instance. If this field is populated, then the backend name must match at least one entry in this field list to establish connection with the backend service.The names in this list are allowed to start with the wildcard character "*" to match more than one backend name. This field is used generally with http_connect=false. For all http_connect=true cases, or where more advanced backend defining patterns are required, use allow_patterns. ### Read-Only From 6318a6ea22a3a418913c197d30d9f849ecd6c36b Mon Sep 17 00:00:00 2001 From: "Omkesh Sajjanwar (Josh)" <121938354+OmkeshJosh@users.noreply.github.com> Date: Fri, 18 Oct 2024 22:02:14 +0530 Subject: [PATCH 30/31] BC-14790 : Ability to Enable / Disable service like on the console for resource service (#190) option in resource web service to enable/disable --- banyan/resource_service_web.go | 36 ++++++++++ banyan/resource_service_web_test.go | 104 ++++++++++++++++++++++++++++ client/service/model.go | 1 + client/service/service.go | 29 ++++++-- 4 files changed, 165 insertions(+), 5 deletions(-) diff --git a/banyan/resource_service_web.go b/banyan/resource_service_web.go index 971d7140..c29a1514 100644 --- a/banyan/resource_service_web.go +++ b/banyan/resource_service_web.go @@ -5,6 +5,7 @@ import ( "fmt" "log" "strconv" + "strings" "github.com/banyansecurity/terraform-banyan-provider/client" "github.com/banyansecurity/terraform-banyan-provider/client/service" @@ -305,6 +306,12 @@ func WebSchema() (s map[string]*schema.Schema) { Description: "redirect the user to the following path after authentication", Default: "/", }, + "enable": { + Type: schema.TypeBool, + Optional: true, + Default: true, + Description: "enable / disable web service", + }, } return } @@ -394,6 +401,16 @@ func resourceServiceWebRead(ctx context.Context, d *schema.ResourceData, m inter } } + isWebServiceEnable := false + if strings.EqualFold(svc.Enabled, "TRUE") { + isWebServiceEnable = true + } + + err = d.Set("enable", isWebServiceEnable) + if err != nil { + return diag.FromErr(err) + } + TLSSNI := d.Get("tls_sni") if TLSSNI != nil { err = d.Set("tls_sni", expandTLSSNIs(d)) @@ -416,10 +433,29 @@ func resourceServiceWebUpdate(ctx context.Context, d *schema.ResourceData, m int if diagnostics.HasError() { return diagnostics } + + // enable/disable web service + err := toggleWebService(d, m) + if err != nil { + return diag.FromErr(err) + } + diagnostics = resourceServiceWebRead(ctx, d, m) return } +func toggleWebService(d *schema.ResourceData, m interface{}) (err error) { + log.Printf("[INFO] toggle web service %s", d.Id()) + c := m.(*client.Holder) + if d.Get("enable").(bool) { + err = c.Service.Enable(d.Id()) + return + } + + err = c.Service.Disable(d.Id()) + return +} + func WebFromState(d *schema.ResourceData) (svc service.CreateService) { svc = service.CreateService{ Metadata: service.Metadata{ diff --git a/banyan/resource_service_web_test.go b/banyan/resource_service_web_test.go index f76309e9..1fd24aa2 100644 --- a/banyan/resource_service_web_test.go +++ b/banyan/resource_service_web_test.go @@ -776,3 +776,107 @@ func testAccService_basic_web_create_with_tls_sni(name string) string { } `, name, name, name, name, name) } + +func TestAccService_disable(t *testing.T) { + var bnnService service.GetServiceSpec + rName := fmt.Sprintf("tf-acc-%s", acctest.RandStringFromCharSet(10, acctest.CharSetAlphaNum)) + + resource.Test(t, resource.TestCase{ + Providers: testAccProviders, + CheckDestroy: testAccCheckServiceDestroy(t, &bnnService.ServiceID), + Steps: []resource.TestStep{ + { + Config: fmt.Sprintf(` + resource "banyan_policy_web" "example" { + name = "%s-pol" + description = "some web policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + resource "banyan_service_web" "example" { + name = "%s-web" + access_tier = "us-west1" + domain = "%s-web.corp.com" + backend_domain = "%s-web.internal" + backend_port = 8443 + policy = banyan_policy_web.example.id + policy_enforcing = false + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_web.example", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_basic_web_create_json(rName), &bnnService.ServiceID), + ), + }, + { + ResourceName: "banyan_service_web.example", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: fmt.Sprintf(` + resource "banyan_policy_web" "example" { + name = "%s-pol" + description = "some web policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + resource "banyan_service_web" "example" { + name = "%s-web" + access_tier = "us-west1" + domain = "%s-web.corp.com" + backend_domain = "%s-web.internal" + backend_port = 8443 + policy = banyan_policy_web.example.id + policy_enforcing = false + enable = false + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_web.example", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_basic_web_create_json(rName), &bnnService.ServiceID), + ), + }, + { + ResourceName: "banyan_service_web.example", + ImportState: true, + ImportStateVerify: true, + }, + { + Config: fmt.Sprintf(` + resource "banyan_policy_web" "example" { + name = "%s-pol" + description = "some web policy description" + access { + roles = ["ANY"] + trust_level = "High" + } + } + resource "banyan_service_web" "example" { + name = "%s-web" + access_tier = "us-west1" + domain = "%s-web.corp.com" + backend_domain = "%s-web.internal" + backend_port = 8443 + policy = banyan_policy_web.example.id + policy_enforcing = false + enable = true + } + `, rName, rName, rName, rName), + Check: resource.ComposeTestCheckFunc( + testAccCheckExistingService("banyan_service_web.example", &bnnService), + testAccCheckServiceAgainstJson(t, testAccService_basic_web_create_json(rName), &bnnService.ServiceID), + ), + }, + { + ResourceName: "banyan_service_web.example", + ImportState: true, + ImportStateVerify: true, + }, + }, + }) +} diff --git a/client/service/model.go b/client/service/model.go index 34f779b5..11e159e0 100644 --- a/client/service/model.go +++ b/client/service/model.go @@ -24,6 +24,7 @@ type Client interface { Delete(id string) (err error) DetachPolicy(id string) (err error) Disable(id string) (err error) + Enable(id string) (err error) GetPolicyForService(id string) (attachedPolicy policy.GetPolicy, err error) } diff --git a/client/service/service.go b/client/service/service.go index f942d440..17b59675 100644 --- a/client/service/service.go +++ b/client/service/service.go @@ -3,12 +3,13 @@ package service import ( "encoding/json" "fmt" - "github.com/banyansecurity/terraform-banyan-provider/client/policy" - "github.com/banyansecurity/terraform-banyan-provider/client/policyattachment" - "github.com/pkg/errors" "html" "log" "net/url" + + "github.com/banyansecurity/terraform-banyan-provider/client/policy" + "github.com/banyansecurity/terraform-banyan-provider/client/policyattachment" + "github.com/pkg/errors" ) const apiVersion = "api/v1" @@ -55,6 +56,25 @@ func (s *Service) Get(id string) (service GetServiceSpec, err error) { func (s *Service) Disable(id string) (err error) { path := "api/v1/disable_registered_service" + err = s.updateService(id, path) + if err != nil { + return + } + log.Printf("disabled service: %q", id) + return +} + +func (s *Service) Enable(id string) (err error) { + path := "api/v1/enable_registered_service" + err = s.updateService(id, path) + if err != nil { + return + } + log.Printf("enabled service: %q", id) + return +} + +func (s *Service) updateService(id, path string) (err error) { myUrl, err := url.Parse(path) if err != nil { return @@ -64,9 +84,8 @@ func (s *Service) Disable(id string) (err error) { myUrl.RawQuery = query.Encode() _, err = s.restClient.DoPost(myUrl.String(), nil) if err != nil { - err = fmt.Errorf("error disabling service %s", err) + err = fmt.Errorf("error while enable/disable service %s", err) } - log.Printf("disabled service: %q", id) return } From 435886e12c67480044d84019705f7107edcea81a Mon Sep 17 00:00:00 2001 From: github-actions Date: Fri, 18 Oct 2024 16:35:08 +0000 Subject: [PATCH 31/31] Documentation generated --- docs/resources/service_web.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/resources/service_web.md b/docs/resources/service_web.md index a6c326cc..924cd6b5 100644 --- a/docs/resources/service_web.md +++ b/docs/resources/service_web.md @@ -112,6 +112,7 @@ resource "banyan_service_web" "example-service" { - `disable_private_dns` (Boolean) By default, Private DNS Override will be set to true i.e disable_private_dns is false. On the device, the domain name will resolve over the service tunnel to the correct Access Tier's public IP address. If you turn off Private DNS Override i.e. disable_private_dns is set to true, you need to explicitly set a private DNS entry for the service domain name. - `dns_overrides` (Map of String) dns_overrides is an optional section that specifies name-to-address or name-to-name mappings. Name-to-address mapping could be used instead of DNS lookup. Format is "FQDN: ip_address". Name-to-name mapping could be used to override one FQDN with the other. Format is "FQDN1: FQDN2" Example: name-to-address -> "internal.myservice.com" : "10.23.0.1" name-to-name -> "exposed.service.com" : "internal.myservice.com" +- `enable` (Boolean) enable / disable web service - `exemptions` (Block Set) (see [below for nested schema](#nestedblock--exemptions)) - `icon` (String) Name of the icon which will be displayed to the end user. The icon names can be found in the UI in the service config - `letsencrypt` (Boolean) Use a Public CA-issued server certificate instead of a Private CA-issued one