Secure a Golang Web Server with a Self-signed or Let's Encrypt SSL Certificate
Introduction
Golang's inbuilt net/http
package allows you to create and serve web applications without installing third-party web servers. However, to protect client-server communications in your applications, you must use an SSL/TLS certificate. Luckily, the Golang rich HTTPS libraries provide this functionality.
An SSL/TLS certificate encrypts data so that it can only be read by the intended recipients. It is very useful in scenarios where you're collecting sensitive information such as credit card numbers and authentication details. SSL certificates improve your customers' trust and affirm your business identity.
In this guide, you'll learn how to install, configure, and use either a self-signed or a Let's Encrypt certificate on your Golang applications hosted on a Linux server.
Prerequisites
To complete testing this Golang SSL/TLS certificate guide, make sure you have the following:
- A Linux server.
- A non-root
sudo
user. - The Golang package
- A domain name. For instance,
example.com
. The domain name is optional when installing a self-signed certificate. However, if you want to use the Let's Encrypt certificate, you must configure a DNS for your domain.
1. Create a Simple HTTP Server
SSH to your Linux server and complete the following steps to create an HTTP server.
Make a new
project
directory under your home directory. This separates your project from the rest of the Linux files to make troubleshooting easier if you encounter errors.$ mkdir project
Navigate to the new
project
directory.$ cd project
Use the
nano
text editor to create a newhttp.go
file.$ nano http.go
Paste the following information into the file.
package main import ( "net/http" "fmt" ) func main() { http.HandleFunc("/", httpRequestHandler) http.ListenAndServe(":8081", nil) } func httpRequestHandler(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Hello, World!") }
Save and close the file when you're through with editing.
In the above file, you've imported the
net/http
package. This allows you to implement the HTTP client and server libraries in your Golang application.You've also imported the
fmt
package to print aHello, World!
message when a client queries your HTTP server.Under the main function (
func main(){...}
), you're calling thehttp.HandleFunc(...)
method. This method accepts two arguments. That is, the URL pattern to match and the handler function that provides the HTTP response. For this guide, you're defining/
as the URL pattern to match all HTTP requests. Next, you've definedhttpRequestHandler(...)
as the handler function, and under it, you're printing aHello, World!
message to HTTP clients.To initiate the web server, you're calling the function
http.ListenAndServe(...)
. This method accepts the port number (for instance8081
) and a handler function. In this guide, you've usednil
as the handler function to instruct thehttp
package to use theDefaultServeMux.Handle
handler that you've already populated using thehttp.HandleFunc(...)
Run the
http.go
application. Please note, this command has a blocking function meaning that your application is now listening on port8081
.$ go run http.go
Next, visit the following address on a web browser. Replace
192.0.2.1
with the correct public IP address of your server. Remember to append port:8081
at the end of the address.You should now get the
Hello, World!
response, indicating that your web server is working as expected.Hello, World!
While you can now begin building your HTTP application by extending the code in the
http.go
file, your app is not secure and sensitive data can be sniffed and captured by hackers when traversing through the network. To safeguard your client's data and make your application secure, this is where an SSL/TLS certificate comes into play.In the next step, you'll secure your application with a self-signed SSL certificate. Terminate the session by pressing Ctrl + C
2. Secure the Server with a Self-Signed Certificate
Self-signed SSL certificates do not require third-party Certificate Authorities(CAs). They're easy to integrate into your existing Golang HTTP applications and come at no cost. Follow the steps below to generate the certificate.
Use the
openssl
command below to create a private key (go-server.key
) and a self-signed certificate (go-server.crt
) valid for365
days with a key size of2,048 bits
.$ openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout go-server.key -out go-server.crt
Depending on your organization name and current location, respond to each question appropriately. Make sure you've provided the correct public IP address or domain name when prompted to enter the
Common Name
.Country Name (2 letter code) [AU]: US State or Province Name (full name) [Some-State]: CALIFORNIA. Locality Name (eg, city) []: LOS ANGELES. Organization Name (eg, company) [Internet Widgits Pty Ltd]: SAMPLE COMPANY Organizational Unit Name (eg, section) []: SECURITY DEPARTMENT Common Name (e.g. server FQDN or YOUR name) []: SAMPLE_DOMAIN or PUBLIC_IP_ADDRESS Email Address []: Enter your email address e.g. info@example.com
Confirm that you've generated the private key and self-signed certificate by listing the files under your current
project
directory.$ ls
You should now see the
go-server.crt
andgo-server.key
files on the file list.go-server.crt go-server.key ...
Next, remove the old
http.go
file and create a newhttps.go
file.$ rm http.go $ nano https.go
Paste the information below into the
https.go
file.package main import ( "net/http" "fmt" ) func main() { http.HandleFunc("/", httpRequestHandler) http.ListenAndServeTLS(":8081","go-server.crt","go-server.key", nil) } func httpRequestHandler(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Hello, World!") }
Save and close the file when you're through with editing. The new
https.go
file is almost similar to thehttp.go
file you created earlier. However, the new file uses the secure SSL/TLS functionhttp.ListenAndServeTLS(...)
instead of the unencryptedhttp.ListenAndServe(...)
function.The new
ListenAndServeTLS(...)
accepts two more parameters. That is thecertFile
and thekeyFile
. This is where you define the file paths for the private key and the self-signed certificate(http.ListenAndServeTLS(":8081","go-server.crt","go-server.key", nil)
).Run the new
https.go
file.$ go run https.go
Again, open a browser and enter your Golang's server domain name or public IP address. This time around, begin the address with the
https
protocol instead ofhttp
.Since you're using a self-signed certificate that a trusted CA does not issue, your browser should complain and display some warnings. Next, bypass the SSL/TLS security warnings to visit your application. You should now get the
Hello, World!
message.Hello, World!
Any further requests made to your Golang application are now encrypted. The approach of using a self-signed certificate is only suitable for testing purposes or for internal applications that you don't intend to release to the public. In the next step, you'll create a fully-developed SSL/TLS certificate from a trusted CA.
3. Secure the Server with a Let's Encrypt Certificate
Let's Encrypt is a non-profit trusted CA that provides free SSL certificates that are just as good as paid certificates. The organization provides different tools, libraries, and APIs to generate and create SSL certificates automatically. Follow the steps below to create and use their certificate.
Delete the old
https.go
file and re-create it.$ rm https.go $ nano https.go
Paste the information below into the file. Replace
example.com
with the correct domain name. Make sure you've configured DNS for your domain.package main import ( "net/http" "fmt" "crypto/tls" "golang.org/x/crypto/acme/autocert" "log" ) func main() { certManager := autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist("example.com", "www.example.com"), Cache: autocert.DirCache("certs"), } server := &http.Server{ Addr: ":https", TLSConfig: &tls.Config{ GetCertificate: certManager.GetCertificate, }, } http.HandleFunc("/", httpRequestHandler) go http.ListenAndServe(":http", certManager.HTTPHandler(nil)) log.Fatal(server.ListenAndServeTLS("", "")) } func httpRequestHandler(w http.ResponseWriter, req *http.Request) { fmt.Fprintf(w, "Hello, World!") }
Save and close the file when you're through with editing. In the above file, you're using the
autocert
manager package(golang.org/x/crypto/acme/autocert
) to pull the Let's Encrypt certificate into your server under thecerts
directory. In case thecerts
directory doesn't exist, theautocert
manager creates it with0700
permissions.Remember to add the domain names and any aliases in the
autocert.HostWhitelist(...)
to allow theautocert
manager to retrieve certificates for them and respond appropriately.Add the
golang.org/x/crypto/acme/autocert
library to your project.$ go get "golang.org/x/crypto/acme/autocert"
This time around, don't run the
https.go
file using the commandgo run https.go
because you're going to encounter some errors since Linux won't allow the application to bind to any privileged TCP/IP ports below1024
. Instead, use the Golangbuild
command to create an executable binary, copy it to the/usr/local/bin/
directory, and run thesetcap
command against the new binary to grant it the appropriate permissions.$ go build https.go $ sudo cp https /usr/local/bin/https $ sudo setcap CAP_NET_BIND_SERVICE+ep /usr/local/bin/https
Run the
https
binary.$ https
Visit the address below on your browser. You don't have to enter port
443
at the end.You should now get the
Hello, World!
message without any SSL/TLS warnings.Hello, World!
This confirms that your Let's Encrypt certificate is now working as expected.
Conclusion
In this guide, you've configured a simple Golang HTTP server and learned how you could either secure it with a self-signed or a Let's Encrypt certificate. Follow the steps in this guide to secure your web applications with SSL/TLS certificate when coding your next Golang project.