Skip to content

Commit

Permalink
feat: natgateway flowlogs rules (#94)
Browse files Browse the repository at this point in the history
* fix: check resources exist before trying to delete

* feat: added nat-flowlogs option

* feat: added nat rules option

* docs: updated readme, fixed tests

* feat: added option to skip adding default nat rules, changed default … (#97)

* feat: added option to skip adding default nat rules, changed default target subnet

* test: remove sleep, fix tests

* fix: check error first

* doc: updated docs with new ionoscloud-skip-default-nat-rules flag
  • Loading branch information
rmocanu-ionos committed Sep 26, 2023
1 parent 8749aa2 commit f7cc6b9
Show file tree
Hide file tree
Showing 6 changed files with 271 additions and 71 deletions.
6 changes: 6 additions & 0 deletions docs/usage/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ Available Options for the IONOS Cloud Docker Machine Driver:
| `--ionoscloud-create-nat` | Create a new NAT with some default open ports |
| `--ionoscloud-nat-public-ips` | If --ionoscloud-create-nat is set, change the NAT's public IPs to these values |
| `--ionoscloud-nat-lans-to-gateways` | If --ionoscloud-create-nat is set, change the NAT's mappings of LANs to Gateway IPs to these values. Must respect format `1=10.0.0.1,10.0.0.2:2=10.0.0.10` |
| `--ionoscloud-nat-flowlogs` | If --ionoscloud-create-nat is set, add flowlogs to the nat. Must respect format `name:action:direction:bucket`, |
| `--ionoscloud-nat-rules` | If --ionoscloud-create-nat is set, add rules to the NAT. Must respect format `name:type:protocol:public_ip:source_subnet:target_subnet:target_port_range_start:target_port_range_end`, to skip providing an optional value just omit it (`name:type:protocol::source_subnet:::`), not setting public IP will use the public IP of the NAT for the rule, not setting source subnet will use the first ip on the NIC with mask 24 |
| `--ionoscloud-skip-default-nat-rules` | Should the driver skip creating default nat rules if creating a NAT, creating only the specified rules, the UI drivers always set this flag |
| `--ionoscloud-ssh-user` | The user to connect to via SSH |
| `--ionoscloud-ssh-in-cloud-init` | Should the driver only add the SSH info in the user data? (required for custom images) |
| `--swarm` | Configure Machine to join a Swarm cluster |
Expand Down Expand Up @@ -107,6 +110,9 @@ Environment variables are also supported for setting options. This is a list of
| `--ionoscloud-nat-name` | `IONOSCLOUD_NAT_NAME` |
| `--ionoscloud-nat-public-ips` | `IONOSCLOUD_NAT_PUBLIC_IPS` |
| `--ionoscloud-nat-lans-to-gateways` | `IONOSCLOUD_NAT_LANS_TO_GATEWAYS` |
| `--ionoscloud-nat-flowlogs` | `IONOSCLOUD_NAT_FLOWLOG` |
| `--ionoscloud-nat-rules` | `IONOSCLOUD_NAT_RULES` |
| `--ionoscloud-skip-default-nat-rules` | `IONOSCLOUD_SKIP_DEFAULT_NAT_RULES` |
| `--ionoscloud-private-lan` | `IONOSCLOUD_PRIVATE_LAN` |
| `--ionoscloud-ssh-user` | `IONOSCLOUD_SSH_USER` |
| `--ionoscloud-ssh-in-cloud-init` | `IONOSCLOUD_SSH_IN_CLOUD_INIT` |
2 changes: 1 addition & 1 deletion internal/utils/client_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type ClientService interface {
CreateLan(datacenterId, name string, public bool) (*ionoscloud.LanPost, error)
RemoveLan(datacenterId, lanId string) error

CreateNat(datacenterId, name string, publicIps []string, lansToGateways map[string][]string, subnet string) (*ionoscloud.NatGateway, error)
CreateNat(datacenterId, name string, publicIps, flowlogs, natRules []string, lansToGateways map[string][]string, sourceSubnet string, skipDefaultRules bool) (*ionoscloud.NatGateway, error)
GetNat(datacenterId string, natId string) (*ionoscloud.NatGateway, error)
GetNats(datacenterId string) (*ionoscloud.NatGateways, error)
RemoveNat(datacenterId, natId string) error
Expand Down
8 changes: 4 additions & 4 deletions internal/utils/mocks/ClientService.go

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

190 changes: 158 additions & 32 deletions internal/utils/nat.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package utils
import (
"fmt"
"strconv"
"strings"
"time"

"github.com/docker/machine/libmachine/log"
Expand Down Expand Up @@ -34,15 +35,15 @@ type NatRuleMaker struct {
defaultProperties sdkgo.NatGatewayRuleProperties
}

func NewNRM(publicIp, srcSubnet, targetSubnet string) NatRuleMaker {
func NewNRM(publicIp, srcSubnet, targetSubnet *string) NatRuleMaker {
return NatRuleMaker{
rules: make([]sdkgo.NatGatewayRule, 0),
defaultProperties: sdkgo.NatGatewayRuleProperties{
Name: pointer.From("Docker Machine NAT Rule"),
Type: pointer.From(sdkgo.NatGatewayRuleType("SNAT")),
SourceSubnet: &srcSubnet,
TargetSubnet: &targetSubnet,
PublicIp: &publicIp,
SourceSubnet: srcSubnet,
TargetSubnet: targetSubnet,
PublicIp: publicIp,
},
}
}
Expand Down Expand Up @@ -70,8 +71,114 @@ func (nrm *NatRuleMaker) OpenPorts(protocol string, start int32, end int32) *Nat
return nrm
}

func (c *Client) CreateNat(datacenterId, name string, publicIps []string, lansToGateways map[string][]string, subnet string) (*sdkgo.NatGateway, error) {
func flowlogStringToModel(flowlog string) sdkgo.FlowLog {
var split = strings.Split(flowlog, ":")

return sdkgo.FlowLog{
Properties: &sdkgo.FlowLogProperties{
Name: &split[0],
Action: &split[1],
Direction: &split[2],
Bucket: &split[3],
},
}
}

func flowlogsStringToModel(flowlogs []string) *sdkgo.FlowLogs {
if len(flowlogs) == 0 {
return nil
}
flowlog_models := sdkgo.NewFlowLogs()
flowlog_models.Items = &[]sdkgo.FlowLog{}

for _, flowlog := range flowlogs {
*flowlog_models.Items = append(*flowlog_models.Items, flowlogStringToModel(flowlog))
}

return flowlog_models
}

func natRuleStringToModel(rule, natPublicIp, defaultsourceSubnet string) (*sdkgo.NatGatewayRule, error) {
var split = strings.Split(rule, ":")
ruleType := sdkgo.NatGatewayRuleType(split[1])
ruleProtocol := sdkgo.NatGatewayRuleProtocol(split[2])
publicIp := split[3]
if publicIp == "" {
publicIp = natPublicIp
}
sourceSubnet := split[4]
if sourceSubnet == "" {
sourceSubnet = defaultsourceSubnet
}

ruleModel := sdkgo.NatGatewayRule{
Properties: &sdkgo.NatGatewayRuleProperties{
Name: &split[0],
Type: &ruleType,
Protocol: &ruleProtocol,
PublicIp: &publicIp,
SourceSubnet: &sourceSubnet,
TargetPortRange: &sdkgo.TargetPortRange{},
},
}

targetSubnet := split[5]
if targetSubnet == "" {
ruleModel.Properties.TargetSubnet = nil
} else {
ruleModel.Properties.TargetSubnet = &targetSubnet
}

if split[6] == "" {
ruleModel.Properties.TargetPortRange.Start = nil
} else {

start, err := strconv.Atoi(split[6])
if err != nil {
return nil, err
}
start32 := int32(start)
ruleModel.Properties.TargetPortRange.Start = &start32
}

if split[7] == "" {
ruleModel.Properties.TargetPortRange.End = nil
} else {

end, err := strconv.Atoi(split[7])
if err != nil {
return nil, err
}
end32 := int32(end)
ruleModel.Properties.TargetPortRange.End = &end32
}

return &ruleModel, nil
}

func natRulesStringToModel(rules []string, natPublicIp, sourceSubnet string) (*sdkgo.NatGatewayRules, error) {
if len(rules) == 0 {
return nil, nil
}
rule_models := sdkgo.NewNatGatewayRules()
rule_models.Items = &[]sdkgo.NatGatewayRule{}

for _, rule := range rules {
rule_model, err := natRuleStringToModel(rule, natPublicIp, sourceSubnet)

if err != nil {
return nil, err
}

*rule_models.Items = append(*rule_models.Items, *rule_model)
}

return rule_models, nil
}

func (c *Client) CreateNat(datacenterId, name string, publicIps, flowlogs, natRules []string, lansToGateways map[string][]string, sourceSubnet string, skipDefaultRules bool) (*sdkgo.NatGateway, error) {
var lans []sdkgo.NatGatewayLanProperties
publicIp := publicIps[0]

err := c.createLansIfNotExist(datacenterId, maps.Keys(lansToGateways))
if err != nil {
Expand All @@ -92,32 +199,51 @@ func (c *Client) CreateNat(datacenterId, name string, publicIps []string, lansTo
lans = append(lans, sdkgo.NatGatewayLanProperties{Id: pointer.From(int32(id)), GatewayIps: ptrGatewayIps})
}

nrm := NewNRM(publicIps[0], subnet, subnet)
nrm.
OpenPort("TCP", 22). // SSH
OpenPort("UDP", 53). // DNS
OpenPort("TCP", 80). // HTTP
OpenPort("TCP", 179). // Calico BGP Port
OpenPort("TCP", 443). //
OpenPort("TCP", 2376). // Node driver Docker daemon TLS port
OpenPort("UDP", 4789). // Flannel VXLAN overlay networking on Windows cluster
OpenPort("TCP", 6443). // Rancher Webhook
OpenPort("TCP", 6783). // Weave Port
OpenPort("TCP", 8443). // Rancher webhook
OpenPort("UDP", 8472). // Canal/Flannel VXLAN overlay networking
OpenPort("TCP", 9099). // Canal/Flannel livenessProbe/readinessProbe
OpenPort("TCP", 9100). // Default port required by Monitoring to scrape metrics from Linux node-exporters
OpenPort("TCP", 9443). // Rancher webhook
OpenPort("TCP", 9796). // Default port required by Monitoring to scrape metrics from Windows node-exporters
OpenPort("TCP", 10254). // Ingress controller livenessProbe/readinessProbe
OpenPort("TCP", 10256). //
OpenPorts("TCP", 2379, 2380). // etcd
OpenPorts("UDP", 6783, 6784). // Weave Port (UDP)
OpenPorts("TCP", 10250, 10252). // Metrics server communication with all nodes API
OpenPorts("TCP", 30000, 32767). //
OpenPorts("UDP", 30000, 32767). //
OpenPort("ALL", 0) // Outbound
rules := nrm.Make()
rules := &[]sdkgo.NatGatewayRule{}
if !skipDefaultRules {
nrm := NewNRM(&publicIp, &sourceSubnet, nil)
nrm.
OpenPort("TCP", 22). // SSH
OpenPort("UDP", 53). // DNS
OpenPort("TCP", 80). // HTTP
OpenPort("TCP", 179). // Calico BGP Port
OpenPort("TCP", 443). //

OpenPort("TCP", 2376). // Node driver Docker daemon TLS port
OpenPort("UDP", 4789). // Flannel VXLAN overlay networking on Windows cluster
OpenPort("TCP", 6443). // Rancher Webhook
OpenPort("TCP", 6783). // Weave Port
OpenPort("TCP", 8443). // Rancher webhook

OpenPort("UDP", 8472). // Canal/Flannel VXLAN overlay networking
OpenPort("TCP", 9099). // Canal/Flannel livenessProbe/readinessProbe
OpenPort("TCP", 9100). // Default port required by Monitoring to scrape metrics from Linux node-exporters
OpenPort("TCP", 9443). // Rancher webhook
OpenPort("TCP", 9796). // Default port required by Monitoring to scrape metrics from Windows node-exporters

OpenPort("TCP", 10254). // Ingress controller livenessProbe/readinessProbe
OpenPort("TCP", 10256). //
OpenPorts("TCP", 2379, 2380). // etcd
OpenPorts("UDP", 6783, 6784). // Weave Port (UDP)
OpenPorts("TCP", 10250, 10252). // Metrics server communication with all nodes API

OpenPorts("TCP", 30000, 32767). //
OpenPorts("UDP", 30000, 32767). //
OpenPort("ALL", 0) // Outbound
default_rules := nrm.Make()

*rules = append(*rules, *default_rules...)
}

new_rules, err := natRulesStringToModel(natRules, publicIps[0], sourceSubnet)

if err != nil {
return nil, err
}

if new_rules != nil {
*rules = append(*rules, *new_rules.Items...)
}

nat, resp, err := c.NATGatewaysApi.DatacentersNatgatewaysPost(c.ctx, datacenterId).NatGateway(
sdkgo.NatGateway{
Expand All @@ -128,7 +254,7 @@ func (c *Client) CreateNat(datacenterId, name string, publicIps []string, lansTo
},
Entities: &sdkgo.NatGatewayEntities{
Rules: &sdkgo.NatGatewayRules{Items: rules},
Flowlogs: nil,
Flowlogs: flowlogsStringToModel(flowlogs),
},
},
).Execute()
Expand Down
Loading

0 comments on commit f7cc6b9

Please sign in to comment.