← Back to All Articles

Deployment of Kali Linux from AWS for Remote Access through Browser

By Samrat Subedi, Arun Khanal subedisamrat30@gmail.com Posted on 01 Sep 2025
Area of Article:
Cloud Security and Privacy


K


Instant Article — Step by Step

Kali Linux in the Browser with VNC + noVNC (No Nginx)


AWS EC2 ➜ XFCE ➜ VNC ➜ noVNC ➜ HTTPS (self‑signed or Let's Encrypt standalone). Copy‑paste friendly.









Prerequisites



  • AWS account and an EC2 instance from a Kali Linux AMI (t2.medium recommended for smoother UI).

  • SSH access (PuTTY on Windows or OpenSSH).

  • Basic Linux shell familiarity.


Best practice: Allocate an Elastic IP to avoid public‑IP changes after stop/start.



1) Create/Update the Security Group


Allow access only from your workstation where possible.



  • TCP 22 — SSH (source: your IP)

  • TCP 6080 — noVNC (source: your IP; use 0.0.0.0/0 only while testing)

  • TCP 5901 — optional native VNC (source: your IP)

  • TCP 80 — required only for Let's Encrypt standalone issuance


[Screenshot Placeholder: AWS Security Group inbound rules]



2) Install XFCE Desktop & TightVNC


Update packages, install a lightweight desktop, then initialize VNC and set a password. Display :1 maps to TCP 5901.


sudo apt update && sudo apt -y upgrade
sudo apt -y install xfce4 xfce4-goodies tightvncserver

# First run: create password and start display :1
vncserver :1

# If you need to stop and retry later:
vncserver -kill :1

[Screenshot Placeholder: Terminal showing vncserver started; ss -tulnp | grep 5901]



3) Configure XFCE as the VNC Session


Create/replace ~/.vnc/xstartup so VNC launches XFCE instead of the minimal defaults.


mkdir -p ~/.vnc
cat > ~/.vnc/xstartup <<'EOF'
#!/bin/sh
unset SESSION_MANAGER
unset DBUS_SESSION_BUS_ADDRESS
xrdb $HOME/.Xresources
startxfce4 &
EOF
chmod +x ~/.vnc/xstartup

# Restart VNC to apply the new session
vncserver -kill :1 || true
vncserver :1

[Screenshot Placeholder: Editing ~/.vnc/xstartup to startxfce4]



4) Install noVNC and Publish over HTTP


noVNC provides the browser client; websockify proxies WebSocket to the local VNC server.


sudo apt -y install novnc websockify

# Serve the client and proxy to localhost:5901
authbind --deep websockify --web=/usr/share/novnc/ 0.0.0.0:6080 localhost:5901
# ("authbind --deep" is optional; if unavailable, just run websockify as shown below)
# websockify --web=/usr/share/novnc/ 0.0.0.0:6080 localhost:5901

Open http://:6080/vnc.html and connect (the page usually auto‑fills localhost:5901).


[Screenshot Placeholder: Browser on http://IP:6080/vnc.html showing XFCE]



5) Enable HTTPS with a Self‑Signed Certificate (IP‑only)


Encrypts traffic quickly when you do not have a domain. Your browser will warn you about the untrusted certificate.


sudo mkdir -p /etc/ssl/novnc
sudo openssl req -x509 -nodes -newkey rsa:2048 \
-keyout /etc/ssl/novnc/novnc.key \
-out /etc/ssl/novnc/novnc.crt -days 365
# Common Name (CN) = your EC2 public IP
sudo chmod 644 /etc/ssl/novnc/novnc.crt
sudo chmod 600 /etc/ssl/novnc/novnc.key

# Relaunch noVNC with TLS (same port 6080)
sudo websockify --web=/usr/share/novnc/ \
--cert=/etc/ssl/novnc/novnc.crt \
--key=/etc/ssl/novnc/novnc.key \
0.0.0.0:6080 localhost:5901

Visit https://:6080/vnc.html and click through the certificate warning.


[Screenshot Placeholder: Browser warning NET::ERR_CERT_AUTHORITY_INVALID with proceed option]



6) HTTPS with Let's Encrypt (Standalone — No Nginx)


This requires a DNS record (A) pointing a hostname to your EC2 IP; Certbot will temporarily bind to port 80 to solve the challenge.



  1. Create DNS A record: yourhost.example.com → .

  2. Ensure the Security Group allows TCP 80 from the internet.

  3. Issue the certificate with Certbot's standalone mode:


sudo apt -y install certbot
sudo systemctl stop novnc || true # free port 80 if needed
sudo certbot certonly --standalone -d yourhost.example.com

# Start noVNC with the issued certs
sudo websockify --web=/usr/share/novnc/ \
--cert=/etc/letsencrypt/live/yourhost.example.com/fullchain.pem \
--key=/etc/letsencrypt/live/yourhost.example.com/privkey.pem \
0.0.0.0:6080 localhost:5901

Auto‑renewal: sudo crontab -e → add 0 3 * * * certbot renew --quiet. Reload noVNC after renew.

[Screenshot Placeholder: DNS A record and successful certbot output]



7) Make noVNC Persistent with systemd


Keep the service running across reboots. Choose one of the HTTPS variants and adjust paths accordingly.


# Example: self‑signed cert paths
sudo tee /etc/systemd/system/novnc.service >/dev/null <<'EOF'
[Unit]
Description=noVNC WebSocket proxy
After=network.target

