adding XML/XSD support for hashtype attr

This commit is contained in:
brent s 2019-08-18 23:17:31 -04:00
parent 0c0f6ee81b
commit af732a1d64
3 changed files with 100 additions and 16 deletions

View File

@ -15,14 +15,15 @@ from lxml import etree


class BootSync(object):
def __init__(self, cfg = None, *args, **kwargs):
def __init__(self, cfg = None, validate = True, *args, **kwargs):
if not cfg:
self.cfgfile = '/etc/bootsync.xml'
else:
self.cfgfile = os.path.abspath(os.path.expanduser(cfg))
self.ns = '{http://git.square-r00t.net/OpTools/tree/sys/BootSync/}'
self.ns = None
self.cfg = None
self.xml = None
self.schema = None
# This is the current live kernel.
self.currentKernVer = self._getRunningKernel()
# This is the installed kernel from the package manager.
@ -33,15 +34,13 @@ class BootSync(object):
self.dummy_uuid = None
self.syncs = {}
##
self.getCfg()
self.getCfg(validate = validate)
self.chkMounts()
self.chkReboot()
self.getHashes()
self.getBlkids()
# self.sync()
# self.writeConfs()

def getCfg(self):
def getCfg(self, validate = True):
if not os.path.isfile(self.cfgfile):
raise FileNotFoundError('Configuration file {0} does not exist!'.format(self.cfgfile))
try:
@ -53,6 +52,19 @@ class BootSync(object):
# self.logger.error('{0} is invalid XML'.format(self.cfgfile))
raise ValueError(('{0} does not seem to be valid XML. '
'See sample.config.xml for an example configuration.').format(self.cfgfile))
self.ns = self.cfg.nsmap.get(None, 'http://git.square-r00t.net/OpTools/tree/sys/BootSync/')
self.ns = '{{{0}}}'.format(self.ns)
if validate:
if not self.schema:
from urllib.request import urlopen
xsi = self.cfg.nsmap.get('xsi', 'http://www.w3.org/2001/XMLSchema-instance')
schemaLocation = '{{{0}}}schemaLocation'.format(xsi)
schemaURL = self.cfg.attrib.get(schemaLocation,
('http://git.square-r00t.net/OpTools/plain/sys/BootSync/bootsync.xsd'))
with urlopen(schemaURL) as url:
self.schema = url.read()
self.schema = etree.XMLSchema(etree.XML(self.schema))
self.schema.assertValid(self.xml)
return()

def chkMounts(self):
@ -278,6 +290,11 @@ class BootSync(object):

