✨ Use TLS client auth
Tim Izzo tim@5ika.ch
Sun, 20 Jul 2025 15:05:36 +0200
4 files changed,
82 insertions(+),
8 deletions(-)
A
config.example.yaml
@@ -0,0 +1,11 @@
+ +# For server HTTPS configuration +server: + certFilePath: certs/cert.pem + keyFilePath: certs/key.pem + +# For client HTTPS authorization +client: + cACertFilePath: ca/root-cert.pem + +todoPath: todo.txt
A
config.go
@@ -0,0 +1,55 @@
+package main + +import ( + "crypto/tls" + "crypto/x509" + "os" + + "github.com/charmbracelet/log" + "gopkg.in/yaml.v3" +) + +type Config struct { + Server struct { + CertFilePath string `yaml:"certFilePath"` + KeyFilePath string `yaml:"keyFilePath"` + } `yaml:"server"` + Client struct { + CACertFilePath string `yaml:"cACertFilePath"` + } `yaml:"client"` + + TodoPath string `yaml:"todoPath"` +} + +func getConfig() Config { + configFile, err := os.ReadFile("./config.yaml") + if err != nil { + log.Fatalf("Can't read config file: %s", err) + } + + var cfg Config + yaml.Unmarshal(configFile, &cfg) + return cfg +} + +func getTLSConfig(config Config) *tls.Config { + + serverTLSCert, err := tls.LoadX509KeyPair(config.Server.CertFilePath, config.Server.KeyFilePath) + if err != nil { + log.Fatalf("error opening certificate and key file for control connection. Error %v", err) + return nil + } + + certPool := x509.NewCertPool() + if caCertPEM, err := os.ReadFile(config.Client.CACertFilePath); err != nil { + panic(err) + } else if ok := certPool.AppendCertsFromPEM(caCertPEM); !ok { + panic("invalid cert in CA PEM") + } + + return &tls.Config{ + ClientAuth: tls.RequireAndVerifyClientCert, + ClientCAs: certPool, + Certificates: []tls.Certificate{serverTLSCert}, + } +}
M
main.go
→
main.go
@@ -10,22 +10,28 @@
todo "github.com/1set/todotxt" ) -var todoFilePath string = "todo.txt" - func main() { - gin.SetMode(gin.DebugMode) router := gin.Default() + config := getConfig() + router.LoadHTMLGlob("templates/*.html") router.Static("/static", "static/") router.GET("/", loadTasksList) router.POST("/", loadTasksList) - router.Run() + server := http.Server{ + Addr: ":4443", + Handler: router, + TLSConfig: getTLSConfig(config), + } + defer server.Close() + log.Fatal(server.ListenAndServeTLS("", "")) } func loadTasksList(c *gin.Context) { - taskList := getTaskList() + config := getConfig() // TODO Fix: YAML config file is read for each request + taskList := getTaskList(config.TodoPath) id := c.PostForm("taskId") content := c.PostForm("taskContent")@@ -47,11 +53,11 @@ } else {
updatedTask.Reopen() } } - taskList.WriteToPath(todoFilePath) + taskList.WriteToPath(config.TodoPath) } else if content != "" { newTask, _ := todo.ParseTask(content) taskList.AddTask(newTask) - taskList.WriteToPath(todoFilePath) + taskList.WriteToPath(config.TodoPath) } c.HTML(http.StatusOK, "tasks-list.html", gin.H{@@ -59,7 +65,7 @@ "tasks": taskList,
}) } -func getTaskList() todo.TaskList { +func getTaskList(todoFilePath string) todo.TaskList { if tasklist, err := todo.LoadFromPath(todoFilePath); err != nil { log.Fatalf("Can't load file %s", todoFilePath) return nil