Accessing Virtual Machines
Graphical and Serial Console Access
Once a virtual machine is started you are able to connect to the consoles it exposes. Usually there are two types of consoles:
- Serial Console
- Graphical Console (VNC)
Note: You need to have
virtctl
installed to gain access to the VirtualMachineInstance.
Accessing the Serial Console
The serial console of a virtual machine can be accessed by using the console
command:
virtctl console testvm
Accessing the Graphical Console (VNC)
To access the graphical console of a virtual machine the VNC protocol is typically used. This requires remote-viewer
to be installed. Once the tool is installed, you can access the graphical console using:
virtctl vnc testvm
If you only want to open a vnc-proxy without executing the remote-viewer
command, it can be accomplished with:
virtctl vnc --proxy-only testvm
This would print the port number on your machine where you can manually connect using any VNC viewer.
Debugging console access
If the connection fails, you can use the -v
flag to get more verbose output from both virtctl
and the remote-viewer
tool to troubleshoot the problem.
virtctl vnc testvm -v 4
Note: If you are using virtctl via SSH on a remote machine, you need to forward the X session to your machine. Look up the -X and -Y flags of
ssh
if you are not familiar with that. As an alternative you can proxy the API server port with SSH to your machine (either direct or in combination withkubectl proxy
).
SSH Access
A common operational pattern used when managing virtual machines is to inject SSH public keys into the virtual machines at boot. This allows automation tools (like Ansible) to provision the virtual machine. It also gives operators a way of gaining secure and passwordless access to a virtual machine.
KubeVirt provides multiple ways to inject SSH public keys into a virtual machine.
In general, these methods fall into two categories: - Static key injection, which places keys on the virtual machine the first time it is booted. - Dynamic key injection, which allows keys to be dynamically updated both at boot and during runtime.
Once a SSH public key is injected into the virtual machine, it can be accessed via virtctl
.
Static SSH public key injection via cloud-init
Users creating virtual machines can provide startup scripts to their virtual machines, allowing multiple customization operations.
One option for injecting public SSH keys into a VM is via cloud-init startup script. However, there are more flexible options available.
The virtual machine’s access credential API allows statically injecting SSH public keys at startup time independently of the cloud-init user data by placing the SSH public key into a Kubernetes Secret
. This allows keeping the application data in the cloud-init user data separate from the credentials used to access the virtual machine.
A Kubernetes Secret
can be created from an SSH public key like this:
# Place SSH public key into a Secret
kubectl create secret generic my-pub-key --from-file=key1=id_rsa.pub
The Secret
containing the public key is then assigned to a virtual machine using the access credentials API with the noCloud
propagation method.
KubeVirt injects the SSH public key into the virtual machine by using the generated cloud-init metadata instead of the user data. This separates the application user data and user credentials.
Note: The cloud-init
userData
is not touched.
# Create a VM referencing the Secret using propagation method noCloud
kubectl create -f - <<EOF
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: testvm
spec:
runStrategy: Always
template:
spec:
domain:
devices:
disks:
- disk:
bus: virtio
name: containerdisk
- disk:
bus: virtio
name: cloudinitdisk
rng: {}
resources:
requests:
memory: 1024M
terminationGracePeriodSeconds: 0
accessCredentials:
- sshPublicKey:
source:
secret:
secretName: my-pub-key
propagationMethod:
noCloud: {}
volumes:
- containerDisk:
image: quay.io/containerdisks/fedora:latest
name: containerdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }
name: cloudinitdisk
EOF
Dynamic SSH public key injection via qemu-guest-agent
KubeVirt allows the dynamic injection of SSH public keys into a VirtualMachine with the access credentials API.
Utilizing the qemuGuestAgent
propagation method, configured Secrets are attached to a VirtualMachine when the VM is started. This allows for dynamic injection of SSH public keys at runtime by updating the attached Secrets.
Please note that new Secrets cannot be attached to a running VM: You must restart the VM to attach the new Secret.
Note: This requires the qemu-guest-agent to be installed within the guest.
Note: When using qemuGuestAgent propagation, the
/home/$USER/.ssh/authorized_keys
file will be owned by the guest agent. Changes to the file not made by the guest agent will be lost.Note: More information about the motivation behind the access credentials API can be found in the pull request description that introduced the API.
In the example below the Secret
containing the SSH public key is attached to the virtual machine via the access credentials API with the qemuGuestAgent
propagation method. This allows updating the contents of the Secret
at any time, which will result in the changes getting applied to the running virtual machine immediately. The Secret
may also contain multiple SSH public keys.
# Place SSH public key into a secret
kubectl create secret generic my-pub-key --from-file=key1=id_rsa.pub
Now reference this secret in the VirtualMachine
spec with the access credentials API using qemuGuestAgent
propagation.
# Create a VM referencing the Secret using propagation method qemuGuestAgent
kubectl create -f - <<EOF
apiVersion: kubevirt.io/v1
kind: VirtualMachine
metadata:
name: testvm
spec:
runStrategy: Always
template:
spec:
domain:
devices:
disks:
- disk:
bus: virtio
name: containerdisk
- disk:
bus: virtio
name: cloudinitdisk
rng: {}
resources:
requests:
memory: 1024M
terminationGracePeriodSeconds: 0
accessCredentials:
- sshPublicKey:
source:
secret:
secretName: my-pub-key
propagationMethod:
qemuGuestAgent:
users:
- fedora
volumes:
- containerDisk:
image: quay.io/containerdisks/fedora:latest
name: containerdisk
- cloudInitNoCloud:
userData: |-
#cloud-config
password: fedora
chpasswd: { expire: False }
# Disable SELinux for now, so qemu-guest-agent can write the authorized_keys file
# The selinux-policy is too restrictive currently, see open bugs:
# - https://bugzilla.redhat.com/show_bug.cgi?id=1917024
# - https://bugzilla.redhat.com/show_bug.cgi?id=2028762
# - https://bugzilla.redhat.com/show_bug.cgi?id=2057310
bootcmd:
- setenforce 0
name: cloudinitdisk
EOF
Accessing the VMI using virtctl
The user can create a websocket backed network tunnel to a port inside the instance by using the virtualmachineinstances/portforward
subresource of the VirtualMachineInstance
.
One use-case for this subresource is to forward SSH traffic into the VirtualMachineInstance
either from the CLI or a web-UI.
To connect to a VirtualMachineInstance
from your local machine, virtctl
provides a lightweight SSH client with the ssh
command, that uses port forwarding. Refer to the command’s help for more details.
virtctl ssh
To transfer files from or to a VirtualMachineInstance
virtctl
also provides a lightweight SCP client with the scp
command. Its usage is similar to the ssh
command. Refer to the command’s help for more details.
virtctl scp
Using virtctl as proxy
If you prefer to use your local OpenSSH client, there are two ways of doing that in combination with virtctl.
Note: Most of this applies to the
virtctl scp
command too.
- The
virtctl ssh
command has a--local-ssh
option. With this optionvirtctl
wraps the local OpenSSH client transparently to the user. The executed SSH command can be viewed by increasing the verbosity (-v 3
).
virtctl ssh --local-ssh -v 3 testvm
- The
virtctl port-forward
command provides an option to tunnel a single port to your local stdout/stdin. This allows the command to be used in combination with the OpenSSH client’sProxyCommand
option.
ssh -o 'ProxyCommand=virtctl port-forward --stdio=true vmi/testvm.mynamespace 22' fedora@testvm.mynamespace
To provide easier access to arbitrary virtual machines you can add the following lines to your SSH config
:
Host vmi/*
ProxyCommand virtctl port-forward --stdio=true %h %p
Host vm/*
ProxyCommand virtctl port-forward --stdio=true %h %p
This allows you to simply call ssh user@vmi/testvmi.mynamespace
and your SSH config and virtctl will do the rest. Using this method it becomes easy to set up different identities for different namespaces inside your SSH config
.
This feature can also be used with Ansible to automate configuration of virtual machines running on KubeVirt. You can put the snippet above into its own file (e.g. ~/.ssh/virtctl-proxy-config
) and add the following lines to your .ansible.cfg
:
[ssh_connection]
ssh_args = -F ~/.ssh/virtctl-proxy-config
Note that all port forwarding traffic will be sent over the Kubernetes control plane. A high amount of connections and traffic can increase pressure on the API server. If you regularly need a high amount of connections and traffic consider using a dedicated Kubernetes Service
instead.
Example
Create virtual machine and inject SSH public key as explained above
SSH into virtual machine
# Add --local-ssh to transparently use local OpenSSH client
virtctl ssh -i id_rsa fedora@testvm
or
ssh -o 'ProxyCommand=virtctl port-forward --stdio=true vmi/testvm.mynamespace 22' -i id_rsa fedora@vmi/testvm.mynamespace
- SCP file to the virtual machine
# Add --local-ssh to transparently use local OpenSSH client
virtctl scp -i id_rsa testfile fedora@testvm:/tmp
or
scp -o 'ProxyCommand=virtctl port-forward --stdio=true vmi/testvm.mynamespace 22' -i id_rsa testfile fedora@testvm.mynamespace:/tmp
RBAC permissions for Console/VNC/SSH access
Using default RBAC cluster roles
Every KubeVirt installation starting with version v0.5.1 ships a set of default RBAC cluster roles that can be used to grant users access to VirtualMachineInstances.
The kubevirt.io:admin
and kubevirt.io:edit
cluster roles have console, VNC and SSH respectively port-forwarding access permissions built into them. By binding either of these roles to a user, they will have the ability to use virtctl to access the console, VNC and SSH.
Using custom RBAC cluster role
The default KubeVirt cluster roles grant access to more than just the console, VNC and port-forwarding. The ClusterRole
below demonstrates how to craft a custom role, that only allows access to the console, VNC and port-forwarding.
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: allow-console-vnc-port-forward-access
rules:
- apiGroups:
- subresources.kubevirt.io
resources:
- virtualmachineinstances/console
- virtualmachineinstances/vnc
verbs:
- get
- apiGroups:
- subresources.kubevirt.io
resources:
- virtualmachineinstances/portforward
verbs:
- update
When bound with a ClusterRoleBinding
the ClusterRole
above grants access to virtual machines across all namespaces.
In order to reduce the scope to a single namespace, bind this ClusterRole
using a RoleBinding
that targets a single namespace.