[Service]
Type=simple
User=kali
ExecStart=/usr/bin/websockify --web=/usr/share/novnc/ \
--cert=/etc/ssl/novnc/novnc.crt --key=/etc/ssl/novnc/novnc.key \
0.0.0.0:6080 localhost:5901
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl daemon-reload
sudo systemctl enable --now novnc

[Screenshot Placeholder: systemctl status novnc (active/running)]



Verification — Quick Checks


# VNC server listening on 5901?
ss -tulnp | grep 5901

# noVNC / websockify listening on 6080?
ss -tulnp | grep 6080

# Logs for certbot / novnc
sudo journalctl -u novnc -e --no-pager
sudo tail -n 200 /var/log/letsencrypt/letsencrypt.log

[Screenshot Placeholder: ss -tulnp output showing 0.0.0.0:6080 and 127.0.0.1:5901]



Troubleshooting (Fast Fixes)



  • VNC connection refused / timeout: open SG ports; confirm vncserver :1 is running; verify with ss -tulnp | grep 5901.

  • noVNC can't reach VNC: ensure websockify started with localhost:5901 and not bound to localhost:6080 only; prefer 0.0.0.0:6080.

  • "Too many authentication failures" in VNC viewer: wait a minute; then reset with vncpasswd and reconnect.

  • HTTPS cert warning: expected for self‑signed; for trusted lock, use a domain + Let's Encrypt standalone.

  • Certbot NXDOMAIN: DNS A record not propagated or wrong host. Verify with dig yourhost.example.com +short.

  • Systemd unit fails (PID/protocol): avoid template complexities; run plain vncserver :1 and manage with simple scripts if needed.




Security Hardening Checklist



  • Restrict SG to your IP for 22 and 6080 (and 5901 if exposed).

  • Use strong VNC passwords: vncpasswd.

  • Prefer HTTPS (even self‑signed) to encrypt browser traffic; for production, use a domain + Let's Encrypt.

  • Consider SSH tunneling instead of exposing 5901 publicly.

  • Enable basic OS hardening: updates, ufw, disable root SSH, and optional fail2ban.


# Example ufw policy
sudo apt -y install ufw
sudo ufw default deny incoming
sudo ufw allow 22/tcp
sudo ufw allow 6080/tcp
sudo ufw enable



Session Notes & Fixes (Highlights from Our Chat)


Below are the specific issues we hit during the build and the concise fixes you can apply if you run into the same symptoms.


A) Public IP changed after stop/start



  • EC2 assigns a new public IP on every stop/start unless you attach an Elastic IP.

  • What to redo after an IP change:

    • Update your Security Group source IP ranges for ports 22, 6080 (and 5901 if used).

    • If you use a domain, update the DNS A record to the new IP and wait for propagation.

    • noVNC/VNC do not need to be reinstalled; just start them again if they were not enabled as services:


    vncserver -kill :1 || true
    vncserver :1
    sudo systemctl restart novnc || \
    sudo websockify --web=/usr/share/novnc/ 0.0.0.0:6080 localhost:5901



B) Keep noVNC running after closing PuTTY


noVNC started in a terminal stops when that terminal closes. Use one of these:



  • systemd (recommended) — see the Make it Persistent section.

  • tmux — lightweight and quick:


sudo apt -y install tmux
# start a session and run websockify inside
tmux new -s novnc
websockify --web=/usr/share/novnc/ 0.0.0.0:6080 localhost:5901
# detach with Ctrl-b then d; reattach later: tmux attach -t novnc

C) See previous commands & capture output


# Show command history with timestamps
HISTTIMEFORMAT="%F %T "; export HISTTIMEFORMAT; history | tail -n 200

# Your persistent history file
cat ~/.bash_history | tail -n 200

# Record an entire terminal session into a file
script -a ~/session.log # type 'exit' to stop; view later with less ~/session.log

D) VNC: "Too many authentication failures"



  1. Wait ~60s (many servers apply a short lockout).

  2. Reset the VNC password: vncpasswd.

  3. Restart VNC cleanly:
    vncserver -kill :1 || true
    vncserver :1


  4. On the viewer, clear saved credentials & retry.


E) XFCE PolicyKit Agent already exists


This warning appears if multiple authentication agents start. It is harmless and safe to close. To silence it:


# Disable the extra agent (example path may vary by build)
sudo chmod -x /etc/xdg/autostart/polkit-gnome-authentication-agent-1.desktop || true

F) Certbot NXDOMAIN


Let's Encrypt could not find your hostname in DNS. Verify and retry once propagated:


dig +short yourhost.example.com
# If no IP returns, fix the DNS A record first.

G) "Site refused to connect"



  • Confirm listeners: ss -tulnp | grep -E "6080|5901".

  • Security Group allows the ports from your client IP.

  • On HTTPS self‑signed, ensure you started websockify with --cert/--key and run with sudo if key permissions are restricted.


H) EC2 status checks 1/2


When only 1/2 checks pass after boot, wait a minute, then consider Reboot. If it persists, stop/start the instance. Ensure the root volume has free space.


I) TightVNC vs Windows Remote Desktop (RDP)



  • VNC shares an existing GUI session; protocol is bitmap‑based; no Windows RDP client can connect to VNC directly.

  • RDP (via xrdp on Linux) creates its own session and uses the RDP protocol. You can install xrdp to use Windows Remote Desktop, but it is separate from VNC/noVNC.


J) Ports quick reference



  • 22/tcp — SSH

  • 5901/tcp — VNC display :1 (usually bound to localhost)

  • 6080/tcp — noVNC WebSocket & web client


 


© AusJournal —samrat subedi, Arun Khanal