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