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  }