Kurumsal güvenlik duvarları, kısıtlı ağ ortamları veya sıkı port filtrelemesi uygulayan sistemlerde çalışıyorsan, SSH bağlantısı kurmak bazen gerçek bir kabusa dönüşebilir. 22. portu engelleyen bir ağdan sunucuna erişmen gerektiğinde ne yaparsın? İşte tam bu noktada Caddy ile SSH over HTTPS tünelleme devreye giriyor. HTTPS trafiği neredeyse her ağda serbest bırakıldığından, 443 üzerinden SSH trafiğini geçirmek hem pratik hem de güvenli bir çözüm sunuyor.
Bu yazıda Caddy’nin güçlü L4 (Layer 4) proxy özelliğini kullanarak SSH trafiğini HTTPS üzerinden nasıl tünelleyeceğini adım adım anlatacağım. Hem sunucu tarafı yapılandırmasını hem de istemci tarafında gerekli ayarları ele alacağız.
SSH over HTTPS Tünelleme Neden Gerekli?
Çoğu kurumsal ortamda ağ yöneticileri güvenlik politikaları gereği 22. portu dış dünyaya kapatır. Bazı oteller, kafeler ve havalimanı ağları da benzer kısıtlamalar uygular. Şöyle bir senaryo düşün: Müşteri ofisindeyken kendi sunucularına erişmen gerekiyor ama ağ politikası SSH portunu bloke ediyor. HTTPS portunu (443) kapatmak ise pratikte mümkün değil çünkü web tarayıcıları çalışmaz hale gelirdi.
Buradaki temel fikir şu: HTTPS bağlantısı kurulurken TLS handshake gerçekleşiyor. Bu handshake sırasında SNI (Server Name Indication) bilgisi okunabiliyor. Caddy’nin L4 eklentisi bu SNI bilgisini okuyarak gelen trafiğin normal HTTPS mi yoksa SSH tüneli mi olduğunu anlıyor ve yönlendirmeyi buna göre yapıyor.
Gereksinimler ve Ortam
Başlamadan önce şunlara ihtiyacın var:
- Internete açık bir sunucu (VPS, dedicated sunucu vb.)
- Sunucuda çalışan SSH servisi
- Caddy web sunucusu (xcaddy ile derlenmiş L4 eklentili versiyon)
- İstemci tarafında
corkscrew,socatveyaproxytunnelaraçlarından biri - Sunucu için geçerli bir domain adı (Let’s Encrypt için)
Test ortamımız için şu yapıyı kullanacağız:
- Sunucu IP:
203.0.113.10 - Domain:
server.example.com - SSH portu (sunucu iç):
22 - Dış erişim portu:
443
Caddy L4 Eklentisini Derleme
Standart Caddy kurulumu L4 eklentisini içermiyor. Bu yüzden xcaddy ile özel bir Caddy binary’si derlememiz gerekiyor. Önce xcaddy‘yi yükleyelim:
# Go kurulu değilse önce Go yükle
wget https://go.dev/dl/go1.21.0.linux-amd64.tar.gz
sudo tar -C /usr/local -xzf go1.21.0.linux-amd64.tar.gz
export PATH=$PATH:/usr/local/go/bin
# xcaddy'yi yükle
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
export PATH=$PATH:$(go env GOPATH)/bin
# L4 eklentisi ile Caddy'yi derle
xcaddy build
--with github.com/mholt/caddy-l4
--output /usr/local/bin/caddy-l4
# Binary'e çalıştırma izni ver
sudo chmod +x /usr/local/bin/caddy-l4
# Versiyonu doğrula
/usr/local/bin/caddy-l4 version
Derleme tamamlandıktan sonra binary’nin L4 modülünü içerip içermediğini kontrol et:
/usr/local/bin/caddy-l4 list-modules | grep layer4
Çıktıda layer4 ile başlayan modülleri görüyorsan derleme başarılı demektir.
Sunucu Tarafı Yapılandırması
Şimdi asıl işin zevkli kısmına geliyoruz. Caddy’yi hem normal HTTPS trafiğini hem de SSH tüneli trafiğini aynı 443 portunda yönetecek şekilde yapılandıracağız.
SSH Servisini Yapılandırma
Öncelikle SSH servisinin localhost’ta dinlediğinden emin ol. Eğer SSH direkt 0.0.0.0:22’de dinliyorsa sorun yok ama güvenlik açısından sadece localhost’ta dinlemesini sağlamak daha iyi bir yaklaşım:
# /etc/ssh/sshd_config dosyasını düzenle
sudo nano /etc/ssh/sshd_config
# Şu satırları ekle veya düzenle:
# ListenAddress 127.0.0.1
# Port 22
# SSH servisini yeniden başlat
sudo systemctl restart sshd
# Durumu kontrol et
sudo ss -tlnp | grep sshd
Caddyfile Yapılandırması
L4 eklentisi ile Caddy’nin JSON yapılandırmasını kullanmak daha esnek. Ancak önce Caddyfile formatıyla başlayalım, sonra JSON’a geçeceğiz. L4 eklentisi için JSON yapılandırması çok daha güçlü:
sudo nano /etc/caddy/caddy-l4.json
# /etc/caddy/caddy-l4.json içeriği:
{
"apps": {
"layer4": {
"servers": {
"ssh_https_mux": {
"listen": ["0.0.0.0:443"],
"routes": [
{
"match": [
{
"ssh": {}
}
],
"handle": [
{
"handler": "proxy",
"upstreams": [
{
"dial": ["127.0.0.1:22"]
}
]
}
]
},
{
"handle": [
{
"handler": "tls"
},
{
"handler": "proxy",
"upstreams": [
{
"dial": ["127.0.0.1:8080"]
}
]
}
]
}
]
}
}
},
"http": {
"servers": {
"main": {
"listen": ["127.0.0.1:8080"],
"routes": [
{
"match": [
{
"host": ["server.example.com"]
}
],
"handle": [
{
"handler": "static_response",
"body": "Merhaba! Web sunucusu calisiyor.",
"status_code": 200
}
]
}
]
}
}
}
}
}
Bu yapılandırmada Caddy 443 portunda gelen trafiği inceliyor: Eğer SSH protokolü tanıyorsa doğrudan 127.0.0.1:22‘ye yönlendiriyor, değilse TLS termination yapıp HTTP sunucusuna (8080) geçiriyor.
Systemd Servis Dosyası
Özel derlediğimiz Caddy için yeni bir systemd servis dosyası oluşturalım:
sudo nano /etc/systemd/system/caddy-l4.service
[Unit]
Description=Caddy L4 - SSH over HTTPS Tunnel
Documentation=https://caddyserver.com/docs/
After=network.target network-online.target
Requires=network-online.target
[Service]
Type=notify
User=caddy
Group=caddy
ExecStart=/usr/local/bin/caddy-l4 run --config /etc/caddy/caddy-l4.json
ExecReload=/usr/local/bin/caddy-l4 reload --config /etc/caddy/caddy-l4.json
TimeoutStopSec=5s
LimitNOFILE=1048576
PrivateTmp=true
ProtectSystem=full
AmbientCapabilities=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
# Caddy kullanıcısı oluştur (yoksa)
sudo useradd --system --home /var/lib/caddy --shell /bin/false caddy
# Servisi etkinleştir ve başlat
sudo systemctl daemon-reload
sudo systemctl enable caddy-l4
sudo systemctl start caddy-l4
# Durumu kontrol et
sudo systemctl status caddy-l4
sudo ss -tlnp | grep caddy
Alternatif: CONNECT Metodu ile Tünelleme
SSH over HTTPS’in bir diğer popüler yöntemi HTTP CONNECT proxy metodunu kullanmak. Bu yöntemde Caddy bir HTTP proxy gibi davranıyor ve istemci CONNECT isteği göndererek tünel açıyor. Bu yaklaşım özellikle kurumsal proxy arkasındaki istemciler için çok uygun.
Caddyfile ile bu yapılandırma şöyle görünür:
# /etc/caddy/Caddyfile
{
servers {
listener_wrappers {
http_redirect
tls
}
}
}
server.example.com {
# Normal web trafiği
root * /var/www/html
file_server
# SSH için CONNECT proxy
@connect method CONNECT
handle @connect {
reverse_proxy {
to 127.0.0.1:22
transport http {
versions h1
}
}
}
# TLS otomatik (Let's Encrypt)
tls [email protected]
}
İstemci Tarafı Yapılandırması
Sunucu hazır, şimdi istemci tarafını yapılandıralım. Farklı araçlarla birden fazla yöntem göstereceğim.
Yöntem 1: corkscrew ile
corkscrew, HTTP proxy üzerinden SSH tüneli açmak için klasik bir araç:
# Ubuntu/Debian
sudo apt-get install corkscrew
# ~/.ssh/config dosyasına ekle
nano ~/.ssh/config
Host sunucu-tunnel
HostName server.example.com
User kullanici_adi
Port 443
ProxyCommand corkscrew server.example.com 443 %h %p
ServerAliveInterval 30
ServerAliveCountMax 3
IdentityFile ~/.ssh/id_rsa
Bağlantıyı test et:
ssh sunucu-tunnel
Yöntem 2: socat ile
socat çok daha esnek bir araç ve HTTPS tüneli için mükemmel:
# socat kurulumu
sudo apt-get install socat
# Doğrudan bağlantı testi
socat - OPENSSL:server.example.com:443,verify=1
# SSH config'e ekle
nano ~/.ssh/config
Host sunucu-https
HostName server.example.com
User kullanici_adi
Port 22
ProxyCommand socat - OPENSSL:server.example.com:443,verify=1
ServerAliveInterval 60
Compression yes
IdentityFile ~/.ssh/id_ed25519
Yöntem 3: ncat/netcat ile
OpenSSL ve ncat kombinasyonu da işe yarıyor:
# ~/.ssh/config
Host sunucu-ncat
HostName server.example.com
User kullanici_adi
ProxyCommand ncat --ssl server.example.com 443
ServerAliveInterval 30
Gerçek Dünya Senaryoları
Senaryo 1: Kurumsal Firewall Arkasından Erişim
Diyelim ki müşteri ofisinde çalışıyorsun ve şirketin 22. portu bloke ediyor ama web trafiğine izin veriyor. Şirket aynı zamanda bir HTTP proxy kullanıyor (proxy.sirket.com:8080).
# ~/.ssh/config
Host ev-sunucusu
HostName server.example.com
User admin
Port 443
ProxyCommand corkscrew proxy.sirket.com 8080 %h %p
ServerAliveInterval 30
ServerAliveCountMax 5
IdentityFile ~/.ssh/id_ed25519
StrictHostKeyChecking yes
Bu yapılandırmayla şirketin proxy’si üzerinden Caddy’ye HTTPS bağlantısı kuruyorsun, Caddy de trafiği SSH’a yönlendiriyor.
Senaryo 2: Birden Fazla Sunucu İçin Çoğullama
Tek bir Caddy instance’ı ile farklı alt domainler üzerinden birden fazla sunucuya SSH erişimi sağlayabilirsin:
# Caddy L4 JSON yapılandırması - çoklu sunucu
{
"apps": {
"layer4": {
"servers": {
"multi_ssh": {
"listen": ["0.0.0.0:443"],
"routes": [
{
"match": [
{
"tls": {
"sni": ["ssh1.example.com"]
}
}
],
"handle": [
{
"handler": "proxy",
"upstreams": [
{
"dial": ["192.168.1.10:22"]
}
]
}
]
},
{
"match": [
{
"tls": {
"sni": ["ssh2.example.com"]
}
}
],
"handle": [
{
"handler": "proxy",
"upstreams": [
{
"dial": ["192.168.1.20:22"]
}
]
}
]
}
]
}
}
}
}
}
İstemci tarafında:
# ~/.ssh/config
Host iç-sunucu-1
HostName ssh1.example.com
User admin
Port 443
ProxyCommand socat - OPENSSL:ssh1.example.com:443,verify=1
IdentityFile ~/.ssh/id_ed25519
Host iç-sunucu-2
HostName ssh2.example.com
User deploy
Port 443
ProxyCommand socat - OPENSSL:ssh2.example.com:443,verify=1
IdentityFile ~/.ssh/id_deploy
Senaryo 3: SCP ve rsync Üzerinden Dosya Transferi
SSH tüneli çalışıyorsa SCP ve rsync de sorunsuz çalışır:
# SCP ile dosya kopyalama (ssh config kullanarak)
scp -F ~/.ssh/config /yerel/dosya.tar.gz sunucu-tunnel:/uzak/dizin/
# rsync ile senkronizasyon
rsync -avz -e "ssh -F ~/.ssh/config"
/yerel/proje/
sunucu-tunnel:/var/www/proje/
# Doğrudan port belirterek
rsync -avz
-e "socat - OPENSSL:server.example.com:443,verify=1"
/yerel/yedek/
[email protected]:/backup/
Güvenlik Sertleştirme
Tüneli çalıştırdıktan sonra güvenlik açısından birkaç önemli adım atmak gerekiyor.
Fail2ban Entegrasyonu
SSH tüneli açık olduğundan brute-force saldırılarına karşı Fail2ban’ı yapılandır:
# /etc/fail2ban/jail.local
[sshd]
enabled = true
port = 22,443
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600
findtime = 600
UFW Kuralları
Gereksiz portları kapat:
# Mevcut kuralları temizle
sudo ufw --force reset
# Varsayılan politikalar
sudo ufw default deny incoming
sudo ufw default allow outgoing
# Sadece gerekli portları aç
sudo ufw allow 443/tcp comment "HTTPS ve SSH Tunnel"
sudo ufw allow from 10.0.0.0/8 to any port 22 comment "Dahili SSH"
# UFW'yi etkinleştir
sudo ufw enable
sudo ufw status verbose
SSH Anahtar Doğrulamasını Zorunlu Kılma
Parola ile girişi devre dışı bırakmak kritik:
# /etc/ssh/sshd_config
PasswordAuthentication no
PubkeyAuthentication yes
PermitRootLogin prohibit-password
MaxAuthTries 3
LoginGraceTime 30
# Sistemi yeniden yükle
sudo systemctl reload sshd
Sorun Giderme
Tünel çalışmıyorsa şu kontrolleri yap:
# Caddy loglarını incele
sudo journalctl -u caddy-l4 -f
# Port dinleme durumunu kontrol et
sudo ss -tlnp | grep 443
# Dışarıdan bağlantı testi
curl -v https://server.example.com
# SSH bağlantısını verbose modda test et
ssh -vvv -o ProxyCommand="socat - OPENSSL:server.example.com:443,verify=0"
-p 22 [email protected]
# TLS handshake testi
openssl s_client -connect server.example.com:443 -servername server.example.com
Sık karşılaşılan sorunlar ve çözümleri:
- “Connection refused” hatası: Caddy L4 servisi düzgün başlamamış olabilir.
systemctl status caddy-l4ile kontrol et. - “Handshake failed” hatası: SSL sertifikası sorunlu ya da SNI yanlış yapılandırılmış.
openssl s_clientile doğrula. - Bağlantı çok yavaş:
Compression yesparametresini SSH config’e ekle veServerAliveInterval‘i ayarla. - Sertifika doğrulama hatası: Test aşamasında
verify=0kullanabilirsin ama üretimde asla.
Performans Optimizasyonu
Tünel üzerinden bağlantı doğal olarak biraz daha yavaş olur. Performansı artırmak için SSH config’e şunları ekle:
Host sunucu-tunnel
HostName server.example.com
User admin
Port 443
ProxyCommand socat - OPENSSL:server.example.com:443,verify=1
# Bağlantı çoğullama - aynı host için tek bağlantı
ControlMaster auto
ControlPath ~/.ssh/sockets/%r@%h-%p
ControlPersist 600
# Sıkıştırma
Compression yes
# Hızlı şifreleme algoritması
Ciphers [email protected],[email protected]
# Canlı tutma
ServerAliveInterval 30
ServerAliveCountMax 3
Socket dizinini oluşturmayı unutma:
mkdir -p ~/.ssh/sockets
chmod 700 ~/.ssh/sockets
ControlMaster özelliği özellikle kritik: Aynı sunucuya ikinci bir bağlantı açtığında yeni bir TLS handshake yapmak yerine mevcut tüneli kullanıyor. Bu hem hızı artırıyor hem de proxy üzerindeki yükü azaltıyor.
Sonuç
Caddy ile SSH over HTTPS tünelleme, kısıtlı ağ ortamlarında sunucu yönetimini mümkün kılan zarif bir çözüm. L4 eklentisi sayesinde Caddy aynı 443 portunda hem normal HTTPS trafiğini hem de SSH tünelini başarıyla yönetebiliyor. Bu yapılandırmanın en büyük avantajı tek bir IP ve port üzerinde hem web sunucusu hem de SSH erişimi sağlaması.
Özellikle vurgulayan noktalar:
- Caddy L4 eklentisi protokol tespitini otomatik yapıyor, özel bir istemci gerektirmiyor.
- socat istemci tarafında en esnek seçenek, corkscrew ise HTTP proxy’si olan ortamlarda daha iyi çalışıyor.
- ControlMaster performans için kritik, uzun vadeli kullanımda mutlaka etkinleştir.
- Fail2ban entegrasyonu ihmal edilemez, tünel açık olduğundan brute-force saldırıları olası.
Bu kurulum bir kez düzgün yapılandırıldıktan sonra neredeyse sıfır bakım gerektiriyor. Caddy’nin otomatik TLS yenileme özelliği sayesinde sertifika sorunlarıyla da uğraşmıyorsun. Kurumsal güvenlik duvarları, seyahat, uzak çalışma gibi senaryolarda bu tünel sana gerçek bir özgürlük sağlayacak.