def parseArgs():
args = argparse.ArgumentParser(description = ('Sync files to assist using mdadm RAID arrays with UEFI'))
args.add_argument('-V', '--no-validate',
dest = 'validate',
action = 'store_false',
help = ('If specified, do not attempt to validate the configuration file (-c/--cfg) against'
'its schema (otherwise it is fetched dynamically and requires network connection)'))
args.add_argument('-c', '--cfg',
dest = 'cfg',
default = '/etc/bootsync.xml',

View File

@ -24,6 +24,28 @@
</xs:restriction>
</xs:simpleType>

<xs:simpleType name="hashtype_choice">
<xs:restriction base="xs:string">
<xs:enumeration value="false"/>
<!-- https://docs.python.org/3/library/hashlib.html#hash-algorithms -->
<xs:enumeration value="md5"/><!-- Not available on FIPS-compliant Python -->
<xs:enumeration value="sha1"/>
<xs:enumeration value="sha224"/>
<xs:enumeration value="sha256"/>
<xs:enumeration value="sha384"/>
<xs:enumeration value="sha512"/>
<xs:enumeration value="blake2b"/>
<xs:enumeration value="blake2s"/>
<!-- The below are only available for more recent versions of system's OpenSSL. -->
<xs:enumeration value="sha3_224"/>
<xs:enumeration value="sha3_256"/>
<xs:enumeration value="sha3_384"/>
<xs:enumeration value="sha3_512"/>
<xs:enumeration value="shake_128"/>
<xs:enumeration value="shake_256"/>
</xs:restriction>
</xs:simpleType>

<xs:element name="bootsync">
<xs:complexType>
<xs:sequence>
@ -56,11 +78,14 @@
<xs:extension base="relpath">
<xs:attribute name="isKernel" type="xs:boolean"
use="optional" default="false"/>
<xs:attribute name="hashtype" type="hashtype_choice"
use="optional" default="md5"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="hashtype" type="hashtype_choice" use="optional" default="md5"/>
</xs:complexType>
<xs:unique name="filechk_unique">
<xs:selector xpath="bootsync:file"/>
@ -76,9 +101,12 @@
<xs:attribute name="target" type="relpath" use="required"/>
<!-- TODO: make this optional? -->
<xs:attribute name="pattern" type="xs:string" use="required"/>
<xs:attribute name="hashtype" type="hashtype_choice"
use="optional" default="md5"/>
</xs:complexType>
</xs:element>
</xs:sequence>
<xs:attribute name="hashtype" type="hashtype_choice" use="optional" default="md5"/>
</xs:complexType>
<xs:unique name="syncpath_unique_source">
<xs:selector xpath="bootsync:path"/>

View File

@ -17,33 +17,72 @@ SEE prep.txt FOR WHAT MUST BE DONE BEFORE RUNNING BOOTSYNC.
<part path="/dev/sdb1" mount="/mnt/boot1"/>
<part path="/dev/sdd1" mount="/mnt/boot2"/>
</partitions>
<!-- If specified, the files in this container are checksummed to determine if a sync is required.
If fileChecks isn't provided, we will always sync no matter what.
<!--
If fileChecks isn't provided, we will still sync syncPaths (which IS required).
These files will be evaluated to and synced to the ESP partitions with the same relative path.
i.e. (part[@mount])/(file)

fileChecks[@hashtype] attribute is used to checksum all <file> children,
unless they specify their own hashtype attribute (the default is hashtype="md5" for speed optimizations).

Guaranteed valid hashtype values are:

sha1
sha224
sha256
sha384
sha512
blake2b
blake2s

If you have a non-FIPS-compliant Python (you very most likely do), the following hashtype is also available:

md5

If you have a recent enough OpenSSL (and python3), you have the additional hashtype values available:

sha3_224
sha3_256
sha3_384
sha3_512
shake_128
shake_256

Additionally, the value "false" is always available to disable hashing for a specific <file> object or to set
the default. No checksumming will be done in this case and the file will always be overwritten on every run.

This is *highly not recommended* for solid-state disks or if you have large files you plan on syncing to
the alternate ESPs (e.g. loop-mounted ISO/IMG files).

"isKernel" is by default false, but if true it is treated specially to get a kernel version.
This is to notify you if a reboot is required. If no <file> item is isKernel="true", no reboot requirement
detection will be done.
Only *the last* <file> with isKernel="true" listed is used as the kernel identifier. -->
<fileChecks>
<!-- RELATIVE paths to /boot -->
Only *the last* <file> with isKernel="true" listed is used as the kernel identifier.
-->
<fileChecks hashtype="sha1">
<!-- RELATIVE paths to /boot on your / mount. -->
<file>initramfs-linux.img</file>
<file>intel-ucode.img</file>
<file>memtest86+/memtest.bin</file>
<file isKernel="true">vmlinuz-linux</file>
<file isKernel="true" hashtype="sha512">vmlinuz-linux</file>
</fileChecks>
<!-- These are system paths to sync to the ESPs. They are synced recursively.
<!--
These are system paths to sync to the ESPs. They are synced recursively.

"hashtype" is also supported for syncPaths and path objects just as the same with fileChecks
and file objects (see above).

"source" should be absolute.
"target" should be relative (to the ESP mount).
"pattern" is a regex to match to restrict *filenames* to sync. Use ".*" for all files. -->
<syncPaths>
"pattern" is a regex to match to restrict *filenames* to sync. Use ".*" for all files.
-->
<syncPaths hashtype="sha1">
<!-- For example, these are grub theme files. -->
<path source="/usr/share/grub/themes" target="grub/themes" pattern=".*"/>
<!-- These are grub modules - specifically, UEFI. -->
<path source="/usr/lib/grub/x86_64-efi" target="grub/x86_64-efi" pattern="^.*\.(mod|lst|sh)$"/>
<!-- And these are ISO files, in case you're like me and do loop mounting in Grub for rescue situations.
(e.g. https://wiki.archlinux.org/index.php/Multiboot_USB_drive#Using_GRUB_and_loopback_devices) -->
<path source="/boot/iso" target="iso" pattern="^.*\.(iso|img)$"/>
<path hashtype="false" source="/boot/iso" target="iso" pattern="^.*\.(iso|img)$"/>
</syncPaths>
</bootsync>