diff --git a/CHANGELOG.md b/CHANGELOG.md index 20eea0d11..a64c8b4b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,16 +1,18 @@ # -## 2.9.3 (Not Released) +## 2.9.3 (October 8, 2024) BUG FIX: - `r/vsphere_tag_category`: Updates resource not to `ForceNew` for cardinality. This will allow the `tag_category` to updated. - [#2263](https://github.com/hashicorp/terraform-provider-vsphere/pull/2263) + ([#2263](https://github.com/hashicorp/terraform-provider-vsphere/pull/2263)) +- `r/vsphere_host`: Updates resource to check thumbprint of the ESXI host thumbprint before adding the host to a cluster or vCenter Server. + ([#2266](https://github.com/hashicorp/terraform-provider-vsphere/pull/2266)) DOCUMENTATION: - `resource/vsphere_resource_pool`: Updates to include steps to create resource pool on standalone ESXi hosts. - [#2264](https://github.com/hashicorp/terraform-provider-vsphere/pull/2264) + ([#2264](https://github.com/hashicorp/terraform-provider-vsphere/pull/2264)) ## 2.9.2 (September 16, 2024) @@ -18,12 +20,12 @@ BUG FIX: - `resource/vsphere_compute_cluster_vm_group`: Updates resource to allow for additional virtual machines to be adding or removed from a VM Group. Must be ran in conjunction with and import. - ([#2260]https://github.com/hashicorp/terraform-provider-vsphere/pull/2260) + ([#2260](https://github.com/hashicorp/terraform-provider-vsphere/pull/2260)) FEATURES: - `resource\vsphere_tag`: Adds a format validation for `catagory_id`. - ([#2261]https://github.com/hashicorp/terraform-provider-vsphere/pull/2261) + ([#2261](https://github.com/hashicorp/terraform-provider-vsphere/pull/2261)) ## 2.9.1 (September 9, 2024) @@ -31,19 +33,19 @@ BUG FIX: - `resource/vsphere_resource_pool`: Removes the default setting for `scale_descendants_shares` to allows for inheritance from the parent resource pool. - ([#2255]https://github.com/hashicorp/terraform-provider-vsphere/pull/2255) + ([#2255](https://github.com/hashicorp/terraform-provider-vsphere/pull/2255)) DOCUMENTATION: - `resource/vsphere_virtual_machine`: Updates to clarify assignment of `network_interface` - resources. ([#2256]https://github.com/hashicorp/terraform-provider-vsphere/pull/2256) + resources. ([#2256](https://github.com/hashicorp/terraform-provider-vsphere/pull/2256)) - `resource/vsphere_host`: Updates to clarify import of `vsphere_hosts`. - ([#2257]https://github.com/hashicorp/terraform-provider-vsphere/pull/2257) + ([#2257](https://github.com/hashicorp/terraform-provider-vsphere/pull/2257)) - `resource/vsphere_compute_cluster`: Updates to clarify import of `vsphere_compute_cluster` - resources. ([#2257]https://github.com/hashicorp/terraform-provider-vsphere/pull/2257) + resources. ([#2257](https://github.com/hashicorp/terraform-provider-vsphere/pull/2257)) - `resource/vsphere_virtual_machine`: Updates to clarify the `vm` path in the import of `virtual_machine` resources. - ([#2257]https://github.com/hashicorp/terraform-provider-vsphere/pull/2257) + ([#2257](https://github.com/hashicorp/terraform-provider-vsphere/pull/2257)) ## 2.9.0 (September 3, 2024) diff --git a/vsphere/data_source_vsphere_host_thumbprint.go b/vsphere/data_source_vsphere_host_thumbprint.go index ff0f2fbe0..4741d6446 100644 --- a/vsphere/data_source_vsphere_host_thumbprint.go +++ b/vsphere/data_source_vsphere_host_thumbprint.go @@ -30,6 +30,7 @@ func dataSourceVSphereHostThumbprint() *schema.Resource { "insecure": { Type: schema.TypeBool, Optional: true, + Default: false, Description: "Boolean that can be set to true to disable SSL certificate verification.", }, }, diff --git a/vsphere/resource_vsphere_host.go b/vsphere/resource_vsphere_host.go index fe8ae8f89..60589f16d 100644 --- a/vsphere/resource_vsphere_host.go +++ b/vsphere/resource_vsphere_host.go @@ -4,11 +4,15 @@ package vsphere import ( + "bytes" "context" + "crypto/sha1" + "crypto/tls" "fmt" - "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/provider" "log" + "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/provider" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/hashicorp/terraform-provider-vsphere/vsphere/internal/helper/clustercomputeresource" @@ -26,6 +30,8 @@ import ( "github.com/vmware/govmomi/vim25/types" ) +const defaultHostPort = "443" + var servicesPolicyAllowedValues = []string{ string(types.HostServicePolicyOff), string(types.HostServicePolicyOn), @@ -162,7 +168,10 @@ func resourceVsphereHostCreate(d *schema.ResourceData, meta interface{}) error { client := meta.(*Client).vimClient - hcs := buildHostConnectSpec(d) + hcs, err := buildHostConnectSpec(d) + if err != nil { + return fmt.Errorf("failed to build host connect spec: %v", err) + } licenseKey := d.Get("license").(string) @@ -714,7 +723,10 @@ func resourceVSphereHostReconnect(d *schema.ResourceData, meta interface{}) erro hostID := d.Id() client := meta.(*Client).vimClient host := object.NewHostSystem(client.Client, types.ManagedObjectReference{Type: "HostSystem", Value: d.Id()}) - hcs := buildHostConnectSpec(d) + hcs, err := buildHostConnectSpec(d) + if err != nil { + return fmt.Errorf("failed to build host connect spec: %v", err) + } task, err := host.Reconnect(context.TODO(), &hcs, nil) if err != nil { @@ -826,15 +838,90 @@ func hostLockdownString(lockdownMode types.HostLockdownMode) (string, error) { return "", fmt.Errorf("unknown Lockdown mode encountered") } -func buildHostConnectSpec(d *schema.ResourceData) types.HostConnectSpec { +func buildHostConnectSpec(d *schema.ResourceData) (types.HostConnectSpec, error) { + thumbprint := d.Get("thumbprint").(string) + hostname := d.Get("hostname").(string) + username := d.Get("username").(string) + password := d.Get("password").(string) + + log.Printf("Building HostConnectSpec for host: %s", hostname) + // Retrieve the actual thumbprint from the ESXi host. + if thumbprint != "" { + actualThumbprint, err := getHostThumbprint(d) + if err != nil { + return types.HostConnectSpec{}, fmt.Errorf("error retrieving host thumbprint: %s", err) + } + + // Compare the the provided and returned thumbprints. + if thumbprint != actualThumbprint { + return types.HostConnectSpec{}, fmt.Errorf("thumbprint mismatch: expected %s, got %s", thumbprint, actualThumbprint) + } + } + hcs := types.HostConnectSpec{ - HostName: d.Get("hostname").(string), - UserName: d.Get("username").(string), - Password: d.Get("password").(string), - SslThumbprint: d.Get("thumbprint").(string), + HostName: hostname, + UserName: username, + Password: password, + SslThumbprint: thumbprint, Force: d.Get("force").(bool), } - return hcs + return hcs, nil +} + +func getHostThumbprint(d *schema.ResourceData) (string, error) { + config := &tls.Config{} + + // Check the hostname. + address, ok := d.Get("hostname").(string) + if !ok { + return "", fmt.Errorf("hostname field is not a string or is nil") + } + + // Default port for HTTPS. + port := defaultHostPort + if p, ok := d.GetOk("port"); ok { + if portStr, ok := p.(string); ok { + port = portStr + } else { + return "", fmt.Errorf("port field is not a string") + } + } + + // Check if allow_unverified_ssl is true. If so, skip the verification. + // Otherwise, use the default value of false. + if thumbprint, ok := d.Get("thumbprint").(string); ok && thumbprint != "" { + return thumbprint, nil + } else { + if insecure, ok := d.GetOk("allow_unverified_ssl"); ok { + if insecureBool, ok := insecure.(bool); ok { + config.InsecureSkipVerify = insecureBool + if config.InsecureSkipVerify { + } + } else { + config.InsecureSkipVerify = false + } + } else { + config.InsecureSkipVerify = false + } + } + + conn, err := tls.Dial("tcp", address+":"+port, config) + if err != nil { + return "", fmt.Errorf("error dialing TLS connection: %w", err) + } + defer conn.Close() + + cert := conn.ConnectionState().PeerCertificates[0] + fingerprint := sha1.Sum(cert.Raw) + + var buf bytes.Buffer + for i, f := range fingerprint { + if i > 0 { + _, _ = fmt.Fprintf(&buf, ":") + } + _, _ = fmt.Fprintf(&buf, "%02X", f) + } + return buf.String(), nil } func isLicenseAssigned(client *vim25.Client, hostID, licenseKey string) (bool, error) { diff --git a/website/docs/d/host_thumbprint.html.markdown b/website/docs/d/host_thumbprint.html.markdown index 638a399e8..143790526 100644 --- a/website/docs/d/host_thumbprint.html.markdown +++ b/website/docs/d/host_thumbprint.html.markdown @@ -10,9 +10,14 @@ description: |- # vsphere\_host\_thumbprint The `vsphere_thumbprint` data source can be used to discover the host thumbprint -of an ESXi host. This can be used when adding the `vsphere_host` resource. If -the ESXi host is using a certificate chain, the first one returned will be used -to generate the thumbprint. +of an ESXi host. This can be used when adding the `vsphere_host` resource to a +cluster or a vCenter Server instance. + +* If the ESXi host is using a certificate chain, the first one returned will be +used to generate the thumbprint. + +* If the ESXi host has a certificate issued by a certificate authority, ensure +that the the certificate authority is trusted on the system running the plan. ## Example Usage @@ -28,9 +33,8 @@ The following arguments are supported: * `address` - (Required) The address of the ESXi host to retrieve the thumbprint from. +* `insecure` - (Optional) Disables SSL certificate verification. Default: `false` * `port` - (Optional) The port to use connecting to the ESXi host. Default: 443 -* `insecure` - (Optional) Disables SSL certificate verification. - Default: `false` ## Attribute Reference diff --git a/website/docs/r/host.html.markdown b/website/docs/r/host.html.markdown index 76ed5a664..8b619744a 100644 --- a/website/docs/r/host.html.markdown +++ b/website/docs/r/host.html.markdown @@ -79,6 +79,10 @@ The following arguments are supported: to the host. * `password` - (Required) Password that will be used by vSphere to authenticate to the host. +* `thumbprint` - (Optional) Host's certificate SHA-1 thumbprint. If not set the + CA that signed the host's certificate should be trusted. If the CA is not + trusted and no thumbprint is set then the operation will fail. See data source + [`vsphere_host_thumbprint`][docs-host-thumbprint-data-source]. * `datacenter` - (Optional) The ID of the datacenter this host should be added to. This should not be set if `cluster` is set. * `cluster` - (Optional) The ID of the Compute Cluster this host should @@ -87,10 +91,6 @@ The following arguments are supported: * `cluster_managed` - (Optional) Can be set to `true` if compute cluster membership will be managed through the `compute_cluster` resource rather than the`host` resource. Conflicts with: `cluster`. -* `thumbprint` - (Optional) Host's certificate SHA-1 thumbprint. If not set the - CA that signed the host's certificate should be trusted. If the CA is not - trusted and no thumbprint is set then the operation will fail. See data source - [`vsphere_host_thumbprint`][docs-host-thumbprint-data-source]. * `license` - (Optional) The license key that will be applied to the host. The license key is expected to be present in vSphere. * `force` - (Optional) If set to `true` then it will force the host to be added,