SSHできない環境でもブラウザで画面転送: OCI ComputeにnoVNC+Nginxを構築
目的
SSH接続が許可されない環境でも、サーバの画面転送が必要になることがあります。
そのようなケースに備えて、OCI(Oracle Cloud Infrastructure)のCompute上にVNC環境を用意し、noVNCを使ってブラウザからHTTPSで操作できる構成を作ります。
最終的には、利用者はブラウザで次のURLへアクセスするだけです。
https://<COMPUTE_PUBLIC_IP_OR_FQDN>/vnc.html
SSHトンネルは使いません。外部に公開するポートはHTTPSの 443 のみです。
全体構成
Browser
-> HTTPS :443
-> Nginx
-> noVNC/websockify 127.0.0.1:6080
-> VNC 127.0.0.1:5901
-> Desktop session
ポイントは、6080 と 5901 を外部公開しないことです。
外部からはNginxの 443 だけを開け、Nginxが内部のnoVNCへリバースプロキシします。
| 用途 | ポート | 外部公開 |
|---|---|---|
| HTTPS入口 | 443 |
する |
| noVNC/websockify | 6080 |
しない |
| VNC | 5901 |
しない |
前提
- OCI ComputeにSSHログインできること
- Compute上で
sudoが使えること - ComputeからOSリポジトリ、GitHub、PyPIへ到達できること
- OCI Security ListまたはNSGで
TCP/443を許可できること
外部通信できないComputeの場合、dnf、git clone、pip install が失敗します。その場合はNAT Gateway、またはオフライン転送などを使います。
1. noVNCセットアップスクリプトを配置
管理端末からComputeへスクリプトを転送します。
scp /path/to/setup-novnc-server.sh \
<SSH_USER>@<COMPUTE_PUBLIC_IP_OR_FQDN>:~/novnc-setup/setup-novnc-server.sh
Compute側で実行します。
mkdir -p ~/novnc-setup
cd ~/novnc-setup
chmod +x setup-novnc-server.sh
./setup-novnc-server.sh
途中でVNCパスワードを聞かれます。これはLinuxログインパスワードではなく、noVNC画面で入力するVNC専用パスワードです。
Password:
Verify:
Would you like to enter a view-only password (y/n)?
操作用に使う場合、view-only passwordは通常 n でOKです。
2. setup-novnc-server.sh
以下がサーバ側セットアップスクリプトです。
#!/usr/bin/env bash
set -euo pipefail
# Server-side setup for noVNC on a compute instance.
# noVNC and VNC are bound to 127.0.0.1 so they can be exposed safely
# through a local reverse proxy such as Nginx on HTTPS/443.
VNC_DISPLAY="${VNC_DISPLAY:-1}"
VNC_GEOMETRY="${VNC_GEOMETRY:-1440x900}"
VNC_DEPTH="${VNC_DEPTH:-24}"
NOVNC_PORT="${NOVNC_PORT:-6080}"
VNC_PORT=$((5900 + VNC_DISPLAY))
USER_HOME="$(cd ~ && pwd)"
log() {
printf '\n[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}
need_cmd() {
command -v "$1" >/dev/null 2>&1
}
install_packages() {
log "Installing desktop, VNC, and Python prerequisites"
if need_cmd dnf; then
sudo dnf install -y \
tigervnc-server \
tigervnc-server-module \
python3 \
python3-pip \
git \
xterm \
dbus-x11 \
xorg-x11-xauth \
xorg-x11-server-Xvfb || {
log "Package install failed. If this instance has no outbound repo access, attach a NAT gateway, mirror repo, OS Management/OSMH source, or install RPMs offline."
exit 1
}
if ! rpm -q xfce4-session >/dev/null 2>&1; then
sudo dnf install -y @xfce-desktop-environment || true
fi
elif need_cmd yum; then
sudo yum install -y \
tigervnc-server \
python3 \
python3-pip \
git \
xterm \
dbus-x11 \
xorg-x11-xauth || {
log "Package install failed. If this instance has no outbound repo access, attach a NAT gateway, mirror repo, OS Management/OSMH source, or install RPMs offline."
exit 1
}
sudo yum groupinstall -y "Xfce" || true
elif need_cmd apt-get; then
sudo apt-get update
sudo apt-get install -y \
tigervnc-standalone-server \
tigervnc-common \
python3 \
python3-pip \
git \
xfce4 \
xfce4-goodies \
xterm \
dbus-x11
else
log "No supported package manager found. Install TigerVNC, noVNC, websockify, and a desktop environment manually."
exit 1
fi
}
install_websockify_if_needed() {
if need_cmd websockify || python3 -c 'import websockify' >/dev/null 2>&1; then
return 0
fi
log "Installing websockify with pip"
python3 -m pip install --user websockify || {
log "Could not install websockify with pip. If this instance has no outbound access, install the websockify Python package offline."
exit 1
}
export PATH="${USER_HOME}/.local/bin:${PATH}"
}
install_novnc_if_needed() {
if find_novnc_proxy >/dev/null 2>&1; then
return 0
fi
if [ -f "${USER_HOME}/noVNC/utils/novnc_proxy" ] || [ -f "${USER_HOME}/noVNC/utils/novnc_proxy.py" ]; then
return 0
fi
log "Installing noVNC under ${USER_HOME}/noVNC"
git clone --depth 1 https://github.com/novnc/noVNC.git "${USER_HOME}/noVNC" || {
log "Could not clone noVNC. If this instance has no outbound access, copy a noVNC checkout to ${USER_HOME}/noVNC from your local machine."
exit 1
}
}
configure_vnc() {
log "Configuring VNC for display :${VNC_DISPLAY}"
mkdir -p "${USER_HOME}/.vnc"
if [ ! -f "${USER_HOME}/.vnc/passwd" ]; then
printf 'Set a VNC password. This protects the desktop session behind noVNC.\n'
vncpasswd
fi
cat >"${USER_HOME}/.vnc/xstartup" <<'EOF'
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
if command -v startxfce4 >/dev/null 2>&1; then
exec startxfce4
fi
if command -v gnome-session >/dev/null 2>&1; then
exec gnome-session
fi
exec xterm
EOF
chmod +x "${USER_HOME}/.vnc/xstartup"
}
start_vnc() {
log "Starting TigerVNC on 127.0.0.1:${VNC_PORT}"
vncserver -kill ":${VNC_DISPLAY}" >/dev/null 2>&1 || true
vncserver ":${VNC_DISPLAY}" \
-localhost yes \
-geometry "${VNC_GEOMETRY}" \
-depth "${VNC_DEPTH}"
}
find_novnc_proxy() {
for candidate in \
"${USER_HOME}/noVNC/utils/novnc_proxy" \
"${USER_HOME}/noVNC/utils/novnc_proxy.py" \
/usr/share/novnc/utils/novnc_proxy \
/usr/share/novnc/utils/novnc_proxy.py \
/usr/share/noVNC/utils/novnc_proxy \
/usr/share/noVNC/utils/novnc_proxy.py; do
if [ -x "$candidate" ] || [ -f "$candidate" ]; then
printf '%s\n' "$candidate"
return 0
fi
done
return 1
}
start_novnc() {
local proxy
proxy="$(find_novnc_proxy)" || {
log "Could not find novnc_proxy. Check the noVNC package installation."
exit 1
}
log "Starting noVNC on 127.0.0.1:${NOVNC_PORT}, forwarding to 127.0.0.1:${VNC_PORT}"
pkill -f "novnc_proxy.*${NOVNC_PORT}" >/dev/null 2>&1 || true
nohup "$proxy" \
--listen "127.0.0.1:${NOVNC_PORT}" \
--vnc "127.0.0.1:${VNC_PORT}" \
>"${USER_HOME}/novnc-${NOVNC_PORT}.log" 2>&1 &
sleep 2
if ! pgrep -f "novnc_proxy.*${NOVNC_PORT}" >/dev/null 2>&1; then
log "noVNC did not stay running. See ${USER_HOME}/novnc-${NOVNC_PORT}.log"
exit 1
fi
}
print_next_steps() {
cat <<EOF
Server setup completed.
noVNC is listening locally on this compute instance:
http://127.0.0.1:${NOVNC_PORT}/vnc.html
VNC is listening locally on:
127.0.0.1:${VNC_PORT}
For HTTPS browser access, put Nginx or another reverse proxy in front of
127.0.0.1:${NOVNC_PORT} and expose only TCP/443 externally.
Useful server checks:
vncserver -list
pgrep -af 'novnc_proxy|websockify|Xtigervnc'
tail -n 100 ~/novnc-${NOVNC_PORT}.log
EOF
}
install_packages
install_websockify_if_needed
install_novnc_if_needed
configure_vnc
start_vnc
start_novnc
print_next_steps
3. 起動確認
vncserver -list
pgrep -af 'novnc_proxy|websockify|Xtigervnc'
tail -n 20 ~/novnc-6080.log
noVNCは 127.0.0.1:6080、VNCは 127.0.0.1:5901 で待ち受けていればOKです。
4. OCIで443を開ける
OCI Consoleで対象ComputeのSecurity ListまたはNSGに、HTTPS用のingress ruleを追加します。
推奨は接続元IPを絞ることです。
Source Type: CIDR
Source CIDR: <YOUR_GLOBAL_IP>/32
IP Protocol: TCP
Destination Port Range: 443
検証だけなら 0.0.0.0/0 でも動きますが、本番運用では避けます。
5. NginxでHTTPS化する
Nginx、Basic認証用の htpasswd、OpenSSLを入れます。
sudo dnf install -y nginx httpd-tools openssl
Basic認証ユーザーを作ります。
sudo htpasswd -c /etc/nginx/.novnc_htpasswd novncuser
検証用の自己署名証明書を作ります。
sudo mkdir -p /etc/nginx/tls
sudo openssl req -x509 -nodes -days 365 \
-newkey rsa:2048 \
-keyout /etc/nginx/tls/novnc.key \
-out /etc/nginx/tls/novnc.crt \
-subj "/CN=<COMPUTE_PUBLIC_IP_OR_FQDN>"
Nginxのリバースプロキシ設定を作ります。
sudo tee /etc/nginx/conf.d/novnc.conf >/dev/null <<'EOF'
server {
listen 443 ssl;
server_name _;
ssl_certificate /etc/nginx/tls/novnc.crt;
ssl_certificate_key /etc/nginx/tls/novnc.key;
auth_basic "noVNC";
auth_basic_user_file /etc/nginx/.novnc_htpasswd;
location / {
proxy_pass http://127.0.0.1:6080;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_read_timeout 3600;
proxy_send_timeout 3600;
}
}
EOF
SELinuxでproxyが止まる場合に備えて、Nginxからローカルポートへ接続できるようにします。
sudo setsebool -P httpd_can_network_connect 1
firewalldを使っている場合はHTTPSを開けます。
sudo firewall-cmd --add-service=https --permanent
sudo firewall-cmd --reload
Nginxを起動します。
sudo nginx -t
sudo systemctl enable --now nginx
sudo systemctl restart nginx
sudo ss -lntp | grep ':443'
次のように nginx が 0.0.0.0:443 で待ち受けていればOKです。
LISTEN ... 0.0.0.0:443 ... users:(("nginx",...))
6. ブラウザから接続
ブラウザで開きます。
https://<COMPUTE_PUBLIC_IP_OR_FQDN>/vnc.html
流れは次の通りです。
- 自己署名証明書の警告を許可
- Nginx Basic認証を入力
- noVNC画面でVNCパスワードを入力
- GUIデスクトップが表示される
トラブルシューティング
ERR_CONNECTION_REFUSED
Compute側でNginxが 443 をlistenしていない可能性があります。
sudo nginx -T | grep -n "conf.d\|listen 443\|novnc"
sudo systemctl restart nginx
sudo ss -lntp | grep ':443'
ERR_TIMED_OUT
OCI Security List / NSG、またはOS firewallで 443 が通っていない可能性があります。
502 Bad Gateway
NginxからnoVNCへ接続できていません。
pgrep -af 'novnc_proxy|websockify'
tail -n 50 ~/novnc-6080.log
まとめ
OCI Compute上に TigerVNC + noVNC + Nginx を構成すると、SSHトンネルなしでブラウザからHTTPSのみの画面転送環境を作れます。
外部公開は 443 だけにし、6080 と 5901 は 127.0.0.1 に閉じるのがポイントです。
検証では自己署名証明書でも動作しますが、本番運用では正式なTLS証明書、接続元IP制限、強力な認証方式を組み合わせることを推奨します。
