Sunstone SPICE console not working through nginx reverse proxy

Hi All,

I have configured reverse proxy through nginx so as to access Sunstone UI with HTTPs.
Below is my nginx config:

# No squealing.
server_tokens off;

# OpenNebula Sunstone upstream
upstream sunstone {
  server 127.0.0.1:9869;
}
# OpenNebula websocketproxy upstream
upstream websocketproxy {
  server 127.0.0.1:29876;
}
# OpenNebula FireEdge
upstream fireedge {
  server 127.0.0.1:2616;
}

# HTTP virtual host, redirect to HTTPS
server {
    listen 80 default_server;
    return 301 https://$host$request_uri;
}

#
# Example Sunstone configuration (/etc/one/sunstone-server.conf)
#
#:vnc_proxy_port: 127.0.0.1:29876
#:vnc_proxy_support_wss: only
#:vnc_proxy_cert: /etc/ssl/certs/one-selfsigned.crt
#:vnc_proxy_key: /etc/ssl/private/one-selfsigned.key
#:vnc_proxy_ipv6: false
#:vnc_request_password: false
#:vnc_client_port: 443

# HTTPS virtual host, proxy to Sunstone
server {
    listen 443 ssl;
    listen [::]:443 ssl;
    server_name frontend;
    root /usr/share/nginx/html;

    access_log  /var/log/nginx/opennebula-sunstone-access.log;
    error_log  /var/log/nginx/opennebula-sunstone-error.log;

    client_max_body_size 1G;

    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }

    location / {
        # Handle inconsistency in the websockify URLs provided by Sunstone
        if ($args ~* "host=.+&port=.+&token=.+&encrypt=.*") {
            rewrite ^/$ /websockify/ last;
        }
        proxy_pass http://sunstone;
        proxy_redirect     off;
        log_not_found      off;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host $http_host;
        proxy_set_header   X-Forwarded-FOR $proxy_add_x_forwarded_for;
    }

    location /websockify {
        proxy_http_version 1.1;
        proxy_pass https://websocketproxy;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 61s;
        proxy_buffering off;
    }

    ssl_certificate     /etc/ssl/certs/one-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/one-selfsigned.key;
    ssl_stapling on;
}

##
## OpenNebula XML-RPC proxy (optional)
##
upstream onexmlrpc {
  server 127.0.0.1:2633;
}
upstream vncxmlrpc {
  server 127.0.0.1:2644;
}
server {
    listen       2634 ssl;
    listen       [::]:2634 ssl;
    server_name  frontend;
    root         /usr/share/nginx/html;

    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }

    location / {
        proxy_http_version 1.1;
        proxy_pass http://onexmlrpc;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_read_timeout 180s;
        proxy_buffering off;
    }
    location /RPC2/vnctoken {
        proxy_http_version 1.1;
        proxy_pass http://vncxmlrpc;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_buffering off;
    }

    ssl_certificate     /etc/ssl/certs/one-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/one-selfsigned.key;
    ssl_stapling on;
}

# FireEdge
server {
    listen 2646 ssl;
    listen [::]:2646 ssl;
    server_name frontend;
    root /usr/share/nginx/html;

    error_page 404 /404.html;
        location = /40x.html {
    }
    error_page 500 502 503 504 /50x.html;
        location = /50x.html {
    }

    location / {
        proxy_http_version 1.1;
        proxy_pass http://fireedge;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header Host $http_host;
        proxy_buffering off;
    }

    ssl_certificate     /etc/ssl/certs/one-selfsigned.crt;
    ssl_certificate_key /etc/ssl/private/one-selfsigned.key;
}

All seem functional except the SPICE console which is not working.
I have made the appropriate changes at /etc/one/sunstone-server.conf as below:

:vnc_proxy_port: 127.0.0.1:29876
:vnc_client_port: 443
:vnc_proxy_support_wss: only
:vnc_proxy_cert: /etc/ssl/certs/one-selfsigned.crt
:vnc_proxy_key: /etc/ssl/private/one-selfsigned.key
:vnc_proxy_ipv6: false
:vnc_request_password: false
:allow_vnc_federation: no

:public_fireedge_endpoint: https://x.x.x.x:2646

When I access VNC through guacamole seems fine. When I try SPICE console access at a VM that has SPICE enabled at its config, I get an empty grey screen. The console connection is not progressing and the browser seems to be trying to access https://x.x.x.x/spice?socket=d3NzO... where x.x.x.x is the public IP of the Opennebula server/frontend.

I have not been able to figure out where the issue lies.
Any idea or working example with nginx?

Thank you very much
Alex

– Details
Opennebula: 6.8.0 CE
Host: KVM
Guest VMs: Windows11, Debian12.

I notice you use a selfsigned cert, I havent played with Guacamole a lot yet, but in the vnc-proxy time, you had to open ip-address:29876 and make an exception in your browser where you accept the self-signed cert. Not sure if this is still needed with Guacamole, but worth a try, I guess.
Also, I am not sure what display-option you set when deploying a VM, could be “VNC” or “Spice”, I am not sure if you can use both at the same time, so maybe try and deploy a VM with the “Spice” option set, instead of VNC?

Thanks for your reply. Finally I was able to address the issue by adjusting the redirect at nginx as below:

location / {
        # Handle inconsistency in the websockify URLs provided by Sunstone
        if ($args ~* "host=.+&port=.+&password=.+&token=.+&encrypt=.*" ) {
            rewrite ^/$ /websockify/ last;
        }
        proxy_pass http://sunstone;
        proxy_redirect     off;
        log_not_found      off;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   Host $http_host;
        proxy_set_header   X-Forwarded-FOR $proxy_add_x_forwarded_for;
    }

    location /websockify {
        proxy_http_version 1.1;
        proxy_pass https://websocketproxy;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        proxy_read_timeout 61s;
        proxy_buffering off;
    }

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;
    ssl_certificate     /etc/one/ssl/one-selfsigned.crt;
    ssl_certificate_key /etc/one/ssl/one-selfsigned.key;
}

When I was using :vnc_client_port: = 29876 at sunstone config I observed that SPICE console worked and it was connecting to wss://x.x.x.x:29876/?password=null&encrypt=yes&token=mvmsa.... So I set :vnc_client_port: = 443 at sunstone and went at nginx config redirect rule and added &password=.. And it finally worked.

2 Likes