HTTPS Service

Establishing an HTTPS service is very simple, using the EnableHTTPS(certFile, keyFile string) error method provided by the framework’s WebServer. Obviously, this method requires two parameters, namely, two certificate files used for HTTPS asymmetric encryption and the corresponding key file.

Preparation

For local demonstrations, we can use the openssl command to generate a local certificate and the corresponding key file for testing. The command is as follows:

  1. Use the common RSA algorithm to generate a key file
  1. openssl genrsa -out server.key 2048

Additionally, we can use the ECDSA algorithm to generate the key file:

  1. openssl ecparam -genkey -name secp384r1 -out server.key
  1. Generate a certificate file based on the key file
  1. openssl req -new -x509 -key server.key -out server.crt -days 365
  1. (Optional) Generate a public key file based on the key, which is used for client-server communication
  1. openssl rsa -in server.key -out server.key.public

The openssl supports many algorithms and command parameters. If you want to learn more, please use the man openssl command to view. In this example, the local environment (Ubuntu) uses the command to generate the related key, public key, and certificate files as follows:

  1. $ openssl genrsa -out server.key 2048
  2. Generating RSA private key, 2048 bit long modulus
  3. .........................+++
  4. .....................................................................+++
  5. unable to write 'random state'
  6. e is 65537 (0x10001)
  7. $ openssl req -new -x509 -key server.key -out server.crt -days 365
  8. You are about to be asked to enter information that will be incorporated
  9. into your certificate request.
  10. What you are about to enter is what is called a Distinguished Name or a DN.
  11. There are quite a few fields but you can leave some blank
  12. For some fields, there will be a default value,
  13. If you enter '.', the field will be left blank.
  14. -----
  15. Country Name (2 letter code) [AU]:CH
  16. State or Province Name (full name) [Some-State]:SiChuan
  17. Locality Name (eg, city) []:Chengdu
  18. Organization Name (eg, company) [Internet Widgits Pty Ltd]:John.cn
  19. Organizational Unit Name (eg, section) []:Dev
  20. Common Name (e.g. server FQDN or YOUR name) []:John
  21. Email Address []:john@johng.cn
  22. $ openssl rsa -in server.key -out server.key.public
  23. writing RSA key
  24. $ ll
  25. total 20
  26. drwxrwxr-x 2 john john 4096 Apr 23 21:26 ./
  27. drwxr-xr-x 90 john john 4096 Apr 23 20:55 ../
  28. -rw-rw-r-- 1 john john 1383 Apr 23 21:26 server.crt
  29. -rw-rw-r-- 1 john john 1675 Apr 23 21:25 server.key
  30. -rw-rw-r-- 1 john john 1675 Apr 23 21:26 server.key.public

When generating the certificate, the command prompts you to enter some information, which you can leave blank by pressing enter. We randomly filled in some information here.

Example Code

Based on the generated key and certificate files, we demonstrate how to use ghttp.Server to implement an HTTPS service. The example code is as follows:

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/net/ghttp"
  4. )
  5. func main() {
  6. s := ghttp.GetServer()
  7. s.BindHandler("/", func(r *ghttp.Request){
  8. r.Response.Writeln("Hello world from HTTPS!")
  9. })
  10. s.EnableHTTPS("/home/john/https/server.crt", "/home/john/https/server.key")
  11. s.SetPort(8199)
  12. s.Run()
  13. }

As you can see, we directly pass the addresses of the previously generated certificate and key files to EnableHTTPS, and set the HTTPS service port using s.SetPort(8199). Of course, we can also use s.SetHTTPSPort(8199) to achieve this. In a single service, there is no difference between the two, but when WebServer needs to support both HTTP and HTTPS services simultaneously, their roles are different. We will introduce this feature later. Afterward, we visit the page https://127.0.0.1:8199/ to see the effect:

HTTPS & TLS - 图1

You can see the browser gives a prompt, mainly because the certificate we generated is private, not provided by a third-party trusted company. Browsers usually come with some third-party trusted HTTPS certificate authorities. HTTPS certificates provided by these authorities are considered authoritative and trustworthy by the browser, which is why this prompt does not appear. Generally, this kind of third-party trusted authority certificate cost ranges from a few thousand to tens of thousands of RMB per year. Interested friends can learn more through search engines.

HTTPS & TLS - 图2

Here, we directly click Advanced, then click Proceed to 127.0.0.1 (unsafe), and finally, you can see the expected result output on the page:

HTTPS & TLS - 图3

Support for HTTPS and HTTP

We often encounter situations where we need to provide the same service through both HTTP and HTTPS, that is, the only difference is the port and access protocol, while everything else is the same. If we run it using the traditional method of multiple WebServers, it will be cumbersome. To easily solve the developer’s troubles, ghttp provides a very convenient feature: supports “the same” WebServer to simultaneously support both HTTPS and HTTP access protocols. Let’s take a look at an example:

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/net/ghttp"
  4. )
  5. func main() {
  6. s := ghttp.GetServer()
  7. s.BindHandler("/", func(r *ghttp.Request){
  8. r.Response.Writeln("You can see this content through both HTTP and HTTPS!")
  9. })
  10. s.EnableHTTPS("/home/john/https/server.crt", "/home/john/https/server.key")
  11. s.SetHTTPSPort(443)
  12. s.SetPort(80)
  13. s.Run()
  14. }

