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 (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"; |