Tarkibga o'tish

TLS konfiguratsiyasi

Self-signed sertifikat darsida server ListenAndServeTLS("cert.pem", "key.pem") bilan ishga tushirildi. Bu holatda barcha TLS sozlamalari standart qiymatlarda qoladi.

Go’ning standart sozlamalari xavfsiz va ko‘p hollarda yetarli. Lekin ba’zan ularni o‘zgartirishga to‘g‘ri keladi: xavfsizlik siyosati faqat TLS 1.3 ni talab qiladi, sertifikatni server to‘xtatilmasdan yangilash kerak yoki klient sertifikatini tekshirish lozim. Bu sozlamalar tls.Config struct’i orqali beriladi.

Standart holatda Go nima qiladi?

Go’ning yangi versiyalarida server tomonidagi TLS sozlamalari quyidagicha:

Sozlama Standart qiymat
Minimal versiya TLS 1.2
Maksimal versiya TLS 1.3
TLS 1.3 shifrlari Barcha zamonaviy shifrlar. Ularni sozlab bo‘lmaydi
TLS 1.2 shifrlari Xavfsiz ro‘yxat. RSA kalit almashish va 3DES olib tashlangan
Kalit almashish X25519, NIST egri chiziqlari va post-kvant gibrid variantlari
HTTP/2 HTTPS’da avtomatik yoqilgan

Bu qiymatlar Go versiyasi bilan birga yangilanadi. Masalan, Go 1.22 da serverning minimal versiyasi TLS 1.0 dan 1.2 ga ko‘tarildi. Go 1.24 da esa post-kvant gibrid kalit almashish (X25519MLKEM768) standart ro‘yxatga qo‘shildi. Go’ni yangilab turish TLS sozlamalarini ham avtomatik zamonaviy holatda saqlaydi.

Tip

Aniq sababsiz standart sozlamalarni o‘zgartirmang. Qo‘lda yozilgan shifrlar ro‘yxati vaqt o‘tishi bilan eskiradi, standart qiymatlar esa Go bilan birga yangilanib boradi.

tls.Config bilan server

cfg := &tls.Config{
    MinVersion: tls.VersionTLS12,
}

srv := &http.Server{
    Addr:      ":8443",
    Handler:   mux,
    TLSConfig: cfg,
}

srv.ListenAndServeTLS("cert.pem", "key.pem")

TLSConfig maydoniga berilgan sozlamalar ListenAndServeTLS ichida ishlatiladi.

Minimal versiya

cfg := &tls.Config{
    MinVersion: tls.VersionTLS13, // faqat TLS 1.3
}

Faqat TLS 1.3 ga ruxsat berish xavfsizroq. Lekin juda eski klientlar, masalan Android 10 dan oldingi qurilmalar yoki eski Java ilovalari, ulana olmay qolishi mumkin. Ommaviy API uchun TLS 1.2 minimal versiya sifatida hali ham keng qo‘llanadi. Ichki servislar orasidagi aloqa uchun esa TLS 1.3 yaxshi tanlov.

Tanlovni API’ga ulanadigan klientlar ro‘yxatiga qarab qiling.

Shifrlar

cfg := &tls.Config{
    MinVersion: tls.VersionTLS12,
    CipherSuites: []uint16{
        tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
        tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
        tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
        tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
        tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
        tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
    },
}

Bu yerda bir nechta muhim jihat bor:

  • CipherSuites faqat TLS 1.0–1.2 uchun ishlaydi. TLS 1.3 shifrlarini Go’da sozlab bo‘lmaydi, ularning hammasi xavfsiz hisoblanadi.
  • Ro‘yxatdagi tartib e’tiborga olinmaydi. Go shifrni o‘zining ichki mantiqi asosida tanlaydi: u protsessorda AES’ni tezlashtiruvchi apparat qo‘llab-quvvatlashi bor-yo‘qligini ham hisobga oladi.
  • PreferServerCipherSuites maydoni eskirgan va hech qanday ta’sir qilmaydi.

Ro‘yxatni faqat xavfsizlik siyosati aniq talab qilsa yozing. Masalan, auditorlar aniq bir ro‘yxatni talab qilgan bo‘lsa.

Go qo‘llab-quvvatlaydigan va xavfsiz deb hisoblaydigan shifrlar ro‘yxatini dastur ichidan ham olish mumkin:

for _, cs := range tls.CipherSuites() {
    fmt.Println(cs.Name, cs.SupportedVersions)
}

tls.InsecureCipherSuites() esa xavfsiz emas deb topilgan shifrlarni qaytaradi.

Kalit almashish

cfg := &tls.Config{
    CurvePreferences: []tls.CurveID{tls.X25519MLKEM768, tls.X25519, tls.CurveP256},
}

