This is an article by Cloudflare Filippo Valsorda, published in Gopher Academy in 2016, although it has been in the past two years, but it still has meaning.
Previously crypto/tls
too slow and net/http
very young, so for Go Web server, it is usually wise of us to put it behind the reverse proxy, such as Nginx, and now do not need.
In CloudFlare we recently experimented with direct burst of pure go service as a host. Go 1.8 net/http
and crypto/tls
provides stable, high-performance and flexible functionality.
Then, some tuning work needs to be done, and this article will show you how to tune and make the Web server more stable.
Crypto/tls
2016 years, you will no longer run an unencrypted HTTP Server, so you need to crypto/tls
. The good news is that the library is already very fast (our test), and now his security attack tracking is excellent.
The default configuration is to use the intermediate recommended configuration in the Mozilla reference, but you should still set up PreferServerCipherSuites
to ensure a faster and more secure password vault, CurvePreferences
avoiding the curves that are not optimized. The client if using the CurveP384
algorithm back causes our machine to consume up to 1 seconds of CPU.
12345678910 |
&tls. config{//causes servers to use Go ' s default ciphersuite preferences,//which is tuned to avoid attacks. Does nothing on clients. true,//only if use curves which has assembly implementations//Go 1.8only},} |
If you want to configure compatibility, you can set MinVersion
and CipherSuites
.
1234567891011121314 |
Minversion:tls. Versiontls12,ciphersuites: []uint16//Go 1.8only TLS. tls_ecdhe_rsa_with_chacha20_poly1305, //Go 1.8only TLS. Tls_ecdhe_ecdsa_with_aes_128_gcm_sha256,tls. tls_ecdhe_rsa_with_aes_128_gcm_sha256,//best disabled, as they don ' t provide Forward secrecy,//but might b E necessary for some clients//TLS. tls_rsa_with_aes_256_gcm_sha384,//TLS. tls_rsa_with_aes_128_gcm_sha256, }, |
Note that the implementation of the CBC cipher Suite for Go (which we disabled above) is easy to receive LUCKY13 attacks, even though go 1.8 implements some of the processing.
Finally, it is important to note that all of these recommendations apply only to the AMD64 architecture because it enables fast, constant-level cryptographic primitives (AES-GCM, chacha20-poly1305, P256), and other architectures may not be suitable for product-level applications.
Since it is a service to be burst with the Internet, it requires a publicly trusted certificate. By Let’s Encrypt
easily applying, you can use golang.org/x/crypto/acme/autocert
the GetCertificate
function.
Do not forget to redirect http to HTTPS, if your client is a browser, you can consider HSTS.
12345678910 |
5 * time. Second,writetimeout: 5 * time. Second,handler:http. Handlerfunc (func(w http. Responsewriter, req *http. Request) {W.header (). Set ("Connection""Close" "https://" + req. Host + req. Url. String () http. Redirect (w, req, URL, http. statusmovedpermanently)}),}gofunc() {log. Fatal (SRV. Listenandserve ())} () |
You can use SSL Labs test to check that the configuration is correct.
Net/http
net/http
Contains HTTP/1.1
and HTTP/2
. You must already be familiar with the development of handler, so this article does not discuss it. We discuss some of the scenarios behind the server side.
Timeout
Timeouts can be the most easily overlooked dangerous scenario. Your service may be spared in the controlled network, but it will not be so fortunate on the Internet, especially (not just) being attacked by malicious means.
There are a number of resources that require timeout control. Although Goroutine consumes little, file descriptors are always limited. Stuck connections, non-working connections, or even malicious disconnected connections should not consume them.
A server that exceeds the maximum file symbol is always unable to accept a new connection and will report the following failure:
1 |
http: Accept error:accept TCP [::]:openfilesin1s |
A default http.Server
, like the example in the pack documentation file http.ListenAndServe
http.ListenAndServeTLS
, without setting any timeout control, you're definitely not what you want.
http.Server
There are three parameters to control the timeout: ReadTimeout
, WriteTimeout
and IdleTimeout
, you can set them in the display:
12345678 |
SRV: = &http. server{ 5 * time. Second, writetimeout: Ten * time. Second, * time. Second, tlsconfig: tlsconfig, Handler: servemux,}log. Println (SRV. Listenandservetls ("" ")) |
ReadTimeout
The time range is self-connected, and ends with the requested body being fully read out. In net/http
the implementation it is Accept
set after the connection SetReadDeadline
.
ReadTimeout
The biggest problem is that it does not allow the server to give the client more time to request the body stream. Go 1.8 introduces a new parameter ReadHeaderTimeout
that ends with reading the request header. Then there have been some unclear ways to set the read timeout, the relevant design discussion can refer to #16100.
WriteTimeout
Time-out normally starting from reading the request header, ending at the end of the response write (that ServeHTTP
is, the life cycle), through readRequest
the SetWriteDeadline
settings.
Then, when connected over HTTPS, SetWriteDeadline
Accept
It is set immediately after, so it also contains the TLS handshake for the packet write. What's annoying is that this means WriteTimeout
containing the HTTP header's read as well as the first byte of the wait.
ReadTimeout
And WriteTimeout
is absolute, it cannot be changed in handler (#16100).
Go 1.8 also introduces new IdleTimeout
parameters to limit the number of idle server Keep-Alive
connections before reuse.
The previous version of Go 1.8, when the ReadTimeout
request was completed and immediately began ticking (tick), this Keep-Alive
is not appropriate for the connection: idle time consumes the client to allow the request to be sent, causing some fast clients to have unexpected timeouts.
For untrusted clients and networks, you should set Read
, Write
and Idle
timeout, so that a client that reads or writes slowly does not consume a connection for a long time.
For the background of the http/1.1 timeout before go 1.8, you can refer to CloudFlare's blog.
Http/2
HTTP/2 is automatically enabled back in Go 1.6+ as long as it meets the following conditions:
- Request through
TLS/HTTPS
Server.TLSNextProto
Nil (if an empty map is set, HTTP/2 is disabled)
Server.TLSConfig
has been set, ListenAndServeTLS
called, or the next
Serve
is called and tls.Config.NextProtos
contains h2
(for example []string{"H2", "http/1.1")
HTTP/2 and http/1.1 are somewhat different because the same connection serves multiple requests at the same time, but go abstracts the Unified Timeout control interface.
Unfortunately, go 1.7 ReadTimeout
interrupts the HTTP/2 connection, which does not reset for each connection, but instead resets it when the connection is first established, and disconnects the HTTP/2 connection when it expires. Go 1.8 fixes this issue.
Based on this and ReadTimeout
the idle time issue, I strongly recommend that you upgrade to 1.8 as soon as possible.
TCP keep-alives
If you use ListenAndServe
(unlike incoming net.Listener
Serve
, this method uses the default value to provide 0 protection), the 3-minute TCP keep-alive will be set automatically, it will let the completely disappearing client have the opportunity to abandon the connection, my experience is not to fully believe it, Set the timeout anyway.
First of all, 3 minutes is too long and you can use your own to tcpKeepAliveListener
adjust it. 、
More importantly, Keep-Alive
just make sure the client is still alive, but will not set a limit on the connection survival. A malicious client will open a very large number of connections, causing your server to open many file descriptors that will cause your service to be denied service through outstanding requests.
Finally, my experience is that connections often lead to leaks, knowing that timeouts work.
Servemux
The package level http.Handle[Func]
(and your web framework) is registered handler to the global http.DefaultServeMux
, and if Server.Handler
it is nil, you should avoid doing so.
Any package you enter, whether direct or indirect, can be accessed http.DefaultServeMux
and may register the route you do not expect.
For example, if any of the libraries in the package dependency are imported net/http/pprof
, the client can get the profile of your application's CPU. You can use net/http/pprof
manual registration.
The right thing to do is initialize your own http.ServeMux
, register the handler on top of it, set it to Server.Handler
, or set your own web framework for Server.Handler
.
Logging
Net/http does a lot of work before calling your handler, such as accepting connections https://github.com/golang/go/blob/1106512db54fc2736c7a9a67dd553fc9e1fca742 /src/net/http/server.go#l2631-l2653, TLS handshake, etc...
When any one step goes wrong, it writes a line of logs to Server.ErrorLog
. Some of these errors, such as timeouts and connection resets, are normal on the Internet. You can connect most of the errors and add them to the metric, thanks to this guarantee:
Each logging operation makes a single call to the Writer ' s Write method.
If you do not want to output the stack log in handler, you can use panic(nil)
or use go 1.8 panic(http.ErrAbortHandler)
.
Metrics
Metric can help you monitor open file descriptors. Prometheus uses proc
the file system to help you do this.
If you need to investigate leaks, you can use Server.ConnState
hooks to get more details of the connection metric. Note that there is no way to maintain the correct number of state without maintaining the state StateActive
, so you need to maintain one map[net.Conn]ConnState
.
Conclusion
Using Nginx to do the front end of the Go service log is gone, but in the face of the internet you still need to do some additional protection, you may need to upgrade to the new go 1.8 version.