/
expose.go
186 lines (150 loc) · 5.78 KB
/
expose.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package application
import (
"strings"
"github.com/juju/cmd/v3"
"github.com/juju/errors"
"github.com/juju/gnuflag"
"github.com/juju/juju/api/client/application"
jujucmd "github.com/juju/juju/cmd"
"github.com/juju/juju/cmd/juju/block"
"github.com/juju/juju/cmd/modelcmd"
"github.com/juju/juju/core/network/firewall"
"github.com/juju/juju/rpc/params"
)
var usageExposeSummary = `
Makes an application publicly available over the network.`[1:]
var usageExposeDetails = `
Adjusts the firewall rules and any relevant security mechanisms of the
cloud to allow public access to the application.
If no additional options are specified, the command will, by default, allow
access from 0.0.0.0/0 to all ports opened by the application. For example, to
expose all ports opened by apache2, you can run:
juju expose apache2
The --endpoints option may be used to restrict the effect of this command to
the list of ports opened for a comma-delimited list of endpoints. For instance,
to only expose the ports opened by apache2 for the "www" endpoint, you can run:
juju expose apache2 --endpoints www
To make the selected set of ports accessible by specific CIDRs, the --to-cidrs
option may be used with a comma-delimited list of CIDR values. For example:
juju expose apache2 --to-cidrs 10.0.0.0/24,192.168.1.0/24
To make the selected set of ports accessible by specific spaces, the --to-spaces
option may be used with a comma-delimited list of space names. For example:
juju expose apache2 --to-spaces public
All of the above options can be combined together. In addition, multiple "juju
expose" invocations can be used to specify granular expose rules for different
endpoints. For example, to allow access to all opened apache ports from
0.0.0.0/0 but restrict access to any port opened for the "logs" endpoint to
CIDR 10.0.0.0/24 you can run:
juju expose apache2
juju expose apache2 --endpoints logs --to-cidrs 10.0.0.0/24
Each "juju expose" invocation always overwrites any previous expose rule for
the same endpoint name. For example, running the following commands instruct
juju to only allow access to ports opened for the "logs" endpoint from CIDR
192.168.0.0/24.
juju expose apache2 --endpoints logs --to-cidrs 10.0.0.0/24
juju expose apache2 --endpoints logs --to-cidrs 192.168.0.0/24
`[1:]
// NewExposeCommand returns a command to expose applications.
func NewExposeCommand() modelcmd.ModelCommand {
return modelcmd.Wrap(&exposeCommand{})
}
// exposeCommand is responsible exposing applications.
type exposeCommand struct {
modelcmd.ModelCommandBase
ApplicationName string
ExposedEndpointsList string
ExposeToSpacesList string
ExposeToCIDRsList string
}
func (c *exposeCommand) Info() *cmd.Info {
return jujucmd.Info(&cmd.Info{
Name: "expose",
Args: "<application name>",
Purpose: usageExposeSummary,
Doc: usageExposeDetails,
SeeAlso: []string{
"unexpose",
},
})
}
func (c *exposeCommand) SetFlags(f *gnuflag.FlagSet) {
c.ModelCommandBase.SetFlags(f)
f.StringVar(&c.ExposedEndpointsList, "endpoints", "", "Expose only the ports that charms have opened for this comma-delimited list of endpoints")
f.StringVar(&c.ExposeToSpacesList, "to-spaces", "", "A comma-delimited list of spaces that should be able to access the application ports once exposed")
f.StringVar(&c.ExposeToCIDRsList, "to-cidrs", "", "A comma-delimited list of CIDRs that should be able to access the application ports once exposed")
}
func (c *exposeCommand) Init(args []string) error {
if len(args) == 0 {
return errors.New("no application name specified")
}
c.ApplicationName = args[0]
return cmd.CheckEmpty(args[1:])
}
type applicationExposeAPI interface {
Close() error
Expose(applicationName string, exposedEndpoints map[string]params.ExposedEndpoint) error
Unexpose(applicationName string, exposedEndpoints []string) error
}
func (c *exposeCommand) getAPI() (applicationExposeAPI, error) {
root, err := c.NewAPIRoot()
if err != nil {
return nil, errors.Trace(err)
}
return application.NewClient(root), nil
}
// Run changes the juju-managed firewall to expose any
// ports that were also explicitly marked by units as open.
func (c *exposeCommand) Run(_ *cmd.Context) error {
client, err := c.getAPI()
if err != nil {
return err
}
defer client.Close()
exposedEndpoints := c.buildExposedEndpoints()
return block.ProcessBlockedError(client.Expose(c.ApplicationName, exposedEndpoints), block.BlockChange)
}
func (c *exposeCommand) buildExposedEndpoints() map[string]params.ExposedEndpoint {
endpoints := splitCommaDelimitedList(c.ExposedEndpointsList)
spaces := splitCommaDelimitedList(c.ExposeToSpacesList)
cidrs := splitCommaDelimitedList(c.ExposeToCIDRsList)
if len(endpoints)+len(spaces)+len(cidrs) == 0 {
// No granular expose params required
return nil
}
var allNetworkCIDRCount int
for _, cidr := range cidrs {
if cidr == firewall.AllNetworksIPV4CIDR || cidr == firewall.AllNetworksIPV6CIDR {
allNetworkCIDRCount++
}
}
if len(endpoints) == 0 && len(spaces) == 0 && len(cidrs) == allNetworkCIDRCount {
// No granular expose params required; this is equivalent
// to "juju expose <application>"
return nil
}
expDetails := make(map[string]params.ExposedEndpoint)
if len(endpoints) == 0 {
// If no endpoints are specified, this applies to all ("") endpoints
endpoints = append(endpoints, "")
}
for _, epName := range endpoints {
expDetails[epName] = params.ExposedEndpoint{
ExposeToSpaces: spaces,
ExposeToCIDRs: cidrs,
}
}
return expDetails
}
func splitCommaDelimitedList(list string) []string {
var items []string
for _, token := range strings.Split(list, ",") {
token = strings.TrimSpace(token)
if len(token) == 0 {
continue
}
items = append(items, token)
}
return items
}