-
-
Save TheJJ/2394cd76d3e2c34d02e3da1bd3e489b2 to your computer and use it in GitHub Desktop.
| #!/usr/bin/env python3 | |
| """ | |
| Dell iDRAC client launcher for Linux, macOS and Windows. | |
| probably works with Dell iDRAC 6/7/8 | |
| Downloads needed Java files and sets up port forwarding via SSH. | |
| example usage: ./idracclient.py -J jumphost.lol.domain srv42-serviceprocessor.lol.domain | |
| for more info, see ./idracclient.py --help | |
| * use python3.6 or later! | |
| * use java 8 (jre 1.8)! | |
| (c) 2018-2019 Jonas Jelten <jelten@in.tum.de> | |
| Released under GNU GPLv3 or any later version | |
| """ | |
| import asyncio | |
| import argparse | |
| import getpass | |
| import pathlib | |
| import socket | |
| import ssl | |
| import subprocess | |
| import sys | |
| import zipfile | |
| import aiohttp | |
| def main(): | |
| cmd = argparse.ArgumentParser() | |
| cmd.add_argument("hostname") | |
| cmd.add_argument("--port", "-p", type=int, default=443, | |
| help="https port to connect to for idrac") | |
| cmd.add_argument("--kvmport", "-k", type=int, default=5900, | |
| help="port for the kvm connection") | |
| cmd.add_argument("--basedir", "-b", default="/tmp/idracvm", | |
| help="base directory where to put in state and downloaded stuff") | |
| cmd.add_argument("--java", default="java", | |
| help="custom location for the java executable") | |
| cmd.add_argument("--username", "-u", default="root", | |
| help="idrac login username") | |
| cmd.add_argument("--tlscheck", action="store_true", | |
| help="do certificat validation which is disabled by default") | |
| cmd.add_argument("--force-download", "-f", action="store_true", | |
| help="re-download kvm viewer files and libraries") | |
| cmd.add_argument("--dryrun", action="store_true", | |
| help="don't actually run the viewer, but to everything else") | |
| cmd.add_argument("--jumphost", "-J", | |
| help="use this jumphost for relaying the connection via ssh port forwards") | |
| cmd.add_argument("--jumpkvmport", type=int, default=0, | |
| help="port for kvm listened on localhost by ssh") | |
| cmd.add_argument("--jumphttpport", type=int, default=0, | |
| help="port for idrac webinterface listened on localhost by ssh") | |
| cmd.add_argument("--sshtimeout", type=int, default=8, | |
| help="timeout for establishing the ssh jump host connection") | |
| cmd.add_argument("--no-native-libs", action="store_true", | |
| help="don't download the native libraries for input and device redirecions") | |
| args = cmd.parse_args() | |
| loop = asyncio.get_event_loop() | |
| ret = loop.run_until_complete(run(args)) | |
| exit(ret) | |
| async def download(url, check_tls=True): | |
| async with aiohttp.ClientSession() as session: | |
| tls_settings = ssl.create_default_context() | |
| # the crappy iDRACs have slightly outdated crypto... | |
| tls_settings.set_ciphers('ALL:!aNULL:!DH') | |
| if not check_tls: | |
| tls_settings.check_hostname = False | |
| tls_settings.verify_mode = ssl.CERT_NONE | |
| async with session.get(url, ssl=tls_settings) as resp: | |
| return await resp.read() | |
| async def download_all(mainjar, libdir, hostname, port, tlscheck=True, use_native_libs=True): | |
| print("downloading files...") | |
| with mainjar.open("wb") as fd: | |
| content = await download('https://%s:%d/software/avctKVM.jar' % (hostname, port), tlscheck) | |
| fd.write(content) | |
| if use_native_libs: | |
| if sys.platform == 'linux': | |
| libdls = ["avctKVMIOLinux64.jar", "avctVMLinux64.jar"] | |
| extension = ".so" | |
| elif sys.platform == 'darwin': | |
| libdls = ["avctKVMIOMac64.jar", "avctVMMac64.jar"] | |
| extension = ".jnilib" | |
| elif sys.platform == 'win32': | |
| libdls = ["avctKVMIOWin64.jar", "avctVMWin64.jar"] | |
| extension = ".dll" | |
| else: | |
| raise Exception("running on unknown platform: %s" % sys.platform) | |
| for dlname in libdls: | |
| dl_lib = (libdir / dlname) | |
| try: | |
| with dl_lib.open("wb") as fd: | |
| content = await download('https://%s:%d/software/%s' % (hostname, port, dlname), tlscheck) | |
| fd.write(content) | |
| except aiohttp.ClientError: | |
| os.unlink(dl_lib) | |
| raise | |
| zf = zipfile.ZipFile(str(dl_lib)) | |
| zf.extractall(str(libdir)) | |
| print("finished downloading files.") | |
| async def create_sec_override(path): | |
| # write funny security overwrite file | |
| with path.open("w") as fd: | |
| fd.write("# iDRAC uses disabled, outdated crypto\n") | |
| fd.write("# we override the disabled algos to enable 3DES_EDE_CBC and SSLv3\n") | |
| fd.write("jdk.tls.disabledAlgorithms=RC4, DES, MD5withRSA, DH keySize < 1024, EC keySize < 224\n") | |
| def find_port(blacklist=[]): | |
| port = 9009 | |
| sock = socket.socket() | |
| while True: | |
| port += 1 | |
| while port in blacklist: | |
| port += 1 | |
| try: | |
| sock.bind(('', port)) | |
| sock.close() | |
| break | |
| except OSError: | |
| pass | |
| return port | |
| async def run(args): | |
| basedir = pathlib.Path(args.basedir) | |
| hostdir = basedir / args.hostname | |
| libdir = hostdir / "lib" | |
| libdir.mkdir(parents=True, exist_ok=True) | |
| java_kvmhost = args.hostname | |
| java_kvmport = args.kvmport | |
| java_idracport = args.port | |
| # jumphost setup | |
| if args.jumphost: | |
| if args.jumphttpport: | |
| jumpkvmport = args.jumphttpport | |
| else: | |
| jumpkvmport = find_port() | |
| if args.jumphttpport: | |
| jumphttpport = args.jumphttpport | |
| else: | |
| jumphttpport = find_port([jumpkvmport]) | |
| print("launching port forwarding...") | |
| forward_invocation = [ | |
| "ssh", | |
| "-S", "none", | |
| "-L", "%d:%s:%d" % (jumphttpport, args.hostname, args.port), | |
| "-L", "%d:%s:%d" % (jumpkvmport, args.hostname, args.kvmport), | |
| args.jumphost, | |
| "echo kay && cat", | |
| ] | |
| print("$ %s" % " ".join(forward_invocation)) | |
| sshproc = await asyncio.create_subprocess_exec(*forward_invocation, stdout=subprocess.PIPE) | |
| # wait until "kay" appears | |
| try: | |
| await asyncio.wait_for(sshproc.stdout.readuntil(b"kay\n"), timeout=args.sshtimeout) | |
| except asyncio.TimeoutError as exc: | |
| raise Exception("failed to set up port forwards via ssh") from exc | |
| print("port forwards established.") | |
| # now java has to connect to localhost | |
| java_kvmhost = "localhost" | |
| java_kvmport = jumpkvmport | |
| java_idracport = jumphttpport | |
| # download java files | |
| mainjar = (hostdir / "avctKVM.jar") | |
| if not (mainjar.is_file() and mainjar.stat().st_size) or args.force_download: | |
| await download_all(mainjar, libdir, java_kvmhost, java_idracport, args.tlscheck, not args.no_native_libs) | |
| else: | |
| print("no need to download files") | |
| # create security override for SSLv3 | |
| sec_overwrite_path = (hostdir / "java_security_overrides") | |
| await create_sec_override(sec_overwrite_path) | |
| # query password | |
| if args.dryrun: | |
| password = "xxx-dryrun-xxx" | |
| else: | |
| password = getpass.getpass("enter idrac password: ") | |
| print("launching viewer...") | |
| invocation = [ | |
| args.java, | |
| "-cp", str(mainjar), | |
| ] | |
| if not args.no_native_libs: | |
| invocation.append("-Djava.library.path=%s" % libdir) | |
| invocation.extend([ | |
| # single = means overwrite only parts, == means overwrite all: | |
| "-Djava.security.properties=%s" % sec_overwrite_path, | |
| "com.avocent.idrac.kvm.Main", | |
| "ip=%s" % java_kvmhost, | |
| "kmport=%d" % java_kvmport, | |
| "vport=%d" % java_kvmport, | |
| "user=%s" % args.username, | |
| "passwd=%s" % password, | |
| "apcp=1", | |
| "version=2", | |
| "reconnect=2", | |
| "vmprivilege=true", | |
| "helpurl=https://%s:%d/help/contents.html" % (java_kvmhost, java_idracport), | |
| ]) | |
| print("$ %s" % " ".join(invocation)) | |
| if args.dryrun: | |
| ret = 0 | |
| else: | |
| proc = await asyncio.create_subprocess_exec( | |
| *invocation | |
| ) | |
| ret = await proc.wait() | |
| if args.jumphost: | |
| sshproc.terminate() | |
| await sshproc.wait() | |
| return ret | |
| if __name__ == "__main__": | |
| main() |
Thanks for the script!
I can't send any keyboard key presses, mouse is working. Any idea why?
Hello thanks for the script but I have the same keyboard issue on endeavouros with :
openjdk version "20.0.1" 2023-04-18
OpenJDK Runtime Environment (build 20.0.1+9)
OpenJDK 64-Bit Server VM (build 20.0.1+9, mixed mode, sharing)
Does anyone have an idea how to fix this
Excellent script! Running on Fedora33 I had to do some additional changes though:
1. For older DRACs I had to create a custom openssl config openssl_allow.cnf:openssl_conf = openssl_init [openssl_init] ssl_conf = ssl_sect [ssl_sect] system_default = system_default_sect [system_default_sect] CipherString = DEFAULT@SECLEVEL=12. Download java 1.6 (available from Centos 7 repos) `wget http://mirror.centos.org/centos/7/os/x86_64/Packages/java-1.6.0-openjdk-1.6.0.41-1.13.13.1.el7_3.x86_64.rpm` 3. Unpack the old java: `rpm2cpio java-1.6.0-openjdk-1.6.0.41-1.13.13.1.el7_3.x86_64.rpm|cpio -idmv`And then call the script like this:
OPENSSL_CONF=./openssl_allow.cnf ./idracclient.py --java ./usr/lib/jvm/java-1.6.0-openjdk-1.6.0.41.x86_64/jre/bin/java -f MY_HOST
This fixed the script for me.
Thanks for the script!
I can't send any keyboard key presses, mouse is working. Any idea why?
I switch to Java8 (Liberica FX) and it works. using Java22 has some error about Swing/AWT, I think these exception will cause input issues.



Excellent script! Running on Fedora33 I had to do some additional changes though:
Download java 1.6 (available from Centos 7 repos)
wget http://mirror.centos.org/centos/7/os/x86_64/Packages/java-1.6.0-openjdk-1.6.0.41-1.13.13.1.el7_3.x86_64.rpmUnpack the old java:
rpm2cpio java-1.6.0-openjdk-1.6.0.41-1.13.13.1.el7_3.x86_64.rpm|cpio -idmvAnd then call the script like this:
OPENSSL_CONF=./openssl_allow.cnf ./idracclient.py --java ./usr/lib/jvm/java-1.6.0-openjdk-1.6.0.41.x86_64/jre/bin/java -f MY_HOST