diff --git a/common/tls/mkcert.go b/common/tls/mkcert.go new file mode 100644 index 00000000..d11ca934 --- /dev/null +++ b/common/tls/mkcert.go @@ -0,0 +1,50 @@ +package tls + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" + "encoding/pem" + "math/big" + "time" +) + +func GenerateKeyPair(serverName string) (*tls.Certificate, error) { + key, err := rsa.GenerateKey(rand.Reader, 2048) + if err != nil { + return nil, err + } + serialNumber, err := rand.Int(rand.Reader, new(big.Int).Lsh(big.NewInt(1), 128)) + if err != nil { + return nil, err + } + template := &x509.Certificate{ + SerialNumber: serialNumber, + NotBefore: time.Now().Add(time.Hour * -1), + NotAfter: time.Now().Add(time.Hour), + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + Subject: pkix.Name{ + CommonName: serverName, + }, + DNSNames: []string{serverName}, + } + publicDer, err := x509.CreateCertificate(rand.Reader, template, template, key.Public(), key) + if err != nil { + return nil, err + } + privateDer, err := x509.MarshalPKCS8PrivateKey(key) + if err != nil { + return nil, err + } + publicPem := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: publicDer}) + privPem := pem.EncodeToMemory(&pem.Block{Type: "PRIVATE KEY", Bytes: privateDer}) + keyPair, err := tls.X509KeyPair(publicPem, privPem) + if err != nil { + return nil, err + } + return &keyPair, err +} diff --git a/common/tls/std_server.go b/common/tls/std_server.go index 22f1924f..b95101a6 100644 --- a/common/tls/std_server.go +++ b/common/tls/std_server.go @@ -34,6 +34,8 @@ func (c *STDServerConfig) SetNextProtos(nextProto []string) { c.config.NextProtos = nextProto } +var errInsecureUnused = E.New("tls: insecure unused") + func newSTDServer(ctx context.Context, logger log.Logger, options option.InboundTLSOptions) (ServerConfig, error) { if !options.Enabled { return nil, nil @@ -46,6 +48,9 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound if err != nil { return nil, err } + if options.Insecure { + return nil, errInsecureUnused + } } else { tlsConfig = &tls.Config{} } @@ -102,17 +107,23 @@ func newSTDServer(ctx context.Context, logger log.Logger, options option.Inbound } key = content } - if certificate == nil { - return nil, E.New("missing certificate") + if certificate == nil && key == nil && options.Insecure { + tlsConfig.GetCertificate = func(info *tls.ClientHelloInfo) (*tls.Certificate, error) { + return GenerateKeyPair(info.ServerName) + } + } else { + if certificate == nil { + return nil, E.New("missing certificate") + } else if key == nil { + return nil, E.New("missing key") + } + + keyPair, err := tls.X509KeyPair(certificate, key) + if err != nil { + return nil, E.Cause(err, "parse x509 key pair") + } + tlsConfig.Certificates = []tls.Certificate{keyPair} } - if key == nil { - return nil, E.New("missing key") - } - keyPair, err := tls.X509KeyPair(certificate, key) - if err != nil { - return nil, E.Cause(err, "parse x509 key pair") - } - tlsConfig.Certificates = []tls.Certificate{keyPair} } return &STDServerConfig{ config: tlsConfig, diff --git a/option/tls.go b/option/tls.go index 33b5d8d1..72a74966 100644 --- a/option/tls.go +++ b/option/tls.go @@ -3,6 +3,7 @@ package option type InboundTLSOptions struct { Enabled bool `json:"enabled,omitempty"` ServerName string `json:"server_name,omitempty"` + Insecure bool `json:"insecure,omitempty"` ALPN Listable[string] `json:"alpn,omitempty"` MinVersion string `json:"min_version,omitempty"` MaxVersion string `json:"max_version,omitempty"`