Skip to content

Instantly share code, notes, and snippets.

@TheJJ
Last active June 13, 2024 21:46
Show Gist options
  • Save TheJJ/2394cd76d3e2c34d02e3da1bd3e489b2 to your computer and use it in GitHub Desktop.
Save TheJJ/2394cd76d3e2c34d02e3da1bd3e489b2 to your computer and use it in GitHub Desktop.
Dell iDRAC linux/mac/windows client launcher
#!/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()
@dyasny
Copy link

dyasny commented Mar 31, 2021

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=1
  1. 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

  2. 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

@fuzunspm
Copy link

Thanks for the script!

I can't send any keyboard key presses, mouse is working. Any idea why?

@delta-whiplash
Copy link

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

@adamboutcher
Copy link

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=1
2. 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.

@chisaato
Copy link

chisaato commented May 31, 2024

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.

@chisaato
Copy link

chisaato commented Jun 4, 2024

Hey, I have some new research
add vm=1 to arguments can enable VirtualMedia
image
image

and on idrac8 the native lib jar name is avctVMAPI_DLLLinux64
image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment