1package firewall 2 3import ( 4 "fmt" 5 "net" 6 "reflect" 7 8 "github.com/hetznercloud/hcloud-go/hcloud" 9 "github.com/spf13/cobra" 10 11 "github.com/hetznercloud/cli/internal/cmd/cmpl" 12 "github.com/hetznercloud/cli/internal/cmd/util" 13 "github.com/hetznercloud/cli/internal/state" 14) 15 16func newDeleteRuleCommand(cli *state.State) *cobra.Command { 17 cmd := &cobra.Command{ 18 Use: "delete-rule FIREWALL FLAGS", 19 Short: "Delete a single rule to a firewall", 20 Args: cobra.ExactArgs(1), 21 ValidArgsFunction: cmpl.SuggestArgs(cmpl.SuggestCandidatesF(cli.FirewallNames)), 22 TraverseChildren: true, 23 DisableFlagsInUseLine: true, 24 PreRunE: util.ChainRunE(cli.EnsureToken), 25 RunE: cli.Wrap(runDeleteRule), 26 } 27 cmd.Flags().String("direction", "", "Direction (in, out) (required)") 28 cmd.RegisterFlagCompletionFunc("direction", cmpl.SuggestCandidates("in", "out")) 29 cmd.MarkFlagRequired("direction") 30 31 cmd.Flags().String("protocol", "", "Protocol (icmp, udp or tcp) (required)") 32 cmd.RegisterFlagCompletionFunc("protocol", cmpl.SuggestCandidates("icmp", "udp", "tcp")) 33 cmd.MarkFlagRequired("protocol") 34 35 cmd.Flags().StringArray("source-ips", []string{}, "Source IPs (CIDR Notation) (required when direction is in)") 36 37 cmd.Flags().StringArray("destination-ips", []string{}, "Destination IPs (CIDR Notation) (required when direction is out)") 38 39 cmd.Flags().String("port", "", "Port to which traffic will be allowed, only applicable for protocols TCP and UDP") 40 return cmd 41} 42 43func runDeleteRule(cli *state.State, cmd *cobra.Command, args []string) error { 44 direction, _ := cmd.Flags().GetString("direction") 45 protocol, _ := cmd.Flags().GetString("protocol") 46 sourceIPs, _ := cmd.Flags().GetStringArray("source-ips") 47 destinationIPs, _ := cmd.Flags().GetStringArray("destination-ips") 48 port, _ := cmd.Flags().GetString("port") 49 50 idOrName := args[0] 51 firewall, _, err := cli.Client().Firewall.Get(cli.Context, idOrName) 52 if err != nil { 53 return err 54 } 55 if firewall == nil { 56 return fmt.Errorf("Firewall not found: %v", idOrName) 57 } 58 59 var sourceNets []net.IPNet 60 for i, sourceIP := range sourceIPs { 61 _, sourceNet, err := net.ParseCIDR(sourceIP) 62 if err != nil { 63 return fmt.Errorf("invalid CIDR on index %d : %s", i, err) 64 } 65 sourceNets = append(sourceNets, *sourceNet) 66 } 67 d := hcloud.FirewallRuleDirection(direction) 68 rule := hcloud.FirewallRule{ 69 Direction: d, 70 Protocol: hcloud.FirewallRuleProtocol(protocol), 71 } 72 if port != "" { 73 rule.Port = hcloud.String(port) 74 } 75 switch d { 76 case hcloud.FirewallRuleDirectionOut: 77 rule.DestinationIPs = make([]net.IPNet, 0, len(destinationIPs)) 78 for i, ip := range destinationIPs { 79 _, n, err := net.ParseCIDR(ip) 80 if err != nil { 81 return fmt.Errorf("invalid CIDR on index %d : %s", i, err) 82 } 83 rule.DestinationIPs[i] = *n 84 } 85 case hcloud.FirewallRuleDirectionIn: 86 rule.SourceIPs = make([]net.IPNet, 0, len(sourceIPs)) 87 for i, ip := range sourceIPs { 88 _, n, err := net.ParseCIDR(ip) 89 if err != nil { 90 return fmt.Errorf("invalid CIDR on index %d : %s", i, err) 91 } 92 rule.SourceIPs[i] = *n 93 } 94 } 95 96 var rules []hcloud.FirewallRule 97 for _, existingRule := range firewall.Rules { 98 if !reflect.DeepEqual(existingRule, rule) { 99 rules = append(rules, existingRule) 100 } 101 } 102 if len(rules) == len(firewall.Rules) { 103 return fmt.Errorf("the specified rule was not found in the ruleset of Firewall %d", firewall.ID) 104 } 105 actions, _, err := cli.Client().Firewall.SetRules(cli.Context, firewall, 106 hcloud.FirewallSetRulesOpts{Rules: rules}, 107 ) 108 if err != nil { 109 return err 110 } 111 if err := cli.ActionsProgresses(cli.Context, actions); err != nil { 112 return err 113 } 114 fmt.Printf("Firewall Rules for Firewall %d updated\n", firewall.ID) 115 116 return nil 117} 118