Caddy ile SSH over HTTPS Tünelleme Nasıl Yapılır

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, socat veya proxytunnel araç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-l4 ile kontrol et.
  • “Handshake failed” hatası: SSL sertifikası sorunlu ya da SNI yanlış yapılandırılmış. openssl s_client ile doğrula.
  • Bağlantı çok yavaş: Compression yes parametresini SSH config’e ekle ve ServerAliveInterval‘i ayarla.
  • Sertifika doğrulama hatası: Test aşamasında verify=0 kullanabilirsin 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.

Yorum yapın