Add GrubPassword module to the fuelmenu
This change adds new GrubPassword module to the fuelmenu which can configure password for the editing grub menu. The module creates the default /boot/grub2/user.cfg file with hashed password only when it entered interactively. For security reasons the plain password never stored and the file always overwritten with new one provided. DocImpact Closes-Bug: #1552164 Change-Id: I3bc330133dd3d71ea62a7169a84d9ad802a4a3ef Signed-off-by: Maksim Malchuk <mmalchuk@mirantis.com>
This commit is contained in:
parent
a8c487954d
commit
1da4d4f45c
|
@ -19,6 +19,7 @@ from fuelmenu.modules.cobblerconf import CobblerConfig
|
|||
from fuelmenu.modules.dnsandhostname import DnsAndHostname
|
||||
from fuelmenu.modules.feature_groups import FeatureGroups
|
||||
from fuelmenu.modules.fueluser import FuelUser
|
||||
from fuelmenu.modules.grubpw import GrubPassword
|
||||
from fuelmenu.modules.interfaces import Interfaces
|
||||
from fuelmenu.modules.ntpsetup import NtpSetup
|
||||
from fuelmenu.modules.restore import Restore
|
||||
|
@ -40,6 +41,7 @@ __all__ = [
|
|||
BootstrapImage,
|
||||
NtpSetup,
|
||||
RootPassword,
|
||||
GrubPassword,
|
||||
FeatureGroups,
|
||||
Shell,
|
||||
Restore,
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/env python
|
||||
# Copyright 2016 Mirantis, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import logging
|
||||
import re
|
||||
import urwid
|
||||
|
||||
from fuelmenu.common import modulehelper as helper
|
||||
from fuelmenu.common import utils
|
||||
|
||||
log = logging.getLogger('fuelmenu.grubpw')
|
||||
|
||||
|
||||
class GrubPassword(urwid.WidgetWrap):
|
||||
def __init__(self, parent):
|
||||
self.name = "Grub Password"
|
||||
self.visible = True
|
||||
self.parent = parent
|
||||
|
||||
# UI text
|
||||
self.header_content = ["Set Grub password.", "",
|
||||
"Default user: root", ""]
|
||||
self.fields = ["PASSWORD", "CONFIRM_PASSWORD"]
|
||||
|
||||
self.defaults = {
|
||||
"PASSWORD": {"label": "Enter new password",
|
||||
"tooltip": "Use ASCII characters only",
|
||||
"value": ""},
|
||||
"CONFIRM_PASSWORD": {"label": "Confirm new password",
|
||||
"tooltip": "Use ASCII characters only",
|
||||
"value": ""},
|
||||
}
|
||||
|
||||
self.screen = None
|
||||
|
||||
def check(self, args):
|
||||
self.parent.footer.set_text("Checking data...")
|
||||
self.parent.refreshScreen()
|
||||
|
||||
responses = dict()
|
||||
for index, fieldname in enumerate(self.fields):
|
||||
if fieldname != helper.BLANK_KEY:
|
||||
responses[fieldname] = self.edits[index].get_edit_text()
|
||||
|
||||
password = responses["PASSWORD"]
|
||||
errors = []
|
||||
|
||||
# passwords must match
|
||||
if password != responses["CONFIRM_PASSWORD"]:
|
||||
errors.append("Passwords do not match.")
|
||||
|
||||
# password needs to be in ASCII character set
|
||||
try:
|
||||
password.decode('ascii')
|
||||
except UnicodeDecodeError:
|
||||
errors.append("Password contains non-ASCII characters.")
|
||||
|
||||
if errors:
|
||||
self.parent.footer.set_text("Errors occurred.")
|
||||
log.error("Errors: %s %s", len(errors), errors)
|
||||
helper.ModuleHelper.display_failed_check_dialog(self, errors)
|
||||
return False
|
||||
|
||||
# check empty password
|
||||
if not password:
|
||||
self.parent.footer.set_text("Password is empty, "
|
||||
"no changes will be made.")
|
||||
log.warning("Empty password, skipping.")
|
||||
else:
|
||||
self.parent.footer.set_text("No errors found.")
|
||||
return password
|
||||
|
||||
def apply(self, args):
|
||||
password = self.check(args)
|
||||
if password is False:
|
||||
log.error("Check failed. Not applying")
|
||||
return False
|
||||
|
||||
if password:
|
||||
return self.save(password)
|
||||
return True
|
||||
|
||||
def save(self, password):
|
||||
if self.parent.save_only:
|
||||
# We shouldn't change root password in save_only mode
|
||||
return True
|
||||
|
||||
# there is no convinient way to create grub2 pbkdf password
|
||||
# grub2-mkpasswd-pbkdf2 can read input password only from stdin
|
||||
# FYI: grub2-setpassword is the bash script which can't be used
|
||||
# here because it blocks buffered input from stdin
|
||||
cmd = ["/usr/bin/grub2-mkpasswd-pbkdf2"]
|
||||
stdin = "{0}\n{0}".format(password)
|
||||
errcode, out, errout = utils.execute(cmd, stdin=stdin)
|
||||
|
||||
# parse grub2-mkpasswd-pbkdf2 output
|
||||
pbkdf2 = re.findall('grub.pbkdf2.*', out, re.M)
|
||||
|
||||
if errcode == 0 and pbkdf2 != []:
|
||||
grub2_password = "GRUB2_PASSWORD={0}".format(pbkdf2[0])
|
||||
log.info(grub2_password)
|
||||
|
||||
log.info("Creating new /boot/grub2/user.cfg file")
|
||||
# overwrite /boot/grub2/user.cfg file if exists
|
||||
with open("/boot/grub2/user.cfg", "w") as usercfg:
|
||||
usercfg.write(grub2_password)
|
||||
usercfg.write("\n")
|
||||
usercfg.close()
|
||||
|
||||
self.parent.footer.set_text("Changes applied successfully.")
|
||||
log.info("Grub password successfully set.")
|
||||
# Reset fields
|
||||
self.cancel(None)
|
||||
else:
|
||||
log.error("Command grub2-mkpasswd-pbkdf2 failed with an error:"
|
||||
"\"{0}\"".format(errout))
|
||||
self.parent.footer.set_text("Unable to apply changes. Check logs "
|
||||
"for more details.")
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def cancel(self, button):
|
||||
helper.ModuleHelper.cancel(self, button)
|
||||
|
||||
def refresh(self):
|
||||
pass
|
||||
|
||||
def screenUI(self):
|
||||
return helper.ModuleHelper.screenUI(self, self.header_content,
|
||||
self.fields, self.defaults)
|
Loading…
Reference in New Issue