i cannot believe i just wiped out an entire night's worth of work.
This commit is contained in:
45
bdisk/GPG.py
45
bdisk/GPG.py
@@ -5,6 +5,51 @@ import gpg.errors
|
||||
|
||||
# http://files.au.adversary.org/crypto/GPGMEpythonHOWTOen.html
|
||||
# https://www.gnupg.org/documentation/manuals/gpgme.pdf
|
||||
# Support ECC? https://www.gnupg.org/faq/whats-new-in-2.1.html#ecc
|
||||
# section 4.1, 4.2, 7.5.1, 7.5.5 in gpgme manual
|
||||
# Please select what kind of key you want:
|
||||
# (1) RSA and RSA (default) - 1024-4096 bits
|
||||
# (2) DSA and Elgamal - 768-3072
|
||||
# (3) DSA (sign only) - 768-3072
|
||||
# (4) RSA (sign only) - 1024-4096
|
||||
# (7) DSA (set your own capabilities) - 768-3072
|
||||
# (8) RSA (set your own capabilities) - 1024-4096
|
||||
# (9) ECC and ECC - (see below)
|
||||
# (10) ECC (sign only) - (see below)
|
||||
# (11) ECC (set your own capabilities) - (see below)
|
||||
# Your selection? 9
|
||||
# Please select which elliptic curve you want:
|
||||
# (2) NIST P-256
|
||||
# (3) NIST P-384
|
||||
# (4) NIST P-521
|
||||
# (5) Brainpool P-256
|
||||
# (6) Brainpool P-384
|
||||
# (7) Brainpool P-512
|
||||
# Your selection? 10
|
||||
# Please select which elliptic curve you want:
|
||||
# (1) Curve 25519
|
||||
# (3) NIST P-256
|
||||
# (4) NIST P-384
|
||||
# (5) NIST P-521
|
||||
# (6) Brainpool P-256
|
||||
# (7) Brainpool P-384
|
||||
# (8) Brainpool P-512
|
||||
# (9) secp256k1
|
||||
# gpgme key creation:
|
||||
#g = gpg.Context()
|
||||
#mainkey = g.create_key('test key via python <test2@test.com>', algorithm = 'rsa4096', expires = False,
|
||||
# #certify = True,
|
||||
# certify = False,
|
||||
# sign = False,
|
||||
# authenticate = False,
|
||||
# encrypt = False)
|
||||
#key = g.get_key(mainkey.fpr, secret = True)
|
||||
#subkey = g.create_subkey(key, algorithm = 'rsa4096', expires = False,
|
||||
# sign = True,
|
||||
# #certify = False,
|
||||
# encrypt = False,
|
||||
# authenticate = False)
|
||||
|
||||
|
||||
class GPGHandler(object):
|
||||
def __init__(self, gnupg_homedir = None, key_id = None, keyservers = None):
|
||||
|
||||
211
bdisk/confgen.py
211
bdisk/confgen.py
@@ -153,6 +153,8 @@ class ConfGenerator(object):
|
||||
self.get_iso()
|
||||
self.get_ipxe()
|
||||
self.get_pki()
|
||||
self.get_gpg()
|
||||
self.get_sync()
|
||||
except KeyboardInterrupt:
|
||||
exit('\n\nCaught KeyboardInterrupt; quitting...')
|
||||
return()
|
||||
@@ -296,8 +298,6 @@ class ConfGenerator(object):
|
||||
'\nWhat is YOUR name?\nName: ')).strip()
|
||||
meta_items['dev']['email'] = (input('\nWhat is your email address?'
|
||||
'\nemail: ')).strip()
|
||||
# TODO: this always returns invalid?? and doesn't seem to trigger
|
||||
# the redo
|
||||
if not valid.email(meta_items['dev']['email']):
|
||||
print('Invalid; skipping...')
|
||||
meta_items['dev']['email'] = None
|
||||
@@ -598,7 +598,10 @@ class ConfGenerator(object):
|
||||
build.attrib['its_full_of_stars'] = 'yes'
|
||||
print('\n++ BUILD || PATHS ++')
|
||||
# Thankfully, we can simplify a lot of this.
|
||||
_dir_strings = {'cache': ('the caching directory (used for temporary '
|
||||
_dir_strings = {'base': ('the base directory (used for files that are '
|
||||
'required for basic guest environment '
|
||||
'support)'),
|
||||
'cache': ('the caching directory (used for temporary '
|
||||
'files, temporary downloads, etc.)'),
|
||||
'chroot': ('the chroot directory (where we store '
|
||||
'the root filesystems that are converted '
|
||||
@@ -795,6 +798,208 @@ class ConfGenerator(object):
|
||||
self.profile.append(pki)
|
||||
return()
|
||||
|
||||
def get_gpg(self):
|
||||
_sigchk = False
|
||||
_xpaths = ['//iso/@sign', '//ipxe/@sign']
|
||||
for x in _xpaths:
|
||||
_x = self.profile.xpath(x)
|
||||
for a in _x:
|
||||
if a == 'yes':
|
||||
_sigchk = True
|
||||
break
|
||||
if _sigchk:
|
||||
break
|
||||
if not _sigchk:
|
||||
# An empty gpg element signifies a blank configuration.
|
||||
lxml.etree.SubElement(self.profile, 'gpg')
|
||||
return()
|
||||
gpg = lxml.etree.Element('gpg')
|
||||
print('\n++ GPG ++')
|
||||
_gpg = None
|
||||
while not _gpg:
|
||||
print('\n++ GPG || KEY ID ++')
|
||||
_gpg = (input('\nYou have specified GPG signing for one or more '
|
||||
'components. If you have a key already, please '
|
||||
'enter the key ID here; otherwise if left blank, '
|
||||
'BDisk will generate one for you.\n'
|
||||
'Key ID: ')).upper().strip()
|
||||
if _gpg == '':
|
||||
_gpg = 'none'
|
||||
else:
|
||||
if not valid.gpgkeyID(_gpg):
|
||||
print('That is not a valid GPG key ID. Retrying.')
|
||||
continue
|
||||
gpg.attrib['keyid'] = _gpg
|
||||
print('\n++ GPG || GPG HOME DIRECTORY ++')
|
||||
_gpghome = None
|
||||
while not _gpghome:
|
||||
_gpghome = (input('\nWhat directory should be used for the GnuPG '
|
||||
'home directory? If left blank, BDisk will use '
|
||||
'the system default (first checking for an '
|
||||
'environment variable called GNUPGHOME, and '
|
||||
'then trying the built-in ~/.gnupg directory).'
|
||||
'\nGPG Home Directory: '))
|
||||
if _gpghome.strip() != '':
|
||||
gpg.attrib['gnupghome'] == _gpghome
|
||||
else:
|
||||
_gpghome = 'none'
|
||||
print('\n++ GPG || KEYSERVER PUSHING ++')
|
||||
_gpgpublish = prompt.confirm_or_no(prompt = (
|
||||
'\nWould you like to push the key to the SKS keyserver pool '
|
||||
'(making it much easier for end-users to look it up)?\n'),
|
||||
usage = ('{0} for yes, {1} for no...\n'))
|
||||
gpg.attrib['publish'] = ('yes' if _gpgpublish else 'no')
|
||||
print('\n++ GPG || PASSWORD HANDLING ++')
|
||||
_gpgpass_prompt = prompt.confirm_or_no(prompt = (
|
||||
'\nWould you like BDisk to prompt you for a passphrase? If not, '
|
||||
'you\'ll either have to include the passphrase in plaintext in '
|
||||
'the configuration (HIGHLY unrecommended) or use a blank '
|
||||
'passphrase (also HIGHLY unrecommended).\n'),
|
||||
usage = ('{0} for yes, {1} for no...\n'))
|
||||
gpg.attrib['prompt_passphrase'] = ('yes' if _gpgpass_prompt else 'no')
|
||||
_pass = None
|
||||
if not _gpgpass_prompt:
|
||||
while not _pass:
|
||||
print('\n++ GPG || PASSPHRASE ++')
|
||||
_pass = getpass.getpass((
|
||||
'\nYou have specified not to use passphrase prompting for '
|
||||
'GPG. As such, you will need to provide the passphrase. '
|
||||
'If left blank, BDisk will assume one is not/should not '
|
||||
'be set.\nPassphrase (will NOT echo back; type '
|
||||
'carefully!): '))
|
||||
if _pass.strip() == '':
|
||||
_pass = 'none'
|
||||
elif not valid.password(_pass):
|
||||
print('As a safety precaution, we are refusing to use '
|
||||
'this password. It should entirely consist of the '
|
||||
'95 printable ASCII characters. Consult the '
|
||||
'manual\'s section on passwords for more '
|
||||
'information.\nLet\'s try this again, shall we?')
|
||||
_pass = None
|
||||
continue
|
||||
else:
|
||||
gpg.attrib['passphrase'] = _pass
|
||||
if gpg.attrib['keyid'] == 'none':
|
||||
_more_subkeys = True
|
||||
while _more_subkeys:
|
||||
_subkey = prompt.gpg_keygen_attribs()
|
||||
subkey = lxml.etree.SubElement(gpg, 'key')
|
||||
for a in _subkey['attribs']:
|
||||
subkey.attrib[a] = _subkey['attribs'][a]
|
||||
for e in _subkey['params']:
|
||||
param = lxml.etree.SubElement(subkey, e)
|
||||
param.text = _subkey['params'][e]
|
||||
_more_subkeys = prompt.confirm_or_no(prompt = (
|
||||
'\nDo you want to add another subkey?\n'),
|
||||
usage = ('{0} for yes, {1} for no...\n'))
|
||||
self.profile.append(gpg)
|
||||
return()
|
||||
|
||||
def get_sync(self):
|
||||
print('\n++ SYNC ++')
|
||||
print('This section will allow you to configure REMOTE paths to copy '
|
||||
'the finished products to. These are COPIES, meaning they will '
|
||||
'exist in the destination paths you specified earlier but will '
|
||||
'also be copied to these destinations. The difference is these '
|
||||
'can be on a remote host and will be copied via rsync.')
|
||||
_sync_chk = prompt.confirm_or_no(prompt = (
|
||||
'\nWould you like to enable remote syncing?\n'),
|
||||
usage = ('{0} for yes, {1} for no...\n'))
|
||||
if not _sync_chk:
|
||||
elem = lxml.etree.SubElement(self.profile, 'sync')
|
||||
rsync = lxml.etree.SubElement(elem, 'rsync')
|
||||
rsync.attrib['enabled'] = 'no'
|
||||
return()
|
||||
sync = lxml.etree.Element('sync')
|
||||
_syncs = {'ipxe': ('the iPXE base'),
|
||||
'tftp': ('the TFTP root'),
|
||||
'iso': ('the ISO images destination'),
|
||||
'gpg': ('the exported GPG public key')}
|
||||
for s in _syncs:
|
||||
print('\n++ SYNC || {0} ++'.format(s.upper()))
|
||||
_item_sync_chk = prompt.confirm_or_no(prompt = (
|
||||
'\nWould you like to sync {0}?\n'.format(_syncs[s])),
|
||||
usage = ('{0} for yes, {1} for no...\n'))
|
||||
elem = lxml.etree.SubElement(sync, s)
|
||||
elem.attrib['enabled'] = ('yes' if _item_sync_chk else 'no')
|
||||
if not _item_sync_chk:
|
||||
continue
|
||||
if s == 'gpg':
|
||||
_choices = ['ASCII', 'binary']
|
||||
_export_type = (input(
|
||||
('\nWhat type of export dump would you like to use '
|
||||
'for the GPG public key? (You can use the first '
|
||||
'letter only as an abbreviation; the default is '
|
||||
'ASCII.)\n'
|
||||
'Choices:\n\n\t{0}\n\nExport type: ').format(
|
||||
'\n\t'.join(_choices)
|
||||
))).strip().lower()
|
||||
if _export_type.startswith('a'):
|
||||
_export_type == 'asc'
|
||||
elif _export_type.startswith('b'):
|
||||
_export_type == 'bin'
|
||||
else:
|
||||
print('Using the default.')
|
||||
_export_type == 'asc'
|
||||
elem.attrib['format'] = _export_type
|
||||
_path = None
|
||||
while not _path:
|
||||
_path = input(
|
||||
('\nWhere (remote path) would you like {0} to be synced?\n'
|
||||
'Path: ').format(_syncs[s]))
|
||||
if _path.strip() == '':
|
||||
print('Please specify a path. Retrying...')
|
||||
_path = None
|
||||
continue
|
||||
elem.text = _path
|
||||
rsync = lxml.etree.SubElement(sync, 'rsync')
|
||||
print('\n++ SYNC || RSYNC ++')
|
||||
_rsync = prompt.confirm_or_no(prompt = (
|
||||
'\nEnable rsync? If disabled, no syncing would be done even if '
|
||||
'enabled by a specific item above.\n'),
|
||||
usage = ('{0} for yes, {1} for no...\n'))
|
||||
if not _rsync:
|
||||
self.profile.append(sync)
|
||||
return()
|
||||
_host = (input(
|
||||
'\nWhat host should we use for rsync?\nHost: ')
|
||||
).strip().lower()
|
||||
if ':' in _host:
|
||||
_h = _host.split(':')
|
||||
_host = _h[0]
|
||||
_port = _h[1]
|
||||
else:
|
||||
_port = None
|
||||
host = lxml.etree.SubElement(rsync, 'host')
|
||||
host.text = _host
|
||||
while not _port:
|
||||
_port = (input(
|
||||
'\nWhat port should we use? (Default: 22)\nPort: ')
|
||||
).strip()
|
||||
if _port == '':
|
||||
_port = '22'
|
||||
if not valid.integer(_port):
|
||||
print('Invalid port number; try again.')
|
||||
_port = None
|
||||
continue
|
||||
port = lxml.etree.SubElement(rsync, 'port')
|
||||
port.text = _port
|
||||
_user = (input(
|
||||
'\nWhat user should we use for {0}?\nUser: '.format(_host)
|
||||
)).strip()
|
||||
while not valid.username(_user):
|
||||
print('Invalid username.')
|
||||
_user = (input('\nUsername: ')).strip()
|
||||
user = lxml.etree.SubElement(rsync, 'user')
|
||||
user.text = _user
|
||||
_pubkey = input('\nWhat path should we use for the SSH private key? '
|
||||
'Default: ~/.ssh/id_rsa\n'
|
||||
'Key path: ')
|
||||
pubkey = lxml.etree.SubElement(rsync, 'pubkey')
|
||||
pubkey.text = _pubkey
|
||||
self.profile.append(sync)
|
||||
return()
|
||||
|
||||
def main():
|
||||
cg = ConfGenerator()
|
||||
cg.main()
|
||||
|
||||
@@ -37,8 +37,9 @@ class Conf(object):
|
||||
You can provide any combination of these
|
||||
(e.g. "profile={'id': 2, 'name' = 'some_profile'}").
|
||||
"""
|
||||
self.raw = _detect_cfg(cfg)
|
||||
self.profile = profile
|
||||
#self.raw = _detect_cfg(cfg) # no longer needed; in utils
|
||||
self.xml_suppl = utils.xml_supplicant(cfg, profile = profile)
|
||||
self.profile = self.xml_suppl
|
||||
self.xml = None
|
||||
self.profile = None
|
||||
# Mad props to https://stackoverflow.com/a/12728199/733214
|
||||
|
||||
Reference in New Issue
Block a user