TLS
Hertz supports TLS secure transmission, helping users achieve data confidentiality and integrity.
If you need TLS, Please use go net lib instead. netpoll is now working on it but not ready yet.
In tls.Config
, the parameters that the server and client both can use are as follows:
Parameter | Introduce |
---|---|
Certificates | Used to add certificates, multiple certificates can be configured. Both ends automatically select the first certificate for verification. |
VerifyPeerCertificate | Used to verify the peer certificate. Called after certificate verification on either side. |
VerifyConnection | After both certificates are verified, TLS connection verification is performed. |
NextProtos | Used to set supported application layer protocols. |
CipherSuites | Used to negotiate encryption policies, supports TLS 1.0-1.2. |
MaxVersion | Used to set the maximum version supported by TLS, currently 1.3. |
Server
Hertz provides the WithTLS
Option in the server
package for configuring TLS services. But currently Hertz only supports TLS in the standard network library, Netpoll network library support is still on the way.
The Transporter
of WithTLS
is set by default to the Transporter
of the standard library.
// WithTLS sets TLS config to start a tls server.
// NOTE: If a tls server is started, it won't accept non-tls request.
func WithTLS(cfg *tls.Config) config.Option {
return config.Option{F: func(o *config.Options) {
route.SetTransporter(standard.NewTransporter)
o.TLS = cfg
}}
}
Parameter
In tls.Config
, in addition to the above basic parameters, the parameters that can be configured by the server are as follows:
Parameter | Introduce |
---|---|
GetCertificate | Returns a certificate based on client SNI information or when the certificate set is empty. |
GetClientCertificate | It is used to return the client certificate when the server requires to verify the client certificate. |
GetConfigForClient | When the server receives ClientHello from the client, it returns the configuration information. If non-empty configuration information is returned, it will be used for this TLS connection. |
ClientAuth | Used for client authentication policy settings, defaults to NoClientCert . |
ClientCAs | When ClientAuth is enabled, used to verify the authenticity of the client certificate. |
The main process of server-side TLS:
- Load the root certificate to verify the authenticity of the client.
- Load the server certificate to send to the client to verify the authenticity of the server.
- Configure
tls.Config
. - Use
WithTLS
to configure server-side TLS. By default, the standard library’s Transporter is used.
Sample Code
ca.key
, ca.crt
, server.key
and server.crt
in this example are all generated by openssl.
First generate the CA’s private key and certificate, the command is as follows:
openssl ecparam -genkey -name prime256v1 -out ca.key
openssl req -new -key ca.key -out ca.req
# country=cn, common name=ca.example.com
openssl x509 -req -in ca.req -signkey ca.key -out ca.crt -days 365
Generate the private key and certificate of the server through CA signature, the command is as follows:
openssl ecparam -genkey -name prime256v1 -out server.key
openssl req -new -key server.key -out server.req
# country=cn, common name=server.example.com
openssl x509 -req -in server.req -CA ca.crt -CAkey ca.key -out server.crt -CAcreateserial -days 365
Server-side sample code:
package main
// ...
func main() {
// load server certificate
cert, err := tls.LoadX509KeyPair("./tls/server.crt", "./tls/server.key")
if err != nil {
fmt.Println(err.Error())
}
// load root certificate
certBytes, err := ioutil.ReadFile("./tls/ca.crt")
if err != nil {
fmt.Println(err.Error())
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(certBytes)
if !ok {
panic("Failed to parse root certificate.")
}
// set server tls.Config
cfg := &tls.Config{
// add certificate
Certificates: []tls.Certificate{cert},
MaxVersion: tls.VersionTLS13,
// enable client authentication
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: caCertPool,
// cipher suites supported
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
},
// set application protocol http2
NextProtos: []string{http2.NextProtoTLS},
}
// set TLS server
// default is standard.NewTransporter
h := server.Default(server.WithTLS(cfg), server.WithHostPorts(":8443"))
h.GET("/ping", func(ctx context.Context, c *app.RequestContext) {
c.String(consts.StatusOK, "TLS test\n")
})
h.Spin()
}
For a complete usage example, see example .
Client
Parameter
In tls.Config
, in addition to the above basic parameters, the parameters that can be configured by the client are as follows:
Parameter | Introduce |
---|---|
ServerName | Validate the hostname against the returned certificate information. |
InsecureSkipVerify | It is used for whether the client enables server-side certificate verification. |
RootCAs | The certificate used by the client to authenticate the server. |
The main process of client-side TLS:
- Load the root certificate to verify the authenticity of the server.
- Load the client certificate for sending to the server to verify the authenticity of the client.
- Configure
tls.Config
. - Use
WithTLS
to configure client-side TLS, which uses the standard library’s Dialer by default.
Sample Code
Generate the client’s private key and certificate through CA signature, the command is as follows:
openssl ecparam -genkey -name prime256v1 -out client.key
openssl req -new -key client.key -out client.req
# country=cn, common name=client.example.com
openssl x509 -req -in client.req -CA ca.crt -CAkey ca.key -out client.crt -CAcreateserial -days 365
Client sample code:
package main
// ...
func main() {
// load root certificate to verify the client validity
certBytes, err := ioutil.ReadFile("./tls/ca.crt")
if err != nil {
fmt.Println(err.Error())
}
caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(certBytes)
if !ok {
panic("Failed to parse root certificate.")
}
// load client certificate to send to server
cert, err := tls.LoadX509KeyPair("./tls/client.crt", "./tls/client.key")
if err != nil {
fmt.Println(err.Error())
}
// set TLS configuration
cfg := &tls.Config{
MaxVersion: tls.VersionTLS13,
Certificates: []tls.Certificate{cert},
// verify the server certificate
RootCAs: caCertPool,
// ignored the server certificate
InsecureSkipVerify: true,
}
c, err := client.NewClient(
// default dialer is standard
client.WithTLSConfig(cfg),
client.WithDialer(standard.NewDialer()),
)
if err != nil {
fmt.Println(err.Error())
}
// ...
}
Note:Currently, Hertz TLS server is not supported Netpoll network library temporarily.
c, err := client.NewClient(client.WithTLSConfig(cfg), client.WithDialer(netpoll.NewDialer())
support is still on the way.
For a complete usage example, see example .
Autotls Middleware
Hertz provides autotls extension adaptation Let’s Encrypt, which is convenient for users to automatically configure TLS services.
Installation
go get github.com/hertz-contrib/autotls
Configuration
NewTlsConfig
The autotls
extension provides NewTlsConfig
to help users support LetsEncrypt HTTPS servers with one line of code.
The NewTlsConfig function signature is as follows:
func NewTlsConfig(domains ...string) *tls.Config
sample code:
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/hertz-contrib/autotls"
)
func main() {
h := server.Default(
server.WithTLS(autotls.NewTlsConfig("example1.com", "example2.com")),
server.WithHostPorts(":https"),
)
// Ping handler
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, map[string]interface{}{
"ping": "pong",
})
})
hlog.Fatal(autotls.Run(h))
}
RunWithContext
The autotls
extension provides RunWithContext
to help users support LetsEncrypt HTTPS servers with a single line of code while enabling graceful shutdown of the service.
The RunWithContext function signature is as follows:
func RunWithContext(ctx context.Context, h *server.Hertz) error
sample code:
package main
import (
"context"
"os/signal"
"syscall"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/app/server"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/hertz-contrib/autotls"
)
func main() {
// Create context that listens for the interrupt signal from the OS.
ctx, stop := signal.NotifyContext(
context.Background(),
syscall.SIGINT,
syscall.SIGTERM,
)
defer stop()
h := server.Default(
server.WithTLS(autotls.NewTlsConfig("example1.com", "example2.com")),
server.WithHostPorts(":https"),
)
// Ping handler
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, map[string]interface{}{
"ping": "pong",
})
})
hlog.Fatal(autotls.RunWithContext(ctx, h))
}
NewServerWithManagerAndTlsConfig
The autotls
extension provides NewServerWithManagerAndTlsConfig
to help users automate certificate management and TLS configuration.
The NewServerWithManagerAndTlsConfig function signature is as follows:
func NewServerWithManagerAndTlsConfig(m *autocert.Manager, tlsc *tls.Config, opts ...config.Option) *server.Hertz
sample code:
package main
import (
"context"
"github.com/cloudwego/hertz/pkg/app"
"github.com/cloudwego/hertz/pkg/common/hlog"
"github.com/hertz-contrib/autotls"
"golang.org/x/crypto/acme/autocert"
)
func main() {
m := autocert.Manager{
Prompt: autocert.AcceptTOS,
HostPolicy: autocert.HostWhitelist("example1.com", "example2.com"),
Cache: autocert.DirCache("/var/www/.cache"),
}
h := autotls.NewServerWithManagerAndTlsConfig(&m, nil)
// Ping handler
h.GET("/ping", func(c context.Context, ctx *app.RequestContext) {
ctx.JSON(200, map[string]interface{}{
"ping": "pong",
})
})
hlog.Fatal(autotls.Run(h))
}
For a complete usage example, see example .
Note
Client raise error not support tls
Hertz uses netpoll
as the network library by default and currently netpoll
does not support TLS. To use TLS you need to switch to the standard network library with the following code:
import (
"github.com/cloudwego/hertz/pkg/app/client"
"github.com/cloudwego/hertz/pkg/network/standard"
"github.com/cloudwego/hertz/pkg/protocol"
)
func main() {
clientCfg := &tls.Config{
InsecureSkipVerify: true,
}
c, err := client.NewClient(
client.WithTLSConfig(clientCfg),
client.WithDialer(standard.NewDialer()),
)
}