CONTENTS
0 why teleport — the pitch
1 main components — the parts
2 the bastion principle — the idea
3 why teleport & why a bastion — the rationale
4 the setup — target architecture
5 reverse proxy — nginx + certbot
6 teleport — the server
7 conclusion — next steps
──[ 0. Why Teleport ]──
What I like about Teleport as a "gateway" into your Homelab:
- Strong auth with MFA and WebAuthn
- One way in for everything (SSH, web apps, Kubernetes, databases…)
- Centralised access management with RBAC
- A modern, decent-looking web UI
- Full audit of every session and action
- Zero Trust by design
Unlike a classic VPN — often blocked everywhere — Teleport reaches all your
resources over HTTPS-encapsulated traffic (rarely blocked), and makes cert and
auth management way less painful.
And where a VPN drops you onto the whole network (real security risk), Teleport
lets you say exactly who can reach what, when, and how.
──[ 1. Main Components ]──
Teleport is split into a few services you can install together or separately,
depending on what you need:
Auth Service
The heart. Handles authentication and authorisation, holds the
user DB, issues short-lived certs, enforces access policies.
Proxy Service
The public entry point that routes connections to internal
resources. Hosts the web UI, handles secure tunnelling.
SSH Service
SSH access to servers, with session recording, replacing the
traditional SSH keys with certs.
Application Service
Access to internal web apps without touching them.
Database Service
Secure DB access with identity management.
These can all live on one machine for a simple setup (perfect for a Homelab), or
spread across several servers if you need to scale.
──[ 2. The Bastion Principle ]──
For people new to Teleport and bastions: a bastion is a server that's exposed
(not necessarily on the internet) and on which auth is strict.
Most importantly, a bastion is a *controlled* way into your resources. Think
reverse proxy, but with security as the main job.
──[ 3. Why Teleport & Why a Bastion ]──
Plenty of solutions let you reach your Homelab from outside — open source
(Apache Guacamole…) and closed (WALLIX, CyberArk…).
But to me none is as well-built and easy to use as Teleport, for a few reasons:
1. Modern, well-documented, active community.
2. The Community edition is free for individuals and small teams.
3. Native Zero Trust with no shortcuts on security.
4. Plays well with modern tooling (Kubernetes, cloud providers…).
5. Audit and compliance built in from day one.
About the bastion: I've often been stuck without VPN for whatever reason —
corporate firewall, no local admin on the workstation, not my own machine,
network restrictions, you name it.
A bastion lets you reach your stuff with nothing but a web browser. That works
almost anywhere.
──[ 4. The Setup ]──
Onto the implementation.
What we're aiming for is what a secure, professional-grade Homelab would look
like. Adapt as needed.
End state:
- One main Teleport server: our secure entry point to everything else.
- One Nginx server: reverse proxy and SSL termination.
- Private client servers, reachable through the bastion.
- Public servers, directly accessible on the internet.
Target architecture:
With this kind of setup you expose each piece the way it deserves: public for
what can be public (website, blog, public services…), private for what shouldn't
be exposed but that you still need to drive remotely from the internet, without
VPN.
NOTE: Reverse proxy and bastion can sit on the same machine for
simplicity. For production, split them.
──[ 5. Reverse Proxy ]──
For this demo, the goal is to expose a bastion + its hosts, plus a public blog
that sits outside the bastion. For more services, repeat the steps and update
configs.
Throughout, the example domain is `domain.test` — swap in yours.
Prereqs:
[ ] A public IP. For a Homelab you can usually request a Full-Stack
IPv4 from your ISP (free for most); a VPS works too.
[ ] A domain with full DNS control (OVH, Cloudflare…).
[ ] Machines (a hypervisor like Proxmox or VMware helps).
5.1 Server prep
Create a machine (or reuse the Teleport one) and install the bits:
# System update
sudo apt update && sudo apt upgrade -y
# Nginx
sudo apt install nginx -y
# Certbot for Let's Encrypt (with the Cloudflare DNS plugin)
sudo apt install curl certbot python3-certbot-nginx \
python3-certbot-dns-cloudflare -y
# Teleport (pin a stable version)
TELEPORT_EDITION="oss"
TELEPORT_VERSION="17.6.0"
curl https://cdn.teleport.dev/install.sh | bash -s \
${TELEPORT_VERSION?} ${TELEPORT_EDITION?}
5.2 DNS configuration
Create the public DNS records at your provider:
*.domain.test -> your public IP (wildcard for Teleport)
domain.test -> your public IP (apex domain)
blog.domain.test -> your public IP (example public service)
5.3 TLS certificates
For wildcards, DNS-01 is the way. First, store your Cloudflare API creds:
sudo mkdir -p /root/.secrets
sudo nano /root/.secrets/cloudflare.ini
# /root/.secrets/cloudflare.ini
dns_cloudflare_api_token = your_scoped_api_token
Tip: use a scoped API *token* (Zone:DNS:Edit) over the legacy global
API key. Same result, way less to lose if the file leaks.
Then issue the certs:
# Lock the file down
sudo chmod 600 /root/.secrets/cloudflare.ini
# Wildcard certificate for Teleport
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
--dns-cloudflare-propagation-seconds 30 \
-d '*.domain.test' -d domain.test
# Certificate for the blog
sudo certbot certonly \
--dns-cloudflare \
--dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
--dns-cloudflare-propagation-seconds 30 \
-d blog.domain.test
5.4 Nginx, the blog (public service)
# /etc/nginx/conf.d/blog.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream blog_backend {
server 192.168.1.4:80; # private IP of your blog server
keepalive 32;
}
server {
listen 443 ssl;
http2 on;
server_name blog.domain.test;
ssl_certificate /etc/letsencrypt/live/blog.domain.test/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/blog.domain.test/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options DENY always;
add_header X-Content-Type-Options nosniff always;
client_max_body_size 100M;
proxy_read_timeout 600s;
location / {
proxy_pass http://blog_backend;
proxy_http_version 1.1;
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 $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
}
server {
listen 80;
listen [::]:80;
server_name blog.domain.test;
return 301 https://$host$request_uri;
}
5.5 Nginx, Teleport
# /etc/nginx/conf.d/teleport.conf
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream teleport_backend {
server 192.168.1.2:443; # IP of your Teleport server
keepalive 32;
}
server {
listen 443 ssl;
http2 on;
server_name *.domain.test domain.test;
ssl_certificate /etc/letsencrypt/live/domain.test/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.test/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Re-encrypt to Teleport and carry the right SNI
proxy_ssl_server_name on;
proxy_ssl_name $host;
location / {
proxy_pass https://teleport_backend;
proxy_http_version 1.1;
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 $scheme;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_read_timeout 600s;
proxy_buffering off;
}
}
server {
listen 80;
listen [::]:80;
server_name *.domain.test domain.test;
return 301 https://$host$request_uri;
}
Test and reload:
sudo nginx -t
sudo systemctl reload nginx
sudo systemctl status nginx
──[ 6. Teleport ]──
Now the main Teleport server. Once installed, one main file is enough to get
going.
6.1 Basic config
sudo nano /etc/teleport.yaml
# /etc/teleport.yaml
version: v3
teleport:
nodename: bastion-teleport
data_dir: /var/lib/teleport
log:
output: stderr
severity: INFO
diag_addr: "127.0.0.1:3000" # internal diagnostics
auth_service:
enabled: true
cluster_name: "domain.test"
listen_addr: 0.0.0.0:3025
proxy_listener_mode: multiplex
authentication:
type: local
second_factor: webauthn # security keys (YubiKey, etc.)
webauthn:
rp_id: domain.test
session_recording: node-sync
tokens:
- "proxy,node:your-secret-token-here"
ssh_service:
enabled: true
listen_addr: 0.0.0.0:3022
x11:
enabled: true
proxy_service:
enabled: true
web_listen_addr: 0.0.0.0:443
tunnel_listen_addr: 0.0.0.0:3024
public_addr: domain.test:443
acme:
enabled: false # TLS is handled by Nginx + Let's Encrypt
# Trust the X-Forwarded-* headers coming from the reverse proxy
trust_x_forwarded_for: true
# Application Service, preferred for exposing specific web UIs
app_service:
enabled: true
apps:
- name: pfsense
uri: https://192.168.1.1
public_addr: "pfsense.domain.test"
insecure_skip_verify: true
labels:
env: "production"
team: "infrastructure"
- name: nas
uri: http://192.168.1.10:5000
public_addr: "nas.domain.test"
insecure_skip_verify: true
labels:
env: "production"
team: "storage"
Note: `insecure_skip_verify: true` skips TLS verification to the
backend app. Fine on a trusted LAN segment; tighten it if the path
to the app isn't fully yours.
6.2 Start it up
# Validate the config
sudo teleport configure --test
# Enable and start
sudo systemctl enable teleport
sudo systemctl start teleport
# Status and logs
sudo systemctl status teleport
sudo journalctl -u teleport -f
6.3 First admin
sudo tctl users add admin --roles=editor,access \
--logins=root,admin,john
6.4 Smoke test
Once configured you should be able to:
1. Hit the web UI at https://domain.test
2. See your apps under the Applications tab
3. SSH into the servers you added
4. Open web apps via their configured subdomains
──[ 7. Conclusion ]──
Your Homelab infra is up. Plenty of room to grow from here, but this is a solid
base for hosting your services, reachable securely from anywhere, with nothing
but a browser.
References:
[0] Teleport documentation
https://goteleport.com/docs/
[1] Configuration examples
https://github.com/gravitational/teleport/tree/master/examples
[2] Teleport community
https://goteleport.com/community/
[3] Security best practices
https://goteleport.com/docs/admin-guides/access-controls/
──[ EOF ]──