Files
ihasvpn/internal/server/tls.go
T
jcoffey 6c006e1d4d WireGuard server with an embedded admin console
Go backend that drives kernel WireGuard over netlink (wireguard-go as the
fallback), nftables NAT with MSS clamping, forwarding and buffer sysctls,
SQLite for peers, users, sessions, traffic history and the audit log.

React console: dashboard with live rates and usage history, peer management
with QR codes and .conf downloads, disconnect, session reset, key rotation,
expiry, client-supplied keys, settings, users with admin and viewer roles,
two-factor authentication with recovery codes, audit log.

Docker image on Alpine with compose files for bridged and host networking,
CI and GHCR publish workflows, performance notes.
2026-09-12 19:56:08 -07:00

81 lines
2.5 KiB
Go

package server
import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"os"
"path/filepath"
"time"
)
// loadCertificate returns the configured certificate, generating and
// persisting a self-signed one when asked to.
func (s *Server) loadCertificate() (tls.Certificate, error) {
if s.cfg.TLSCert != "" {
cert, err := tls.LoadX509KeyPair(s.cfg.TLSCert, s.cfg.TLSKey)
if err != nil {
return tls.Certificate{}, fmt.Errorf("load TLS certificate: %w", err)
}
return cert, nil
}
certPath := filepath.Join(s.cfg.DataDir, "tls.crt")
keyPath := filepath.Join(s.cfg.DataDir, "tls.key")
if cert, err := tls.LoadX509KeyPair(certPath, keyPath); err == nil {
if leaf, err := x509.ParseCertificate(cert.Certificate[0]); err == nil && time.Now().Before(leaf.NotAfter.Add(-30*24*time.Hour)) {
return cert, nil
}
}
s.log.Info("generating a self-signed TLS certificate", "path", certPath)
key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
if err != nil {
return tls.Certificate{}, err
}
serial, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128))
if err != nil {
return tls.Certificate{}, err
}
host := s.eng.Settings().EndpointHost
tmpl := &x509.Certificate{
SerialNumber: serial,
Subject: pkix.Name{CommonName: "WGX", Organization: []string{"WGX"}},
NotBefore: time.Now().Add(-time.Hour),
NotAfter: time.Now().Add(3 * 365 * 24 * time.Hour),
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
DNSNames: []string{"localhost"},
IPAddresses: []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
}
if host != "" {
if ip := net.ParseIP(host); ip != nil {
tmpl.IPAddresses = append(tmpl.IPAddresses, ip)
} else {
tmpl.DNSNames = append(tmpl.DNSNames, host)
}
}
der, err := x509.CreateCertificate(rand.Reader, tmpl, tmpl, &key.PublicKey, key)
if err != nil {
return tls.Certificate{}, err
}
keyDER, err := x509.MarshalECPrivateKey(key)
if err != nil {
return tls.Certificate{}, err
}
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyDER})
if err := os.WriteFile(certPath, certPEM, 0o644); err != nil {
return tls.Certificate{}, err
}
if err := os.WriteFile(keyPath, keyPEM, 0o600); err != nil {
return tls.Certificate{}, err
}
return tls.X509KeyPair(certPEM, keyPEM)
}