After execution, accessing these two addresses locally http://127.0.0.1/ and https://127.0.0.1/ will show the same content (it should be noted that due to some systems’ permission restrictions, WebServer binding 80 and 443 ports require root/administrator permissions. If an error is reported when starting, you can change the port number and re-execute it).

In this example, we used two methods to enable HTTPS features:

  1. func (s *Server) EnableHTTPS(certFile, keyFile string) error
  2. func (s *Server) SetHTTPSPort(port ...int) error

One is to add certificate and key files, and the other is to set the listening port for the HTTPS protocol. Once these two properties are set, the WebServer will enable the HTTPS feature. Moreover, the example also sets the listening port for HTTP services through the SetPort method, so the WebServer will simultaneously listen to the specified HTTPS and HTTP service ports.

Using Let’s Encrypt Free Certificates

There are many SSL free certificate authorities, such as:

  1. Tencent Cloud DV SSL Certificate: https://cloud.tencent.com/product/ssl
  2. Let’s Encrypt: https://letsencrypt.org/
  3. CloudFlare SSL: https://www.cloudflare.com/
  4. StartSSL: https://www.startcomca.com/
  5. Wosign沃通SSL: https://www.wosign.com/
  6. loovit.net AlphaSSL: https://www.lowendtalk.com/entry/register?Target=discussion%2Fcomment%2F2306096

The following example uses Let's Encrypt to introduce how to apply, use, and renew a free certificate.

Let’s Encrypt official website: https://letsencrypt.org/

The following is an example of how to apply for a Let's Encrypt free certificate and use it with the GoFrame framework under the Ubuntu system.

Install Certbot

Certbot official website: https://certbot.eff.org/

To apply for a Let’s Encrypt free certificate, you need to use the certbot tool:

  1. sudo apt-get update
  2. sudo apt-get install software-properties-common
  3. sudo add-apt-repository ppa:certbot/certbot
  4. sudo apt-get update
  5. sudo apt-get install certbot

Apply for a Certificate

Use the following command:

  1. certbot certonly --standalone -d domain_name --staple-ocsp -m email_address --agree-tos

For example:

  1. root@ip-172-31-41-204:~# certbot certonly --standalone -d goframe.org --staple-ocsp -m john@goframe.org --agree-tos
  2. Saving debug log to /var/log/letsencrypt/letsencrypt.log
  3. Plugins selected: Authenticator standalone, Installer None
  4. Starting new HTTPS connection (1): acme-v02.api.letsencrypt.org
  5. Obtaining a new certificate
  6. Performing the following challenges:
  7. http-01 challenge for goframe.org
  8. Waiting for verification...
  9. Cleaning up challenges
  10. IMPORTANT NOTES:
  11. - Congratulations! Your certificate and chain have been saved at:
  12. /etc/letsencrypt/live/goframe.org/fullchain.pem
  13. Your key file has been saved at:
  14. /etc/letsencrypt/live/goframe.org/privkey.pem
  15. Your cert will expire on 2019-01-25. To obtain a new or tweaked
  16. version of this certificate in the future, simply run certbot
  17. again. To non-interactively renew *all* of your certificates, run
  18. "certbot renew"
  19. - If you like Certbot, please consider supporting our work by:
  20. Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
  21. Donating to EFF: https://eff.org/donate-le

By default, the certificate will be installed in /etc/letsencrypt/, with the certificate and private key files respectively being:

  1. /etc/letsencrypt/live/goframe.org/fullchain.pem
  2. /etc/letsencrypt/live/goframe.org/privkey.pem

Use the Certificate

  1. package main
  2. import (
  3. "github.com/gogf/gf/v2/net/ghttp"
  4. )
  5. func main() {
  6. s := ghttp.GetServer()
  7. s.BindHandler("/", func(r *ghttp.Request){
  8. r.Response.Writeln("Hello world from HTTPS!")
  9. })
  10. s.EnableHTTPS("/etc/letsencrypt/live/goframe.org/fullchain.pem", "/etc/letsencrypt/live/goframe.org/privkey.pem")
  11. s.Run()
  12. }

Certificate Renewal

The certificate is valid for 3 months by default, and it needs to be renewed manually after expiration, using the following command:

  1. certbot renew

Example 1, we can use the crontab scheduled task to achieve automatic renewal:

  1. # Try to renew once a day, restart the WebServer running on `GoFrame` framework successfully
  2. 0 3 * * * certbot renew --quiet --renew-hook "kill -SIGUSR1 $(pidof process_name)"

Example 2, if we manage the certificate through nginx, we can set up a scheduled task like this:

  1. # Try to renew once a day, certificate renewal requires closing the WebServer listening on port 80
  2. 0 3 * * * service nginx stop && certbot renew --quiet --renew-hook "service nginx start"

To prevent potential failures of the certbot renew command leading to nginx not restarting, to ensure stability, you can do this:

  1. # Try to renew once a day, certificate renewal requires closing the WebServer listening on port 80
  2. 0 3 * * * service nginx stop && certbot renew --quiet --renew-hook "service nginx start"
  3. 5 3 * * * service nginx start