Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/bosh_azure_cpi/lib/cloud/azure.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ module AzureCloud; end
require 'cloud/azure/models/props_factory'
require 'cloud/azure/models/root_disk'
require 'cloud/azure/models/security_group'
require 'cloud/azure/models/security_profile'
require 'cloud/azure/models/managed_identity'
require 'cloud/azure/models/vm_cloud_props'
require 'cloud/azure/cloud'
Expand Down
52 changes: 52 additions & 0 deletions src/bosh_azure_cpi/lib/cloud/azure/models/security_profile.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# frozen_string_literal: true

module Bosh::AzureCloud
class SecurityProfile
include Helpers

SECURITY_TYPE_KEY = 'security_type'
SECURE_BOOT_ENABLED_KEY = 'secure_boot_enabled'

SUPPORTED_SECURITY_TYPES = [SECURITY_TYPE_TRUSTED_LAUNCH, SECURITY_TYPE_STANDARD].freeze

VTPM_ENABLED = true

attr_reader :security_type, :secure_boot_enabled

def initialize(security_profile_config_hash)
cloud_error("'security_profile' must be a Hash, but is a #{security_profile_config_hash.class}.") unless security_profile_config_hash.is_a?(Hash)

@security_type = security_profile_config_hash.fetch(SECURITY_TYPE_KEY, SECURITY_TYPE_TRUSTED_LAUNCH)
_validate_security_type

@secure_boot_enabled = security_profile_config_hash.fetch(SECURE_BOOT_ENABLED_KEY, trusted_launch?)
cloud_error("'#{SECURE_BOOT_ENABLED_KEY}' must be a boolean, but is '#{@secure_boot_enabled.inspect}'.") unless [true, false].include?(@secure_boot_enabled)
cloud_error("'#{SECURE_BOOT_ENABLED_KEY}' must be false when '#{SECURITY_TYPE_KEY}' is '#{@security_type}'.") if @secure_boot_enabled && !trusted_launch?
end

def trusted_launch?
@security_type == SECURITY_TYPE_TRUSTED_LAUNCH
end

def uefi_settings
return nil unless trusted_launch?

{
secure_boot_enabled: @secure_boot_enabled,
vtpm_enabled: VTPM_ENABLED
}
end

private

def _validate_security_type
return if SUPPORTED_SECURITY_TYPES.include?(@security_type)

if @security_type == SECURITY_TYPE_CONFIDENTIAL_VM
cloud_error("'#{SECURITY_TYPE_KEY}' '#{SECURITY_TYPE_CONFIDENTIAL_VM}' is not supported by this CPI, yet.")
end

cloud_error("Invalid '#{SECURITY_TYPE_KEY}' '#{@security_type}'. Supported values: #{SUPPORTED_SECURITY_TYPES.join(', ')}.")
end
end
end
4 changes: 4 additions & 0 deletions src/bosh_azure_cpi/lib/cloud/azure/models/vm_cloud_props.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class VMCloudProps
attr_reader :tags
attr_reader :capacity_reservation_group
attr_reader :capacity_reservation_group_id
attr_reader :security_profile

# Below defines are for test purpose
# NOTE: The following 3 attr_writer (and their paired readers above) are explicitly separate (instead of using `attr_accessor`)
Expand Down Expand Up @@ -98,6 +99,9 @@ def initialize(vm_properties, global_azure_config)

@capacity_reservation_group = vm_properties['capacity_reservation_group']
@capacity_reservation_group_id = vm_properties['capacity_reservation_group_id']

security_profile_hash = vm_properties.fetch('security_profile', nil)
@security_profile = Bosh::AzureCloud::SecurityProfile.new(security_profile_hash) unless security_profile_hash.nil?
end

private
Expand Down
16 changes: 16 additions & 0 deletions src/bosh_azure_cpi/lib/cloud/azure/restapi/azure_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,22 @@ def create_virtual_machine(resource_group_name, vm_params, network_interfaces, a
}
end

unless vm_params[:security_profile].nil?
security_profile = {
'securityType' => vm_params[:security_profile][:security_type]
}

uefi_settings = vm_params[:security_profile][:uefi_settings]
unless uefi_settings.nil?
security_profile['uefiSettings'] = {
'secureBootEnabled' => uefi_settings[:secure_boot_enabled],
'vTpmEnabled' => uefi_settings[:vtpm_enabled]
}
end

vm['properties']['securityProfile'] = security_profile
end

params = {
'validating' => 'true'
}
Expand Down
6 changes: 5 additions & 1 deletion src/bosh_azure_cpi/lib/cloud/azure/utils/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ module Helpers
ENVIRONMENT_AZUREGERMANCLOUD = 'AzureGermanCloud'

