feat: implement optional mTLS and helper scripts

Signed-off-by: HeshamTB <hishaminv@gmail.com>
This commit is contained in:
HeshamTB 2024-03-18 23:02:22 +03:00
parent 1f3eca1b1b
commit da0b1c720e
Signed by: Hesham
GPG Key ID: 74876157D199B09E
3 changed files with 131 additions and 2 deletions

View File

@ -1,2 +1,7 @@
hvpn-node hvpn-node
tmp/
.air.toml
client_cert.pem
client_cert.key
cert.key
cert.pem

View File

@ -2,6 +2,8 @@ package main
import ( import (
"bufio" "bufio"
"crypto/tls"
"crypto/x509"
"errors" "errors"
"fmt" "fmt"
"log/slog" "log/slog"
@ -32,6 +34,8 @@ var WgPort int
var wgLink *hvpnnode3.WGLink var wgLink *hvpnnode3.WGLink
var httpPort int var httpPort int
var TLS_ENABLED bool
var tlsConfig *tls.Config
func main() { func main() {
@ -76,10 +80,17 @@ func run(ctx *cli.Context) {
IdleTimeout: 120 * time.Second, IdleTimeout: 120 * time.Second,
Handler: handler, Handler: handler,
Addr: hostPort, Addr: hostPort,
TLSConfig: tlsConfig,
} }
defer wgLink.Close() defer wgLink.Close()
if TLS_ENABLED {
slog.Debug("Running with TLS")
slog.Info("Running TLS or mTLS with a reverse proxy is recommended")
slog.Warn(srv.ListenAndServeTLS(ctx.Path("cert"), ctx.Path("cert-private-key")).Error())
} else {
slog.Warn(srv.ListenAndServe().Error()) slog.Warn(srv.ListenAndServe().Error())
}
} }
func createCliApp() *cli.App { func createCliApp() *cli.App {
@ -175,6 +186,48 @@ func createCliApp() *cli.App {
app.Flags = append(app.Flags, &httpPort) app.Flags = append(app.Flags, &httpPort)
/* TLS Flags */
TLS := cli.BoolFlag{
Name: "enable-tls",
Aliases: []string{ "tls" },
Value: false,
Category: "\rTLS:",
Destination: &TLS_ENABLED,
Action: func(ctx *cli.Context, b bool) error {
tlsConf, err := setupTLS(ctx)
if err != nil {
return err
}
tlsConfig = tlsConf
return nil
},
}
app.Flags = append(app.Flags, &TLS)
mTLSClientCerts := cli.PathFlag{
Name: "client-certs",
Aliases: []string{"ca"},
Usage: "A path to PEM file with client certificates; Enables TLS",
Category: "\rTLS:",
}
app.Flags = append(app.Flags, &mTLSClientCerts)
TLSCert := cli.PathFlag{
Name: "cert",
Usage: "Server x509 certificate file",
Category: "\rTLS:",
}
app.Flags = append(app.Flags, &TLSCert)
TLSCertKey := cli.PathFlag{
Name: "cert-private-key",
Usage: "Server x509 certificate private key file",
Category: "\rTLS:",
}
app.Flags = append(app.Flags, &TLSCertKey)
app.Action = func(ctx *cli.Context) error { app.Action = func(ctx *cli.Context) error {
err := setup(ctx) err := setup(ctx)
if err != nil { if err != nil {
@ -367,3 +420,46 @@ func handleStdin(c chan struct{}) {
} }
} }
} }
func createMTLS(clinetCert, serverCert, serverKey string) (*x509.CertPool, error) {
clientCert, err := os.ReadFile(clinetCert)
if err != nil {
return nil, err
}
certPool := x509.NewCertPool()
ok := certPool.AppendCertsFromPEM(clientCert)
if !ok {
return nil, errors.New("mTLS: Could read and parase a single cert from client cert")
}
return certPool, nil
}
func setupTLS(ctx *cli.Context) (*tls.Config, error) {
ca := ctx.Path("client-certs")
if ca == "" {
return nil, errors.New("client-certs flag is not set to enable TLS")
}
serverCert := ctx.Path("ca")
if serverCert == "" {
return nil, errors.New("cert flag is not set to enable TLS")
}
serverKey := ctx.Path("cert-private-key")
if serverKey == "" {
return nil, errors.New("cert-private-key is not set to enable TLS")
}
certPool, err := createMTLS(ca, serverCert, serverKey)
if err != nil {
return nil, err
}
conf := tls.Config{
ClientAuth: tls.RequireAndVerifyClientCert,
ClientCAs: certPool,
}
return &conf, nil
}

28
cmd/hvpn-node/init-certs.sh Executable file
View File

@ -0,0 +1,28 @@
#!/usr/bin/env bash
openssl req \
-x509 \
-new \
-noenc \
-sha256 \
-newkey rsa:4096 \
-outform PEM \
-keyout client_cert.key \
-out client_cert.pem \
-days 3600 \
-new \
-subj "/C=US/ST=Ohio/L=Columbus/O=HVPN/OU=HVPN Client"
openssl req \
-x509 \
-new \
-noenc \
-sha256 \
-newkey rsa:4096 \
-outform PEM \
-keyout cert.key \
-out cert.pem \
-days 3600 \
-new \
-subj "/C=US/ST=Ohio/L=Columbus/O=HVPN/OU=HVPN Server"