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