You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
82 lines
2.9 KiB
82 lines
2.9 KiB
package main
|
|
|
|
import (
|
|
"crypto/x509"
|
|
_ "embed"
|
|
"log"
|
|
"os"
|
|
"strings"
|
|
)
|
|
|
|
//go:embed certs/cacert.pem
|
|
var mozillaCABundlePEM []byte
|
|
|
|
// loadCABundle returns a *x509.CertPool that can verify VK (and any other
|
|
// TLS site) even on platforms where the system certificate store is empty
|
|
// or unavailable — most notably Android apps that ship without /etc/ssl/certs
|
|
// and have no access to the OS cert store.
|
|
//
|
|
// Strategy:
|
|
// 1. Try x509.SystemCertPool(). If it returns a non-empty pool, use it
|
|
// (this is the normal case on Linux/macOS/Windows desktops and servers).
|
|
// 2. If SystemCertPool is unavailable or empty, fall back to the embedded
|
|
// Mozilla CA bundle (cacert.pem, the same one curl ships). This contains
|
|
// every CA in the Mozilla trust program, including HARICA TLS RSA/ECC
|
|
// Root CA 2021 which VK uses for *.vk.com since mid-2026.
|
|
// 3. Also honor the SSL_CERT_FILE / SSL_CERT_DIR env vars if set, so users
|
|
// can override on unusual systems.
|
|
//
|
|
// Without this, on Android the tls-client (bogdanfinn) leaves RootCAs == nil,
|
|
// which makes Go fall back to SystemCertPool — and on Android that pool is
|
|
// empty, so every HTTPS request fails with:
|
|
//
|
|
// tls: failed to verify certificate: x509: certificate signed by unknown authority
|
|
//
|
|
// See: https://github.com/cacggghp/vk-turn-proxy/issues/182 (and follow-ups).
|
|
func loadCABundle() *x509.CertPool {
|
|
// 1. Honor explicit env overrides first (mirrors crypto/x509 behavior).
|
|
if file := os.Getenv("SSL_CERT_FILE"); file != "" {
|
|
if p, err := loadPoolFromFile(file); err == nil && p != nil {
|
|
log.Printf("[CABundle] using SSL_CERT_FILE=%s", file)
|
|
return p
|
|
}
|
|
}
|
|
|
|
// 2. Try the system pool.
|
|
if sys, err := x509.SystemCertPool(); err == nil && sys != nil && len(sys.Subjects()) > 0 {
|
|
log.Printf("[CABundle] using system cert pool (%d roots)", len(sys.Subjects()))
|
|
return sys
|
|
}
|
|
|
|
// 3. Fall back to the embedded Mozilla bundle.
|
|
pool := x509.NewCertPool()
|
|
if !pool.AppendCertsFromPEM(mozillaCABundlePEM) {
|
|
log.Printf("[CABundle] WARNING: embedded Mozilla bundle did not parse; HTTPS will likely fail")
|
|
} else {
|
|
log.Printf("[CABundle] using embedded Mozilla CA bundle (%d bytes)", len(mozillaCABundlePEM))
|
|
}
|
|
return pool
|
|
}
|
|
|
|
// loadPoolFromFile loads a PEM file with one or more certificates into a fresh
|
|
// CertPool. Returns nil if the file contained no usable certificates.
|
|
func loadPoolFromFile(path string) (*x509.CertPool, error) {
|
|
data, err := os.ReadFile(path)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
pool := x509.NewCertPool()
|
|
if !pool.AppendCertsFromPEM(data) {
|
|
return nil, nil
|
|
}
|
|
return pool, nil
|
|
}
|
|
|
|
// hasHTTPSInsecureEnv returns true if the user explicitly requested skipping
|
|
// TLS verification (useful for debugging MITM proxies or stale CA bundles).
|
|
// This is intentionally NOT exposed as a CLI flag — production should always
|
|
// verify.
|
|
func hasHTTPSInsecureEnv() bool {
|
|
v := strings.ToLower(os.Getenv("VKTURN_INSECURE_TLS"))
|
|
return v == "1" || v == "true" || v == "yes"
|
|
}
|
|
|