CurvePreferences kalit almashish mexanizmlarini belgilaydi. Bo‘sh qoldirilsa, Go standart ro‘yxatni ishlatadi. U post-kvant gibrid variantlarni ham o‘z ichiga oladi. Bu variantlar kelajakda kvant kompyuterlar paydo bo‘lganda bugun yozib olingan trafikni ochish xavfidan himoya qiladi.

Odatda bu maydonni o‘zgartirish kerak emas.

Sertifikatni fayldan emas, kod orqali yuklash

ListenAndServeTLS fayllarni dastur ishga tushganda bir marta o‘qiydi. Sertifikatni boshqa manbadan olish kerak bo‘lsa, masalan muhit o‘zgaruvchisidan yoki secret manager’dan, u tls.Config.Certificates maydoniga beriladi:

cert, err := tls.X509KeyPair(certPEM, keyPEM) // []byte ko‘rinishidagi PEM ma’lumotlar
if err != nil {
    log.Fatal(err)
}

srv := &http.Server{
    Addr:      ":8443",
    Handler:   mux,
    TLSConfig: &tls.Config{Certificates: []tls.Certificate{cert}},
}

srv.ListenAndServeTLS("", "") // fayl nomlari bo‘sh: sertifikat TLSConfig'da

Fayl nomlari bo‘sh satr sifatida berilganda, Go sertifikatni TLSConfig ichidan oladi.

Sertifikatni server to‘xtatilmasdan yangilash

Sertifikatlarning muddati cheklangan. Let's Encrypt sertifikatlari odatda 90 kun amal qiladi. Sertifikat fayli yangilanganda serverni qayta ishga tushirmaslik uchun GetCertificate funksiyasi ishlatiladi. U har bir yangi TLS ulanishida chaqiriladi:

main.go
package main

import (
    "crypto/tls"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"
)

// certReloader sertifikatni xotirada saqlaydi va signal kelganda qayta yuklaydi.
type certReloader struct {
    mu       sync.RWMutex
    cert     *tls.Certificate
    certPath string
    keyPath  string
}

func newCertReloader(certPath, keyPath string) (*certReloader, error) {
    r := &certReloader{certPath: certPath, keyPath: keyPath}
    if err := r.reload(); err != nil {
        return nil, err
    }
    return r, nil
}

func (r *certReloader) reload() error {
    cert, err := tls.LoadX509KeyPair(r.certPath, r.keyPath)
    if err != nil {
        return err
    }
    r.mu.Lock()
    r.cert = &cert
    r.mu.Unlock()
    return nil
}

func (r *certReloader) GetCertificate(*tls.ClientHelloInfo) (*tls.Certificate, error) {
    r.mu.RLock()
    defer r.mu.RUnlock()
    return r.cert, nil
}

func main() {
    reloader, err := newCertReloader("cert.pem", "key.pem")
    if err != nil {
        log.Fatalf("sertifikatni yuklab bo‘lmadi: %v", err)
    }

    // SIGHUP kelganda sertifikat qayta o‘qiladi
    go func() {
        sig := make(chan os.Signal, 1)
        signal.Notify(sig, syscall.SIGHUP)
        for range sig {
            if err := reloader.reload(); err != nil {
                log.Printf("sertifikatni yangilab bo‘lmadi, eskisi ishlatilmoqda: %v", err)
                continue
            }
            log.Println("sertifikat yangilandi")
        }
    }()

    mux := http.NewServeMux()
    mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
        st := r.TLS
        fmt.Fprintf(w, "TLS: %s, shifr: %s, SNI: %q, ALPN: %q\n",
            tls.VersionName(st.Version), tls.CipherSuiteName(st.CipherSuite),
            st.ServerName, st.NegotiatedProtocol)
    })

    srv := &http.Server{
        Addr:    ":8443",
        Handler: mux,
        TLSConfig: &tls.Config{
            MinVersion:     tls.VersionTLS12,
            GetCertificate: reloader.GetCertificate,
        },
        ReadHeaderTimeout: 5 * time.Second,
        ReadTimeout:       10 * time.Second,
        WriteTimeout:      15 * time.Second,
        IdleTimeout:       60 * time.Second,
    }

    log.Printf("Server: https://localhost:8443 (PID %d)", os.Getpid())
    log.Fatal(srv.ListenAndServeTLS("", ""))
}

Sinab ko‘ramiz:

curl --cacert cert.pem https://localhost:8443
curl --cacert cert.pem --tlsv1.2 --tls-max 1.2 https://localhost:8443
TLS: TLS 1.3, shifr: TLS_AES_128_GCM_SHA256, SNI: "localhost", ALPN: "h2"
TLS: TLS 1.2, shifr: TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, SNI: "localhost", ALPN: "h2"
  • Birinchi so‘rovda klient va server TLS 1.3 bo‘yicha kelishdi.
  • Ikkinchisida curl TLS 1.2 dan yuqori versiyani ishlatmadi va server ham shu versiyaga moslashdi.
  • ALPN: "h2" — ulanish HTTP/2 orqali ishlayapti. ALPN (Application-Layer Protocol Negotiation) handshake paytida HTTP versiyasini tanlash imkonini beradi.

