diff --git a/CHANGELOG.md b/CHANGELOG.md index b872c2d..dbc4645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # kitchen-oci CHANGELOG +# 2.0.0 +- feat: set default value for `are_legacy_imds_endpoints_disabled` to `true` + > BREAKING CHANGE: This overrides the default value to `true` in accordance with latest [OCI secuirty guidelines](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm) + # 1.28.0 - feat: add instance_options to compute instance type diff --git a/README.md b/README.md index 39835af..6db6e1a 100644 --- a/README.md +++ b/README.md @@ -121,11 +121,10 @@ These settings are optional: - `volume_id`, If you wish to clone your volume from an existing volume set this to the source volume's ID. They must be in the same Availability Domain. - `nsg_ids`, The option to connect up to 5 Network Security Groups to compute instance. - `custom_metadata`, Add metadata to the compute instance request - - `instance_options`, A hash of optional mutable instance options. - Initially, the only option [supported in the Ruby SDK](https://docs.oracle.com/en-us/iaas/tools/ruby/latest/OCI/Core/Models/InstanceOptions.html) - is `are_legacy_imds_endpoints_disabled`. - Customers who have [migrated to IMDSv2](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm#upgrading-v2) - should set this to `true` + - `instance_options`, A hash of optional mutable instance options.\ + Available options [supported in the Ruby SDK](https://docs.oracle.com/en-us/iaas/tools/ruby/latest/OCI/Core/Models/InstanceOptions.html) + - `are_legacy_imds_endpoints_disabled`, Boolean (default: `true`) + > Customers who have not [migrated to IMDSv2](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm#upgrading-v2) will need to set this to `false`. [[more](#imdsv2)] - `all_plugins_disabled`, Whether Oracle Cloud Agent can run all the available plugins (default: `false`) - `management_disabled`, Whether Oracle Cloud Agent can run all the available management plugins (default: `false`) - `monitoring_disabled`, Whether Oracle Cloud Agent can gather performance metrics and monitor the instance using the monitoring plugins (default: `false`) @@ -407,6 +406,19 @@ driver: vpus_per_gb: 30 ``` +## IMDSv2 +In accordance with [OCI security guidelines](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm), the driver is disabling the IMDSv1 endpoint by default. This overrides the current default setting +in OCI and could cause issues with [unsupported images](https://docs.oracle.com/en-us/iaas/Content/Compute/Tasks/gettingmetadata.htm#upgrading-v2__supported-images). In the event legacy IMDS support is required, +the option can be provided to the driver: + +```yml +--- +driver: + name: oci + instance_options: + are_legacy_imds_endpoints_disabled: false +``` + ## Windows Support When launching Oracle provided Windows images, it may be helpful to allow kitchen-oci to inject powershell to configure WinRM and to set a randomized password that does not need to be changed on first login. If the `setup_winrm` parameter is set to true then the following steps will happen: diff --git a/lib/kitchen/driver/oci/instance/compute.rb b/lib/kitchen/driver/oci/instance/compute.rb index 3e7339c..47ac1c0 100644 --- a/lib/kitchen/driver/oci/instance/compute.rb +++ b/lib/kitchen/driver/oci/instance/compute.rb @@ -81,13 +81,6 @@ def instance_source_via_image ) end - # Adds the instance options property to the launch details. - def instance_options - return if config[:instance_options].empty? - - launch_details.instance_options = OCI::Core::Models::InstanceOptions.new(config[:instance_options]) - end - # Adds the source_details property to the launch_details for an instance that is being created from a boot volume. def instance_source_via_boot_volume return unless config[:boot_volume_id] diff --git a/lib/kitchen/driver/oci/mixin/actions.rb b/lib/kitchen/driver/oci/mixin/actions.rb index 7ea22f0..1d8498f 100644 --- a/lib/kitchen/driver/oci/mixin/actions.rb +++ b/lib/kitchen/driver/oci/mixin/actions.rb @@ -33,6 +33,8 @@ def launch(state, inst) state_details = inst.launch state.merge!(state_details) instance.transport.connection(state).wait_until_ready + instance_options(state, inst) + are_legacy_imds_endpoints_disbled?(state, inst) end # Executes the post script on the instance. @@ -68,6 +70,36 @@ def execute_post_create_file(state) end end + # Applies instance options. + # + # @param state [Hash] (see Kitchen::StateFile) + # @param inst [Class] the specific class of instance being rebooted. + def instance_options(state, inst) + return unless instance_options? + + inst.logger.info("Applying the following instance options:") + config[:instance_options].each { |o, v| inst.logger.info("- #{o}: #{v}") } + inst.api.compute.update_instance(state[:server_id], OCI::Core::Models::UpdateInstanceDetails.new(instance_options: OCI::Core::Models::InstanceOptions.new(config[:instance_options]))) + end + + # Attempts to disable IMDSv1 even if not explicitly specified in the config. This is in line with current security guidance from OCI. + # Acts as a guard for setting instance options. + def instance_options? + return false unless config[:instance_type] == "compute" + + config[:instance_options].merge!(are_legacy_imds_endpoints_disabled: true) unless config[:instance_options].key?(:are_legacy_imds_endpoints_disabled) + # Basically tell me if there's more stuff in there than `are_legacy_imds_endpoints_disabled: false`. If so, then proceed to setting it. + config[:instance_options].reject { |o, v| o == :are_legacy_imds_endpoints_disabled && !v }.any? + end + + # Checks if legacy metadata is disabled. + def are_legacy_imds_endpoints_disbled?(state, inst) + return unless config[:instance_type] == "compute" + + imds = inst.api.compute.get_instance(state[:server_id]).data.instance_options.are_legacy_imds_endpoints_disabled + inst.logger.warn("Legacy IMDSv1 endpoint is enabled.") unless imds + end + # Reboots an instance. # # @param state [Hash] (see Kitchen::StateFile) diff --git a/lib/kitchen/driver/oci_version.rb b/lib/kitchen/driver/oci_version.rb index fe47b77..aead3aa 100644 --- a/lib/kitchen/driver/oci_version.rb +++ b/lib/kitchen/driver/oci_version.rb @@ -22,6 +22,6 @@ module Driver # Version string for Oracle OCI Kitchen driver # # @author Stephen Pearson () - OCI_VERSION = "1.28.0" + OCI_VERSION = "2.0.0" end end diff --git a/spec/spec_helper/compute_helper.rb b/spec/spec_helper/compute_helper.rb index e5f27c2..0678bd5 100644 --- a/spec/spec_helper/compute_helper.rb +++ b/spec/spec_helper/compute_helper.rb @@ -23,8 +23,13 @@ include_context "oci" include_context "net" - let(:compute_driver_config) { base_driver_config.merge!({ capacity_reservation_id: capacity_reservation }) } + let(:compute_driver_config) { base_driver_config.merge!(capacity_reservation_id: capacity_reservation) } let(:instance_ocid) { "ocid1.instance.oc1.fake.aaaaaaaaaabcdefghijklmnopqrstuvwxyz12345" } + let(:instance_options) { + OCI::Core::Models::UpdateInstanceDetails.new( + instance_options: OCI::Core::Models::InstanceOptions.new(are_legacy_imds_endpoints_disabled: true) + ) + } let(:instance_metadata) do { "ssh_authorized_keys" => ssh_pub_key, @@ -111,5 +116,6 @@ allow(compute_client).to receive(:get_volume_attachment).with(pv_attachment_ocid).and_return(pv_attachment_response) allow(compute_client).to receive(:detach_volume).with(iscsi_attachment_ocid).and_return(nil_response) allow(compute_client).to receive(:detach_volume).with(pv_attachment_ocid).and_return(nil_response) + allow(compute_client).to receive(:update_instance).with(instance_ocid, instance_options).and_return(nil_response) end end diff --git a/spec/spec_helper/create_helper.rb b/spec/spec_helper/create_helper.rb index 0103c06..f71a2af 100644 --- a/spec/spec_helper/create_helper.rb +++ b/spec/spec_helper/create_helper.rb @@ -21,7 +21,8 @@ let(:compute_response) do OCI::Response.new(200, nil, OCI::Core::Models::Instance.new(id: instance_ocid, image_id: image_ocid, - lifecycle_state: Lifecycle.compute("running"))) + lifecycle_state: Lifecycle.compute("running"), + instance_options: OCI::Core::Models::InstanceOptions.new(are_legacy_imds_endpoints_disabled: true))) end let(:dbaas_response) do OCI::Response.new(200, nil, OCI::Database::Models::DbSystem.new(id: db_system_ocid, lifecycle_state: Lifecycle.dbaas("available")))