DEV Community

Felix
Felix

Posted on

Secure your go REST Api in Docker using Letsencrypt

I recently had to figure this out and could not find a good resource, I hope this is the first one then.



How to set up HTTPS for a Go (Gin) REST API using Docker and Letsencrypt

What is used (what you need to install):

  1. Go
  2. Gin
  3. Docker

Source code for the demo Go REST API:

package main

import (
    "net/http"
    "github.com/gin-gonic/gin"
)

func setupRouter() *gin.Engine {
    r := gin.Default()
    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, "pong")
    })
    return r
}

func main() {
    r := setupRouter()
    r.Run(":80")
}
Enter fullscreen mode Exit fullscreen mode

How to encrypt it using letsencrypt

  1. Add "github.com/gin-gonic/autotls" to your imports.
  2. Open a goroutine for the run on port 80 to prevent blocking the thread while still enabling access via http: Change:
r.Run(":80")
Enter fullscreen mode Exit fullscreen mode

To:

go r.Run(":80")
Enter fullscreen mode Exit fullscreen mode
  1. Add the option to access the service via ssl:
m := autocert.Manager{
    Prompt:     autocert.AcceptTOS,
    HostPolicy: autocert.HostWhitelist("domain.com"),
    Cache:      autocert.DirCache("/var/www/.cache"),
}
autotls.RunWithManager(r, &m)
Enter fullscreen mode Exit fullscreen mode

You have to enter your domain instead of "domain.com"

Dockerize it

Dockerfile:

FROM golang:latest

WORKDIR /

COPY . .

EXPOSE 80

EXPOSE 443

ENV GIN_MODE=release

VOLUME ["/var/www/.cache"]

CMD [ "go","run", "main.go" ]
Enter fullscreen mode Exit fullscreen mode

You should also add a volume to docker-compose because you will be rate limited by letsencrypt if you request to many certificates

go-backend:
  build:
    context: ./go-backend
    dockerfile: Dockerfile
  ports:
    - 80:80
    - 443:443
  container_name: go-backend
  volumes:
    - certcache:/var/www/.cache
Enter fullscreen mode Exit fullscreen mode

Here the gin project is located in ./go-backend which might be different for your project. The important part is that the volume is created at /var/www/.cache to prevent to many requests to letsencrypt.

Final notes:

If you want to encrypt your api and make it accessible using https, it might be a good idea to prevent using http. This would be especially important if you send passwords or other sensitive data to or from the server via your api. In this case you should remove this from your go code:

go r.Run(":80")
Enter fullscreen mode Exit fullscreen mode

You also should close (not open) port 80 in your Dockerfile and in docker-compose because they are not needed.

Full go code:

package main

import (
    "github.com/gin-gonic/autotls"
    "github.com/gin-gonic/gin"
    "golang.org/x/crypto/acme/autocert"
    "net/http"
)

func setupRouter() *gin.Engine {
    r := gin.Default()

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(http.StatusOK, "pong")
    })
    return r
}

func main() {
    r := setupRouter()
    go r.Run(":80")
    m := autocert.Manager{
        Prompt:     autocert.AcceptTOS,
        HostPolicy: autocert.HostWhitelist("domain.com"),
        Cache:      autocert.DirCache("/var/www/.cache"),
    }
    autotls.RunWithManager(r, &m)
}
Enter fullscreen mode Exit fullscreen mode

Discussion (0)