1// Copyright 2016-2018 VMware, Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//    http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package config
16
17import (
18	"crypto/tls"
19	"crypto/x509"
20	"errors"
21	"net"
22	"net/mail"
23	"net/url"
24	"time"
25
26	"github.com/vmware/govmomi/vim25/types"
27	"github.com/vmware/vic/lib/config/executor"
28	"github.com/vmware/vic/pkg/certificate"
29)
30
31// PatternToken is a set of tokens that can be placed into string constants
32// for containerVMs that will be replaced with the specific values
33type PatternToken string
34
35const (
36	// VM is the VM name - i.e. [ds] {vm}/{vm}.vmx
37	VMToken PatternToken = "{vm}"
38	// ID is the container ID for the VM
39	IDToken PatternToken = "{id}"
40	// Name is the container name of the VM
41	NameToken PatternToken = "{name}"
42
43	// The default naming pattern that gets applied if no convention is supplied
44	DefaultNamePattern = "{name}-{id}"
45
46	// ID represents the VCH in creating status, which helps to identify VCH VM which still does not have a valid VM moref set
47	CreatingVCH = "CreatingVCH"
48
49	PublicNetworkName     = "public"
50	ClientNetworkName     = "client"
51	ManagementNetworkName = "management"
52
53	PersonaService   = "docker-personality"
54	PortLayerService = "port-layer"
55	VicAdminService  = "vicadmin"
56
57	GeneralHTTPProxy   = "HTTP_PROXY"
58	GeneralHTTPSProxy  = "HTTPS_PROXY"
59	GeneralNoProxy     = "NO_PROXY"
60	VICAdminHTTPProxy  = "VICADMIN_HTTP_PROXY"
61	VICAdminHTTPSProxy = "VICADMIN_HTTPS_PROXY"
62	VICAdminNoProxy    = "NO_PROXY"
63	VICAdminCSPath     = "--cluster"
64	VICAdminPoolPath   = "--pool"
65	VICAdminDCPath     = "--dc"
66	PortLayerCSPath    = "CS_PATH"
67	PortLayerPoolPath  = "POOL_PATH"
68	PortLayerDCPath    = "DC_PATH"
69
70	AddPerms = "ADD"
71)
72
73func (p PatternToken) String() string {
74	return string(p)
75}
76
77// Can we just treat the VCH appliance as a containerVM booting off a specific bootstrap image
78// It has many of the same requirements (around networks being attached, version recorded,
79// volumes mounted, et al). Each of the components can easily be captured as a Session given they
80// are simply processes.
81// This would require that the bootstrap read session record for the VM and relaunch them - that
82// actually aligns very well with containerVMs restarting their processes if restarted directly
83// (this is obviously a behaviour we'd want to toggles for in regular containers).
84
85// VirtualContainerHostConfigSpec holds the metadata for a Virtual Container Host that should be visible inside the appliance VM.
86type VirtualContainerHostConfigSpec struct {
87	// The base config for the appliance. This includes the networks that are to be attached
88	// and disks to be mounted.
89	// Networks are keyed by interface name
90	executor.ExecutorConfig `vic:"0.1" scope:"read-only" key:"init"`
91
92	// vSphere connection configuration
93	Connection `vic:"0.1" scope:"read-only" key:"connect"`
94
95	// basic contact information
96	Contacts `vic:"0.1" scope:"read-only" key:"contact"`
97
98	// certificate configuration, for both inbound and outbound access
99	Certificate `vic:"0.1" scope:"read-only" key:"cert"`
100
101	// Port Layer - storage
102	Storage `vic:"0.1" scope:"read-only" key:"storage"`
103
104	// Port Layer - network
105	Network `vic:"0.1" scope:"read-only" key:"network"`
106
107	// Port Layer - exec
108	Container `vic:"0.1" scope:"read-only" key:"container"`
109
110	// Registry configuration for Imagec
111	Registry `vic:"0.1" scope:"read-only" key:"registry"`
112
113	// configuration for vic-machine
114	CreateBridgeNetwork bool `vic:"0.1" scope:"read-only" key:"create_bridge_network"`
115
116	// grant ops-user permissions, string instead of bool for future enhancements
117	GrantPermsLevel string `vic:"0.1" scope:"read-only" key:"grant_permissions"`
118
119	// vic-machine create options used to create or reconfigure the VCH
120	VicMachineCreateOptions []string `vic:"0.1" scope:"read-only" key:"vic_machine_create_options"`
121}
122
123// ContainerConfig holds the container configuration for a virtual container host
124type Container struct {
125	// Default containerVM capacity
126	ContainerVMSize Resources `vic:"0.1" scope:"read-only" recurse:"depth=0"`
127	// Resource pools under which all containers will be created
128	ComputeResources []types.ManagedObjectReference `vic:"0.1" scope:"read-only"`
129	// Path of the ISO to use for bootstrapping containers
130	BootstrapImagePath string `vic:"0.1" scope:"read-only" key:"bootstrap_image_path"`
131	// Allow custom naming convention for containerVMs
132	ContainerNameConvention string
133	// Whether to create and manage a DRS VM Group for the VCH and its containerVMs
134	UseVMGroup bool
135	// Name to use for the DRS VM Group
136	VMGroupName string
137	// Permitted datastore URLs for container storage for this virtual container host
138	ContainerStores []url.URL `vic:"0.1" scope:"read-only" recurse:"depth=0"`
139	// Total number of containers that can exist in this virtual container host
140	ContainerCount int `vic:"0.1" scope:"read-only" key:"container_count"`
141}
142
143// RegistryConfig defines the registries virtual container host can talk to
144type Registry struct {
145	// Whitelist of registries
146	RegistryWhitelist []string `vic:"0.1" scope:"read-only" key:"whitelist_registries"`
147	// Blacklist of registries
148	RegistryBlacklist []string `vic:"0.1" scope:"read-only" recurse:"depth=0"`
149	// Insecure registries
150	InsecureRegistries []string `vic:"0.1" scope:"read-only" key:"insecure_registries"`
151}
152
153// NetworkConfig defines the network configuration of virtual container host
154type Network struct {
155	// The network to use by default to provide access to the world
156	BridgeNetwork string `vic:"0.1" scope:"read-only" key:"bridge_network"`
157	// Published networks available for containers to join, keyed by consumption name
158	ContainerNetworks map[string]*executor.ContainerNetwork `vic:"0.1" scope:"read-only" key:"container_networks"`
159	// The IP range for the bridge networks
160	BridgeIPRange *net.IPNet `vic:"0.1" scope:"read-only" key:"bridge-ip-range"`
161	// The width of each new bridge network
162	BridgeNetworkWidth *net.IPMask `vic:"0.1" scope:"read-only" key:"bridge-net-width"`
163}
164
165// StorageConfig defines the storage configuration including images and volumes
166type Storage struct {
167	// Datastore URLs for image stores - the top layer is [0], the bottom layer is [len-1]
168	ImageStores []url.URL `vic:"0.1" scope:"read-only" key:"image_stores"`
169	// Storage quota for images and container vms
170	StorageQuota int64 `vic:"0.1" scope:"read-only" key:"storage_quota"`
171	// Permitted datastore URL roots for volumes
172	// Keyed by the volume store name (which is used by the docker user to
173	// refer to the datstore + path), valued by the datastores and the path.
174	VolumeLocations map[string]*url.URL `vic:"0.1" scope:"read-only"`
175	// default size for root image
176	ScratchSize int64 `vic:"0.1" scope:"read-only" key:"scratch_size"`
177}
178
179type Certificate struct {
180	// Certificates for user authentication - this needs to be expanded to allow for directory server auth
181	UserCertificates []*RawCertificate
182	// Certificates for general outgoing network access, keyed by CIDR (IPNet.String())
183	NetworkCertificates map[string]*RawCertificate
184	// The certificate used to validate the appliance to clients
185	HostCertificate *RawCertificate `vic:"0.1" scope:"read-only"`
186	// The CAs to validate client connections
187	CertificateAuthorities []byte `vic:"0.1" scope:"read-only"`
188	// The CAs to validate docker registry connections
189	RegistryCertificateAuthorities []byte `vic:"0.1" scope:"read-only"`
190	// Certificates for specific system access, keyed by FQDN
191	HostCertificates map[string]*RawCertificate
192}
193
194// Connection holds the vSphere connection configuration
195type Connection struct {
196	// The sdk URL
197	Target string `vic:"0.1" scope:"read-only" key:"target"`
198	// Username for target login
199	Username string `vic:"0.1" scope:"read-only" key:"username"`
200	// Token is an SSO token or password
201	Token string `vic:"0.1" scope:"secret" key:"token"`
202	// TargetThumbprint is the SHA-1 digest of the Target's public certificate
203	TargetThumbprint string `vic:"0.1" scope:"read-only" key:"target_thumbprint"`
204	// The session timeout
205	Keepalive time.Duration `vic:"0.1" scope:"read-only" key:"keepalive"`
206}
207
208type Contacts struct {
209	// Administrative contact for the Virtual Container Host
210	Admin []mail.Address
211	// Administrative contact for hosting infrastructure
212	InfrastructureAdmin []mail.Address
213}
214
215// RawCertificate is present until we add extraconfig support for [][]byte slices that are present
216// in tls.Certificate
217type RawCertificate struct {
218	Key  []byte `vic:"0.1" scope:"secret"`
219	Cert []byte
220}
221
222// CustomerExperienceImprovementProgram provides configuration for the phone home mechanism
223// This is broken out so that we can have more granular configuration in here in the future
224// and so that it is insulated from changes to Virtual Container Host structure
225type CustomerExperienceImprovementProgram struct {
226	// The server target is as follows, where the uuid is the raw number, no dashes
227	// "https://vcsa.vmware.com/ph-stg/api/hyper/send?_v=1.0&_c=vic.1_0&_i="+vc.uuid
228	// If this is non-nil then it's enabled
229	CEIPGateway url.URL
230}
231
232// Resources is used instead of the ResourceAllocation structs in govmomi as
233// those don't currently hold IO or storage related data.
234type Resources struct {
235	CPU     types.ResourceAllocationInfo
236	Memory  types.ResourceAllocationInfo
237	IO      types.ResourceAllocationInfo
238	Storage types.ResourceAllocationInfo
239}
240
241// SetHostCertificate sets the certificate for authenticting with the appliance itself
242func (t *VirtualContainerHostConfigSpec) SetHostCertificate(key *[]byte) {
243	t.ExecutorConfig.Key = *key
244}
245
246// SetName sets the name of the VCH - this will be used as the hostname for the appliance
247func (t *VirtualContainerHostConfigSpec) SetName(name string) {
248	t.ExecutorConfig.Name = name
249}
250
251// SetDebug configures the debug logging level for the VCH
252func (t *VirtualContainerHostConfigSpec) SetDebug(level int) {
253	t.ExecutorConfig.Diagnostics.DebugLevel = level
254}
255
256// SetMoref sets the moref of the VCH - this allows components to acquire a handle to
257// the appliance VM.
258func (t *VirtualContainerHostConfigSpec) SetMoref(moref *types.ManagedObjectReference) {
259	if moref != nil {
260		t.ExecutorConfig.ID = moref.String()
261	}
262}
263
264// SetIsCreating sets the ID of the VCH to a constant if creating is true, to identify the creating VCH VM before the VM moref can be set into this property
265// Reset the property back to empty string if creating is false
266func (t *VirtualContainerHostConfigSpec) SetIsCreating(creating bool) {
267	if creating {
268		t.ExecutorConfig.ID = CreatingVCH
269	} else {
270		t.ExecutorConfig.ID = ""
271	}
272}
273
274// IsCreating is checking if this configuration is for one creating VCH VM
275func (t *VirtualContainerHostConfigSpec) IsCreating() bool {
276	return t.ExecutorConfig.ID == CreatingVCH
277}
278
279func (t *VirtualContainerHostConfigSpec) SetGrantPerms() {
280	t.GrantPermsLevel = AddPerms
281}
282
283func (t *VirtualContainerHostConfigSpec) ClearGrantPerms() {
284	t.GrantPermsLevel = ""
285}
286
287func (t *VirtualContainerHostConfigSpec) ShouldGrantPerms() bool {
288	return t.GrantPermsLevel == AddPerms
289}
290
291// AddNetwork adds a network that will be configured on the appliance VM
292func (t *VirtualContainerHostConfigSpec) AddNetwork(net *executor.NetworkEndpoint) {
293	if net != nil {
294		if t.ExecutorConfig.Networks == nil {
295			t.ExecutorConfig.Networks = make(map[string]*executor.NetworkEndpoint)
296		}
297
298		t.ExecutorConfig.Networks[net.Network.Name] = net
299	}
300}
301
302// AddContainerNetwork adds a network that will be configured on the appliance VM
303func (t *VirtualContainerHostConfigSpec) AddContainerNetwork(net *executor.ContainerNetwork) {
304	if net != nil {
305		if t.ContainerNetworks == nil {
306			t.ContainerNetworks = make(map[string]*executor.ContainerNetwork)
307		}
308
309		t.ContainerNetworks[net.Name] = net
310	}
311}
312
313func (t *VirtualContainerHostConfigSpec) AddComponent(name string, component *executor.SessionConfig) {
314	if component != nil {
315		if t.ExecutorConfig.Sessions == nil {
316			t.ExecutorConfig.Sessions = make(map[string]*executor.SessionConfig)
317		}
318
319		if component.Name == "" {
320			component.Name = name
321		}
322		if component.ID == "" {
323			component.ID = name
324		}
325		t.ExecutorConfig.Sessions[name] = component
326	}
327}
328
329func (t *VirtualContainerHostConfigSpec) AddImageStore(url *url.URL) {
330	if url != nil {
331		t.ImageStores = append(t.ImageStores, *url)
332	}
333}
334
335func (t *VirtualContainerHostConfigSpec) AddVolumeLocation(name string, u *url.URL) {
336
337	if u != nil {
338		if t.VolumeLocations == nil {
339			t.VolumeLocations = make(map[string]*url.URL)
340		}
341
342		t.VolumeLocations[name] = u
343	}
344}
345
346// AddComputeResource adds a moref to the set of permitted root pools. It takes a ResourcePool rather than
347// an inventory path to encourage validation.
348func (t *VirtualContainerHostConfigSpec) AddComputeResource(pool *types.ManagedObjectReference) {
349	if pool != nil {
350		t.ComputeResources = append(t.ComputeResources, *pool)
351	}
352}
353
354func CreateSession(cmd string, args ...string) *executor.SessionConfig {
355	cfg := &executor.SessionConfig{
356		Cmd: executor.Cmd{
357			Path: cmd,
358			Args: []string{
359				cmd,
360			},
361		},
362	}
363
364	cfg.Cmd.Args = append(cfg.Cmd.Args, args...)
365
366	return cfg
367}
368
369func (t *RawCertificate) Certificate() (*tls.Certificate, error) {
370	if t.IsNil() {
371		return nil, errors.New("nil certificate")
372	}
373	cert, err := tls.X509KeyPair(t.Cert, t.Key)
374	return &cert, err
375}
376
377func (t *RawCertificate) X509Certificate() (*x509.Certificate, error) {
378	if t.IsNil() {
379		return nil, errors.New("nil certificate")
380	}
381	cert, _, err := certificate.ParseCertificate(t.Cert, t.Key)
382	return cert, err
383}
384
385func (t *RawCertificate) IsNil() bool {
386	if t == nil {
387		return true
388	}
389
390	return len(t.Cert) == 0 && len(t.Key) == 0
391}
392