VNC from API - Error 401: permission denied - invalid PVE ticket

aleksa

New Member
Oct 20, 2021
13
0
1
24
Hi,

I'm in the process of building a custom UI for Proxmox, utilizing its API.
Backend is C# and frontend is Vue.

For interfacing with Proxmox, I'm using this:
https://github.com/Corsinvest/cv4pve-api-dotnet

I've come to a point where I wanted to implement VNC functionality, and this is the code that I have for handling that:
In the backend on `/api/vnc/`
C-like:
// Note: a lot of this is hardcoded while testing
var host = "vms.dev.local";
var node = "prox1";
var vmid = "100";

var config = pve.Nodes["prox1"].Lxc["100"].Vncproxy.CreateRest(websocket: true).Response.data;
Console.WriteLine(JsonConvert.SerializeObject(config, Formatting.Indented));

return Ok(new Response<VNCResponse>(
    new VNCResponse()
    {
        Ticket = config.ticket,
        Port = ushort.Parse(config.port),
        Url =
            $"https://{host}:8006/" +
            $"?console=lxc" +
            $"&novnc=1" +
            $"&node={node}" +
            $"&resize=off" +
            $"&vmid={vmid}" +
            $"&vmname=hello" + // Hardcoded while testing
            $"&path=api2/json/nodes/{node}/lxc/{vmid}/vncwebsocket/port/{config.port}/vncticket/{HttpUtility.UrlEncode(config.ticket)}"
        }
));
And in the frontend:
HTML:
<template>
    <iframe v-if="!isFetching" :src="vncSrc" frameborder="0" scrolling="no" width="800px" height="600px" />
</template>

<script>
    import api from '../api'

    export default ({
        data() {
            return {
                isFetching: true,
                vncSrc: ''
            }
        },
        async mounted() {
            let vncResponse = await api.get('vnc/')
            if (vncResponse.success === true)
            {    
                this.vncSrc = vncResponse.data.url
                // Sets the cookie for authorizing with Proxmox
                this.$cookies.set('PVEAuthCookie', vncResponse.data.ticket, 0, '/', 'dev.local', true, "None")
                // Makes the iframe load
                this.isFetching = false
            }
        },
    })
</script>

I've mostly been following what has been discussed here (https://github.com/ZzAntares/ProxmoxVE/issues/17#issuecomment-688937674) and adapting it to my use case.

If I go to vms.dev.local (my proxmox instance) and login, then try the VNC, it works, I'm guessing its because the ticket from my root login is still sent and its using it instead.
Then, if I try it in incognito, I get the before mentioned error, aka "permission denied - invalid PVE ticket".
This is the ticket I have set in cookies, an example:
`0:"PVEVNC:616F4F81::B+Nvv4pN2AxfxUaY4QobKMIU6SETof65QDP43ihQmbuxdppQdcISsVbJ6rEnoXDRnfAaojXjK82eRxmGUgnLjNWrmUIZLF5jTFqnQ41tIblL62UwbYChLF5JWEXRolSNLFZfxANQC+qa3m/erKW1iJtBtN20Syc4IXVAd/QSKPlnSMDPUk4/SWPXtZLsrlL1WkHrZkTcOytBjJ70HWbpFj1aVn91dNwCKNFQzqNL+RFniNjra3vA8OEUIiLzr7B0Uj6B2r9DVA/z4WiBCeu6xn6AnVnAL+vI+vPu7+Afao1MUrJu+j6ZQIMH9R+O70IioEM1MDGmj6eXBr4FkJT4mg"`
Browser inspector: https://imgur.com/uMYygVX

Has anyone got any clues as to why this ticket might be invalid?
Is the provided ticket when creating a VNC connection not actually valid for VNC but used for something else?
Do I need a user in proxmox, and use their ticket instead? (Preferably wouldn't want to do this and I want zero access to anything else, other than VNC trough my custom panel - Aka I want to go fully trough my backend and proxmox api)
 
Last edited:
Here's what I ended up doing to fix this in a sense, I created a new role which has only console access:
C-like:
pve.Access.Roles.CreateRole("PVEVMVNC", privs: "VM.Console");
Then, when a user wants to VNC, they have an account created:
C-like:
// Example
var user = "vm100usr@pve";
var password = "temppass";

// Create user if missing
if (pve.Access.Users[user].ReadUser().Response.data == null)
{
    pve.Access.Users.CreateUser(
        user,
        enable: true,
        expire: Helper.SecondsSinceEpoch(DateTime.UtcNow + TimeSpan.FromHours(2)),
        password: password);
}
Then just give them the role, get their login ticket and send that to the frontend where it will be used instead of the VNC ticket:
C-like:
pve.Access.Acl.UpdateAcl($"/vms/{vmid}", "PVEVMVNC", users: user);
var loginTicket = pve.Access.Ticket.CreateTicket(password, user).Response.data.ticket;

return Ok(new Response<VNCResponse>(
    new VNCResponse()
    {
        Ticket = loginTicket,
        VNCTicket = vncConfig.ticket,
        Port = ushort.Parse(vncConfig.port),
        Url =
            $"https://{host}:8006/" +
            $"?console=lxc" +
            $"&xtermjs=1" +
            $"&node={node}" +
            $"&vmid={vmid}"
        }
));

(Also using xtermjs now, seems to work better for LXC and its simpler)
 

About

The Proxmox community has been around for many years and offers help and support for Proxmox VE, Proxmox Backup Server, and Proxmox Mail Gateway.
We think our community is one of the best thanks to people like you!

Get your subscription!

The Proxmox team works very hard to make sure you are running the best software and getting stable updates and security enhancements, as well as quick enterprise support. Tens of thousands of happy customers have a Proxmox subscription. Get yours easily in our online shop.

Buy now!