Permit hashed passwords to be sent to IPA.
Change-Id: Icfbad84a5a456ea06876c4829400b2f626fb24fe Story: 2006777 Task: 37300
This commit is contained in:
parent
436d857593
commit
de90f54b9b
@ -35,14 +35,28 @@ class RescueExtension(base.BaseAgentExtension):
|
|||||||
allowed_chars = string.ascii_letters + string.digits
|
allowed_chars = string.ascii_letters + string.digits
|
||||||
return random.choice(allowed_chars) + random.choice(allowed_chars)
|
return random.choice(allowed_chars) + random.choice(allowed_chars)
|
||||||
|
|
||||||
def write_rescue_password(self, rescue_password=""):
|
def write_rescue_password(self, rescue_password="", hashed=False):
|
||||||
"""Write rescue password to a file for use after IPA exits.
|
"""Write rescue password to a file for use after IPA exits.
|
||||||
|
|
||||||
:param rescue_password: Rescue password.
|
:param rescue_password: Rescue password.
|
||||||
|
:param hashed: Boolean default False indicating if the password
|
||||||
|
being provided is hashed or not. This will be changed
|
||||||
|
in a future version of ironic.
|
||||||
"""
|
"""
|
||||||
|
# DEPRECATED(TheJulia): In a future version of the ramdisk, we need
|
||||||
|
# change the default such that a password is the default and that
|
||||||
|
# if it is not, then the operation fails. Providing a default and
|
||||||
|
# an override now that matches the present state allows us to
|
||||||
|
# maintain our n-1, n, and n+1 theoretical support. Change
|
||||||
|
# in the V or W cycles.
|
||||||
LOG.debug('Writing hashed rescue password to %s', PASSWORD_FILE)
|
LOG.debug('Writing hashed rescue password to %s', PASSWORD_FILE)
|
||||||
salt = self.make_salt()
|
password = str(rescue_password)
|
||||||
hashed_password = crypt.crypt(rescue_password, salt)
|
hashed_password = None
|
||||||
|
if hashed:
|
||||||
|
hashed_password = password
|
||||||
|
else:
|
||||||
|
salt = self.make_salt()
|
||||||
|
hashed_password = crypt.crypt(rescue_password, salt)
|
||||||
try:
|
try:
|
||||||
with open(PASSWORD_FILE, 'w') as f:
|
with open(PASSWORD_FILE, 'w') as f:
|
||||||
f.write(hashed_password)
|
f.write(hashed_password)
|
||||||
@ -53,9 +67,9 @@ class RescueExtension(base.BaseAgentExtension):
|
|||||||
raise IOError(msg)
|
raise IOError(msg)
|
||||||
|
|
||||||
@base.sync_command('finalize_rescue')
|
@base.sync_command('finalize_rescue')
|
||||||
def finalize_rescue(self, rescue_password=""):
|
def finalize_rescue(self, rescue_password="", hashed=False):
|
||||||
"""Sets the rescue password for the rescue user."""
|
"""Sets the rescue password for the rescue user."""
|
||||||
self.write_rescue_password(rescue_password)
|
self.write_rescue_password(rescue_password, hashed)
|
||||||
# IPA will terminate after the result of finalize_rescue is returned to
|
# IPA will terminate after the result of finalize_rescue is returned to
|
||||||
# ironic to avoid exposing the IPA API to a tenant or public network
|
# ironic to avoid exposing the IPA API to a tenant or public network
|
||||||
self.agent.serve_api = False
|
self.agent.serve_api = False
|
||||||
|
@ -66,6 +66,38 @@ class TestRescueExtension(test_base.BaseTestCase):
|
|||||||
IOError, self.agent_extension.write_rescue_password,
|
IOError, self.agent_extension.write_rescue_password,
|
||||||
'password')
|
'password')
|
||||||
|
|
||||||
|
@mock.patch('ironic_python_agent.extensions.rescue.crypt.crypt',
|
||||||
|
autospec=True)
|
||||||
|
@mock.patch('ironic_python_agent.extensions.rescue.RescueExtension.'
|
||||||
|
'make_salt', autospec=True)
|
||||||
|
def _write_password_hashed_test(self, password, mock_salt,
|
||||||
|
mock_crypt):
|
||||||
|
mock_open = mock.mock_open()
|
||||||
|
with mock.patch('ironic_python_agent.extensions.rescue.open',
|
||||||
|
mock_open):
|
||||||
|
self.agent_extension.write_rescue_password(password,
|
||||||
|
hashed=True)
|
||||||
|
self.assertFalse(mock_salt.called)
|
||||||
|
self.assertFalse(mock_crypt.called)
|
||||||
|
mock_open.assert_called_once_with(
|
||||||
|
'/etc/ipa-rescue-config/ipa-rescue-password', 'w')
|
||||||
|
file_handle = mock_open()
|
||||||
|
file_handle.write.assert_called_once_with(password)
|
||||||
|
|
||||||
|
def test_hashed_passwords(self):
|
||||||
|
# NOTE(TheJulia): Sort of redundant in that we're not actually
|
||||||
|
# verifying content here, but these are semi-realistic values
|
||||||
|
# that may be passed in, so best to just keep it regardless.
|
||||||
|
passwds = ['$1$1234567890234567890123456789001',
|
||||||
|
'$2a$012345678901234566789012345678901234567890123'
|
||||||
|
'45678901234',
|
||||||
|
'$5$1234567890123456789012345678901234567890123456'
|
||||||
|
'789012',
|
||||||
|
'$6$1234567890123456789012345678901234567890123456'
|
||||||
|
'7890123456789012345678901234567890123456789012345']
|
||||||
|
for passwd in passwds:
|
||||||
|
self._write_password_hashed_test(passwd)
|
||||||
|
|
||||||
@mock.patch('ironic_python_agent.extensions.rescue.RescueExtension.'
|
@mock.patch('ironic_python_agent.extensions.rescue.RescueExtension.'
|
||||||
'write_rescue_password', autospec=True)
|
'write_rescue_password', autospec=True)
|
||||||
def test_finalize_rescue(self, mock_write_rescue_password):
|
def test_finalize_rescue(self, mock_write_rescue_password):
|
||||||
@ -73,5 +105,5 @@ class TestRescueExtension(test_base.BaseTestCase):
|
|||||||
self.agent_extension.finalize_rescue(rescue_password='password')
|
self.agent_extension.finalize_rescue(rescue_password='password')
|
||||||
mock_write_rescue_password.assert_called_once_with(
|
mock_write_rescue_password.assert_called_once_with(
|
||||||
mock.ANY,
|
mock.ANY,
|
||||||
rescue_password='password')
|
rescue_password='password', hashed=False)
|
||||||
self.assertFalse(self.agent_extension.agent.serve_api)
|
self.assertFalse(self.agent_extension.agent.serve_api)
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
---
|
||||||
|
security:
|
||||||
|
- |
|
||||||
|
Enables pre-hashed passwords to be supplied to the ``rescue`` extension.
|
||||||
|
See `story 2006777 <https://storyboard.openstack.org/#!/story/2006777>`_
|
||||||
|
for more information.
|
Loading…
x
Reference in New Issue
Block a user