updating sshsecure
This commit is contained in:
parent
55b61fab65
commit
58ee4cff4d
@ -345,6 +345,10 @@ def clientKeys(user = 'root'):
|
|||||||
return(pubkeys)
|
return(pubkeys)
|
||||||
|
|
||||||
def daemonMgr():
|
def daemonMgr():
|
||||||
|
# In case the script is running without sshd running.
|
||||||
|
pidfile = '/var/run/sshd.pid'
|
||||||
|
if not os.path.isfile(pidfile):
|
||||||
|
return()
|
||||||
# We're about to do somethin' stupid. Let's make it a teeny bit less stupid.
|
# We're about to do somethin' stupid. Let's make it a teeny bit less stupid.
|
||||||
with open(os.devnull, 'w') as devnull:
|
with open(os.devnull, 'w') as devnull:
|
||||||
confchk = subprocess.run(['sshd', '-T'], stdout = devnull)
|
confchk = subprocess.run(['sshd', '-T'], stdout = devnull)
|
||||||
@ -357,7 +361,6 @@ def daemonMgr():
|
|||||||
os.rename('{0}.{1}'.format(cf, tstamp),
|
os.rename('{0}.{1}'.format(cf, tstamp),
|
||||||
cf)
|
cf)
|
||||||
exit('OOPS. We goofed. Backup restored and bailing out.')
|
exit('OOPS. We goofed. Backup restored and bailing out.')
|
||||||
pidfile = '/var/run/sshd.pid'
|
|
||||||
# We need to restart sshd once we're done. I feel dirty doing this, but this is the most cross-platform way I can
|
# We need to restart sshd once we're done. I feel dirty doing this, but this is the most cross-platform way I can
|
||||||
# do it. First, we need the path to the PID file.
|
# do it. First, we need the path to the PID file.
|
||||||
# TODO: do some kind of better way of doing this.
|
# TODO: do some kind of better way of doing this.
|
||||||
|
288
arch/repo-maint.py
Executable file
288
arch/repo-maint.py
Executable file
@ -0,0 +1,288 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import io
|
||||||
|
import os
|
||||||
|
import pprint
|
||||||
|
import re
|
||||||
|
import sys
|
||||||
|
import tarfile
|
||||||
|
|
||||||
|
|
||||||
|
# PREREQS:
|
||||||
|
# Mostly stdlib.
|
||||||
|
#
|
||||||
|
# IF:
|
||||||
|
# 1.) You want to sign or verify packages (-s/--sign and -v/--verify, respectively),
|
||||||
|
# 2.) You want to work with delta updates,
|
||||||
|
# THEN:
|
||||||
|
# 1.) You need to install the python GnuPG GPGME bindings (the "gpg" module; NOT the "gpgme" module). They're
|
||||||
|
# distributed with the GPG source. They're also in PyPI (https://pypi.org/project/gpg/).
|
||||||
|
# 2.) You need to install the xdelta3 module (https://pypi.org/project/xdelta3/).
|
||||||
|
|
||||||
|
_delta_re = re.compile('(.*)-*-*_to*')
|
||||||
|
|
||||||
|
|
||||||
|
class RepoMaint(object):
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
# https://stackoverflow.com/a/2912884/733214
|
||||||
|
user_params = kwargs
|
||||||
|
# Define a set of defaults to update with kwargs since we
|
||||||
|
# aren't explicitly defining params.
|
||||||
|
self.args = {'color': True,
|
||||||
|
'db': './repo.db.tar.xz',
|
||||||
|
'key': None,
|
||||||
|
'pkgs': [],
|
||||||
|
'quiet': False,
|
||||||
|
'sign': False,
|
||||||
|
'verify': False}
|
||||||
|
self.args.update(user_params)
|
||||||
|
self.db_exts = {'db.tar': False, # No compression
|
||||||
|
'db.tar.xz': 'xz',
|
||||||
|
'db.tar.gz': 'gz',
|
||||||
|
'db.tar.bz2': 'bz2',
|
||||||
|
# We explicitly check False vs. None.
|
||||||
|
# For None, we do a custom check and wrap it.
|
||||||
|
# In .Z's case, we use the lzw module. It's the only non-stdlib compression
|
||||||
|
# that Arch Linux repo DB files support.
|
||||||
|
'db.tar.Z': None}
|
||||||
|
self.args['db'] = os.path.abspath(os.path.expanduser(self.args['db']))
|
||||||
|
self.db = None
|
||||||
|
_is_valid_repo_db = False
|
||||||
|
if not _is_valid_repo_db:
|
||||||
|
raise ValueError(('Repo DB {0} is not a valid DB type. '
|
||||||
|
'Must be one of {1}.').format(self.args['db'],
|
||||||
|
', '.join(['*.{0}'.format(i) for i in self.db_exts])))
|
||||||
|
self.repo_dir = os.path.dirname(self.args['db'])
|
||||||
|
self.lockfile = '{0}.lck'.format(self.args['db'])
|
||||||
|
os.makedirs(self.repo_dir, exist_ok = True)
|
||||||
|
self.gpg = None
|
||||||
|
self.sigkey = None
|
||||||
|
if self.args['sign'] or self.args['verify']:
|
||||||
|
# Set up GPG handler.
|
||||||
|
self._initGPG()
|
||||||
|
self._importDB()
|
||||||
|
|
||||||
|
def _initGPG(self):
|
||||||
|
import gpg
|
||||||
|
self.gpg = gpg.Context()
|
||||||
|
if self.args['sign']:
|
||||||
|
_seckeys = [k for k in self.gpg.keylist(secret = True) if k.can_sign]
|
||||||
|
if self.args['key']:
|
||||||
|
for k in _seckeys:
|
||||||
|
if self.sigkey:
|
||||||
|
break
|
||||||
|
for s in k.subkeys:
|
||||||
|
if self.sigkey:
|
||||||
|
break
|
||||||
|
if s.can_sign:
|
||||||
|
if self.args['key'].lower() in (s.keyid.lower(),
|
||||||
|
s.fpr.lower()):
|
||||||
|
self.sigkey = k
|
||||||
|
self.gpg.signers = [k]
|
||||||
|
else:
|
||||||
|
# Grab the first key that can sign.
|
||||||
|
if _seckeys:
|
||||||
|
self.sigkey = _seckeys[0]
|
||||||
|
self.gpg.signers = [_seckeys[0]]
|
||||||
|
if not self.args['quiet']:
|
||||||
|
print('Key ID not specified; using {0} as the default'.format(self.sigkey.fpr))
|
||||||
|
if not self.sigkey:
|
||||||
|
raise RuntimeError('Private key ID not found, cannot sign, or no secret keys exist.')
|
||||||
|
# TODO: confirm verifying works without a key
|
||||||
|
return()
|
||||||
|
|
||||||
|
def _LZWcompress(self, data):
|
||||||
|
# Based largely on:
|
||||||
|
# https://github.com/HugoPouliquen/lzw-tools/blob/master/utils/compression.py
|
||||||
|
data_arr = []
|
||||||
|
rawdata = io.BytesIO(data)
|
||||||
|
for i in range(int(len(data) / 2)):
|
||||||
|
data_arr.insert(i, rawdata.read(2))
|
||||||
|
w = bytes()
|
||||||
|
b_size = 256
|
||||||
|
b = []
|
||||||
|
compressed = io.BytesIO()
|
||||||
|
for c in data_arr:
|
||||||
|
c = c.to_bytes(2, 'big')
|
||||||
|
wc = w + c
|
||||||
|
if wc in b:
|
||||||
|
w = wc
|
||||||
|
else:
|
||||||
|
b.insert(b_size, wc)
|
||||||
|
compressed.write(b.index(wc).to_bytes(2, 'big'))
|
||||||
|
b_size += 1
|
||||||
|
w = c
|
||||||
|
return(compressed.getvalue())
|
||||||
|
|
||||||
|
def _LZWdecompress(self, data):
|
||||||
|
# Based largely on:
|
||||||
|
# https://github.com/HugoPouliquen/lzw-tools/blob/master/utils/decompression.py
|
||||||
|
b_size = 256
|
||||||
|
b = []
|
||||||
|
out = io.BytesIO()
|
||||||
|
for i in range(b_size):
|
||||||
|
b.insert(i, i.to_bytes(2, 'big'))
|
||||||
|
w = data.pop(0)
|
||||||
|
out.write(w)
|
||||||
|
i = 0
|
||||||
|
for byte in data:
|
||||||
|
x = int.from_bytes(byte, byteorder = 'big')
|
||||||
|
if x < b_size:
|
||||||
|
entry = b[x]
|
||||||
|
elif x == b_size:
|
||||||
|
entry = w + w
|
||||||
|
else:
|
||||||
|
raise ValueError('Bad uncompressed value for "{0}"'.format(byte))
|
||||||
|
for y in entry:
|
||||||
|
if i % 2 == 1:
|
||||||
|
out.write(y.to_bytes(1, byteorder = 'big'))
|
||||||
|
i += 1
|
||||||
|
b.insert(b_size, w + x)
|
||||||
|
b_size += 1
|
||||||
|
w = entry
|
||||||
|
return(out.getvalue())
|
||||||
|
|
||||||
|
def _importDB(self):
|
||||||
|
# Get the compression type.
|
||||||
|
for ct in self.db_exts:
|
||||||
|
if self.args['db'].lower().endswith(ct):
|
||||||
|
if self.db_exts[ct] == False:
|
||||||
|
if ct.endswith('.Z'): # Currently the only custom one.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def add(self):
|
||||||
|
# Fresh pkg set (in case the instance was re-used).
|
||||||
|
self.pkgs = {}
|
||||||
|
# First handle any wildcard
|
||||||
|
for p in self.args['pkgs'][:]:
|
||||||
|
if p.strip() == '*':
|
||||||
|
for root, dirs, files in os.walk(self.repo_dir):
|
||||||
|
for f in files:
|
||||||
|
abspath = os.path.join(root, f)
|
||||||
|
if f.endswith('.pkg.tar.xz'): # Recommended not to be changed per makepkg.conf
|
||||||
|
if abspath not in self.args['pkgs']:
|
||||||
|
self.args['pkgs'].append(abspath)
|
||||||
|
if self.args['delta']:
|
||||||
|
if f.endswith('.delta'):
|
||||||
|
if abspath not in self.args['pkgs']:
|
||||||
|
self.args['pkgs'].append(abspath)
|
||||||
|
self.args['pkgs'].remove(p)
|
||||||
|
# Then de-dupe and convert to full path.
|
||||||
|
self.args['pkgs'] = sorted(list(set([os.path.abspath(os.path.expanduser(d)) for d in self.args['pkgs']])))
|
||||||
|
for p in self.args['pkgs']:
|
||||||
|
pkgfnm = os.path.basename(p)
|
||||||
|
if p.endswith('.delta'):
|
||||||
|
pkgnm = _delta_re.sub('\g<1>', os.path.basename(pkgfnm))
|
||||||
|
|
||||||
|
return()
|
||||||
|
|
||||||
|
def remove(self):
|
||||||
|
for p in self.args['pkgs']:
|
||||||
|
pass
|
||||||
|
return()
|
||||||
|
|
||||||
|
|
||||||
|
def hatch():
|
||||||
|
import base64
|
||||||
|
import lzma
|
||||||
|
import random
|
||||||
|
h = ((
|
||||||
|
'/Td6WFoAAATm1rRGAgAhARwAAAAQz1jM4AB6AEtdABBok+MQCtEh'
|
||||||
|
'BisubEtc2ebacaLGrSRAMmHrcwUr39J24q4iODdNz7wfQl9e6I3C'
|
||||||
|
'ooyuOkptNISdo50CRdknGAU4JBBh+IQTkHwiAAAABW1d7drLmkUA'
|
||||||
|
'AWd7/+DtzR+2830BAAAAAARZWg=='
|
||||||
|
).encode('utf-8'),
|
||||||
|
(
|
||||||
|
'/Td6WFoAAATm1rRGAgAhARwAAAAQz1jM4AHEALtdABBpE/AVEKFC'
|
||||||
|
'fdT16ly2cCwT/MnXTY2D4r8nWgH6mLetLPn17nza3ZK+tSFU7d5j'
|
||||||
|
'my91M8fvPGu9Tf0NYkWlRU7vJM8r2V3kK/Gs6/GS7tq2qIum/C/X'
|
||||||
|
'sOnYUewVB2yMvlACqwp3gWJlmXSfwcpGiU662EmATS8kUgF+OdP+'
|
||||||
|
'EATXhM/1bAn07wJbVWPoAL2SBmJBo2zL1tXQklbQu1J20eWfd1bD'
|
||||||
|
'cgSBGqcU1/CdHnW6lcb6BmWKTg0p9IAAAEoEyN1gLkAMAAHXAcUD'
|
||||||
|
'AACXcduyscRn+wIAAAAABFla'
|
||||||
|
).encode('utf-8'))
|
||||||
|
h = lzma.decompress(base64.b64decode(h[random.randint(0, 1)]))
|
||||||
|
return(h.decode('utf-8'))
|
||||||
|
|
||||||
|
|
||||||
|
def parseArgs():
|
||||||
|
args = argparse.ArgumentParser(description = ('Python implementation of repo-add/repo-remove.'),
|
||||||
|
epilog = ('See https://wiki.archlinux.org/index.php/Pacman/'
|
||||||
|
'Tips_and_tricks#Custom_local_repository for more information.\n'
|
||||||
|
'Each operation has sub-help (e.g. "... add -h")'),
|
||||||
|
formatter_class = argparse.RawDescriptionHelpFormatter)
|
||||||
|
operargs = args.add_subparsers(dest = 'oper',
|
||||||
|
help = ('Operation to perform'))
|
||||||
|
commonargs = argparse.ArgumentParser(add_help = False)
|
||||||
|
commonargs.add_argument('db',
|
||||||
|
metavar = '</path/to/repository/repo.db.tar.xz>',
|
||||||
|
help = ('The path to the repository DB (required)'))
|
||||||
|
commonargs.add_argument('pkgs',
|
||||||
|
nargs = '+',
|
||||||
|
metavar = '<package|delta>',
|
||||||
|
help = ('Package filepath (for adding)/name (for removing) or delta; '
|
||||||
|
'can be specified multiple times (at least 1 required)'))
|
||||||
|
commonargs.add_argument('--nocolor',
|
||||||
|
dest = 'color',
|
||||||
|
action = 'store_false',
|
||||||
|
help = ('If specified, turn off color in output (currently does nothing; '
|
||||||
|
'output is currently not colorized)'))
|
||||||
|
commonargs.add_argument('-q', '--quiet',
|
||||||
|
dest = 'quiet',
|
||||||
|
action = 'store_true',
|
||||||
|
help = ('Minimize output'))
|
||||||
|
commonargs.add_argument('-s', '--sign',
|
||||||
|
dest = 'sign',
|
||||||
|
action = 'store_true',
|
||||||
|
help = ('If specified, sign database with GnuPG after update'))
|
||||||
|
commonargs.add_argument('-k', '--key',
|
||||||
|
metavar = 'KEY_ID',
|
||||||
|
nargs = 1,
|
||||||
|
help = ('Use the specified GPG key to sign the database '
|
||||||
|
'(only used if -s/--sign is active)'))
|
||||||
|
commonargs.add_argument('-v', '--verify',
|
||||||
|
dest = 'verify',
|
||||||
|
action = 'store_true',
|
||||||
|
help = ('If specified, verify the database\'s signature before update'))
|
||||||
|
addargs = operargs.add_parser('add',
|
||||||
|
parents = [commonargs],
|
||||||
|
help = ('Add package(s) to a repository'))
|
||||||
|
remargs = operargs.add_parser('remove',
|
||||||
|
parents = [commonargs],
|
||||||
|
help = ('Remove package(s) from a repository'))
|
||||||
|
addargs.add_argument('-d', '--delta',
|
||||||
|
dest = 'delta',
|
||||||
|
action = 'store_true',
|
||||||
|
help = ('If specified, generate and add package deltas for the update'))
|
||||||
|
addargs.add_argument('-n', '--new',
|
||||||
|
dest = 'new_only',
|
||||||
|
action = 'store_true',
|
||||||
|
help = ('If specified, only add packages that are not already in the database'))
|
||||||
|
addargs.add_argument('-R', '--remove',
|
||||||
|
dest = 'remove_old',
|
||||||
|
action = 'store_true',
|
||||||
|
help = ('If specified, remove old packages from disk after updating the database'))
|
||||||
|
# Removal args have no add'l arguments, just the common ones.
|
||||||
|
return(args)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
if (len(sys.argv) == 2) and (sys.argv[1] == 'elephant'):
|
||||||
|
print(hatch())
|
||||||
|
return()
|
||||||
|
else:
|
||||||
|
rawargs = parseArgs()
|
||||||
|
args = rawargs.parse_args()
|
||||||
|
if not args.oper:
|
||||||
|
rawargs.print_help()
|
||||||
|
exit()
|
||||||
|
rm = RepoMaint(**vars(args))
|
||||||
|
if args.oper == 'add':
|
||||||
|
rm.add()
|
||||||
|
elif args.oper == 'remove':
|
||||||
|
rm.remove()
|
||||||
|
return()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
Loading…
Reference in New Issue
Block a user