2
0
mirror of https://github.com/xcat2/confluent.git synced 2026-06-08 08:38:33 +00:00

Merge branch 'master' into async

Need to rework httpapi further for changes to the firmware staging.
This commit is contained in:
Jarrod Johnson
2025-01-08 14:23:52 -05:00
53 changed files with 1824 additions and 197 deletions
+2 -1
View File
@@ -961,8 +961,9 @@ def main():
except IOError:
pass
if powerstate is None or powertime < time.time() - 10: # Check powerstate every 10 seconds
if powerstate == None:
powerstate = True
powertime = time.time()
powerstate = True
check_power_state()
else:
currcommand = prompt()
+1 -1
View File
@@ -67,7 +67,7 @@ argparser.add_option('-n', '--numreadings', type='int',
argparser.add_option('-c', '--csv', action='store_true',
help='Output in CSV format')
argparser.add_option('-s', '--skipnumberless', action='store_true',
help='Output in CSV format')
help='Do not show non-numeric sensors')
(options, args) = argparser.parse_args()
repeatmode = False
if options.interval:
+6 -2
View File
@@ -171,7 +171,9 @@ class GroupedData(object):
self.byoutput[outdata])))
currout += '\n====================================\n'
currout += outdata
currout += '\n\n'
if currout[-1] != '\n':
currout += '\n'
currout += '\n'
output.write(currout)
output.flush()
@@ -211,7 +213,9 @@ class GroupedData(object):
else:
currout += '\n'.join(colordiff(modaloutput.split('\n'),
outdata.split('\n')))
currout += '\n\n'
if currout[-1] != '\n':
currout += '\n'
currout += '\n'
if reverse:
revoutput.append(currout)
else:
@@ -226,7 +226,11 @@ class WickedManager(object):
self.cfgbydev[devname] = currcfg
for cfg in open(ifcfg).read().splitlines():
cfg = cfg.split('#', 1)[0]
kv = ' '.join(shlex.split(cfg)).split('=', 1)
try:
kv = ' '.join(shlex.split(cfg)).split('=', 1)
except Exception:
# unparseable line, likely having something we can't handle
del self.cfgbydev[devname]
if len(kv) != 2:
continue
k, v = kv
@@ -348,7 +352,8 @@ class NetworkManager(object):
bondcfg[stg] = deats[stg]
if member in self.uuidbyname:
subprocess.check_call(['nmcli', 'c', 'del', self.uuidbyname[member]])
subprocess.check_call(['nmcli', 'c', 'add', 'type', 'bond-slave', 'master', team, 'con-name', member, 'connection.interface-name', member])
devtype = self.devtypes.get(member, 'bond-slave')
subprocess.check_call(['nmcli', 'c', 'add', 'type', devtype, 'master', team, 'con-name', member, 'connection.interface-name', member])
if bondcfg:
args = []
for parm in bondcfg:
@@ -10,6 +10,15 @@ for pubkey in /etc/ssh/ssh_host*key.pub; do
rm $certfile
confluentpython $confapiclient /confluent-api/self/sshcert $pubkey -o $certfile
done
if [ -d /etc/ssh/sshd_config.d/ -a ! -e /etc/ssh/sshd_config.d/90-confluent.conf ]; then
for cert in /etc/ssh/ssh*-cert.pub; do
echo HostCertificate $cert >> /etc/ssh/sshd_config.d/90-confluent.conf
done
echo HostbasedAuthentication yes >> /etc/ssh/sshd_config.d/90-confluent.conf
echo HostbasedUsesNameFromPacketOnly yes >> /etc/ssh/sshd_config.d/90-confluent.conf
echo IgnoreRhosts no >> /etc/ssh/sshd_config.d/90-confluent.conf
fi
TMPDIR=$(mktemp -d)
cd $TMPDIR
confluentpython $confapiclient /confluent-public/site/initramfs.tgz -o initramfs.tgz
@@ -28,11 +28,15 @@ This contains support utilities for enabling deployment of x86_64 architecture s
#cp start_root urlmount ../stateless-bin/
#cd ..
ln -s el8 el9
for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9; do
cp -a el8 el10
mv el10/initramfs/usr el10/initramfs/var
for os in rhvh4 el7 genesis el8 suse15 ubuntu18.04 ubuntu20.04 ubuntu22.04 ubuntu24.04 coreos el9 el10; do
mkdir ${os}out
cd ${os}out
if [ -d ../${os}bin ]; then
cp -a ../${os}bin/opt .
elif [ $os = el10 ]; then
cp -a ../el9bin/opt .
else
cp -a ../el8bin/opt .
fi
@@ -78,7 +82,7 @@ cp -a esxi7 esxi8
%install
mkdir -p %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
cp LICENSE %{buildroot}/opt/confluent/share/licenses/confluent_osdeploy/
for os in rhvh4 el7 el8 el9 genesis suse15 ubuntu20.04 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
for os in rhvh4 el7 el8 el9 el10 genesis suse15 ubuntu20.04 ubuntu18.04 ubuntu22.04 ubuntu24.04 esxi6 esxi7 esxi8 coreos; do
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
mkdir -p %{buildroot}/opt/confluent/lib/osdeploy/$os/profiles
cp ${os}out/addons.* %{buildroot}/opt/confluent/lib/osdeploy/$os/initramfs
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -65,7 +65,11 @@ def get_image_metadata(imgpath):
continue
yield md
else:
raise Exception('Installation from single part image not supported')
# plausible filesystem structure to apply to a nominally "diskless" image
yield {'mount': '/', 'filesystem': 'xfs', 'minsize': 39513563136, 'initsize': 954128662528, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/mapper/root', 'compressed_size': 27022069760}
yield {'mount': '/boot', 'filesystem': 'xfs', 'minsize': 232316928, 'initsize': 1006632960, 'flags': 'rw,seclabel,relatime,attr2,inode64,logbufs=8,logbsize=32k,noquota', 'device': '/dev/nvme1n1p2', 'compressed_size': 171462656}
yield {'mount': '/boot/efi', 'filesystem': 'vfat', 'minsize': 7835648, 'initsize': 627900416, 'flags': 'rw,relatime,fmask=0077,dmask=0077,codepage=437,iocharset=ascii,shortname=winnt,errors=remount-ro', 'device': '/dev/nvme1n1p1', 'compressed_size': 1576960}
#raise Exception('Installation from single part image not supported')
class PartedRunner():
def __init__(self, disk):
@@ -84,8 +88,17 @@ def fixup(rootdir, vols):
for vol in vols:
devbymount[vol['mount']] = vol['targetdisk']
fstabfile = os.path.join(rootdir, 'etc/fstab')
with open(fstabfile) as tfile:
fstab = tfile.read().split('\n')
if os.path.exists(fstabfile):
with open(fstabfile) as tfile:
fstab = tfile.read().split('\n')
else:
#diskless image, need to invent fstab
fstab = [
"#ORIGFSTAB#/dev/mapper/root# / xfs defaults 0 0",
"#ORIGFSTAB#UUID=aaf9e0f9-aa4d-4d74-9e75-3537620cfe23# /boot xfs defaults 0 0",
"#ORIGFSTAB#UUID=C21D-B881# /boot/efi vfat umask=0077,shortname=winnt 0 2",
"#ORIGFSTAB#/dev/mapper/swap# none swap defaults 0 0",
]
while not fstab[0]:
fstab = fstab[1:]
if os.path.exists(os.path.join(rootdir, '.autorelabel')):
@@ -135,8 +148,10 @@ def fixup(rootdir, vols):
newcfg = ifcfg.split('/')[-1]
newcfg = os.path.join(rootdir, 'etc/NetworkManager/system-connections/{0}'.format(newcfg))
shutil.copy2(ifcfg, newcfg)
shutil.rmtree(os.path.join(rootdir, 'etc/confluent/'))
shutil.copytree('/etc/confluent', os.path.join(rootdir, 'etc/confluent'))
rootconfluentdir = os.path.join(rootdir, 'etc/confluent/')
if os.path.exists(rootconfluentdir):
shutil.rmtree(rootconfluentdir)
shutil.copytree('/etc/confluent', rootconfluentdir)
if policy:
sys.stdout.write('Applying SELinux labeling...')
sys.stdout.flush()
@@ -191,8 +206,24 @@ def fixup(rootdir, vols):
else:
newcfgparts.append(cfgpart)
loadentout.write(' '.join(newcfgparts) + '\n')
with open(grubsyscfg) as defgrubin:
defgrub = defgrubin.read().split('\n')
if os.path.exists(grubsyscfg):
with open(grubsyscfg) as defgrubin:
defgrub = defgrubin.read().split('\n')
else:
defgrub = [
'GRUB_TIMEOUT=5',
'GRUB_DISTRIBUTOR="$(sed ' + "'s, release .*$,,g'" + ' /etc/system-release)"',
'GRUB_DEFAULT=saved',
'GRUB_DISABLE_SUBMENU=true',
'GRUB_TERMINAL=""',
'GRUB_SERIAL_COMMAND=""',
'GRUB_CMDLINE_LINUX="crashkernel=1G-4G:192M,4G-64G:256M,64G-:512M rd.lvm.lv=vg/root rd.lvm.lv=vg/swap"',
'GRUB_DISABLE_RECOVERY="true"',
'GRUB_ENABLE_BLSCFG=true',
]
if not os.path.exists(os.path.join(rootdir, "etc/kernel/cmdline")):
with open(os.path.join(rootdir, "etc/kernel/cmdline"), "w") as cmdlineout:
cmdlineout.write("root=/dev/mapper/localstorage-root rd.lvm.lv=localstorage/root")
with open(grubsyscfg, 'w') as defgrubout:
for gline in defgrub:
gline = gline.split()
@@ -217,6 +248,12 @@ def fixup(rootdir, vols):
grubcfg = grubcfg[:-1]
if len(grubcfg) == 1:
grubcfg = grubcfg[0]
elif not grubcfg:
grubcfg = '/boot/grub2/grub.cfg'
paths = glob.glob(os.path.join(rootdir, 'boot/efi/EFI/*'))
for path in paths:
with open(os.path.join(path, 'grub.cfg'), 'w') as stubgrubout:
stubgrubout.write("search --no-floppy --root-dev-only --fs-uuid --set=dev " + bootuuid + "\nset prefix=($dev)/grub2\nexport $prefix\nconfigfile $prefix/grub.cfg\n")
else:
for gcfg in grubcfg:
rgcfg = os.path.join(rootdir, gcfg[1:]) # gcfg has a leading / to get rid of
@@ -272,10 +309,19 @@ def fixup(rootdir, vols):
shimpath = subprocess.check_output(['find', os.path.join(rootdir, 'boot/efi'), '-name', 'shimx64.efi']).decode('utf8').strip()
shimpath = shimpath.replace(rootdir, '/').replace('/boot/efi', '').replace('//', '/').replace('/', '\\')
subprocess.check_call(['efibootmgr', '-c', '-d', targblock, '-l', shimpath, '--part', partnum])
try:
os.makedirs(os.path.join(rootdir, 'opt/confluent/bin'))
except Exception:
pass
shutil.copy2('/opt/confluent/bin/apiclient', os.path.join(rootdir, 'opt/confluent/bin/apiclient'))
#other network interfaces
def had_swap():
if not os.path.exists('/etc/fstab'):
# diskless source, assume swap
return True
with open('/etc/fstab') as tabfile:
tabs = tabfile.read().split('\n')
for tab in tabs:
@@ -440,6 +486,8 @@ def install_to_disk(imgpath):
subprocess.check_call(['mount', vol['targetdisk'], '/run/imginst/targ'])
source = vol['mount'].replace('/', '_')
source = '/run/imginst/sources/' + source
if not os.path.exists(source):
source = '/run/imginst/sources/_' + vol['mount']
blankfsstat = os.statvfs('/run/imginst/targ')
blankused = (blankfsstat.f_blocks - blankfsstat.f_bfree) * blankfsstat.f_bsize
sys.stdout.write('\nWriting {0}: '.format(vol['mount']))
@@ -41,6 +41,8 @@ echo "Port 22" >> /etc/ssh/sshd_config
echo 'Match LocalPort 22' >> /etc/ssh/sshd_config
echo ' ChrootDirectory /sysroot/run/imginst/targ' >> /etc/ssh/sshd_config
kill -HUP $(cat /run/sshd.pid)
cp /sysroot/etc/pki/ca-trust/source/anchors/* /sysroot/run/imginst/targ/etc/pki/ca-trust/source/anchors/
chroot /sysroot/run/imginst/targ update-ca-trust
chroot /sysroot/run/imginst/targ bash -c "source /etc/confluent/functions; run_remote post.sh"
chroot /sysroot bash -c "umount \$(tac /proc/mounts|awk '{print \$2}'|grep ^/run/imginst/targ)"
@@ -2,7 +2,10 @@
# This script is executed 'chrooted' into a cloned disk target before rebooting
#
if [ -f /etc/dracut.conf.d/diskless.conf ]; then
rm /etc/dracut.conf.d/diskless.conf
fi
for kver in /lib/modules/*; do kver=$(basename $kver); kernel-install add $kver /boot/vmlinuz-$kver; done
nodename=$(grep ^NODENAME /etc/confluent/confluent.info|awk '{print $2}')
confluent_apikey=$(cat /etc/confluent/confluent.apikey)
confluent_profile=$(grep ^profile: /etc/confluent/confluent.deploycfg|awk '{print $2}')
@@ -16,6 +19,7 @@ if [[ "$confluent_mgr" == *:* ]]; then
fi
export nodename confluent_mgr confluent_profile confluent_websrv
. /etc/confluent/functions
run_remote setupssh
mkdir -p /var/log/confluent
chmod 700 /var/log/confluent
exec >> /var/log/confluent/confluent-post.log
@@ -40,6 +44,9 @@ run_remote_parts post.d
# Induce execution of remote configuration, e.g. ansible plays in ansible/post.d/
run_remote_config post.d
# rebuild initrd, pick up new drivers if needed
dracut -f /boot/initramfs-$(uname -r).img $(uname -r)
curl -sf -X POST -d 'status: staged' -H "CONFLUENT_NODENAME: $nodename" -H "CONFLUENT_APIKEY: $confluent_apikey" https://$confluent_websrv/confluent-api/self/updatestatus
kill $logshowpid
@@ -1,3 +1,3 @@
label: Confluent installation of VMware ESXi %%VERSION%% Hypervisor
label: VMware ESXi %%VERSION%% Hypervisor
ostype: esxi
kernelargs: runweasel
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -140,4 +140,5 @@ mv /lib/modules/$(uname -r) /lib/modules/$(uname -r)-ramfs
ln -s /sysroot/lib/modules/$(uname -r) /lib/modules/
mv /lib/firmware /lib/firmware-ramfs
ln -s /sysroot/lib/firmware /lib/firmware
chroot /sysroot chkstat --system --set --noheader > /dev/null
exec /opt/confluent/bin/start_root
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -31,23 +31,26 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
MYGW=""
fi
MYNM=$(grep ^ipv4_netmask: $tcfg | awk '{print $2}')
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
ip addr add dev $NICGUESS $v4addr
if [ ! -z "$MYGW" ]; then
ip route add default via $MYGW
fi
for dsrv in $deploysrvs; do
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
deploysrvs=$dsrv
NIC=$NICGUESS
NIC=""
while [ -z "$NIC" ]; do
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
ip addr add dev $NICGUESS $v4addr
if [ ! -z "$MYGW" ]; then
ip route add default via $MYGW
fi
for dsrv in $deploysrvs; do
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
deploysrvs=$dsrv
NIC=$NICGUESS
break
fi
done
if [ -z "$NIC" ]; then
ip -4 a flush dev $NICGUESS
else
break
fi
done
if [ -z "$NIC" ]; then
ip -4 a flush dev $NICGUESS
else
break
fi
done
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
echo $NIC > /tmp/autodetectnic
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
@@ -31,23 +31,26 @@ if [ -e /dev/disk/by-label/CNFLNT_IDNT ]; then
MYGW=""
fi
MYNM=$(grep ^ipv4_netmask: $tcfg | awk '{print $2}')
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
ip addr add dev $NICGUESS $v4addr
if [ ! -z "$MYGW" ]; then
ip route add default via $MYGW
fi
for dsrv in $deploysrvs; do
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
deploysrvs=$dsrv
NIC=$NICGUESS
NIC=""
while [ -z "$NIC" ]; do
for NICGUESS in $(ip link|grep LOWER_UP|grep -v LOOPBACK|cut -d ' ' -f 2 | sed -e 's/:$//'); do
ip addr add dev $NICGUESS $v4addr
if [ ! -z "$MYGW" ]; then
ip route add default via $MYGW
fi
for dsrv in $deploysrvs; do
if openssl s_client -connect $dsrv:443 > /dev/null 2>&1; then
deploysrvs=$dsrv
NIC=$NICGUESS
break
fi
done
if [ -z "$NIC" ]; then
ip -4 a flush dev $NICGUESS
else
break
fi
done
if [ -z "$NIC" ]; then
ip -4 a flush dev $NICGUESS
else
break
fi
done
ipconfig -d $MYIP::$MYGW:$MYNM::$NIC
echo $NIC > /tmp/autodetectnic
@@ -1,3 +1,4 @@
#!/usr/bin/python3
import subprocess
import os
@@ -10,8 +11,9 @@ class DiskInfo(object):
self.path = None
self.model = ''
self.size = 0
self.driver = None
self.driver = ''
self.mdcontainer = ''
self.subsystype = ''
devnode = '/dev/{0}'.format(devname)
qprop = subprocess.check_output(
['udevadm', 'info', '--query=property', devnode])
@@ -46,7 +48,9 @@ class DiskInfo(object):
elif (k == 'DRIVERS' and not self.driver
and v not in ('"sd"', '""')):
self.driver = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer:
elif k == 'ATTRS{subsystype}':
self.subsystype = v.replace('"', '')
if not self.driver and 'imsm' not in self.mdcontainer and self.subsystype != 'nvm':
raise Exception("No driver detected")
if os.path.exists('/sys/block/{0}/size'.format(self.name)):
with open('/sys/block/{0}/size'.format(self.name), 'r') as sizesrc:
+11 -5
View File
@@ -215,13 +215,19 @@ async def create_certificate(keyout=None, certout=None, csrout=None):
longname = shortname # socket.getfqdn()
if not csrout:
await util.check_call(
'openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
keyout)
san = ['IP:{0}'.format(x) async for x in get_ip_addresses()]
['openssl', 'ecparam', '-name', 'secp384r1', '-genkey', '-out',
keyout])
ipaddrs = list(get_ip_addresses())
san = ['IP:{0}'.format(x) for x in ipaddrs]
# It is incorrect to put IP addresses as DNS type. However
# there exists non-compliant clients that fail with them as IP
san.extend(['DNS:{0}'.format(x) async for x in get_ip_addresses()])
san.append('DNS:{0}'.format(shortname))
# san.extend(['DNS:{0}'.format(x) for x in ipaddrs])
dnsnames = set(ipaddrs)
dnsnames.add(shortname)
for currip in ipaddrs:
dnsnames.add(socket.getnameinfo((currip, 0), 0)[0])
for currname in dnsnames:
san.append('DNS:{0}'.format(currname))
#san.append('DNS:{0}'.format(longname))
san = ','.join(san)
sslcfg = get_openssl_conf_location()
@@ -94,8 +94,8 @@ node = {
'considered a member'),
},
'type': {
'description': ('Classification of node as server or switch. By default a node is presumed to be a server.'),
'validvalues': ('switch', 'server'),
'description': ('The type of node. This may be switch, server, rackmount, dense, enclosure or not set to be generic.'),
'validvalues': ('switch', 'server', 'rackmount', 'dense', 'enclosure', ''),
},
'crypted.rootpassword': {
'description': 'The password of the local root password. '
@@ -2367,6 +2367,9 @@ class ConfigManager(object):
lidx = self._cfgstore['nodes'][node]['groups'].index(name)
self._cfgstore['nodes'][node]['groups'][lidx] = renamemap[name]
_mark_dirtykey('nodes', node, self.tenant)
for node in self._cfgstore['nodegroups'][renamemap[name]].get('nodes', []):
self._node_removed_from_group(node, name, {})
self._node_added_to_group(node, renamemap[name], {})
self._bg_sync_to_file()
+8 -6
View File
@@ -704,10 +704,11 @@ class ProxyConsole(object):
try:
remote = await collective.connect_to_collective(None, self.managerinfo['address'])
except Exception as e:
print(repr(e))
if _tracelog:
_tracelog.log(traceback.format_exc(), ltype=log.DataTypes.event, event=log.Events.stacktrace)
await asyncio.sleep(3)
if self.clisession:
await self.clisession.detach()
await self.clisession.detach(False)
await self.detachsession(None)
return
await tlvdata.recv(remote)
@@ -836,7 +837,7 @@ class ConsoleSession(object):
self._evt = None
self.reghdl = None
async def detach(self):
async def detach(self, reattach=True):
"""Handler for the console handler to detach so it can reattach,
currently to facilitate changing from one collective.manager to
another
@@ -844,9 +845,10 @@ class ConsoleSession(object):
:return:
"""
await self.conshdl.detachsession(self)
self.connect_session()
await self.conshdl.attachsession(self)
self.write = self.conshdl.write
if reattach:
self.connect_session()
await self.conshdl.attachsession(self)
self.write = self.conshdl.write
def got_data(self, data):
"""Receive data from console and buffer
+100 -2
View File
@@ -63,7 +63,10 @@ import msgpack
import os
import struct
import sys
import uuid
import yaml
import shutil
pluginmap = {}
dispatch_plugins = (b'ipmi', u'ipmi', b'redfish', u'redfish', b'tsmsol', u'tsmsol', b'geist', u'geist', b'deltapdu', u'deltapdu', b'eatonpdu', u'eatonpdu', b'affluent', u'affluent', b'cnos', u'cnos', b'enos', u'enos')
@@ -180,8 +183,9 @@ def _merge_dict(original, custom):
rootcollections = ['deployment/', 'discovery/', 'events/', 'networking/',
'noderange/', 'nodes/', 'nodegroups/', 'storage/', 'usergroups/' ,
'users/', 'uuid', 'version']
'noderange/', 'nodes/', 'nodegroups/', 'storage/', 'usergroups/',
'users/', 'uuid', 'version', 'staging/']
class PluginRoute(object):
@@ -1294,6 +1298,98 @@ def handle_discovery(pathcomponents, operation, configmanager, inputdata):
if pathcomponents[0] == 'detected':
pass
class Staging:
def __init__(self, user, uuid):
self.uuid_str = uuid
self.storage_folder = '/var/lib/confluent/client_assets/' + self.uuid_str
self.filename = None
self.user = user
self.base_folder = os.path.exists('/var/lib/confluent/client_assets/')
if not self.base_folder:
try:
os.mkdir('/var/lib/confluent/client_assets/')
except Exception as e:
raise OSError(str(e))
def getUUID(self):
return self.uuid_str
def get_push_url(self):
return 'staging/{0}/{1}'.format(self.user,self.uuid_str)
def create_directory(self):
try:
os.mkdir(self.storage_folder)
return True
except OSError as e:
raise exc.InvalidArgumentException(str(e))
def get_file_name(self):
stage_file = '{}/filename.txt'.format(self.storage_folder)
try:
with open(stage_file, 'r') as f:
filename = f.readline()
os.remove(stage_file)
return self.storage_folder + '/{}'.format(filename)
except FileNotFoundError:
file = None
return False
@staticmethod
def remove_directory(directory):
storage_folder = '/var/lib/confluent/client_assets/' + directory
if os.path.exists(storage_folder):
shutil.rmtree(storage_folder)
else:
raise FileNotFoundError
return directory
def handle_staging(pathcomponents, operation, configmanager, inputdata):
'''
e.g push_url: /confluent-api/staging/user/<unique_id>
'''
if operation == 'create':
if len(pathcomponents) == 1:
stage = Staging(inputdata['user'],str(uuid.uuid1()))
if stage.create_directory():
if 'filename' in inputdata:
data_file = stage.storage_folder + '/filename.txt'
with open(data_file, 'w') as f:
f.write(inputdata['filename'])
else:
raise Exception('Error: Missing filename arg')
push_url = stage.get_push_url()
yield msg.CreatedResource(push_url)
elif len(pathcomponents) == 3:
stage = Staging(pathcomponents[1], pathcomponents[2])
file = stage.get_file_name()
if 'filedata' in inputdata and file:
content_length = inputdata['content_length']
remaining_length = content_length
filedata = inputdata['filedata']
chunk_size = 16384
progress = 0.0
with open(file, 'wb') as f:
while remaining_length > 0:
progress = (1 - (remaining_length/content_length)) * 100
datachunk = filedata['wsgi.input'].read(min(chunk_size, remaining_length))
f.write(datachunk)
remaining_length -= len(datachunk)
eventlet.sleep(0)
yield msg.FileUploadProgress(progress)
yield msg.FileUploadProgress(100)
elif operation == 'delete':
if len(pathcomponents) == 3:
asset = Staging.remove_directory(pathcomponents[2])
yield msg.DeletedResource(asset)
else:
raise Exception("Invalid url")
async def handle_path(path, operation, configmanager, inputdata=None, autostrip=True):
"""Given a full path request, return an object.
@@ -1402,5 +1498,7 @@ async def handle_path(path, operation, configmanager, inputdata=None, autostrip=
elif pathcomponents[0] == 'discovery':
return handle_discovery(pathcomponents[1:], operation, configmanager,
inputdata)
elif pathcomponents[0] == 'staging':
return handle_staging(pathcomponents, operation, configmanager, inputdata)
else:
raise exc.NotFoundException()
@@ -76,6 +76,7 @@ import confluent.discovery.handlers.pxe as pxeh
import confluent.discovery.handlers.smm as smm
import confluent.discovery.handlers.xcc as xcc
import confluent.discovery.handlers.xcc3 as xcc3
import confluent.discovery.handlers.smm3 as smm3
import confluent.discovery.handlers.megarac as megarac
import confluent.exceptions as exc
import confluent.log as log
@@ -110,6 +111,7 @@ class nesteddict(dict):
nodehandlers = {
'service:lenovo-smm': smm,
'service:lenovo-smm2': smm,
'lenovo-smm3': smm3,
'lenovo-xcc': xcc,
'lenovo-xcc3': xcc3,
'megarac-bmc': megarac,
@@ -130,6 +132,7 @@ servicenames = {
'cumulus-switch': 'cumulus-switch',
'service:lenovo-smm': 'lenovo-smm',
'service:lenovo-smm2': 'lenovo-smm2',
'lenovo-smm3': 'lenovo-smm3',
'affluent-switch': 'affluent-switch',
'lenovo-xcc': 'lenovo-xcc',
'lenovo-xcc3': 'lenovo-xcc3',
@@ -147,6 +150,7 @@ servicebyname = {
'cumulus-switch': 'cumulus-switch',
'lenovo-smm': 'service:lenovo-smm',
'lenovo-smm2': 'service:lenovo-smm2',
'lenovo-smm3': 'lenovo-smm3',
'affluent-switch': 'affluent-switch',
'lenovo-xcc': 'lenovo-xcc',
'lenovo-xcc3': 'lenovo-xcc3',
@@ -31,7 +31,7 @@ async def get_host_interface_urls(wc, mginfo):
returls = []
hifurl = mginfo.get('HostInterfaces', {}).get('@odata.id', None)
if not hifurl:
return None
return []
hifinfo = await wc.grab_json_response(hifurl)
hifurls = hifinfo.get('Members', [])
for hifurl in hifurls:
@@ -111,7 +111,7 @@ class NodeHandler(generic.NodeHandler):
await self.target_account_url(wc))
acctinfo = acctinfo[0]
actypes = acctinfo['AccountTypes']
candidates = acctinfo['AccountTypes@Redfish.AllowableValues']
candidates = acctinfo.get('AccountTypes@Redfish.AllowableValues', [])
if 'IPMI' not in actypes and 'IPMI' in candidates:
actypes.append('IPMI')
acctupd = {
@@ -137,7 +137,14 @@ class NodeHandler(generic.NodeHandler):
rsp = json.loads(rsp)
currerr = rsp.get('error', {})
ecode = currerr.get('code', None)
if ecode.endswith('PasswordChangeRequired'):
if not ecode:
for msg in rsp['@Message.ExtendedInfo']:
if 'PasswordChangeRequired' in msg['MessageId']:
chgurl = msg['MessageArgs'][0]
break
else:
raise Exception("Failed to ascertain login failure reason")
elif ecode.endswith('PasswordChangeRequired'):
for einfo in currerr.get('@Message.ExtendedInfo', []):
if einfo.get('MessageId', None).endswith('PasswordChangeRequired'):
for msgarg in einfo.get('MessageArgs'):
@@ -0,0 +1,69 @@
# Copyright 2024 Lenovo
#
# 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 confluent.discovery.handlers.redfishbmc as redfishbmc
import eventlet.support.greendns
import confluent.util as util
webclient = eventlet.import_patched('pyghmi.util.webclient')
getaddrinfo = eventlet.support.greendns.getaddrinfo
class NodeHandler(redfishbmc.NodeHandler):
devname = 'SMM3'
def scan(self):
attrs = self.info.get('attributes', {})
mtm = attrs.get('enclosure-machinetype-model', None)
if mtm:
self.info['modelnumber'] = mtm.strip()
sn = attrs.get('enclosure-serial-number', None)
if sn:
self.info['serialnumber'] = sn.strip()
modelname = attrs.get('enclosure-component-name', None)
if modelname:
modelname = modelname.split(' MT:')[0]
self.info['modelname'] = modelname
def get_firmware_default_account_info(self):
return ('USERID', 'PASSW0RD')
def remote_nodecfg(nodename, cfm):
cfg = cfm.get_node_attributes(
nodename, 'hardwaremanagement.manager')
ipaddr = cfg.get(nodename, {}).get('hardwaremanagement.manager', {}).get(
'value', None)
ipaddr = ipaddr.split('/', 1)[0]
ipaddr = getaddrinfo(ipaddr, 0)[0][-1]
if not ipaddr:
raise Exception('Cannot remote configure a system without known '
'address')
info = {'addresses': [ipaddr]}
nh = NodeHandler(info, cfm)
nh.config(nodename)
if __name__ == '__main__':
import confluent.config.configmanager as cfm
c = cfm.ConfigManager(None)
import sys
info = {'addresses': [[sys.argv[1]]]}
print(repr(info))
testr = NodeHandler(info, c)
testr.config(sys.argv[2])
@@ -602,6 +602,7 @@ class NodeHandler(immhandler.NodeHandler):
'/redfish/v1/AccountService/Accounts/1',
updateinf, method='PATCH')
if targbmc and not targbmc.startswith('fe80::'):
attribsuffix = ''
newip = targbmc.split('/', 1)[0]
newipinfo = socket.getaddrinfo(newip, 0)[0]
newip = newipinfo[-1][0]
@@ -611,6 +612,25 @@ class NodeHandler(immhandler.NodeHandler):
newmask = netutil.cidr_to_mask(netconfig['prefix'])
currinfo = await wc.grab_json_response('/api/providers/logoninfo')
currip = currinfo.get('items', [{}])[0].get('ipv4_address', '')
curreth1 = wc.grab_json_response('/api/dataset/imm_ethernet')
if curreth1:
if self.ipaddr.startswith('fe80::'):
ipkey = 'ipv6_link_local_address'
elif '.' in self.ipaddr:
ipkey = 'ipv4_address'
else:
raise Exception('Non-Link-Local IPv6 TODO')
nic1ip = curreth1.get('items', [{}])[0].get(ipkey, None)
if nic1ip != self.ipaddr:
# check second nic instead
curreth2 = wc.grab_json_response('/api/dataset/imm_ethernet_2')
if curreth2 and curreth2.get('items', [{}])[0].get('if_second_port_exist', 0):
nic2ip = curreth2.get('items', [{}])[0].get(ipkey + '_2', None)
if nic2ip != self.ipaddr:
raise Exception("Unable to determine which NIC is active")
# ok, second nic is active, target it
currip = curreth2.get('items', [{}])[0].get("ipv4_address", None)
attribsuffix = '_2'
# do not change the ipv4_config if the current config looks right already
if currip != newip:
statargs = {
@@ -621,6 +641,10 @@ class NodeHandler(immhandler.NodeHandler):
statargs['ENET_IPv4GatewayIPAddr'] = netconfig['ipv4_gateway']
elif not netutil.address_is_local(newip):
raise exc.InvalidArgumentException('Will not remotely configure a device with no gateway')
if attribsuffix:
for currkey in list(statargs):
statargs[currkey + attribsuffix] = statargs[currkey]
del statargs[currkey]
netset, status = await wc.grab_json_response_with_status('/api/dataset', statargs)
print(repr(netset))
print(repr(status))
@@ -57,7 +57,7 @@ smsg = ('M-SEARCH * HTTP/1.1\r\n'
async def active_scan(handler, protocol=None):
known_peers = set([])
async for scanned in scan(['urn:dmtf-org:service:redfish-rest:1', 'urn::service:affluent']):
async for scanned in scan(['urn:dmtf-org:service:redfish-rest:1', 'urn::dmtf-org:service:redfish-rest:', 'urn::service:affluent']):
for addr in scanned['addresses']:
addr = addr[0:1] + addr[2:]
if addr in known_peers:
@@ -452,10 +452,10 @@ async def _find_service(service, target):
mya['enclosure-machinetype-model'] = [val]
yield peerdata[nid]
continue
if '/redfish/v1/' not in peerdata[nid].get('urls', ()) and '/redfish/v1' not in peerdata[nid].get('urls', ()):
continue
if '/DeviceDescription.json' in peerdata[nid]['urls']:
pooltargs.append(('/DeviceDescription.json', peerdata[nid], 'lenovo-xcc'))
elif '/redfish/v1/' not in peerdata[nid].get('urls', ()) and '/redfish/v1' not in peerdata[nid].get('urls', ()):
continue
else:
for targurl in peerdata[nid]['urls']:
if '/eth' in targurl and targurl.endswith('.xml'):
@@ -493,6 +493,12 @@ async def check_fish(urldata, port=443, verifycallback=None):
return None
if url == '/DeviceDescription.json':
if not peerinfo:
if data.get('services', None) == ['urn::dmtf-org:service:redfish-rest:']:
peerinfo = wc.grab_json_response('/redfish/v1/')
if peerinfo:
data['services'] = ['lenovo-smm3']
data['uuid'] = peerinfo['UUID'].lower()
return data
return None
try:
peerinfo = peerinfo[0]
@@ -509,6 +515,12 @@ async def check_fish(urldata, port=443, verifycallback=None):
data['services'] = ['lenovo-xcc'] if 'xcc-variant' not in peerinfo else ['lenovo-xcc' + peerinfo['xcc-variant']]
return data
except (IndexError, KeyError):
if 'type' in peerinfo and peerinfo['type'].lower() == 'lenovo-smm3':
del peerinfo['xcc-variant']
data['uuid'] = peerinfo['enclosure-uuid']
data['services'] = ['lenovo-smm3']
data['attributes'] = peerinfo
return data
return None
url = '/redfish/v1/'
peerinfo = await wc.grab_json_response('/redfish/v1/')
+72 -4
View File
@@ -44,6 +44,7 @@ import confluent.asynctlvdata as tlvdata
import confluent.util as util
import copy
import json
import os
import socket
import sys
import traceback
@@ -435,6 +436,8 @@ def websockify_data(data):
data = data.decode('utf8')
except UnicodeDecodeError:
data = data.decode('cp437')
except AttributeError: # already str
pass
data = u' ' + data
return data
@@ -641,7 +644,6 @@ async def resourcehandler(request):
rsp.content_type = mimetype
await rsp.prepare(request)
return rsp
try:
if 'Sec-WebSocket-Version' in request.headers:
return await wsock_handler(request)
@@ -667,6 +669,11 @@ async def resourcehandler_backend(req, make_response):
reqpath = req.rel_url.path
if reqpath.startswith('/self/'):
return await selfservice.handle_request(req, make_response, mimetype)
if reqpath == '/httpapi_initialized':
if (len(configmanager.ConfigManager(None).list_usergroups()) > 0
or len(configmanager.ConfigManager(None).list_users()) > 0):
return await make_response(mimetype, 200, "OK")
return await make_response(mimetype, 500, "No authorized users")
if reqpath.startswith('/boot/'):
request = reqpath.split('/')
if not request[0]:
@@ -688,14 +695,14 @@ async def resourcehandler_backend(req, make_response):
if not pprofile:
return await make_response(mimetype, 404, 'Not Found')
redir = '/confluent-public/os/{0}/boot.{1}'.format(pprofile, bootfile)
rsp = make_response(mimetype, 302, 'Found', {'Location': redir})
rsp = await make_response(mimetype, 302, 'Found', {'Location': redir})
return
if req.content_length:
if req.content_length and nat '/staging' in reqpath:
reqbody = await req.read()
reqtype = req.content_type
operation = opmap.get(req.method, None)
if not operation:
rsp = make_response(mimetype, 400, 'Bad Request')
rsp = await make_response(mimetype, 400, 'Bad Request')
await rsp.write(b'Unsupported method')
return rsp
querydict = _get_query_dict(req, reqbody, reqtype)
@@ -893,6 +900,67 @@ async def resourcehandler_backend(req, make_response):
return rsp
else: # no keys, but a session, means it's hooking to receive data
raise Exception("long polling console sessions are discontinued")
======
elif (operation == 'create' and ('/firmware/updates/active' in reqpath)):
if 'application/json' in reqtype:
if not isinstance(reqbody, str):
reqbody = reqbody.decode('utf8')
pbody = json.loads(reqbody)
args = pbody['args']
file_directory = '/var/lib/confluent/client_assets/{}'.format(args.split('/')[-1])
filepath = '{0}/{1}'.format(file_directory, os.listdir(file_directory)[0]) # TODO find a way to validate that the file is found and its the expected one
args_dict = {'filename': filepath}
noderrs = {}
nodeurls = {}
hdlr = pluginapi.handle_path(reqpath, operation, cfgmgr, args_dict)
for res in hdlr:
if isinstance(res, confluent.messages.CreatedResource):
watchurl = res.kvpairs['created']
currnode = watchurl.split('/')[1]
nodeurls[currnode] = '/' + watchurl
rsp = await make_response(mimetype, 200, 'OK', headers=headers)
await rsp.write(json.dumps({'data': nodeurls}))
return
elif (operation == 'create' and ('/staging' in reqpath)):
url = reqpath
args_dict = {}
content_length = int(req.content_length)
if content_length > 0 and (len(url.split('/')) > 2):
# check if the user and the url defined user are the same
if authorized['username'] == url.split('/')[2]:
args_dict.update({'filedata':env, 'content_length': content_length}) # TODO: replace env
hdlr = pluginapi.handle_path(url, operation, cfgmgr, args_dict)
for resp in hdlr:
if isinstance(resp, confluent.messages.FileUploadProgress):
if resp.kvpairs['progress']['value'] == 100:
progress = resp.kvpairs['progress']['value']
rsp = await make_response(mimetype, 200, 'OK', headers=headers)
await rsp.write(json.dumps({'data': 'done'}))
return
else:
rsp = await make_response(mimetype, 401 'Unauthorized', headers=headers)
await rsp.write(json.dumps({'data': 'You do not have permission to write to file'}))
return
elif len(url.split('/')) == 2:
reqbody = env['wsgi.input'].read(int(env['CONTENT_LENGTH'])) # TODO: replace env
reqtype = env['CONTENT_TYPE']
if not isinstance(reqbody, str):
reqbody = reqbody.decode('utf8')
pbody = json.loads(reqbody)
args = pbody['args']
args_dict.update({'filename': args, 'user': authorized['username']})
try:
args_dict.update({'bank': pbody['bank']})
except KeyError:
pass
hdlr = pluginapi.handle_path(url, operation, cfgmgr, args_dict)
for res in hdlr:
if isinstance(res, confluent.messages.CreatedResource):
stageurl = res.kvpairs['created']
rsp = await make_response(mimetype, 200, 'OK', headers=headers)
await rsp.write(json.dumps({'data': stageurl}))
return
else:
# normal request
url = reqpath
+12
View File
@@ -645,6 +645,18 @@ class SavedFile(ConfluentMessage):
self.myargs = (node, file)
self.kvpairs = {node: {'filename': file}}
class FileUploadProgress(ConfluentMessage):
readonly = True
def __init__(self, progress, name=None):
self.myargs = (progress)
self.stripped = False
self.notnode = name is None
if self.notnode:
self.kvpairs = {'progress': {'value': progress}}
else:
self.kvpairs = {name: {'progress': {'value': progress}}}
class InputAlertData(ConfluentMessage):
def __init__(self, path, inputdata, nodes=None):
+19 -3
View File
@@ -91,10 +91,13 @@ async def update_boot_esxi(profiledir, profile, label):
newbootcfg = ''
efibootcfg = ''
filesneeded = []
localabel = label
if 'installation of' not in localabel:
localabel = 'Confluent installation of {}'.format(localabel)
for cfgline in bootcfg:
if cfgline.startswith('title='):
newbootcfg += 'title={0}\n'.format(label)
efibootcfg += 'title={0}\n'.format(label)
newbootcfg += 'title={0}\n'.format(localabel)
efibootcfg += 'title={0}\n'.format(localabel)
elif cfgline.startswith('kernelopt='):
newbootcfg += 'kernelopt={0}\n'.format(kernelargs)
efibootcfg += 'kernelopt={0}\n'.format(kernelargs)
@@ -312,6 +315,7 @@ def check_alma(isoinfo):
ver = None
arch = None
cat = None
suffix = ""
for entry in isoinfo[0]:
if 'almalinux-release-8' in entry:
ver = entry.split('-')[2]
@@ -323,6 +327,12 @@ def check_alma(isoinfo):
arch = entry.split('.')[-2]
cat = 'el9'
break
elif 'almalinux-kitten-release-10' in entry:
ver = entry.split('-')[3]
arch = entry.split('.')[-2]
cat = 'el10'
suffix = '_kitten'
break
else:
return None
if arch == 'noarch' and '.discinfo' in isoinfo[1]:
@@ -330,7 +340,7 @@ def check_alma(isoinfo):
arch = prodinfo.split(b'\n')[2]
if not isinstance(arch, str):
arch = arch.decode('utf-8')
return {'name': 'alma-{0}-{1}'.format(ver, arch), 'method': EXTRACT, 'category': cat}
return {'name': 'alma{0}-{1}-{2}'.format(suffix, ver, arch), 'method': EXTRACT, 'category': cat}
def check_centos(isoinfo):
@@ -362,6 +372,12 @@ def check_centos(isoinfo):
cat = 'el9'
isstream = '_stream'
break
elif 'centos-stream-release-10' in entry:
ver = entry.split('-')[3]
arch = entry.split('.')[-2]
cat = 'el10'
isstream = '_stream'
break
elif 'centos-linux-release-8' in entry:
ver = entry.split('-')[3]
arch = entry.split('.')[-2]
@@ -104,6 +104,17 @@ class OpenBmcConsole(conapi.Console):
except asyncio.CancelledError:
pass
def recvdata(self):
while self.connected:
try:
pendingdata = self.ws.recv()
except websocket.WebSocketConnectionClosedException:
pendingdata = ''
if pendingdata == '':
self.datacallback(conapi.ConsoleEvent.Disconnect)
return
self.datacallback(pendingdata)
async def connect(self, callback):
self.datacallback = callback
@@ -134,7 +145,11 @@ class OpenBmcConsole(conapi.Console):
return
async def write(self, data):
await self.ws.send_str(data.decode())
try:
await self.ws.send_str(data.decode())
except Exception as e:
print(repr(e))
await self.datacallback(conapi.ConsoleEvent.Disconnect)
async def close(self):
if self.recvr:
@@ -0,0 +1,279 @@
# Copyright 2022 Lenovo
#
# 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 pyghmi.util.webclient as wc
import confluent.util as util
import confluent.messages as msg
import confluent.exceptions as exc
import eventlet.green.time as time
import eventlet
import eventlet.greenpool as greenpool
def simplify_name(name):
return name.lower().replace(' ', '_').replace('/', '-').replace('_-_', '-')
pdupool = greenpool.GreenPool(128)
_pduclients = {}
class EnlogicClient(object):
def __init__(self, pdu, configmanager):
self.node = pdu
self.configmanager = configmanager
self._token = None
self._wc = None
self.username = None
@property
def token(self):
if not self._token:
self._token = self.login(self.configmanager)
return self._token
@property
def wc(self):
if self._wc:
print("done cache")
return self._wc
print("loggin")
targcfg = self.configmanager.get_node_attributes(
self.node, ['hardwaremanagement.manager'], decrypt=True
)
targcfg = targcfg.get(self.node, {})
target = targcfg.get('hardwaremanagement.manager', {}).get('value', None)
if not target:
target = self.node
target = target.split('/', 1)[0]
cv = util.TLSCertVerifier(
self.configmanager, self.node, 'pubkeys.tls_hardwaremanager'
).verify_cert
self._wc = wc.SecureHTTPConnection(target, port=443, verifycallback=cv)
return self._wc
def grab_json_response(self, url, body=None):
rsp, status = self.wc.grab_json_response_with_status(url, body)
if status == 401:
self._token = None
if body and 'cookie' in body:
body['cookie'] = self.token
rsp, status = self.wc.grab_json_response_with_status(url, body)
if status < 300:
return rsp
return {}
def login(self, configmanager):
credcfg = configmanager.get_node_attributes(
self.node,
['secret.hardwaremanagementuser', 'secret.hardwaremanagementpassword'],
decrypt=True,
)
credcfg = credcfg.get(self.node, {})
username = credcfg.get('secret.hardwaremanagementuser', {}).get('value', None)
passwd = credcfg.get('secret.hardwaremanagementpassword', {}).get('value', None)
if not isinstance(username, str):
username = username.decode('utf8')
if not isinstance(passwd, str):
passwd = passwd.decode('utf8')
if not username or not passwd:
raise Exception('Missing username or password')
self.username = username
rsp = self.wc.grab_json_response(
'/xhrlogin.jsp',
{'username': username, 'password': passwd, 'cookie': 0}
)
print(repr(rsp))
self.authtoken = rsp['cookie']
self.wc.set_header('Authorization', self.authtoken)
return self.authtoken
def logout(self):
if self._token:
self.wc.grab_json_response(
'/xhrlogout.jsp',
{'timeout': 0, 'cookie': self._token},
)
self._token = None
def get_outlet(self, outlet):
rsp = self.grab_json_response(
'/xhroutgetgrid.jsp', {
'cookie': self.token,
'pduid': 1
})
outlets = rsp['outlet']
for olet in outlets:
if olet['id'] == int(outlet):
state = "on" if olet['powstat'] == 1 else "off"
return state
def set_outlet(self, outlet, state):
bitflags = 2**(int(outlet) - 1)
outlet1 = bitflags & (2**24-1)
outlet2 = bitflags >> 24
if state == 'off':
state = 0
elif state == 'on':
state = 1
else:
raise Exception("Unrecognized state " + repr(state))
request = {
'cookie': self.token,
'outlet1': outlet1,
'outlet2': outlet2,
'pduid': 1,
'powstat': state
}
rsp = self.grab_json_response('/xhroutpowstatset.jsp', request)
_sensors_by_node = {}
def read_sensors(element, node, configmanager):
category, name = element[-2:]
justnames = False
if len(element) == 3:
# just get names
category = name
name = 'all'
justnames = True
if category in ('leds, fans', 'temperature'):
return
if justnames:
yield msg.ChildCollection('total_energy')
yield msg.ChildCollection('total_apparent_power')
yield msg.ChildCollection('total_real_power')
return
sn = _sensors_by_node.get(node, None)
if not sn or sn[1] < time.time():
gc = get_client(node, configmanager)
adev = gc.grab_json_response('/energy_get', {'cookie': gc.token, 'end': 1, 'start': 1})
_sensors_by_node[node] = (adev, time.time() + 1)
sn = _sensors_by_node.get(node, None)
if sn:
sn = sn[0]
readings = [
{
'name': 'Total Energy',
'value': float(sn[0]['total_energy']) * 0.001,
'units': 'kWh',
'type': 'Energy',
},
{
'name': 'Total Real Power',
'value': float(sn[0]['active_power']),
'units': 'W',
'type': 'Power',
},
{
'name': 'Total Apparent Power',
'value': float(sn[0]['apparent_power']),
'units': 'W',
'type': 'Power',
},
]
yield msg.SensorReadings(readings, name=node)
return
def get_client(node, configmanager):
if node not in _pduclients:
_pduclients[node] = EnlogicClient(node, configmanager)
return _pduclients[node]
def get_outlet(element, node, configmanager):
gc = get_client(node, configmanager)
state = gc.get_outlet(element[-1])
return msg.PowerState(node=node, state=state)
def read_firmware(node, configmanager):
gc = get_client(node, configmanager)
adev = gc.grab_json_response('/xhrgetuserlist.jsp')
myversion = adev[0]['fwver']
yield msg.Firmware([{'PDU Firmware': {'version': myversion}}], node)
def read_inventory(element, node, configmanager):
_inventory = {}
inventory = {}
gc = get_client(node, configmanager)
adev = gc.grab_json_response('/sys_info_get', {
'cookie': gc.token, 'pduid': 1
})
inventory['present'] = True
inventory['name'] = 'PDU'
info = {}
info['Serial Number'] = adev['pdu'][0]['serial_number']
info['Product Name'] = adev['pdu'][0]['model']
info['Model'] = adev['pdu'][0]['part_number']
inventory['information'] = info
yield msg.KeyValueData({'inventory': [inventory]}, node)
def retrieve(nodes, element, configmanager, inputdata):
if 'outlets' in element:
gp = greenpool.GreenPile(pdupool)
for node in nodes:
gp.spawn(get_outlet, element, node, configmanager)
for res in gp:
yield res
return
elif element[0] == 'sensors':
gp = greenpool.GreenPile(pdupool)
for node in nodes:
gp.spawn(read_sensors, element, node, configmanager)
for rsp in gp:
for datum in rsp:
yield datum
return
elif '/'.join(element).startswith('inventory/firmware/all'):
gp = greenpool.GreenPile(pdupool)
for node in nodes:
gp.spawn(read_firmware, node, configmanager)
for rsp in gp:
for datum in rsp:
yield datum
elif '/'.join(element).startswith('inventory/hardware/all'):
gp = greenpool.GreenPile(pdupool)
for node in nodes:
gp.spawn(read_inventory, element, node, configmanager)
for rsp in gp:
for datum in rsp:
yield datum
else:
for node in nodes:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
return
def update(nodes, element, configmanager, inputdata):
if 'outlets' not in element:
for node in nodes:
yield msg.ConfluentResourceUnavailable(node, 'Not implemented')
return
for node in nodes:
gc = get_client(node, configmanager)
newstate = inputdata.powerstate(node)
gc.set_outlet(element[-1], newstate)
eventlet.sleep(1)
for res in retrieve(nodes, element, configmanager, inputdata):
yield res
@@ -1371,10 +1371,8 @@ class IpmiHandler:
def identify(self):
if 'update' == self.op:
identifystate = self.inputdata.inputbynode[self.node] == 'on'
if self.inputdata.inputbynode[self.node] == 'blink':
raise exc.InvalidArgumentException(
'"blink" is not supported with ipmi')
self.ipmicmd.set_identify(on=identifystate)
blinkstate = self.inputdata.inputbynode[self.node] == 'blink'
self.ipmicmd.set_identify(on=identifystate, blink=blinkstate)
self.output.put(msg.IdentifyState(
node=self.node, state=self.inputdata.inputbynode[self.node]))
return
+5 -1
View File
@@ -52,7 +52,7 @@ def listdump(input):
def get_extra_names(nodename, cfg, myip=None):
names = set([])
names = set(['127.0.0.1', '::1', 'localhost', 'localhost.localdomain'])
dnsinfo = cfg.get_node_attributes(nodename, ('dns.*', 'net.*hostname'))
dnsinfo = dnsinfo.get(nodename, {})
domain = dnsinfo.get('dns.domain', {}).get('value', None)
@@ -625,4 +625,8 @@ def get_cluster_list(nodename=None, cfg=None):
nodes.add(myname)
if domain and domain not in myname:
nodes.add('{0}.{1}'.format(myname, domain))
nodes.add('::1')
nodes.add('127.0.0.1')
nodes.add('localhost')
nodes.add('localhost.domain')
return nodes, domain
+19 -9
View File
@@ -16,26 +16,36 @@ _vinztoken = None
import socket
import aiohmi.util.webclient as webclient
startingup = False
# Handle the vinz VNC session
async def assure_vinz():
global _vinzfd
global _vinztoken
if _vinzfd is None:
_vinztoken = base64.b64encode(os.urandom(33), altchars=b'_-').decode()
os.environ['VINZ_TOKEN'] = _vinztoken
os.makedirs('/var/run/confluent/vinz/sessions', exist_ok=True)
_vinzfd = await asyncio.subprocess.create_subprocess_exec(
global startingup
while startingup:
await asyncio.sleep(0.5)
try:
startingup = True
if _vinzfd is None:
_vinztoken = base64.b64encode(os.urandom(33), altchars=b'_-').decode()
os.environ['VINZ_TOKEN'] = _vinztoken
os.makedirs('/var/run/confluent/vinz/sessions', exist_ok=True)
os.chmod('/var/run/confluent/vinz', 0o711)
os.chmod('/var/run/confluent/vinz/sessions', 0o711)
_vinzfd = await asyncio.subprocess.create_subprocess_exec(
'/opt/confluent/bin/vinz',
'-c', '/var/run/confluent/vinz/control',
'-w', '127.0.0.1:4007',
'-a', '/var/run/confluent/vinz/approval',
# vinz supports unix domain websocket, however apache reverse proxy is dicey that way in some versions
'-d', '/var/run/confluent/vinz/sessions')
while not os.path.exists('/var/run/confluent/vinz/control'):
await asyncio.sleep(0.5)
util.spawn(monitor_requests())
while not os.path.exists('/var/run/confluent/vinz/control'):
await asyncio.sleep(0.5)
util.spawn(monitor_requests)
finally:
startingup = False
_unix_by_nodename = {}
async def get_url(nodename, inputdata):
+292 -79
View File
@@ -1,110 +1,323 @@
import base64
import confluent.tlvdata as tlvdata
import confluent.util as util
import json
import pywarp
import pywarp.backends
import pywarp.credentials
import copy
import base64
import secrets, time
from typing import Any, Optional
from webauthn import (
generate_registration_options,
options_to_json,
generate_authentication_options,
)
from webauthn.helpers.structs import (
AuthenticatorSelectionCriteria,
UserVerificationRequirement,
)
from webauthn import verify_registration_response
from webauthn import verify_authentication_response
challenges = {}
class ConfluentBackend(pywarp.backends.CredentialStorageBackend):
def __init__(self, cfg):
self.cfg = cfg
CONFIG_MANAGER = None
def get_credential_ids_by_email(self, email):
if not isinstance(email, str):
email = email.decode('utf8')
authenticators = self.cfg.get_user(email).get('authenticators', {})
if not authenticators:
raise Exception('No authenticators found')
for cid in authenticators:
yield base64.b64decode(cid)
class Credential():
def __init__(self, id, signature_count, public_key):
self.id = id
self.signature_count = signature_count
self.credential_public_key = public_key
def get_credential_by_email_id(self, email, id):
if not isinstance(email, str):
email = email.decode('utf8')
authenticators = self.cfg.get_user(email).get('authenticators', {})
cid = base64.b64encode(id).decode('utf8')
pk = authenticators[cid]['cpk']
pk = base64.b64decode(pk)
return pywarp.credentials.Credential(credential_id=id, credential_public_key=pk)
class Challenge():
def __init__(self, request, id=None) -> None:
if id is None:
self.id = util.randomstring(16)
else:
self.id = id
self.request = request
def get_credential_by_email(self, email):
if not isinstance(email, str):
email = email.decode('utf8')
authenticators = self.cfg.get_user(email)
cid = list(authenticators)[0]
cred = authenticators[cid]
cid = base64.b64decode(cred['cid'])
cpk = base64.b64decode(cred['cpk'])
return pywarp.credentials.Credential(credential_id=cid, credential_public_key=cpk)
def _load_credentials(creds):
if creds is None:
return None
ret = copy.deepcopy(creds)
ret['credential_public_key'] = base64.b64decode(creds['credential_public_key'])
ret['id'] = base64.b64decode(creds['id'])
return ret
def save_credential_for_user(self, email, credential):
if not isinstance(email, str):
email = email.decode('utf8')
cid = base64.b64encode(credential.id).decode('utf8')
credential = {'cid': cid, 'cpk': base64.b64encode(bytes(credential.public_key)).decode('utf8')}
authenticators = self.cfg.get_user(email).get('authenticators', {})
authenticators[cid] = credential
self.cfg.set_user(email, {'authenticators': authenticators})
def _load_authenticators(authenticators):
ret = authenticators
if 'challenges' in ret:
if not ret['challenges'] is None:
ret['challenges']['request'] = base64.b64decode(ret['challenges']['request'])
if 'credentials' in ret:
ret['credentials'] = _load_credentials(ret['credentials'])
return ret
def save_challenge_for_user(self, email, challenge, type):
if not isinstance(email, str):
email = email.decode('utf8')
challenges[email] = challenge
class User():
def __init__(self, id, username, user_handle, challenge: Challenge = None, credential: Credential = None):
self.id = id
self.username = username
self.user_handle = user_handle
self.challenges = challenge
self.credentials = credential
def get_challenge_for_user(self, email, type):
if not isinstance(email, str):
email = email.decode('utf8')
return challenges[email]
def __parse_credentials(self):
if self.credentials:
credid = base64.b64encode(self.credentials.id).decode()
pubkey = base64.b64encode(self.credentials.credential_public_key).decode()
return {"id": credid, "signature_count": self.credentials.signature_count, "credential_public_key": pubkey}
def __parse_challenges(self):
if self.challenges:
request = base64.b64encode(self.challenges.request).decode()
return {"id": self.challenges.id, 'request': request}
@staticmethod
def seek_credential_by_id(credential_id):
"""
There certainly is a better way to do this but for now lets try the wrong way that works
"""
for username in CONFIG_MANAGER.list_users():
authenticators = CONFIG_MANAGER.get_user(username).get('authenticators', {})
authenticators = _load_authenticators(authenticators)
try:
credential = authenticators['credentials']
except KeyError:
continue
if "id" in credential.keys() and credential["id"] == credential_id:
#for now leaving signature count as None
return (Credential(id=credential["id"], signature_count=None, public_key=credential["credential_public_key"]), username)
return None
@staticmethod
def get_credential(credential_id, username):
if not isinstance(username, str):
username = username.decode('utf8')
authenticators = CONFIG_MANAGER.get_user(username).get('authenticators', {})
authenticators = _load_authenticators(authenticators)
credential = authenticators.get('credentials', None)
if credential is None:
return None
if credential_id is None:
return Credential(id=credential["id"], signature_count=credential["signature_count"], public_key=credential["credential_public_key"])
return None
@staticmethod
def get_challenge(username):
if not isinstance(username, str):
username = username.decode('utf8')
authuser = CONFIG_MANAGER.get_user(username)
if not authuser:
return None
authenticators = authuser.get('authenticators', {})
authenticators = _load_authenticators(authenticators)
challenge = authenticators['challenges']
return Challenge(request=challenge["request"], id=challenge["id"])
@staticmethod
def get(username):
challenges_return = None
credentials_return = None
if not CONFIG_MANAGER:
raise Exception('config manager is not set up')
if not isinstance(username, str):
username = username.decode('utf8')
userinfo = CONFIG_MANAGER.get_user(username)
try:
authenticators = CONFIG_MANAGER.get_user(username).get('authenticators', {})
except AttributeError:
return None
if userinfo is None:
return None
authenticators = _load_authenticators(authenticators)
b64authid = userinfo.get('webauthid', None)
if b64authid is None:
authid = None
else:
authid = base64.b64decode(b64authid)
challenge = authenticators.get("challenges", None)
if challenge:
challenges_return = Challenge(challenge['request'], id=challenge["id"])
credential = authenticators.get("credentials", None)
if credential:
credentials_return = (Credential(credential['id'], credential['signature_count'], credential["credential_public_key"]))
return User(id=None, username=username, user_handle=authid, challenge=challenges_return, credential=credentials_return)
def save(self):
authenticators = CONFIG_MANAGER.get_user(self.username).get('authenticators', {})
authenticators = _load_authenticators(authenticators)
authenticators['challenges'] = self.__parse_challenges() # Looks like the bigger the array we encounter problems changing to just save one challenge
authenticators['credentials'] = self.__parse_credentials()
CONFIG_MANAGER.set_user(self.username, {'authenticators': authenticators})
def add(self, item):
if isinstance(item, Challenge):
self.challenges = item
elif isinstance(item, Credential):
self.credentials = item
def update(self, item):
if isinstance(item, Challenge):
self.challenges = item
elif isinstance(item, Credential):
self.credentials = item
return
#raise Exception("Credential item not found")
def registration_request(username, cfg, APP_RELYING_PARTY):
user_model = User.get(username)
if user_model is None:
raise Exception("User not foud")
options = generate_registration_options(
rp_name=APP_RELYING_PARTY.name,
rp_id=APP_RELYING_PARTY.id,
user_id=user_model.user_handle,
user_name=username,
authenticator_selection=AuthenticatorSelectionCriteria(
user_verification=UserVerificationRequirement.REQUIRED,
),
)
challenge = Challenge(options.challenge)
user_model.add(challenge)
user_model.save()
options_json = options_to_json(options)
return options_json
def registration_response(request, username, APP_RELYING_PARTY, APP_ORIGIN):
challenge_model = User.get_challenge(username)
if not challenge_model:
raise Exception("Could not find challenge matching given id")
user_model = User.get(username)
if not user_model:
raise Exception("Invalid Username")
try:
registration_verification = verify_registration_response(
credential=request,
expected_challenge=challenge_model.request,
expected_rp_id=APP_RELYING_PARTY.id,
expected_origin=APP_ORIGIN,
require_user_verification=True,
)
except Exception as err:
raise Exception("Could not handle credential attestation")
credential = Credential(id=registration_verification.credential_id, signature_count=registration_verification.sign_count, public_key=registration_verification.credential_public_key)
user_model.add(credential)
user_model.save()
return {"verified": True}
def authentication_request(username, APP_RELYING_PARTY):
user_model = User.get(username)
if not user_model:
raise Exception("Invalid Username")
options = generate_authentication_options(
rp_id=APP_RELYING_PARTY.id,
user_verification=UserVerificationRequirement.REQUIRED,
)
challenge = Challenge(options.challenge)
user_model.add(challenge)
user_model.save()
opts = options_to_json(options)
return opts
def authentication_response(request, username, APP_RELYING_PARTY, APP_ORIGIN):
user_model = User.get(username)
if not user_model:
raise Exception("Invalid Username")
challenge_model = User.get_challenge(username)
if not challenge_model:
raise Exception("Could not find challenge matching given id")
credential_model = User.get_credential(credential_id=None, username=username)
if not credential_model:
raise Exception("No credential for user")
verification = verify_authentication_response(
credential=request,
expected_challenge=challenge_model.request,
expected_rp_id=APP_RELYING_PARTY.id,
expected_origin=APP_ORIGIN,
credential_public_key = credential_model.credential_public_key,
credential_current_sign_count = 0,
require_user_verification = True
)
return {"verified": True}
class RpEntity(object):
def __init__(self, name, id):
self.name = name
self.id = id
def handle_api_request(url, env, start_response, username, cfm, headers, reqbody, authorized):
"""
For now webauth is going to be limited to just one passkey per user
If you try to register a new passkey this will just clear the old one and register the new passkey
"""
global CONFIG_MANAGER
CONFIG_MANAGER = cfm
APP_ORIGIN = 'https://' + env['HTTP_X_FORWARDED_HOST']
HOST = env['HTTP_X_FORWARDED_HOST']
APP_RELYING_PARTY = RpEntity(name='Confluent Web UI', id=HOST)
if env['REQUEST_METHOD'] != 'POST':
raise Exception('Only POST supported for webauthn operations')
url = url.replace('/sessions/current/webauthn', '')
if url == '/registration_options':
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm), require_attestation=False)
userinfo = cfm.get_user(username)
if not userinfo:
cfm.create_user(username, role='Stub')
userinfo = cfm.get_user(username)
authid = userinfo.get('authid', None)
authid = userinfo.get('webauthid', None)
if not authid:
authid = util.randomstring(64)
cfm.set_user(username, {'authid': authid})
opts = rp.get_registration_options(username)
# pywarp generates an id derived
# from username, which is a 'must not' in the spec
# we replace that with a complying approach
opts['user']['id'] = authid
if 'icon' in opts['user']:
del opts['user']['icon']
if 'id' in opts['rp']:
del opts['rp']['id']
authid = secrets.token_bytes(64)
b64authid = base64.b64encode(authid).decode()
cfm.set_user(username, {'webauthid': b64authid})
opts = registration_request(username, cfm, APP_RELYING_PARTY)
start_response('200 OK', headers)
yield json.dumps(opts)
yield opts
elif url.startswith('/registered_credentials/'):
username = url.rsplit('/', 1)[-1]
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm))
userinfo = cfm.get_user(username)
if not isinstance(username, bytes):
username = username.encode('utf8')
opts = rp.get_authentication_options(username)
opts['challenge'] = base64.b64encode(opts['challenge']).decode('utf8')
opts = authentication_request(username, APP_RELYING_PARTY)
start_response('200 OK', headers)
yield json.dumps(opts)
yield opts
elif url.startswith('/validate/'):
username = url.rsplit('/', 1)[-1]
userinfo = cfm.get_user(username)
if not isinstance(username, bytes):
username = username.encode('utf8')
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm))
req = json.loads(reqbody)
for x in req:
req[x] = base64.b64decode(req[x].replace('-', '+').replace('_', '/'))
req['email'] = username
rsp = rp.verify(**req)
if start_response:
rsp = authentication_response(req, username, APP_RELYING_PARTY, APP_ORIGIN)
if rsp == 'Timeout':
start_response('408 Timeout', headers)
elif rsp['verified'] and start_response:
start_response('200 OK', headers)
sessinfo = {'username': username}
if 'authtoken' in authorized:
@@ -116,13 +329,13 @@ def handle_api_request(url, env, start_response, username, cfm, headers, reqbody
else:
yield rsp
elif url == '/register_credential':
rp = pywarp.RelyingPartyManager('Confluent Web UI', credential_storage_backend=ConfluentBackend(cfm), require_attestation=False)
req = json.loads(reqbody)
for x in req:
req[x] = base64.b64decode(req[x].replace('-', '+').replace('_', '/'))
userinfo = cfm.get_user(username)
if not isinstance(username, bytes):
username = username.encode('utf8')
req['email'] = username
rsp = rp.register(**req)
start_response('200 OK', headers)
yield json.dumps(rsp)
rsp = registration_response(req, username, APP_RELYING_PARTY, APP_ORIGIN)
if rsp.get('verified', False):
start_response('200 OK', headers)
yield json.dumps({'status': 'Success'})
+5 -3
View File
@@ -18,15 +18,17 @@ Prefix: %{_prefix}
BuildArch: noarch
Requires: confluent_vtbufferd
%if "%{dist}" == ".el7"
Requires: python-pyghmi >= 1.5.71, python-eventlet, python-greenlet, python-pycryptodomex >= 3.4.7, confluent_client == %{version}, python-pyparsing, python-paramiko, python-dnspython, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-lxml, python-eficompressor, python-setuptools, python-dateutil, python-websocket-client python2-msgpack python-libarchive-c python-yaml python-monotonic cpio
Requires: python-pyghmi >= 1.5.71, python-eventlet, python-greenlet, python-pycryptodomex >= 3.4.7, confluent_client == %{version}, python-pyparsing, python-paramiko, python-dnspython, python-netifaces, python2-pyasn1 >= 0.2.3, python-pysnmp >= 4.3.4, python-lxml, python-eficompressor, python-setuptools, python-dateutil, python-websocket-client python2-msgpack python-libarchive-c python-yaml python-monotonic
%else
%if "%{dist}" == ".el8"
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute cpio
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-enum34, python3-asn1crypto, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute
%else
%if "%{dist}" == ".el9"
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute cpio
Requires: python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodomex >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dns, python3-webauthn, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-yaml openssl iproute
%else
Requires: python3-dbm,python3-pyghmi >= 1.5.71, python3-eventlet, python3-greenlet, python3-pycryptodome >= 3.4.7, confluent_client == %{version}, python3-pyparsing, python3-paramiko, python3-dnspython, python3-netifaces, python3-pyasn1 >= 0.2.3, python3-pysnmp >= 4.3.4, python3-lxml, python3-eficompressor, python3-setuptools, python3-dateutil, python3-cffi, python3-pyOpenSSL, python3-websocket-client python3-msgpack python3-libarchive-c python3-PyYAML openssl iproute
%endif
%endif
%endif
+1 -1
View File
@@ -1,5 +1,5 @@
%define arch x86_64
Version: 3.10.0
Version: 3.12.0
Release: 1
Name: confluent-genesis-%{arch}
BuildArch: noarch
+54
View File
@@ -0,0 +1,54 @@
Copyright and licensing for additional files included in the bash package:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Files: lib/sh/inet_aton.c
Copyright: 1983, 1990, 1993 The Regents of the University of California. All rights reserved.
License: BSD-4-UC AND HPND
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* -
* Portions Copyright (c) 1993 by Digital Equipment Corporation.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies, and that
* the name of Digital Equipment Corporation not be used in advertising or
* publicity pertaining to distribution of the document or software without
* specific, written prior permission.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND DIGITAL EQUIPMENT CORP. DISCLAIMS ALL
* WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL DIGITAL EQUIPMENT
* CORPORATION BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
* SOFTWARE.
* -
* --Copyright--
+157
View File
@@ -0,0 +1,157 @@
Copyright and licensing for additional files included in the dhcp package:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Files:
server/ldap_krb_helper.c
omapip/inet_addr.c
Copyright: Copyright (c) 2015 by Internet Systems Consortium, Inc. ("ISC") All rights reserved.
License: BSD-3
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of The Internet Software Consortium nor the names
* of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This helper was written by William Brown <william@adelaide.edu.au>,
* inspired by krb5_helper.c from bind-dyndb-ldap by Simo Sorce (Redhat)
Files: server/ldap_casa.c
Copyright: Copyright (c) 2006 Novell, Inc.
License: BSD-3 AND ISC
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* 1.Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* 2.Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3.Neither the name of ISC, ISC DHCP, nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
* THIS SOFTWARE IS PROVIDED BY INTERNET SYSTEMS CONSORTIUM AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL ISC OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
* This file was written by S Kalyanasundaram <skalyanasundaram@novell.com>
*/
/*
* Copyright (c) 2004-2010 by Internet Systems Consortium, Inc. ("ISC")
* Copyright (c) 1995-2003 by Internet Software Consortium
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Internet Systems Consortium, Inc.
* 950 Charter Street
* Redwood City, CA 94063
* <info@isc.org>
* https://www.isc.org/
Files: server/ldap.c.cloexec
server/ldap.c
Copyright:
Copyright (c) 2010,2015-2016 by Internet Systems Consortium, Inc. ("ISC")
Copyright (c) 2003-2006 Ntelos, Inc.
All rights reserved.
License: BSD-3
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of The Internet Software Consortium nor the names
* of its contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INTERNET SOFTWARE CONSORTIUM AND
* CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE INTERNET SOFTWARE CONSORTIUM OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This LDAP module was written by Brian Masney <masneyb@ntelos.net>. Its
* development was sponsored by Ntelos, Inc. (www.ntelos.com).
Files: omapip/inet_addr.c
Copyright: Copyright (c) 1983, 1990, 1993 The Regents of the University of California. All rights reserved.
License: BSD-3-UC
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
@@ -0,0 +1,238 @@
Additional license notices for Libgcrypt. -*- org -*-
This file contains the copying permission notices for various files in
the Libgcrypt distribution which are not covered by the GNU Lesser
General Public License (LGPL) or the GNU General Public License (GPL).
These notices all require that a copy of the notice be included
in the accompanying documentation and be distributed with binary
distributions of the code, so be sure to include this file along
with any binary distributions derived from the GNU C Library.
* BSD_3Clause
For files:
- cipher/sha256-avx-amd64.S
- cipher/sha256-avx2-bmi2-amd64.S
- cipher/sha256-ssse3-amd64.S
- cipher/sha512-avx-amd64.S
- cipher/sha512-avx2-bmi2-amd64.S
- cipher/sha512-ssse3-amd64.S
#+begin_quote
Copyright (c) 2012, Intel Corporation
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the
distribution.
* Neither the name of the Intel Corporation nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY INTEL CORPORATION "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTEL CORPORATION OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#+end_quote
For files:
- random/jitterentropy-base.c
- random/jitterentropy.h
- random/rndjent.c (plus common Libgcrypt copyright holders)
#+begin_quote
* Copyright Stephan Mueller <smueller@chronox.de>, 2013
*
* License
* =======
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* ALTERNATIVELY, this product may be distributed under the terms of
* the GNU General Public License, in which case the provisions of the GPL are
* required INSTEAD OF the above restrictions. (This clause is
* necessary due to a potential bad interaction between the GPL and
* the restrictions contained in a BSD-style copyright.)
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ALL OF
* WHICH ARE HEREBY DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF NOT ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
#+end_quote
* X License
For files:
- install.sh
#+begin_quote
Copyright (C) 1994 X Consortium
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC-
TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
Except as contained in this notice, the name of the X Consortium shall not
be used in advertising or otherwise to promote the sale, use or other deal-
ings in this Software without prior written authorization from the X Consor-
tium.
#+end_quote
* Public domain
For files:
- cipher/arcfour-amd64.S
#+begin_quote
Author: Marc Bevand <bevand_m (at) epita.fr>
Licence: I hereby disclaim the copyright on this code and place it
in the public domain.
#+end_quote
* OCB license 1
For files:
- cipher/cipher-ocb.c
#+begin_quote
OCB is covered by several patents but may be used freely by most
software. See http://web.cs.ucdavis.edu/~rogaway/ocb/license.htm .
In particular license 1 is suitable for Libgcrypt: See
http://web.cs.ucdavis.edu/~rogaway/ocb/license1.pdf for the full
license document; it basically says:
License 1 — License for Open-Source Software Implementations of OCB
(Jan 9, 2013)
Under this license, you are authorized to make, use, and
distribute open-source software implementations of OCB. This
license terminates for you if you sue someone over their
open-source software implementation of OCB claiming that you have
a patent covering their implementation.
License for Open Source Software Implementations of OCB
January 9, 2013
1 Definitions
1.1 “Licensor” means Phillip Rogaway.
1.2 “Licensed Patents” means any patent that claims priority to United
States Patent Application No. 09/918,615 entitled “Method and Apparatus
for Facilitating Efficient Authenticated Encryption,” and any utility,
divisional, provisional, continuation, continuations-in-part, reexamination,
reissue, or foreign counterpart patents that may issue with respect to the
aforesaid patent application. This includes, but is not limited to, United
States Patent No. 7,046,802; United States Patent No. 7,200,227; United
States Patent No. 7,949,129; United States Patent No. 8,321,675 ; and any
patent that issues out of United States Patent Application No. 13/669,114.
1.3 “Use” means any practice of any invention claimed in the Licensed Patents.
1.4 “Software Implementation” means any practice of any invention
claimed in the Licensed Patents that takes the form of software executing on
a user-programmable, general-purpose computer or that takes the form of a
computer-readable medium storing such software. Software Implementation does
not include, for example, application-specific integrated circuits (ASICs),
field-programmable gate arrays (FPGAs), embedded systems, or IP cores.
1.5 “Open Source Software” means software whose source code is published
and made available for inspection and use by anyone because either (a) the
source code is subject to a license that permits recipients to copy, modify,
and distribute the source code without payment of fees or royalties, or
(b) the source code is in the public domain, including code released for
public use through a CC0 waiver. All licenses certified by the Open Source
Initiative at opensource.org as of January 9, 2013 and all Creative Commons
licenses identified on the creativecommons.org website as of January 9,
2013, including the Public License Fallback of the CC0 waiver, satisfy these
requirements for the purposes of this license.
1.6 “Open Source Software Implementation” means a Software
Implementation in which the software implicating the Licensed Patents is
Open Source Software. Open Source Software Implementation does not include
any Software Implementation in which the software implicating the Licensed
Patents is combined, so as to form a larger program, with software that is
not Open Source Software.
2 License Grant
2.1 License. Subject to your compliance with the term s of this license,
including the restriction set forth in Section 2.2, Licensor hereby
grants to you a perpetual, worldwide, non-exclusive, non-transferable,
non-sublicenseable, no-charge, royalty-free, irrevocable license to practice
any invention claimed in the Licensed Patents in any Open Source Software
Implementation.
2.2 Restriction. If you or your affiliates institute patent litigation
(including, but not limited to, a cross-claim or counterclaim in a lawsuit)
against any entity alleging that any Use authorized by this license
infringes another patent, then any rights granted to you under this license
automatically terminate as of the date such litigation is filed.
3 Disclaimer
YOUR USE OF THE LICENSED PATENTS IS AT YOUR OWN RISK AND UNLESS REQUIRED
BY APPLICABLE LAW, LICENSOR MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY
KIND CONCERNING THE LICENSED PATENTS OR ANY PRODUCT EMBODYING ANY LICENSED
PATENT, EXPRESS OR IMPLIED, STATUT ORY OR OTHERWISE, INCLUDING, WITHOUT
LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR
PURPOSE, OR NONINFRINGEMENT. IN NO EVENT WILL LICENSOR BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM OR RELATED TO ANY USE OF THE LICENSED PATENTS, INCLUDING,
WITHOUT LIMITATION, DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
OR SPECIAL DAMAGES, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES PRIOR TO SUCH AN OCCURRENCE.
#+end_quote
+54
View File
@@ -0,0 +1,54 @@
Copyright and licensing for additional files included in the libsepol package:
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Files: cil/*
Copyright: Copyright 2011, 2014 Tresys Technology, LLC. All rights reserved.
License: BSD-2
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY TRESYS TECHNOLOGY, LLC ``AS IS'' AND ANY EXPRESS
* OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL TRESYS TECHNOLOGY, LLC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those
* of the authors and should not be interpreted as representing official policies,
* either expressed or implied, of Tresys Technology, LLC.
Files: cil/test/unit/CuTest.c
Copyright: Copyright (c) 2003 Asim Jalis
License: Zlib
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any damages
* arising from the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not
* claim that you wrote the original software. If you use this software in
* a product, an acknowledgment in the product documentation would be
* appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
+5 -4
View File
@@ -76,7 +76,7 @@ manuallicenses = [
'/usr/share/licenses/lz4/LICENSE.BSD',
'/usr/share/licenses/nss/LICENSE.APACHE', # http://www.apache.org/licenses/LICENSE-2.0
'/usr/share/licenses/openssh/COPYING.blowfish', # from header of blowfish file in bsd-compat
'/usr/share/licenses/bc/COPYING.GPLv2',
'/usr/share/licenses/bc/COPYING.GPLv2', # generic copy of GPLv2
'/usr/share/licenses/bind-license/LICENSE', # MPLv2 from the source code
'/usr/share/licenses/procps-ng/COPYING.LIBv2.1', # fetched internet
# cp /usr/share/doc/lz4-libs/LICENSE /usr/share/licenses/lz4/LICENSE.BSD
@@ -87,7 +87,7 @@ manuallicenses = [
'/usr/share/licenses/pcre/LICENSE.BSD2', # stack-less just in time compiler, Zoltan Herzeg
'/usr/share/licenses/sqlite/LICENSE.md', # https://raw.githubusercontent.com/sqlite/sqlite/master/LICENSE.md
'/usr/share/licenses/pcre2/LICENSE.BSD2',
'/usr/share/licenses/dhcp-common/NOTICE',
'/usr/share/licenses/dhcp-common/NOTICE', # from exlicenses
'/usr/share/licenses/xz/COPYING.GPLv3', # manually extracted from xz source
'/usr/share/licenses/bash/NOTICE',
'/usr/share/licenses/libsepol/NOTICE',
@@ -111,6 +111,7 @@ manuallicenses = [
'/usr/share/licenses/tmux/NOTICE', # built by extracttmuxlicenses.py
'/usr/share/licenses/tmux/COPYING', # extracted from source
'/usr/share/licenses/tmux/README', # extracted from source
# yum download kernel soruce, cp -a from LICENSES to kernel-extra
'/usr/share/licenses/kernel-extra/preferred/BSD-2-Clause',
'/usr/share/licenses/kernel-extra/preferred/BSD-3-Clause',
'/usr/share/licenses/kernel-extra/preferred/BSD-3-Clause-Clear',
@@ -132,10 +133,10 @@ manuallicenses = [
'/usr/share/licenses/kernel-extra/exceptions/GCC-exception-2.0',
'/usr/share/licenses/kernel-extra/exceptions/Linux-syscall-note',
'/usr/share/licenses/util-linux/COPYING.GPLv3', # extract from parse-date.c, from srpm
'/usr/share/licenses/kmod/COPYING', # GPL not LGPL, must extract from kmod srpm
'/usr/share/licenses/kmod/COPYING', # GPL not LGPL, must extract from kmod srpm, tools subdir
'/usr/share/licenses/krb5-libs/NOTICE', # copy it verbatim from LICENSE, exact same file
'/usr/share/doc/less/README',
'/usr/share/centos-release/EULA',
'/usr/share/almalinux-release/EULA',
#'/usr/share/doc/almalinux-release/GPL',
'/usr/share/licenses/libcap-ng-utils/COPYING',
'/usr/share/licenses/libdb/copyright', # from libdb, db-5.3.28, lang/sql/odbc/debian/copyright
+16
View File
@@ -612,6 +612,22 @@ class SuseHandler(OsHandler):
else:
subprocess.check_call(['zypper', '-n', '-R', self.targpath, 'install'] + self.zyppargs)
os.symlink('/usr/lib/systemd/system/sshd.service', os.path.join(self.targpath, 'etc/systemd/system/multi-user.target.wants/sshd.service'))
with open(os.path.join(self.targpath, 'etc/permissions.local'), 'a') as permout:
permout.write(
'/usr/lib/ssh/ssh-keysign root:ssh_keys 2711\n'
'/etc/ssh/ssh_host_dsa_key root:ssh_keys 640\n'
'/etc/ssh/ssh_host_ecdsa_key root:ssh_keys 640\n'
'/etc/ssh/ssh_host_ed25519_key root:ssh_keys 640\n'
'/etc/ssh/ssh_host_rsa_key root:ssh_keys 640\n'
)
args.cmd = ['groupadd', 'ssh_keys']
run_constrainedx(fancy_chroot, (args,
self.targpath))
args.cmd = ['chkstat', '--system', '--set'],
run_constrainedx(fancy_chroot, (args,
self.targpath))
if os.path.exists(os.path.join(self.targpath, 'sbin/mkinitrd')):
args.cmd = ['mkinitrd']
else:
+31
View File
@@ -0,0 +1,31 @@
import base64
import pyghmi.redfish.command as ic
import pyghmi.util.webclient as webclient
import sys
import os
import time
def iterm_draw(databuf):
datalen = len(databuf)
data = base64.b64encode(databuf).decode('utf8')
sys.stdout.write(
'\x1b]1337;File=inline=1;size={}:'.format(datalen))
sys.stdout.write(data)
sys.stdout.write('\a')
sys.stdout.write('\n')
sys.stdout.flush()
i = ic.Command(sys.argv[1], os.environ['XCCUSER'], os.environ['XCCPASS'], verifycallback=lambda x: True)
i.get_health()
#url = '/download/Mini_ScreenShot.png?t={}'.format(int(time.time()*1000))
i.oem.wc.grab_json_response('/api/providers/rp_screenshot')
url = '/download/HostScreenShot.png'
fd = webclient.FileDownloader(i.oem.wc, url, sys.argv[2])
fd.start()
fd.join()
if sys.argv[3]:
imgdata = open(sys.argv[2], 'rb').read()
iterm_draw(imgdata)
+103
View File
@@ -0,0 +1,103 @@
# This creates a locked down variant of a confluent CA
# The confluent CA naturally doesn't have any name constraints, which
# is great for flexibility.
# However, if actually being imported into a web browser, it is likely
# to want to limit the CA to apply to cluster resources rather
# than having a blank check to vouch for any and all subjects.
# This particular approach causes a certificate authority that
# can be imported and vouch for the subset of certificates
# that were issued by the normal CA that match name constraints
# Unfortunately, *ALL* names are checked, whether they are relevant
# to the conversation or not, so we reproduce the logic and the resultant
# CA is fragile to IP addresses.
# The better approach would be to issue a separate, full name only certificate
# and have a simpler alt CA. The same CA can be used to sign both certificates,
# and still import the limited one
import subprocess
import socket
import confluent.certutil as certutil
import sys
def create_alt_ca(certout, permitdomains):
# This is to create a constrained variant of the existing authority
# this will allow a client browser to only trust it for select domains
# while the nodes can more broadly trust it (e.g. to vouch for IP addresses)
sslcfg = certutil.get_openssl_conf_location()
newcfg = '/etc/confluent/tls/ca-alt/openssl-alt.cfg'
subj = subprocess.check_output(
['openssl', 'x509', '-subject', '-noout', '-in', '/etc/confluent/tls/ca/cacert.pem']
).decode().replace('subject=', '')
serial = subprocess.check_output(
['openssl', 'x509', '-serial', '-noout', '-in', '/etc/confluent/tls/ca/cacert.pem']
).decode().replace('serial=', '')
with open('/etc/confluent/tls/ca-alt/serial', 'w') as srl:
srl.write(serial)
settings = {
'dir': '/etc/confluent/tls/ca-alt',
'certificate': '$dir/cacert.pem',
'private_key': '$dir/private/cakey.pem',
'countryName': 'optional',
'stateOrProvinceName': 'optional',
'organizationName': 'optional',
}
keyin = '/etc/confluent/tls/ca/private/cakey.pem'
csrin = '/etc/confluent/tls/ca/ca.csr'
shortname = 'r3u20'
ipaddrs = list(certutil.get_ip_addresses())
san = [] # 'IP:{0}'.format(x) for x in ipaddrs]
# It is incorrect to put IP addresses as DNS type. However
# there exists non-compliant clients that fail with them as IP
# san.extend(['DNS:{0}'.format(x) for x in ipaddrs])
dnsnames = set(ipaddrs)
dnsnames.add(shortname)
for currip in ipaddrs:
dnsnames.add(socket.getnameinfo((currip, 0), 0)[0])
for currname in dnsnames:
san.append('DNS:{0}'.format(currname))
if permitdomains[0] == '':
permitdomains = []
nameconstraints = ['permitted;DNS:{}'.format(x) for x in permitdomains]
nameconstraints.extend(['permitted;{}'.format(x) for x in san])
nameconstraints = ','.join(nameconstraints)
if nameconstraints:
nameconstraints = f'nameConstraints = critical,{nameconstraints}\n'
certutil.mkdirp('/etc/confluent/tls/ca-alt/newcerts')
with open('/etc/confluent/tls/ca-alt/index.txt', 'w') as idx:
pass
with open(sslcfg, 'r') as cfgin:
with open(newcfg, 'w') as cfgfile:
for line in cfgin.readlines():
cfg = line.split('#')[0]
if '=' in cfg:
key, val = cfg.split('=', 1)
for stg in settings:
if certutil.substitute_cfg(stg, key, val, settings[stg], cfgfile, line):
break
else:
cfgfile.write(line.strip() + '\n')
continue
cfgfile.write(line.strip() + '\n')
cfgfile.write(f'\n[CACert]\nbasicConstraints = CA:true\n{nameconstraints}[ca_confluent]\n')
subprocess.check_call(
['openssl', 'ca', '-config', newcfg, '-batch', '-selfsign',
'-extensions', 'CACert', '-extfile', newcfg,
'-notext', '-startdate',
'19700101010101Z', '-enddate', '21000101010101Z', '-keyfile',
keyin, '-out', certout, '-in', csrin]
)
if __name__ == '__main__':
create_alt_ca(sys.argv[1], sys.argv[2].split(','))