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