[Spice-devel] Automated, PERL-based Spice launch script for Proxmox

Richard Cantin richard.cantin at ayuda.ca
Thu Mar 16 14:21:36 UTC 2023


Spice protocols are SO much better/faster/more fucntional/. (thank you) than
alternatives.

 

I could not see where to put this on your forum so if you want it, please
feel free to add it wherever you want - no attribution required.

 

I have enclosed below a Perl script I wrote - using material from various
online forums and adding my code - that allows a Windows user to  use Spice
protocols to access a Proxmox VM (in my case Linux Mint) by launching a
Shortcut on the Windows machine.  

 

This way you do NOT have to launch the Proxmox GUI and use the Proxmox
Console to generate the SPICE certificate to use the Spice connection.

 

The script is initiated via a shortcut on the Windows machine that points to
the script.

 

The script creates an SSH connection between the WIndows machine and the
Proxmox VM, generates the Spice certificate and launches the Spice client
(Virtual Viewer) on the Windows machine.

 

I installed Strawberry Perl and Remote Viewer on the Windows machine for
this to work.

 

It makes it a LOT easier to access the Linux Mint VM, instead of using the
intermediate Proxmox GUI.

 

I you want to use/post/share this, feel free.  No attribution required. See
below.

 

Richard

 

Richard Cantin

richard.cantin at ayuda.ca <mailto:richard.cantin at ayuda.ca> 

(519) 957-2414 [home]

(519) 574-9401 [mobile]

 

===================================================================

 

#!C:\Strawberry\perl\bin\perl.exe

###!/usr/bin/perl

###!perl

 

# See perldoc.perl.org for information on Perl

# # comments out a line

 

# This script allows a Windows user to access a Proxmox VM via Spice
protocols

# WITHOUT having to launch the Proxmox GUI to use the Proxmox Console

# by simply double-clicking on a Shortcut on the Windows PC

#

# This script is installed on a Windows PC

# It requires 

#             a Perl interpreter

#                            ( I use Strawberry Perl at
https://strawberryperl.com )

#             a module on the PC to enable SSH connections via script

#                            ( I use Net::SSH::Perl installed with "cpan
install Net::SSH::Perl" )

#             a Spice-compatible Remote Viewer Windows package 

#                            (I use the msi installer for virt-viewer at
https://virt-manager.org/download )

#             a Proxmox VM with the Spice client enabled

#                            ( I use Linux Mint as a Proxmox VM )

#             a Public/Private key pair enabling SSH connection from the
Windows to the Proxmox host

#                            ( I use PuttyGen to create the keys on the PC -
Export the Private key in OpenSSH format )

#

# Install this script on the Windows PC

# Put the public key onto the Proxmox host and manually SSH to the Proxmox
host from the Wondows PC

#   to establish an entry in the "kown-hosts" file on the Windows PC

# Set the script parameters specific to your installation in the variables
section below

# Point a Shortcut to this script

# Launch the shortcut

#

 

# ##############################################################

# Installation parameters

 

# Establish Parameters for the connection and command to use for fetching
the SPICE ticket

# Replace the values enclosed in <> and delete the enclosing brackets < and
>

#

# The address of the Proxmox host (NOT the VM to which you want to connect)

my $host = '<FQDN or IP Address of Proxmox Host>';

# Set the Port number used by the remote Proxmox host to accept SSH
connections (usually 22)

my $port_number = 22;

# Set the SSH protocol version (usually 2) accepted by the remote Proxmox
host

my $protocol_selection = '2';

# Set the (non-root) remote user username for the VM (NOT the Proxmox host)

my $non_root_user = '<Non-root username on VM>';

# Set the (non-root) remote user password for the VM (NOT the Proxmox host)

my $non_root_user_password = "<Non-root password on VM>";

# Set the name of the Proxmox Node (the host or physical machine) 

my $node_name = "<Name of Proxmox Node>";

# Set the VM Number of the VM to which you want to connect via Spice

my $vm_number = <VM Number of VM to which you want to connect via Spice>;

# Set the location of the Private key on the Windows PC (in OpenSSH format)

# Remember to use Windows directory delimiters (backslash) and precede with
a backslash to make it a literal value

my @identity_file_location = ( "<DiskID:\\Full\\Path\\To\\Private\\Key.pem>"
);

# Set the filename and path for edited file containing Spice Certificate

# The file is created on a temporary basis and deleted once used

# File suffix is .vv

my $output_filename_with_path =
"<DiskID:\\Full\\Path\\To\\Temporary\\Storage\\Of\\Spice\\Certificate.vv>";

# Set the location of the Remote viewer application that will use the ticket
/ configuration file generated

# Usually looks something like the path shown - adjust to your specifics

