Skip to content

Commit 36827c0

Browse files
Add source port range to security list rules (another rebase) (#385)
Ported from PR #366. Currently, TCP and UDP options can be specified with a single set of max/min values, which correspond to the destination port range. This change adds the ability to specify the source port range as well, issue #338. Ideally, we should deprecate the existing max/min params and add a new destination_port_range option next to the new source_port_range. Unfortunately, a number of issue with nested schemas in Terraform are preventing support of both of these params simultaneously. Instead, max/min params will remain, and source_port_range will exist beside it. This change also enables security lists with no rules. Some updates are made to the docs, but the docs for the security list resource and data source could really use a bigger update that is not included in this change.
1 parent babab56 commit 36827c0

File tree

10 files changed

+536
-242
lines changed

10 files changed

+536
-242
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file.
33

44
The format is based on [Keep a Changelog](http://keepachangelog.com/).
55

6+
## Unreleased
7+
8+
### Added
9+
- Support for security list rule source port ranges (#340). This can be specified in "tcp_options" and "udp_options" using "source_port_range".
10+
611
## 2.0.4 - 2017-11-2
712

813
### Added

docs/datasources/core/security_lists.md

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ Gets a list of security lists. Each security list is a set of virtual firewall r
1111
```
1212
data "oci_core_security_lists" "t" {
1313
compartment_id = "compartment_id"
14-
limit = 1
15-
page = "page"
1614
vcn_id = "vcn_id"
1715
}
1816
```

docs/examples/networking/security_list/security_list.tf

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ variable "private_key_path" {}
99
variable "compartment_ocid" {}
1010
variable "region" {}
1111

12-
variable "vcn_ocid" {}
13-
14-
1512
provider "oci" {
1613
tenancy_ocid = "${var.tenancy_ocid}"
1714
user_ocid = "${var.user_ocid}"
@@ -20,13 +17,20 @@ provider "oci" {
2017
region = "${var.region}"
2118
}
2219

20+
resource "oci_core_virtual_network" "ExampleVCN" {
21+
cidr_block = "10.0.0.0/16"
22+
dns_label = "examplevcn"
23+
compartment_id = "${var.compartment_ocid}"
24+
display_name = "ExampleVCN"
25+
}
26+
2327
# Protocols are specified as protocol numbers.
2428
# http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
2529

26-
resource "oci_core_security_list" "security_list1" {
30+
resource "oci_core_security_list" "ExampleSecurityList" {
2731
compartment_id = "${var.compartment_ocid}"
28-
vcn_id = "${var.vcn_ocid}"
29-
display_name = "security_list1"
32+
vcn_id = "${oci_core_virtual_network.ExampleVCN.id}"
33+
display_name = "ExampleSecurityList"
3034

3135
// allow outbound tcp traffic on all ports
3236
egress_security_rules {
@@ -41,18 +45,24 @@ resource "oci_core_security_list" "security_list1" {
4145
stateless = true
4246

4347
udp_options {
48+
// These values correspond to the destination port range.
4449
"min" = 319
4550
"max" = 320
4651
}
4752
}
4853

49-
// allow inbound ssh traffic
54+
// allow inbound ssh traffic from a specific port
5055
ingress_security_rules {
5156
protocol = "6" // tcp
5257
source = "0.0.0.0/0"
5358
stateless = false
5459

5560
tcp_options {
61+
source_port_range {
62+
"min" = 100
63+
"max" = 100
64+
}
65+
// These values correspond to the destination port range.
5666
"min" = 22
5767
"max" = 22
5868
}

docs/resources/core/security_list.md

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,13 @@ resource "oci_core_security_list" "t" {
3838
stateless = true
3939
4040
tcp_options {
41-
"min" = 80
42-
"max" = 82
41+
source_port_range {
42+
"min" = 100
43+
"max" = 100
44+
}
45+
// These values correspond to the destination port range.
46+
"min" = 22
47+
"max" = 22
4348
}
4449
}
4550
@@ -49,6 +54,8 @@ resource "oci_core_security_list" "t" {
4954
stateless = true
5055
5156
udp_options {
57+
// These values correspond to the destination port range.
58+
// source_port_range may also be specified.
5259
"min" = 319
5360
"max" = 320
5461
}

main.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ package main
55
import (
66
"github.com/hashicorp/terraform/plugin"
77
"github.com/hashicorp/terraform/terraform"
8+
89
"github.com/oracle/terraform-provider-oci/provider"
910
)
1011

provider/core_security_list_resource.go

Lines changed: 123 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,31 @@ var transportSchema = &schema.Schema{
1515
MaxItems: 1,
1616
Elem: &schema.Resource{
1717
Schema: map[string]*schema.Schema{
18+
"source_port_range": {
19+
Type: schema.TypeList,
20+
Optional: true,
21+
MaxItems: 1,
22+
MinItems: 1,
23+
Elem: &schema.Resource{
24+
Schema: map[string]*schema.Schema{
25+
"max": {
26+
Type: schema.TypeInt,
27+
Required: true,
28+
},
29+
"min": {
30+
Type: schema.TypeInt,
31+
Required: true,
32+
},
33+
},
34+
},
35+
},
1836
"max": {
1937
Type: schema.TypeInt,
20-
Required: true,
38+
Optional: true,
2139
},
2240
"min": {
2341
Type: schema.TypeInt,
24-
Required: true,
42+
Optional: true,
2543
},
2644
},
2745
},
@@ -153,7 +171,7 @@ func SecurityListResource() *schema.Resource {
153171
},
154172
"egress_security_rules": {
155173
Type: schema.TypeList,
156-
Required: true,
174+
Optional: true,
157175
Elem: &schema.Resource{
158176
Schema: map[string]*schema.Schema{
159177
"destination": {
@@ -181,7 +199,7 @@ func SecurityListResource() *schema.Resource {
181199
},
182200
"ingress_security_rules": {
183201
Type: schema.TypeList,
184-
Required: true,
202+
Optional: true,
185203
Elem: &schema.Resource{
186204
Schema: map[string]*schema.Schema{
187205
"icmp_options": icmpSchema,
@@ -336,48 +354,20 @@ func (s *SecurityListResourceCrud) Update() (e error) {
336354
}
337355

338356
s.Res, e = s.Client.UpdateSecurityList(s.D.Id(), opts)
357+
339358
return
340359
}
341360

342361
func (s *SecurityListResourceCrud) SetData() {
343362
s.D.Set("compartment_id", s.Res.CompartmentID)
344363
s.D.Set("display_name", s.Res.DisplayName)
345-
346-
confEgressRules := []map[string]interface{}{}
347-
for _, egressRule := range s.Res.EgressSecurityRules {
348-
confEgressRule := map[string]interface{}{}
349-
confEgressRule["destination"] = egressRule.Destination
350-
confEgressRule = buildConfRule(
351-
confEgressRule,
352-
egressRule.Protocol,
353-
egressRule.ICMPOptions,
354-
egressRule.TCPOptions,
355-
egressRule.UDPOptions,
356-
&egressRule.IsStateless,
357-
)
358-
confEgressRules = append(confEgressRules, confEgressRule)
359-
}
360-
s.D.Set("egress_security_rules", confEgressRules)
361-
362-
confIngressRules := []map[string]interface{}{}
363-
for _, ingressRule := range s.Res.IngressSecurityRules {
364-
confIngressRule := map[string]interface{}{}
365-
confIngressRule["source"] = ingressRule.Source
366-
confIngressRule = buildConfRule(
367-
confIngressRule,
368-
ingressRule.Protocol,
369-
ingressRule.ICMPOptions,
370-
ingressRule.TCPOptions,
371-
ingressRule.UDPOptions,
372-
&ingressRule.IsStateless,
373-
)
374-
confIngressRules = append(confIngressRules, confIngressRule)
375-
}
376-
s.D.Set("ingress_security_rules", confIngressRules)
377-
378364
s.D.Set("state", s.Res.State)
379365
s.D.Set("time_created", s.Res.TimeCreated.String())
380366
s.D.Set("vcn_id", s.Res.VcnID)
367+
368+
confEgressRules, confIngressRules := buildConfRuleLists(s.Res)
369+
s.D.Set("egress_security_rules", confEgressRules)
370+
s.D.Set("ingress_security_rules", confIngressRules)
381371
}
382372

383373
func (s *SecurityListResourceCrud) reset() (e error) {
@@ -453,69 +443,137 @@ func (s *SecurityListResourceCrud) buildICMPOptions(conf map[string]interface{})
453443
}
454444

455445
func (s *SecurityListResourceCrud) buildTCPOptions(conf map[string]interface{}) (opts *baremetal.TCPOptions) {
456-
l := conf["tcp_options"].([]interface{})
457-
if len(l) > 0 {
458-
confOpts := l[0].(map[string]interface{})
446+
options := conf["tcp_options"].([]interface{})
447+
if len(options) > 0 {
448+
sourcePortRange, destinationPortRange := s.buildSourceAndDestinationPortRanges(options)
459449
opts = &baremetal.TCPOptions{
460-
baremetal.PortRange{
461-
Max: uint64(confOpts["max"].(int)),
462-
Min: uint64(confOpts["min"].(int)),
463-
},
450+
DestinationPortRange: destinationPortRange,
451+
SourcePortRange: sourcePortRange,
464452
}
465453
}
466454
return
467455
}
468456

469457
func (s *SecurityListResourceCrud) buildUDPOptions(conf map[string]interface{}) (opts *baremetal.UDPOptions) {
470-
l := conf["udp_options"].([]interface{})
471-
if len(l) > 0 {
472-
confOpts := l[0].(map[string]interface{})
458+
options := conf["udp_options"].([]interface{})
459+
if len(options) > 0 {
460+
sourcePortRange, destinationPortRange := s.buildSourceAndDestinationPortRanges(options)
473461
opts = &baremetal.UDPOptions{
474-
baremetal.PortRange{
475-
Max: uint64(confOpts["max"].(int)),
476-
Min: uint64(confOpts["min"].(int)),
477-
},
462+
DestinationPortRange: destinationPortRange,
463+
SourcePortRange: sourcePortRange,
478464
}
479465
}
480466
return
481467
}
482468

483-
func buildConfICMPOptions(opts *baremetal.ICMPOptions) (list []interface{}) {
484-
confOpts := map[string]interface{}{
485-
"code": int(opts.Code),
486-
"type": int(opts.Type),
469+
func buildPortRange(conf []interface{}) (portRange *baremetal.PortRange) {
470+
if len(conf) > 0 && conf[0] != nil {
471+
mapConf := conf[0].(map[string]interface{})
472+
473+
max := mapConf["max"].(int)
474+
min := mapConf["min"].(int)
475+
476+
// Max and Min default to 0, and that is not a valid port number, so we can assume that if
477+
// the value is 0 then the user has not set the port number.
478+
// Also, note that if either max or min is set, then the service will return an error if both are not
479+
// set. However, we want to create the PortRange if either is set and let the service return the error.
480+
if max != 0 || min != 0 {
481+
portRange = &baremetal.PortRange{
482+
Max: uint64(max),
483+
Min: uint64(min),
484+
}
485+
}
487486
}
488-
return []interface{}{confOpts}
487+
return
489488
}
490489

491-
func buildConfTransportOptions(portRange baremetal.PortRange) (list []interface{}) {
492-
confOpts := map[string]interface{}{
493-
"max": int(portRange.Max),
494-
"min": int(portRange.Min),
490+
func (s *SecurityListResourceCrud) buildSourceAndDestinationPortRanges(conf []interface{}) (sourcePortRange, destinationPortRange *baremetal.PortRange) {
491+
if len(conf) > 0 && conf[0] != nil {
492+
mapConf := conf[0].(map[string]interface{})
493+
sourcePortRange = buildPortRange(mapConf["source_port_range"].([]interface{}))
494+
destinationPortRange = buildPortRange(conf)
495495
}
496-
return []interface{}{confOpts}
496+
497+
return
498+
}
499+
500+
// Used to build rule lists for SetData.
501+
func buildConfRuleLists(res *baremetal.SecurityList) (confEgressRules, confIngressRules []map[string]interface{}) {
502+
for _, egressRule := range res.EgressSecurityRules {
503+
confEgressRule := buildConfRule(
504+
egressRule.Protocol,
505+
egressRule.ICMPOptions,
506+
egressRule.TCPOptions,
507+
egressRule.UDPOptions,
508+
&egressRule.IsStateless,
509+
)
510+
confEgressRule["destination"] = egressRule.Destination
511+
confEgressRules = append(confEgressRules, confEgressRule)
512+
}
513+
514+
for _, ingressRule := range res.IngressSecurityRules {
515+
confIngressRule := buildConfRule(
516+
ingressRule.Protocol,
517+
ingressRule.ICMPOptions,
518+
ingressRule.TCPOptions,
519+
ingressRule.UDPOptions,
520+
&ingressRule.IsStateless,
521+
)
522+
confIngressRule["source"] = ingressRule.Source
523+
confIngressRules = append(confIngressRules, confIngressRule)
524+
}
525+
526+
return
497527
}
498528

529+
// Used to build rules for SetData.
499530
func buildConfRule(
500-
confRule map[string]interface{},
501531
protocol string,
502532
icmpOpts *baremetal.ICMPOptions,
503533
tcpOpts *baremetal.TCPOptions,
504534
udpOpts *baremetal.UDPOptions,
505535
stateless *bool,
506-
) map[string]interface{} {
536+
) (confRule map[string]interface{}) {
537+
confRule = map[string]interface{}{}
507538
confRule["protocol"] = protocol
508539
if icmpOpts != nil {
509540
confRule["icmp_options"] = buildConfICMPOptions(icmpOpts)
510541
}
511542
if tcpOpts != nil {
512-
confRule["tcp_options"] = buildConfTransportOptions(tcpOpts.DestinationPortRange)
543+
confRule["tcp_options"] = buildConfTransportOptions(tcpOpts.DestinationPortRange, tcpOpts.SourcePortRange)
513544
}
514545
if udpOpts != nil {
515-
confRule["udp_options"] = buildConfTransportOptions(udpOpts.DestinationPortRange)
546+
confRule["udp_options"] = buildConfTransportOptions(udpOpts.DestinationPortRange, udpOpts.SourcePortRange)
516547
}
517548
if stateless != nil {
518549
confRule["stateless"] = *stateless
519550
}
520551
return confRule
521552
}
553+
554+
// Used to build ICMP options for SetData.
555+
func buildConfICMPOptions(opts *baremetal.ICMPOptions) (list []interface{}) {
556+
confOpts := map[string]interface{}{
557+
"code": int(opts.Code),
558+
"type": int(opts.Type),
559+
}
560+
return []interface{}{confOpts}
561+
}
562+
563+
// Used to build TCP/UDP port ranges for SetData.
564+
func buildConfTransportOptions(destinationPortRange *baremetal.PortRange, sourcePortRange *baremetal.PortRange) (list []interface{}) {
565+
confOpts := map[string]interface{}{}
566+
if destinationPortRange != nil {
567+
confOpts["max"] = int(destinationPortRange.Max)
568+
confOpts["min"] = int(destinationPortRange.Min)
569+
}
570+
571+
if sourcePortRange != nil {
572+
confOpts["source_port_range"] = []interface{}{map[string]interface{}{
573+
"max": int(sourcePortRange.Max),
574+
"min": int(sourcePortRange.Min),
575+
}}
576+
}
577+
578+
return []interface{}{confOpts}
579+
}

0 commit comments

Comments
 (0)