github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/provider/common/instance_configurator.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "os" 8 "strings" 9 10 "github.com/juju/errors" 11 "github.com/juju/utils/v3/ssh" 12 13 "github.com/juju/juju/core/network/firewall" 14 "github.com/juju/juju/network/iptables" 15 ) 16 17 // InstanceConfigurator describes methods for manipulating firewall 18 // rules directly on a single instance. 19 // 20 //go:generate go run go.uber.org/mock/mockgen -package mocks -destination mocks/instance_configurator.go github.com/juju/juju/provider/common InstanceConfigurator 21 type InstanceConfigurator interface { 22 23 // DropAllPorts denies access to all ports. 24 DropAllPorts(exceptPorts []int, addr string) error 25 26 // ChangeIngressRules opens and/or closes ports. 27 ChangeIngressRules(ipAddress string, insert bool, rules firewall.IngressRules) error 28 29 // FindIngressRules returns all firewall rules. 30 FindIngressRules() (firewall.IngressRules, error) 31 } 32 33 type sshInstanceConfigurator struct { 34 client ssh.Client 35 host string 36 options *ssh.Options 37 } 38 39 // NewSshInstanceConfigurator creates new sshInstanceConfigurator. 40 func NewSshInstanceConfigurator(host string) InstanceConfigurator { 41 options := ssh.Options{} 42 options.SetIdentities("/var/lib/juju/system-identity") 43 44 // Disable host key checking. We're not sending any sensitive data 45 // across, and we don't have access to the host's keys from here. 46 // 47 // TODO(axw) 2017-12-07 #1732665 48 // Stop using SSH, instead manage iptables on the machine 49 // itself. This will also provide firewalling for MAAS and 50 // LXD machines. 51 options.SetStrictHostKeyChecking(ssh.StrictHostChecksNo) 52 options.SetKnownHostsFile(os.DevNull) 53 54 return &sshInstanceConfigurator{ 55 client: ssh.DefaultClient, 56 host: "ubuntu@" + host, 57 options: &options, 58 } 59 } 60 61 func (c *sshInstanceConfigurator) runCommand(cmd string) (string, error) { 62 command := c.client.Command(c.host, []string{"/bin/bash"}, c.options) 63 command.Stdin = strings.NewReader(cmd) 64 output, err := command.CombinedOutput() 65 if err != nil { 66 return "", errors.Trace(err) 67 } 68 return string(output), nil 69 } 70 71 // DropAllPorts implements InstanceConfigurator interface. 72 func (c *sshInstanceConfigurator) DropAllPorts(exceptPorts []int, addr string) error { 73 cmds := []string{ 74 iptables.DropCommand{DestinationAddress: addr}.Render(), 75 } 76 for _, port := range exceptPorts { 77 cmds = append(cmds, iptables.AcceptInternalCommand{ 78 Protocol: "tcp", 79 DestinationAddress: addr, 80 DestinationPort: port, 81 }.Render()) 82 } 83 84 output, err := c.runCommand(strings.Join(cmds, "\n")) 85 if err != nil { 86 return errors.Errorf("failed to drop all ports: %s", output) 87 } 88 logger.Tracef("drop all ports output: %s", output) 89 return nil 90 } 91 92 // ChangeIngressRules implements InstanceConfigurator interface. 93 func (c *sshInstanceConfigurator) ChangeIngressRules(ipAddress string, insert bool, rules firewall.IngressRules) error { 94 var cmds []string 95 for _, rule := range rules { 96 cmds = append(cmds, iptables.IngressRuleCommand{ 97 Rule: rule, 98 DestinationAddress: ipAddress, 99 Delete: !insert, 100 }.Render()) 101 } 102 103 output, err := c.runCommand(strings.Join(cmds, "\n")) 104 if err != nil { 105 return errors.Annotatef(err, "configuring ports for address %q: %s", ipAddress, output) 106 } 107 logger.Tracef("change ports output: %s", output) 108 return nil 109 } 110 111 // FindIngressRules implements InstanceConfigurator interface. 112 func (c *sshInstanceConfigurator) FindIngressRules() (firewall.IngressRules, error) { 113 output, err := c.runCommand("sudo iptables -L INPUT -n") 114 if err != nil { 115 return nil, errors.Errorf("failed to list open ports: %s", output) 116 } 117 logger.Tracef("find open ports output: %s", output) 118 return iptables.ParseIngressRules(strings.NewReader(output)) 119 }