my $path_to_remoteviewer_program = 'C:\Program Files\VirtViewer
v11.0-256\bin\remote-viewer.exe';

 

# ##############################################################

 

# Nothing after this normally needs customizing

#   unless your spice generator has a different format for the certificates
it generates

#  (ie the layout uses something other than | as a separator)

# Try this as is and if it works, great, but if not

#             Set the parameters above for your installation

#             Comment out the line near the end of this script "system :
'$path_to_remoteviewer_program" ...'

#             Run this script and use a text editor to view the certificate

#                            It will be stored at the location specified
above in $output_filename_with_path

#             Adjust the RegEx below to suit

#             Restore the line near the end of this script "system :
'$path_to_remoteviewer_program" ...'

#             Run this script

 

# Set the debug flag to true

my $debug_flag = 1;

# Include this whenever creating code; If you want to comment it out for
production, your call

use warnings;

# You may want to comment this out for production (resource usage) but use
for development

use diagnostics;

# Use this to ensure you properly declare variables prior to their use and
reserve space for them

use strict;

# establish the encoding protocol to use for all script elements

use utf8;

# Set the STDIN STDOUT and STDERR to show with UTF-8 formatting 

# This helps with debugging with print statements

use open ":std", ":encoding(UTF-8)";

# Enable the module on the PC that will setup SSH connections

# Make sure the module is installed and if not, install it with "cpan
install Net::SSH::Perl"

use Net::SSH::Perl;

# Establish Parameters for the conversion of the raw ticket to a format
remoteviewer can use

# Tell the system what encoding you will be using: more necessary for read
but a good habit to get into

my $encoding = ":encoding(UTF-8)";

 

# =======================================================

 

# Instantiate the SSH connection object using the method enabled above
NET:SSH:Perl

my $ssh_connection = Net::SSH::Perl->new( $host , debug => $debug_flag ,
port => $port_number , protocol => $protocol_selection , identity_files =>
\@identity_file_location );

# Or, with some options that are not required for basic Debian access but
some shared hosting require

# my $ssh_connection = Net::SSH::Perl->new( $host , debug => $debug_flag ,
port => $port_number , protocol => $protocol_selection , identity_files =>
\@identity_file_location , options => [ "HostKeyAlgorithms
+ssh-rsa,ssh-ed25519" , "PubkeyAcceptedAlgorithms +ssh-rsa,ssh-ed25519" ] );

# Or, to have it work with user/password (when user/password is enabled)

# my $ssh_connection = Net::SSH::Perl->new( $host , debug => $debug_flag ,
port => $port_number , protocol => $protocol_selection , options => [
"HostKeyAlgorithms +ssh-rsa,ssh-ed25519" , "PubkeyAcceptedAlgorithms
+ssh-rsa,ssh-ed25519" ] );

 

# Login to the remote host

# If the host system has previously been associated with a different
"known_host"

# make sure to delete the previous host entry in Windows

# For Windows 7 find this by entering %USERPROFILE% in the search bar and
looking in the .ssh directory

# I disable remote SSH with root for security purposes so you need to login
with a non-root user

# and then use sudo to perform superuser commands like pvesh

$ssh_connection->login($non_root_user);

# Or, to have it work with user/password (when user/password is enabled)

# $ssh_connection->login($non_root_user,$non_root_user_password);

 

# Issue the command at the remote host to generate the SPICE ticket and put
it into a variable called $output

# I know storing a password in a script and sending it is a security issue

# If someone else wants to use Expect or React for making an interactive
script, be my guest

# This script uses key-pair to make the encrypted connection, then sends the
password so I am ok with the transmission

# Yes, storage of password in a readable text file is risky; I password
protect my PC and do not allow others access

my $command = ( "echo $non_root_user_password | sudo -S pvesh create
/nodes/$node_name/qemu/$vm_number/spiceproxy" );

my ($output, $error_message, $exit_code) = $ssh_connection->cmd( "$command"
);

 

# Read the raw ticket content, edit the content to match what remote-viewer
is looking for

# and write the edited content to a file on the system that is hosting the
script

# We will use this edited content file as the configuration settings when we
launch remoteviewer

# Create the file handler, with read and/or write permission (include the
$encoding as shown)

#             The '>' designator tells the system this file handler is for
writing

#             To Read, use "< $encoding"

#             To Read and Write use "+< $encoding"

#             To Read and Write by deleting existing content in a file
(truncating) use "+> $encoding"

#             To Read and Write by appending to existing contents of a file
use "+>> $encoding"

# If the file deos not already exist, it will be created

# Note: the $! system variable contains the error code for the failure.  If
$! is empty, there was no error

open ( my $raw_content_handler , "< $encoding" , \$output ) or die "Could
not open the raw content due to $! \r\n";

open ( my $output_file_handler , "> $encoding" , $output_filename_with_path
) or die "Could not open file $output_filename_with_path due to $! \r\n";

 

# Bring the file contents in one line at a time

# When you use <filehandler> syntax, the <> brackets bring in the next line
of a file 

# When you us <STDIN>, the system assumes there will user input from STDIN
and waits

# If you do not define a variable for bringing the contents in, Perl uses
the system variable $_ to hold each line

# So if you use while ( <file_handler> ) without assigning it to a variable,
the line will be in $_

# I prefer the explicit assignment to a variable so I use while ( my
$variable = <file_handler> ) and use $variable

# This script is parsing a specific format to convert it to another specific
format, 

# so the sequence may not be exactly what you will want to use in your
situation

# but the functions shown give you a sample of what text manipulation looks
like in Perl

#

# Re-set the file pointer for the input file to the start of the file

# Usually not needed but a good habit to get into

seek $raw_content_handler , 0 , 0;

# Since we are creating a linux file from a Windows script, 

# the newline character will be Windows and will be problematic on linux

# We need to change the $INPUT_RECORD_SEPARATOR = $/ but do not want to do
that globally, just in this script

# so we put the editing functions into a subroutine and change a local
version of $/

# Initialize the first line of the file (specific to this situation;
normally initialize to blank)

