1515
1616from unittest import mock
1717
18+ from castellan .common import exception as castellan_exc
1819from castellan .common .objects import passphrase
1920from castellan .key_manager import key_manager
2021import fixtures
2829from nova import objects
2930from nova .tests .functional .api import client
3031from nova .tests .functional .libvirt import base
32+ from nova import utils
3133
3234CONF = nova .conf .CONF
3335LOG = logging .getLogger (__name__ )
@@ -48,6 +50,9 @@ def __init__(self, configuration):
4850 #: A mapping of UUIDs to passphrases.
4951 self ._passphrases = {}
5052
53+ #: A mapping of UUIDs to RequestContext objects.
54+ self ._contexts = {}
55+
5156 def create_key (self , context , algorithm , length , ** kwargs ):
5257 """Creates a symmetric key.
5358
@@ -78,6 +83,7 @@ def store(self, context, managed_object, **kwargs):
7883 uuid = uuidutils .generate_uuid ()
7984 managed_object ._id = uuid # set the id to simulate persistence
8085 self ._passphrases [uuid ] = managed_object
86+ self ._contexts [uuid ] = context
8187
8288 return uuid
8389
@@ -91,6 +97,12 @@ def get(self, context, managed_object_id):
9197 if context is None :
9298 raise exception .Forbidden ()
9399
100+ if context .user_id != self ._contexts [managed_object_id ].user_id :
101+ raise castellan_exc .KeyManagerError (
102+ 'Key manager error: Forbidden: Secret payload retrieval '
103+ 'attempt not allowed - please review your user/project '
104+ 'privileges' )
105+
94106 if managed_object_id not in self ._passphrases :
95107 raise KeyError ('cannot retrieve non-existent secret' )
96108
@@ -104,11 +116,18 @@ def delete(self, context, managed_object_id):
104116 if context is None :
105117 raise exception .Forbidden ()
106118
119+ if context .user_id != self ._contexts [managed_object_id ].user_id :
120+ raise castellan_exc .KeyManagerError (
121+ 'Key manager error: Forbidden: Secret payload retrieval '
122+ 'attempt not allowed - please review your user/project '
123+ 'privileges' )
124+
107125 if managed_object_id not in self ._passphrases :
108126 raise exception .KeyManagerError (
109127 reason = "cannot delete non-existent secret" )
110128
111129 del self ._passphrases [managed_object_id ]
130+ del self ._contexts [managed_object_id ]
112131
113132 def add_consumer (self , context , managed_object_id , consumer_data ):
114133 raise NotImplementedError (
@@ -123,8 +142,11 @@ def remove_consumer(self, context, managed_object_id, consumer_data):
123142
124143class VTPMServersTest (base .ServersTestBase ):
125144
126- # many move operations are admin-only
127- ADMIN_API = True
145+ # NOTE: ADMIN_API is intentionally not set to True in order to catch key
146+ # manager service secret ownership issues.
147+
148+ # Reflect reality more for async API requests like migration
149+ CAST_AS_CALL = False
128150
129151 def setUp (self ):
130152 # enable vTPM and use our own fake key service
@@ -372,6 +394,16 @@ def test_resize_server__vtpm_to_no_vtpm(self):
372394 self .assertInstanceHasNoSecret (server )
373395
374396 def test_migrate_server (self ):
397+ """Test cold migrate as a non-admin user.
398+
399+ Cold migrate policy defaults to admin-only but this will not currently
400+ work as admin due to key manager service secret ownership.
401+ """
402+ # Allow non-admin to cold migrate a server.
403+ rules = {
404+ 'os_compute_api:os-migrate-server:migrate' : 'rule:admin_or_owner' }
405+ self .policy .set_rules (rules , overwrite = False )
406+
375407 for host in ('test_compute0' , 'test_compute1' ):
376408 self .start_compute (host )
377409
@@ -394,6 +426,39 @@ def test_migrate_server(self):
394426 # ensure nothing has changed
395427 self .assertInstanceHasSecret (server )
396428
429+ def test_migrate_server_as_admin (self ):
430+ """Test cold migrate as an admin user.
431+
432+ Cold migrate policy defaults to admin-only but this will not currently
433+ work as admin due to key manager service secret ownership.
434+ """
435+ for host in ('test_compute0' , 'test_compute1' ):
436+ self .start_compute (host )
437+
438+ # create a server with vTPM
439+ server = self ._create_server_with_vtpm ()
440+
441+ # ensure our instance's system_metadata field is correct
442+ self .assertInstanceHasSecret (server )
443+
444+ with mock .patch (
445+ 'nova.virt.libvirt.driver.LibvirtDriver'
446+ '.migrate_disk_and_power_off' , return_value = '{}' ,
447+ ):
448+ # cold migrate the server
449+ self ._migrate_server (
450+ server , expected_state = 'ERROR' , api = self .admin_api )
451+
452+ # Migration should have failed due to permissions error.
453+ # Need microversion 2.84 to get events.details field.
454+ with utils .temporary_mutation (self .admin_api , microversion = '2.84' ):
455+ event = self ._wait_for_instance_action_event (
456+ server , 'migrate' , 'compute_finish_resize' , 'Error' )
457+ msg = ('Key manager error: Forbidden: Secret payload retrieval '
458+ 'attempt not allowed - please review your user/project '
459+ 'privileges' )
460+ self .assertIn (msg , event ['details' ])
461+
397462 def test_live_migrate_server (self ):
398463 for host in ('test_compute0' , 'test_compute1' ):
399464 self .start_compute (host )
0 commit comments