/
config.go
195 lines (172 loc) · 5.65 KB
/
config.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
187
188
189
190
191
192
193
194
195
package v2
import (
"fmt"
"log"
"sort"
"gopkg.in/yaml.v2"
"github.com/cyberark/secretless-broker/pkg/secretless/plugin"
"github.com/cyberark/secretless-broker/pkg/secretless/plugin/sharedobj"
)
// Config represents a full configuration of Secretless, which is just a list of
// individual Service configurations.
type Config struct {
Debug bool
Services []*Service
}
// Serialize Config to YAML
func (c Config) String() string {
out, err := yaml.Marshal(c)
if err != nil {
return ""
}
return string(out)
}
// MarshalYAML serializes Config to the secretless.yml format
func (c Config) MarshalYAML() (interface{}, error) {
servicesAsYAML := map[string]*serviceYAML{}
for _, svc := range c.Services {
credentialYamls := credentialsYAML{}
for _, cred := range svc.Credentials {
credentialYamls[cred.Name] = struct {
From string `yaml:"from" json:"from"`
Get string `yaml:"get" json:"get"`
}{
From: cred.From,
Get: cred.Get,
}
}
servicesAsYAML[svc.Name] = &serviceYAML{
Connector: svc.Connector,
ListenOn: string(svc.ListenOn),
Credentials: credentialYamls,
Config: svc.ConnectorConfig,
}
}
return struct {
Version string `yaml:"version" json:"version"`
Services map[string]*serviceYAML `yaml:"services" json:"services"`
}{
Version: "2",
Services: servicesAsYAML,
}, nil
}
// NewConfig creates a v2.Config from yaml bytes
func NewConfig(v2YAML []byte) (*Config, error) {
cfgYAML, err := newConfigYAML(v2YAML)
if err != nil {
return nil, err
}
services := make([]*Service, 0)
for svcName, svcYAML := range cfgYAML.Services {
svc, err := NewService(svcName, svcYAML)
if err != nil {
return nil, err
}
services = append(services, svc)
}
// sort Services
sort.Slice(services, func(i, j int) bool {
return services[i].Name < services[j].Name
})
return &Config{
Services: services,
}, nil
}
// NewConfigsByType converts a slice of v2.Service configs into the configs
// needed to actually created ProxyServices -- configsByType. In particular, it
// takes all the http configs and creates proper HTTPServiceConfig objects out
// of them -- grouping the raw v2.Service configs by their listenOn property.
// The remaining services are tcp, and already correspond 1-1 to the services
// we'll run.
// TODO: Eventually the application code should not be dealing directly with
// []Service at all, but the processing into these more appropriate domain
// configs should occur entirely at the border.
func NewConfigsByType(
uncheckedConfigs []*Service,
availPlugins plugin.AvailablePlugins,
) ConfigsByType {
// Get the nil checks out of the way.
var rawConfigs []Service
for _, cfg := range uncheckedConfigs {
if cfg == nil {
// Hard-coding log here is okay since nils should never occur and
// we won't be unit testing this. Instead, we'll make the change
// in the TODO_ above and this code will be deleted then.
log.Fatalln("Nil configuration is not allowed!")
}
rawConfigs = append(rawConfigs, *cfg)
}
var httpConfigs, tcpConfigs, sshConfigs, sshAgentConfigs []Service
for _, cfg := range rawConfigs {
switch {
case sharedobj.IsHTTPPlugin(availPlugins, cfg.Connector):
httpConfigs = append(httpConfigs, cfg)
continue
case cfg.Connector == "ssh":
sshConfigs = append(sshConfigs, cfg)
continue
case cfg.Connector == "ssh-agent":
sshAgentConfigs = append(sshAgentConfigs, cfg)
continue
default:
tcpConfigs = append(tcpConfigs, cfg)
}
}
httpByListenOn := groupedByListenOn(httpConfigs)
// Now create proper HTTPServiceConfig objects from our map
var httpServiceConfigs []HTTPServiceConfig
for listenOn, configs := range httpByListenOn {
httpServiceConfig := HTTPServiceConfig{
SharedListenOn: listenOn,
SubserviceConfigs: configs,
}
httpServiceConfigs = append(httpServiceConfigs, httpServiceConfig)
}
return ConfigsByType{
HTTP: httpServiceConfigs,
SSH: sshConfigs,
SSHAgent: sshAgentConfigs,
TCP: tcpConfigs,
}
}
// HTTPServiceConfig represents an HTTP proxy service configuration. Multiple
// http entries within a v2.Service config slice that share a listenOn actually
// represent a single HTTP proxy service, with sub-handlers for different
// traffic. This type captures that fact.
type HTTPServiceConfig struct {
SharedListenOn NetworkAddress
SubserviceConfigs []Service
}
// Name returns the name of an HTTPServiceConfig
func (cfg *HTTPServiceConfig) Name() string {
return fmt.Sprintf("HTTP Proxy on %s", cfg.SharedListenOn)
}
// ConfigsByType holds proxy service configuration in a form that directly
// corresponds to the ProxyService objects we want to create. One ProxyService
// will be created for each entry in http, and one for each entry in tcp.
type ConfigsByType struct {
HTTP []HTTPServiceConfig
SSH []Service
SSHAgent []Service
TCP []Service
}
// groupedByListenOn returns a map grouping the configs provided by their ListenOn
// property. Merely a helper function to reduce bloat in newConfigsByType.
func groupedByListenOn(httpConfigs []Service) map[NetworkAddress][]Service {
httpByListenOn := map[NetworkAddress][]Service{}
for _, httpConfig := range httpConfigs {
// default group for this ListenOn, in case we don't yet have one yet
var groupedConfigs []Service
// but replace it with the existing group, if one exists
for listenOn, alreadyGrouped := range httpByListenOn {
if listenOn == httpConfig.ListenOn {
groupedConfigs = alreadyGrouped
break
}
}
// append the current config to this ListenOn group
groupedConfigs = append(groupedConfigs, httpConfig)
httpByListenOn[httpConfig.ListenOn] = groupedConfigs
}
return httpByListenOn
}