Get root password for build image from settings
Image building process reads from settings hashed version of root password and apply it instead of default. Conflicts: bareon/drivers/data/nailgun.py bareon/drivers/deploy/nailgun.py bareon/utils/utils.py fuel_agent/tests/test_manager.py Change-Id: Ibb614ddd1973c8fae25dae8217d207ffc92f1b15 Partial-Bug: #1537496 Depends-On: I2092bfca78fb721a8df3c8c6e4e6fd18e64ba353
This commit is contained in:
parent
b6f7ee6935
commit
10687832f2
@ -42,6 +42,7 @@ LOG = logging.getLogger(__name__)
|
|||||||
CONF = cfg.CONF
|
CONF = cfg.CONF
|
||||||
CONF.import_opt('prepare_configdrive', 'bareon.drivers.deploy.nailgun')
|
CONF.import_opt('prepare_configdrive', 'bareon.drivers.deploy.nailgun')
|
||||||
CONF.import_opt('config_drive_path', 'bareon.drivers.deploy.nailgun')
|
CONF.import_opt('config_drive_path', 'bareon.drivers.deploy.nailgun')
|
||||||
|
CONF.import_opt('default_root_password', 'bareon.drivers.deploy.nailgun')
|
||||||
|
|
||||||
|
|
||||||
def match_device(hu_disk, ks_disk):
|
def match_device(hu_disk, ks_disk):
|
||||||
@ -827,6 +828,21 @@ class NailgunBuildImage(BaseDataDriver,
|
|||||||
|
|
||||||
os = objects.Ubuntu(repos=repos, packages=packages, major=14, minor=4,
|
os = objects.Ubuntu(repos=repos, packages=packages, major=14, minor=4,
|
||||||
proxies=proxies)
|
proxies=proxies)
|
||||||
|
|
||||||
|
# add root account
|
||||||
|
root_password = self.data.get('root_password')
|
||||||
|
hashed_root_password = self.data.get('hashed_root_password')
|
||||||
|
|
||||||
|
# for backward compatibily set default password is no password provided
|
||||||
|
if root_password is None and hashed_root_password is None:
|
||||||
|
root_password = CONF.default_root_password
|
||||||
|
|
||||||
|
os.add_user_account(
|
||||||
|
name='root',
|
||||||
|
password=root_password,
|
||||||
|
homedir='/root',
|
||||||
|
hashed_password=hashed_root_password,
|
||||||
|
)
|
||||||
return os
|
return os
|
||||||
|
|
||||||
def parse_schemes(self):
|
def parse_schemes(self):
|
||||||
|
@ -108,6 +108,11 @@ opts = [
|
|||||||
default=True,
|
default=True,
|
||||||
help='Add udev rules for NIC remapping'
|
help='Add udev rules for NIC remapping'
|
||||||
),
|
),
|
||||||
|
cfg.StrOpt(
|
||||||
|
'default_root_password',
|
||||||
|
default='r00tme',
|
||||||
|
help='Default password for root user',
|
||||||
|
)
|
||||||
]
|
]
|
||||||
|
|
||||||
cli_opts = [
|
cli_opts = [
|
||||||
@ -492,7 +497,10 @@ class Manager(BaseDeployDriver):
|
|||||||
'etc/nailgun-agent/config.yaml'))
|
'etc/nailgun-agent/config.yaml'))
|
||||||
bu.append_lvm_devices_filter(chroot, CONF.multipath_lvm_filter,
|
bu.append_lvm_devices_filter(chroot, CONF.multipath_lvm_filter,
|
||||||
CONF.lvm_conf_path)
|
CONF.lvm_conf_path)
|
||||||
|
|
||||||
|
root = driver_os.get_user_by_name('root')
|
||||||
bu.do_post_inst(chroot,
|
bu.do_post_inst(chroot,
|
||||||
|
hashed_root_password=root.hashed_password,
|
||||||
allow_unsigned_file=CONF.allow_unsigned_file,
|
allow_unsigned_file=CONF.allow_unsigned_file,
|
||||||
force_ipv4_file=CONF.force_ipv4_file)
|
force_ipv4_file=CONF.force_ipv4_file)
|
||||||
# restore disabled hosts/resolv files
|
# restore disabled hosts/resolv files
|
||||||
@ -594,7 +602,9 @@ class Manager(BaseDeployDriver):
|
|||||||
attempts=CONF.fetch_packages_attempts)
|
attempts=CONF.fetch_packages_attempts)
|
||||||
|
|
||||||
LOG.debug('Post-install OS configuration')
|
LOG.debug('Post-install OS configuration')
|
||||||
|
root = driver_os.get_user_by_name('root')
|
||||||
bu.do_post_inst(chroot,
|
bu.do_post_inst(chroot,
|
||||||
|
hashed_root_password=root.hashed_password,
|
||||||
allow_unsigned_file=CONF.allow_unsigned_file,
|
allow_unsigned_file=CONF.allow_unsigned_file,
|
||||||
force_ipv4_file=CONF.force_ipv4_file)
|
force_ipv4_file=CONF.force_ipv4_file)
|
||||||
|
|
||||||
|
@ -28,6 +28,15 @@ class OperatingSystem(object):
|
|||||||
def add_user_account(self, **kwargs):
|
def add_user_account(self, **kwargs):
|
||||||
self.user_accounts.append(users.User(**kwargs))
|
self.user_accounts.append(users.User(**kwargs))
|
||||||
|
|
||||||
|
def get_user_by_name(self, name):
|
||||||
|
"""Get User object by name from user_accounts.
|
||||||
|
|
||||||
|
If there is no users with such name return None
|
||||||
|
"""
|
||||||
|
for user in self.user_accounts:
|
||||||
|
if user.name == name:
|
||||||
|
return user
|
||||||
|
|
||||||
def to_dict(self):
|
def to_dict(self):
|
||||||
return {'major': self.major,
|
return {'major': self.major,
|
||||||
'minor': self.minor,
|
'minor': self.minor,
|
||||||
|
@ -12,13 +12,28 @@
|
|||||||
# See the License for the specific language governing permissions and
|
# See the License for the specific language governing permissions and
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
|
import crypt
|
||||||
|
|
||||||
|
from bareon.utils import utils
|
||||||
|
|
||||||
|
|
||||||
class User(object):
|
class User(object):
|
||||||
def __init__(self, name, password, homedir, sudo=None, ssh_keys=None,
|
def __init__(self, name, password, homedir, sudo=None, ssh_keys=None,
|
||||||
shell="/bin/bash"):
|
shell="/bin/bash", hashed_password=None):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.password = password
|
self.password = password
|
||||||
self.homedir = homedir
|
self.homedir = homedir
|
||||||
self.sudo = sudo or []
|
self.sudo = sudo or []
|
||||||
self.ssh_keys = ssh_keys or []
|
self.ssh_keys = ssh_keys or []
|
||||||
self.shell = shell
|
self.shell = shell
|
||||||
|
self._hashed_password = hashed_password
|
||||||
|
|
||||||
|
@property
|
||||||
|
def hashed_password(self):
|
||||||
|
if self.password is None:
|
||||||
|
return self._hashed_password
|
||||||
|
|
||||||
|
if self._hashed_password is None:
|
||||||
|
self._hashed_password = crypt.crypt(self.password, utils.gensalt())
|
||||||
|
|
||||||
|
return self._hashed_password
|
||||||
|
@ -181,14 +181,22 @@ class BuildUtilsTestCase(unittest2.TestCase):
|
|||||||
mock_open):
|
mock_open):
|
||||||
mock_path.join.return_value = 'fake_path'
|
mock_path.join.return_value = 'fake_path'
|
||||||
mock_path.exists.return_value = True
|
mock_path.exists.return_value = True
|
||||||
bu.do_post_inst('chroot', allow_unsigned_file='fake_unsigned',
|
|
||||||
|
# crypt.crypt('qwerty')
|
||||||
|
password = ('$6$KyOsgFgf9cLbGNST$Ej0Usihfy7W/WT2H0z0mC1DapC/IUpA0jF'
|
||||||
|
'.Fs83mFIdkGYHL9IOYykRCjfssH.YL4lHbmrvOd/6TIfiyh1hDY1')
|
||||||
|
|
||||||
|
bu.do_post_inst('chroot',
|
||||||
|
hashed_root_password=password,
|
||||||
|
allow_unsigned_file='fake_unsigned',
|
||||||
force_ipv4_file='fake_force_ipv4')
|
force_ipv4_file='fake_force_ipv4')
|
||||||
file_handle_mock = mock_open.return_value.__enter__.return_value
|
file_handle_mock = mock_open.return_value.__enter__.return_value
|
||||||
file_handle_mock.write.assert_called_once_with('manual\n')
|
file_handle_mock.write.assert_called_once_with('manual\n')
|
||||||
mock_exec_expected_calls = [
|
mock_exec_expected_calls = [
|
||||||
mock.call('sed', '-i', 's%root:[\*,\!]%root:$6$IInX3Cqo$5xytL1VZb'
|
mock.call('sed',
|
||||||
'ZTusOewFnG6couuF0Ia61yS3rbC6P5YbZP2TYclwHqMq9e3Tg8rvQx'
|
'-i',
|
||||||
'hxSlBXP1DZhdUamxdOBXK0.%', 'fake_path'),
|
's%root:[\*,\!]%root:{}%'.format(password),
|
||||||
|
'fake_path'),
|
||||||
mock.call('chroot', 'chroot', 'update-rc.d', 'puppet', 'disable'),
|
mock.call('chroot', 'chroot', 'update-rc.d', 'puppet', 'disable'),
|
||||||
mock.call('chroot', 'chroot', 'dpkg-divert', '--local', '--add',
|
mock.call('chroot', 'chroot', 'dpkg-divert', '--local', '--add',
|
||||||
'fake_path'),
|
'fake_path'),
|
||||||
|
@ -40,6 +40,12 @@ class TestImageBuild(unittest2.TestCase):
|
|||||||
def setUp(self, mock_driver, mock_http, mock_yaml):
|
def setUp(self, mock_driver, mock_http, mock_yaml):
|
||||||
super(self.__class__, self).setUp()
|
super(self.__class__, self).setUp()
|
||||||
mock_driver.return_value = nailgun_data.NailgunBuildImage
|
mock_driver.return_value = nailgun_data.NailgunBuildImage
|
||||||
|
|
||||||
|
# TEST_ROOT_PASSWORD = crypt.crypt('qwerty')
|
||||||
|
self.TEST_ROOT_PASSWORD = ('$6$KyOsgFgf9cLbGNST$Ej0Usihfy7W/WT2H0z0mC'
|
||||||
|
'1DapC/IUpA0jF.Fs83mFIdkGYHL9IOYykRCjfssH.'
|
||||||
|
'YL4lHbmrvOd/6TIfiyh1hDY1')
|
||||||
|
|
||||||
image_conf = {
|
image_conf = {
|
||||||
"image_data": {
|
"image_data": {
|
||||||
"/": {
|
"/": {
|
||||||
@ -59,7 +65,8 @@ class TestImageBuild(unittest2.TestCase):
|
|||||||
'priority': 1001
|
'priority': 1001
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"codename": "trusty"
|
"codename": "trusty",
|
||||||
|
"hashed_root_password": self.TEST_ROOT_PASSWORD,
|
||||||
}
|
}
|
||||||
self.mgr = nailgun_deploy.Manager(
|
self.mgr = nailgun_deploy.Manager(
|
||||||
nailgun_data.NailgunBuildImage(image_conf))
|
nailgun_data.NailgunBuildImage(image_conf))
|
||||||
@ -99,7 +106,10 @@ class TestImageBuild(unittest2.TestCase):
|
|||||||
'trusty', 'fakesection', priority=None),
|
'trusty', 'fakesection', priority=None),
|
||||||
objects.DEBRepo('mos', 'http://fakemos',
|
objects.DEBRepo('mos', 'http://fakemos',
|
||||||
'mosX.Y', 'fakesection', priority=1000)],
|
'mosX.Y', 'fakesection', priority=1000)],
|
||||||
packages=['fakepackage1', 'fakepackage2'])
|
packages=['fakepackage1', 'fakepackage2'],
|
||||||
|
user_accounts=[
|
||||||
|
objects.User(name='root', password=None, homedir='/root',
|
||||||
|
hashed_password=self.TEST_ROOT_PASSWORD)])
|
||||||
self.mgr.driver.operating_system.proxies = objects.RepoProxies(
|
self.mgr.driver.operating_system.proxies = objects.RepoProxies(
|
||||||
proxies={'fake': 'fake'},
|
proxies={'fake': 'fake'},
|
||||||
direct_repo_addr_list='fake_addr')
|
direct_repo_addr_list='fake_addr')
|
||||||
@ -187,7 +197,9 @@ class TestImageBuild(unittest2.TestCase):
|
|||||||
'/tmp/imgdir', packages=['fakepackage1', 'fakepackage2'],
|
'/tmp/imgdir', packages=['fakepackage1', 'fakepackage2'],
|
||||||
attempts=CONF.fetch_packages_attempts)
|
attempts=CONF.fetch_packages_attempts)
|
||||||
mock_bu.do_post_inst.assert_called_once_with(
|
mock_bu.do_post_inst.assert_called_once_with(
|
||||||
'/tmp/imgdir', allow_unsigned_file=CONF.allow_unsigned_file,
|
'/tmp/imgdir',
|
||||||
|
hashed_root_password=self.TEST_ROOT_PASSWORD,
|
||||||
|
allow_unsigned_file=CONF.allow_unsigned_file,
|
||||||
force_ipv4_file=CONF.force_ipv4_file)
|
force_ipv4_file=CONF.force_ipv4_file)
|
||||||
|
|
||||||
signal_calls = mock_bu.stop_chrooted_processes.call_args_list
|
signal_calls = mock_bu.stop_chrooted_processes.call_args_list
|
||||||
|
@ -56,10 +56,6 @@ PROXY_PROTOCOLS = {
|
|||||||
ADDITIONAL_DEBOOTSTRAP_PACKAGES = ['ca-certificates',
|
ADDITIONAL_DEBOOTSTRAP_PACKAGES = ['ca-certificates',
|
||||||
'apt-transport-https']
|
'apt-transport-https']
|
||||||
|
|
||||||
# NOTE(agordeev): hardcoded to r00tme
|
|
||||||
ROOT_PASSWORD = '$6$IInX3Cqo$5xytL1VZbZTusOewFnG6couuF0Ia61yS3rbC6P5YbZP2TYcl'\
|
|
||||||
'wHqMq9e3Tg8rvQxhxSlBXP1DZhdUamxdOBXK0.'
|
|
||||||
|
|
||||||
|
|
||||||
def run_debootstrap(uri, suite, chroot, arch='amd64', eatmydata=False,
|
def run_debootstrap(uri, suite, chroot, arch='amd64', eatmydata=False,
|
||||||
attempts=10, proxies=None, direct_repo_addr=None):
|
attempts=10, proxies=None, direct_repo_addr=None):
|
||||||
@ -180,11 +176,12 @@ def clean_apt_settings(chroot, allow_unsigned_file='allow_unsigned_packages',
|
|||||||
clean_dirs(chroot, dirs)
|
clean_dirs(chroot, dirs)
|
||||||
|
|
||||||
|
|
||||||
def do_post_inst(chroot, allow_unsigned_file='allow_unsigned_packages',
|
def do_post_inst(chroot, hashed_root_password,
|
||||||
|
allow_unsigned_file='allow_unsigned_packages',
|
||||||
force_ipv4_file='force_ipv4'):
|
force_ipv4_file='force_ipv4'):
|
||||||
# NOTE(agordeev): set up password for root
|
# NOTE(agordeev): set up password for root
|
||||||
utils.execute('sed', '-i',
|
utils.execute('sed', '-i',
|
||||||
's%root:[\*,\!]%root:' + ROOT_PASSWORD + '%',
|
's%root:[\*,\!]%root:' + hashed_root_password + '%',
|
||||||
os.path.join(chroot, 'etc/shadow'))
|
os.path.join(chroot, 'etc/shadow'))
|
||||||
# NOTE(agordeev): backport from bash-script:
|
# NOTE(agordeev): backport from bash-script:
|
||||||
# in order to prevent the later puppet workflow outage, puppet service
|
# in order to prevent the later puppet workflow outage, puppet service
|
||||||
|
@ -18,9 +18,11 @@ import json
|
|||||||
import locale
|
import locale
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
|
import random as _random
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import socket
|
import socket
|
||||||
|
import string
|
||||||
import subprocess
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
@ -36,6 +38,7 @@ import urllib3
|
|||||||
from bareon import errors
|
from bareon import errors
|
||||||
from bareon.openstack.common import log as logging
|
from bareon.openstack.common import log as logging
|
||||||
|
|
||||||
|
random = _random.SystemRandom()
|
||||||
|
|
||||||
LOG = logging.getLogger(__name__)
|
LOG = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -419,6 +422,14 @@ def udevadm_settle():
|
|||||||
execute('udevadm', 'settle', check_exit_code=[0])
|
execute('udevadm', 'settle', check_exit_code=[0])
|
||||||
|
|
||||||
|
|
||||||
|
def gensalt():
|
||||||
|
"""Generate SHA-512 salt for crypt.crypt function."""
|
||||||
|
letters = string.ascii_letters + string.digits + './'
|
||||||
|
sha512prefix = "$6$"
|
||||||
|
random_letters = ''.join(random.choice(letters) for _ in range(16))
|
||||||
|
return sha512prefix + random_letters
|
||||||
|
|
||||||
|
|
||||||
def dict_diff(dict1, dict2, sfrom="from", sto="to"):
|
def dict_diff(dict1, dict2, sfrom="from", sto="to"):
|
||||||
j1 = json.dumps(dict1, indent=2)
|
j1 = json.dumps(dict1, indent=2)
|
||||||
j2 = json.dumps(dict2, indent=2)
|
j2 = json.dumps(dict2, indent=2)
|
||||||
|
@ -189,6 +189,13 @@ class BuildCommand(command.Command):
|
|||||||
" files",
|
" files",
|
||||||
action='append'
|
action='append'
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
'--root-password',
|
||||||
|
type=str,
|
||||||
|
help=("Root password for bootstrap image. PasswordAuthentication"
|
||||||
|
" by ssh still rejected by default! This password actual"
|
||||||
|
" only for tty login!"),
|
||||||
|
)
|
||||||
return parser
|
return parser
|
||||||
|
|
||||||
def take_action(self, parsed_args):
|
def take_action(self, parsed_args):
|
||||||
|
@ -116,3 +116,5 @@ active_bootstrap_symlink: "/var/www/nailgun/bootstraps/active_bootstrap"
|
|||||||
# "user": "admin"
|
# "user": "admin"
|
||||||
# "password": "admin"
|
# "password": "admin"
|
||||||
|
|
||||||
|
# User can provide default hashed root password for bootstrap image
|
||||||
|
# hashed_root_password: "$6$IInX3Cqo$5xytL1VZbZTusOewFnG6couuF0Ia61yS3rbC6P5YbZP2TYclwHqMq9e3Tg8rvQxhxSlBXP1DZhdUamxdOBXK0."
|
||||||
|
@ -72,6 +72,12 @@ class BootstrapDataBuilder(object):
|
|||||||
|
|
||||||
self.certs = data.get('certs')
|
self.certs = data.get('certs')
|
||||||
|
|
||||||
|
self.root_password = data.get('root_password')
|
||||||
|
self.hashed_root_password = None
|
||||||
|
|
||||||
|
if self.root_password is None:
|
||||||
|
self.hashed_root_password = CONF.hashed_root_password
|
||||||
|
|
||||||
def build(self):
|
def build(self):
|
||||||
return {
|
return {
|
||||||
'bootstrap': {
|
'bootstrap': {
|
||||||
@ -93,7 +99,9 @@ class BootstrapDataBuilder(object):
|
|||||||
'codename': self.ubuntu_release,
|
'codename': self.ubuntu_release,
|
||||||
'output': self.output,
|
'output': self.output,
|
||||||
'packages': self._get_packages(),
|
'packages': self._get_packages(),
|
||||||
'image_data': self._prepare_image_data()
|
'image_data': self._prepare_image_data(),
|
||||||
|
'hashed_root_password': self.hashed_root_password,
|
||||||
|
'root_password': self.root_password,
|
||||||
}
|
}
|
||||||
|
|
||||||
def _get_extra_dirs(self):
|
def _get_extra_dirs(self):
|
||||||
|
@ -209,3 +209,6 @@ log_file=/var/log/bareon.log
|
|||||||
#execute_retry_delay=2.0
|
#execute_retry_delay=2.0
|
||||||
|
|
||||||
|
|
||||||
|
# Default password for root user
|
||||||
|
# (string value)
|
||||||
|
default_root_password=r00tme
|
||||||
|
Loading…
Reference in New Issue
Block a user