TQ
dev.com

Blog about software development

Subscribe

Boot RDP connected VM on-demand

04 Jan 2022 - by 'Maurits van der Schee'

I'm running a Ubuntu Server 20.04 LTS as (headless) KVM host for my Windows VMs. When somebody tries to connect to a powered off VM via RDP, I want that VM to power on. Fortunately all RDP connections are tunneled over SSH and the "auth.log" logs these failed attempts:

Jan  2 08:41:53 bastion sshd[26080]: error: connect_to win10-vm1 port 3389: failed.

Bash script that responds to this log line

I wrote a small bash script that continuously reads lines from "/var/log/auth.log" and tries to start virtual machines (called "domains" in KVM) with the name of the host that the RDP connection (on port 3389) is made to. This is the script "wake-domain.sh":

tail -F /var/log/auth.log | while read line || { sleep 1 ; continue; }; do
    if [[ $line =~ error:\ connect_to\ ([-a-zA-Z0-9]+)\ port\ 3389 ]]; then
        /usr/bin/virsh start ${BASH_REMATCH[1]}
    fi
done

These are the virtual machines I have (as reported by "virsh list"):

 Id   Name        State
---------------------------
 3    win10-vm1   running
 -    win10-vm2   shut off

I'm using Remmina to connect to the machine "win10-vm2" over the internet using SSH tunneling to the KVM host. This SSH tunneled RDP connection will fail. This will lead to the log line:

Jan  2 09:40:23 bastion sshd[26082]: error: connect_to win10-vm2 port 3389: failed.

This line will be read by the above shell script and automatically start the virtual machine using:

virsh start win10-vm2

This means that if you then retry the connection it might succeed. This is not pretty as the user receives an error on the first attempt, but it is good enough for my use case.

Static IP addresses and hostfile entries

In order for this setup to work, you need to be able to identify the VMs by name. You can simply add the IP addresses of the virtual machines to the "/etc/hosts" file of the server:

# libvirt  
192.168.122.11     win10-vm1
192.168.122.12     win10-vm2

You also need to register the MAC address in the network configuration of KVM as described in one of my earlier posts:

https://tqdev.com/2020-kvm-network-static-ip-addresses

Installing the bash script as a service

In order to create a service we create the service definition in "wake-domain.service":

[Unit]
Description=Service to start KVM domain on failed RDP connection
#Documentation=
#After=networking.service
[Service]
Type=simple
User=root
Group=root
TimeoutStartSec=0
Restart=on-failure
RestartSec=30s
#ExecStartPre=
ExecStart=/usr/local/bin/wake-domain.sh
SyslogIdentifier=Wakedomain
#ExecStop=
[Install]
WantedBy=multi-user.target

The installation bash script is stored in "install.sh" and reads:

#!/bin/bash
sudo systemctl stop wake-domain
sudo systemctl disable wake-domain
sudo cp wake-domain.service /etc/systemd/system/
sudo cp wake-domain.sh /usr/local/bin/
sudo systemctl daemon-reload
sudo systemctl enable wake-domain
sudo systemctl start wake-domain

Now in order to install the "wake-domain.sh" and "wake-domain.service" you can run:

bash install.sh

This will install and start the service. As always you can find the code on my Github account:

https://github.com/mevdschee/wake-domain.sh


PS: Liked this article? Please share it on Facebook, Twitter or LinkedIn.