our $edited_contents = "[virt-viewer]\n";

 

while ( my $file_line = <$raw_content_handler> ) {

               # Delete / skip lines that do not have space as the second
character; they are delimeter lines

               if ( substr( $file_line , 1 , 1 ) ne ' ' ) {

                              next;

               # Delete / skip the header line

               } elsif ( substr( $file_line , 2 , 3 ) eq 'key' ) {

                              next;

               } else {

                              # Remove the Windows newline

                              chomp $file_line;

                              # The remaining lines have the content we
want, so we will manipulate them to our format

                              # Delete the first two characters of each line
(they are "| ")

                              substr( $file_line , 0 , 2 ) = '';

                              # Convert what was the middle | character (now
the first | in the line) to an = sign

                              # The raw syntax also has a space after the
middle | so include it in the substitution

                              # and insert a ~ character before and after
the = sign so we can hold a spot for them later

                              $file_line =~ s/\| /~=~/;

                              # Convert all the remaining | to spaces

                              $file_line =~ s/\|/ /g;

                              # Trim white space at start and end of each
line

                              # Remove white spaces from left side of string

                              $file_line =~ s/^\s+//;

                              # Remove white spaces from right side of
string

                              $file_line =~ s/\s+$//;

                              # Remove white space between first "word" and
= sign

                              # Make sure there is at least one space before
making the edit

                              my $first_space_location = index( $file_line ,
" " , 0 );

                              if ( $first_space_location != -1 ) {

                                             my
$first_character_location_after_space = index($file_line , "~" ,
$first_space_location + 1 );

                                             substr( $file_line ,
$first_space_location , $first_character_location_after_space -
$first_space_location ) = '';

                              }

                              # Delete the temporary placeholder ~
characters surrounding the = sign

                              $file_line =~ s/\~//g;

                              # Save the remaining characters into the
output line with the linux newline

                              $edited_contents .= $file_line . "\n";

               }

}

 

# For debugging if running the script from a Windows command line

# print "The ticket created will contain \r\n";

# print $edited_contents;

 

# Save the edited contents into the output file

# Set the file pointer to the start of the file and erase the existing
contents to create a blank file

# Not necessary for a new file but a good habit to get into IF you do not
want to append to a file's contents

seek $output_file_handler , 0 , 0;

truncate $output_file_handler , 0;

# Write your edited contents to the output file

print $output_file_handler $edited_contents;

# Close the file - which will ensure that buffered content is completely
written and any lock is released

close $raw_content_handler or die "Could not close the raw content holder
due to $! so content may not be properly written \r\n";

close $output_file_handler or die "Could not close the file
$output_filename_with_path due to $! so content may not be properly written
\r\n";

 

# Launch the remote-viewer application using the formatted configuration
file 

# There will be a series of warnings generated but the viewer will launch
and work

system ( "$path_to_remoteviewer_program" , "$output_filename_with_path" ) or
die "Errors encountered while launching from $path_to_remoteviewer_program
\r\n";

 

# For debugging if running the script from a Windows command line

# Show that you made it to the end of the script

# print "\r\n\r\nMade it to the end of the script!\r\n\r\n";

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.freedesktop.org/archives/spice-devel/attachments/20230316/48fdc42e/attachment-0001.htm>


More information about the Spice-devel mailing list