Minimal versiyani tekshiramiz. Zamonaviy curl TLS 1.1 ni o‘zi ham ishlatmaydi, shuning uchun eski klientni kichik Go dasturi bilan taqlid qilamiz:

oldclient.go
package main

import (
    "crypto/tls"
    "crypto/x509"
    "fmt"
    "os"
)

func main() {
    pem, _ := os.ReadFile("cert.pem")
    pool := x509.NewCertPool()
    pool.AppendCertsFromPEM(pem)

    for _, v := range []uint16{tls.VersionTLS11, tls.VersionTLS12} {
        conn, err := tls.Dial("tcp", "localhost:8443", &tls.Config{
            RootCAs:    pool,
            MinVersion: tls.VersionTLS10,
            MaxVersion: v,
        })
        if err != nil {
            fmt.Printf("%s: rad etildi (%v)\n", tls.VersionName(v), err)
            continue
        }
        fmt.Printf("%s: ulanish o‘rnatildi\n", tls.VersionName(v))
        conn.Close()
    }
}
go run oldclient.go
TLS 1.1: rad etildi (remote error: tls: protocol version not supported)
TLS 1.2: ulanish o‘rnatildi

Server TLS 1.1 ulanishni rad etdi. Server logida esa quyidagi yozuv paydo bo‘ladi:

http: TLS handshake error from 127.0.0.1:40908: tls: client offered only unsupported versions: [302 301]

Sertifikat faylini yangilagandan keyin serverga signal yuboriladi:

kill -HUP <PID>
2026/09/30 10:05:00 sertifikat yangilandi

Mavjud ulanishlar uzilmaydi. Keyingi yangi ulanishlar esa yangi sertifikatni oladi. Yangi fayl noto‘g‘ri bo‘lsa, eski sertifikat ishlashda davom etadi.

Bir nechta domen: SNI

Bitta server bir nechta domen uchun turli sertifikatlardan foydalanishi mumkin. Certificates maydoniga bir nechta sertifikat berilsa, Go klient SNI orqali so‘ragan domenga mos sertifikatni o‘zi tanlaydi:

cfg := &tls.Config{
    Certificates: []tls.Certificate{apiCert, adminCert},
}

Murakkabroq mantiq kerak bo‘lsa, GetCertificate ichida hello.ServerName qiymatiga qarab tanlash mumkin. Let's Encrypt darsidagi autocert aynan shu usulda ishlaydi.

Klient sertifikatini tekshirish (mTLS)

Odatda faqat klient serverni tekshiradi. mTLS (mutual TLS) da server ham klientdan sertifikat so‘raydi. Bu usul mikroservislar orasidagi aloqada yoki bank API’larida ishlatiladi:

caPEM, _ := os.ReadFile("clients-ca.pem")
pool := x509.NewCertPool()
pool.AppendCertsFromPEM(caPEM)

cfg := &tls.Config{
    ClientCAs:  pool,
    ClientAuth: tls.RequireAndVerifyClientCert,
}

Handler ichida klient sertifikati r.TLS.PeerCertificates[0] orqali olinadi. Undagi Subject.CommonName yoki SAN qiymati klientni aniqlash uchun ishlatiladi.

Sozlamalarni tekshirish

Server TLS sozlamalarini tashqaridan tekshirish uchun vositalar bor:

  • testssl.sh — buyruq qatori vositasi. Lokal va ichki serverlarni ham tekshira oladi;
  • SSL Labs Server Test — ommaviy domenlar uchun onlayn tekshiruv.

Ular qo‘llab-quvvatlanadigan versiyalar, shifrlar va sertifikat zanjiridagi muammolarni ko‘rsatadi.

Xulosa

  • Go’ning standart TLS sozlamalari xavfsiz va har yangi versiyada yangilanadi. Ularni aniq sababsiz o‘zgartirmang.
  • tls.Config http.Serverning TLSConfig maydoni orqali beriladi.
  • MinVersion bilan eski versiyalar o‘chiriladi. TLS 1.3 shifrlarini sozlab bo‘lmaydi, TLS 1.2 shifrlar tartibi esa e’tiborga olinmaydi.
  • GetCertificate sertifikatni server to‘xtatilmasdan yangilash imkonini beradi.
  • Bir nechta domen SNI orqali, klientni tekshirish esa mTLS orqali tashkil qilinadi.

Keyingi darsda Let's Encrypt’dan bepul sertifikatni avtomatik olish va yangilashni ko‘ramiz.