API_VERSIONS = {
AZURE_RESOURCE_PROVIDER_COMPUTE => '2021-11-01',
AZURE_RESOURCE_PROVIDER_COMPUTE => '2025-11-01',
AZURE_RESOURCE_PROVIDER_COMPUTE_DISK => '2023-04-02',
AZURE_RESOURCE_PROVIDER_COMPUTE_SNAPSHOT => '2021-04-01',
AZURE_RESOURCE_PROVIDER_COMPUTE_GALLERY => '2023-07-03',
Expand Down Expand Up @@ -162,6 +162,10 @@ module Helpers
MANAGED_IDENTITY_TYPE_SYSTEM_ASSIGNED = 'SystemAssigned'
MANAGED_IDENTITY_TYPE_USER_ASSIGNED = 'UserAssigned'

SECURITY_TYPE_TRUSTED_LAUNCH = 'TrustedLaunch'
SECURITY_TYPE_CONFIDENTIAL_VM = 'ConfidentialVM'
SECURITY_TYPE_STANDARD = 'Standard'

# Availability Zones
AVAILABILITY_ZONES = %w[1 2 3].freeze

Expand Down
7 changes: 7 additions & 0 deletions src/bosh_azure_cpi/lib/cloud/azure/vms/vm_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,13 @@ def create(bosh_vm_meta, location, vm_props, disk_cids, network_configurator, en
vm_params[:capacity_reservation_group_id] = vm_props.capacity_reservation_group_id
end

unless vm_props.security_profile.nil?
vm_params[:security_profile] = {
security_type: vm_props.security_profile.security_type,
uefi_settings: vm_props.security_profile.uefi_settings
}
end
Comment thread
coderabbitai[bot] marked this conversation as resolved.

unless vm_props.managed_identity.nil?
vm_params[:identity] = {
type: vm_props.managed_identity.type,
Expand Down
2 changes: 1 addition & 1 deletion src/bosh_azure_cpi/spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
AZURE_CHINA_API_VERSION = '2015-06-15'
AZURE_USGOV_API_VERSION = '2015-06-15'
AZURE_GERMAN_API_VERSION = '2015-06-15'
AZURE_RESOURCE_PROVIDER_COMPUTE = '2021-11-01'
AZURE_RESOURCE_PROVIDER_COMPUTE = '2025-11-01'
AZURE_RESOURCE_PROVIDER_COMPUTE_DISK = '2023-04-02'
AZURE_RESOURCE_PROVIDER_COMPUTE_SNAPSHOT = '2021-04-01'
AZURE_RESOURCE_PROVIDER_COMPUTE_GALLERY = '2023-07-03'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2003,5 +2003,182 @@
end.not_to raise_error
end
end

context 'when security_profile is specified' do
let(:vm_params_with_security_profile) do
vm_params_dupped = vm_params.dup
vm_params_dupped.delete(:ephemeral_disk)
vm_params_dupped.delete(:image_uri)
vm_params_dupped[:image_id] = 'g'
vm_params_dupped[:managed] = true
vm_params_dupped[:security_profile] = security_profile
vm_params_dupped
end

let(:base_request_body) do
{
name: vm_name,
location: 'b',
type: 'Microsoft.Compute/virtualMachines',
tags: {
foo: 'bar'
},
properties: {
hardwareProfile: {
vmSize: 'c'
},
osProfile: {
customData: 'f',
computerName: vm_name,
adminUsername: 'd',
linuxConfiguration: {
disablePasswordAuthentication: 'true',
ssh: {
publicKeys: [
{
path: '/home/d/.ssh/authorized_keys',
keyData: 'e'
}
]
}
}
},
networkProfile: {
networkInterfaces: [
{
id: 'a',
properties: {
primary: true
}
},
{
id: 'b',
properties: {
primary: false
}
}
]
},
storageProfile: {
imageReference: {
id: 'g'
},
osDisk: {
name: 'h',
createOption: 'FromImage',
caching: 'j',
diskSizeGB: 'k'
}
}
}
}
end

before do
stub_request(:post, token_uri).to_return(
status: 200,
body: {
'access_token' => valid_access_token,
'expires_on' => expires_on
}.to_json,
headers: {}
)
stub_request(:put, vm_uri).with(body: request_body).to_return(
status: 200,
body: '',
headers: {
'azure-asyncoperation' => operation_status_link
}
)
stub_request(:get, operation_status_link).to_return(
status: 200,
body: '{"status":"Succeeded"}',
headers: {}
)
end

context 'when Secure Boot is enabled' do
let(:security_profile) do
{
security_type: 'TrustedLaunch',
uefi_settings: { secure_boot_enabled: true, vtpm_enabled: true }
}
end

let(:request_body) do
base_request_body.deep_merge(
properties: {
securityProfile: {
securityType: 'TrustedLaunch',
uefiSettings: {
secureBootEnabled: true,
vTpmEnabled: true
}
}
}
)
end

it 'sends securityProfile with both UEFI settings enabled' do
expect do
azure_client.create_virtual_machine(resource_group, vm_params_with_security_profile, network_interfaces)
end.not_to raise_error
end
end

context 'when Secure Boot is disabled for custom unsigned kernels or drivers' do
let(:security_profile) do
{
security_type: 'TrustedLaunch',
uefi_settings: { secure_boot_enabled: false, vtpm_enabled: true }
}
end

let(:request_body) do
base_request_body.deep_merge(
properties: {
securityProfile: {
securityType: 'TrustedLaunch',
uefiSettings: {
secureBootEnabled: false,
vTpmEnabled: true
}
}
}
)
end

it 'sends secureBootEnabled false while vTpmEnabled stays true' do
expect do
azure_client.create_virtual_machine(resource_group, vm_params_with_security_profile, network_interfaces)
end.not_to raise_error
end
end

context 'when the security type is Standard' do
let(:security_profile) do
{
security_type: 'Standard',
uefi_settings: nil
}
end

let(:request_body) do
base_request_body.deep_merge(
properties: {
securityProfile: {
securityType: 'Standard'
}
}
)
end

it 'sends securityType without uefiSettings' do
expect do
azure_client.create_virtual_machine(resource_group, vm_params_with_security_profile, network_interfaces)
end.not_to raise_error
end
end
end
end
end
Loading