159 lines
4.6 KiB
Go
159 lines
4.6 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"fmt"
|
|
"log"
|
|
"log/slog"
|
|
"net/http"
|
|
"os"
|
|
"os/signal"
|
|
"time"
|
|
"vm-ctp/api/router"
|
|
utils "vm-ctp/internal/utils"
|
|
|
|
"github.com/gorilla/handlers"
|
|
"github.com/joho/godotenv"
|
|
)
|
|
|
|
// For build numbers, from https://blog.kowalczyk.info/article/vEja/embedding-build-number-in-go-executable.html
|
|
var sha1ver string // sha1 revision used to build the program
|
|
var buildTime string // when the executable was built
|
|
var wait time.Duration
|
|
var bindDisableTls bool
|
|
|
|
func main() {
|
|
// Load data from environment file
|
|
envFilename := utils.GetFilePath(".env")
|
|
err := godotenv.Load(envFilename)
|
|
if err != nil {
|
|
panic("Error loading .env file")
|
|
}
|
|
|
|
// Open connection to logfile
|
|
// From https://ispycode.com/GO/Logging/Logging-to-multiple-destinations
|
|
logFile := os.Getenv("LOG_FILE")
|
|
if logFile == "" {
|
|
logFile = "./vctp.log"
|
|
}
|
|
logfileWriter, err := os.OpenFile(logFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
|
if err != nil {
|
|
fmt.Printf("Unable to write logfile '%s' : '%s'\n", logFile, err)
|
|
os.Exit(1)
|
|
}
|
|
|
|
h := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: true})
|
|
slog.SetDefault(slog.New(h))
|
|
|
|
//log.SetOutput(logfileWriter)
|
|
//log.Printf("vCTP starting execution. Built on %s from sha1 %s. Runtime %s\n", buildTime, sha1ver, runtime.Version())
|
|
|
|
r := router.New()
|
|
|
|
// Log everything to stdout in Apache Common Log Format
|
|
var loggedRouter http.Handler
|
|
loggedRouter = handlers.LoggingHandler(logfileWriter, r)
|
|
|
|
// Set some options for TLS
|
|
tlsConfig := &tls.Config{
|
|
MinVersion: tls.VersionTLS12,
|
|
CurvePreferences: []tls.CurveID{tls.CurveP521, tls.CurveP384, tls.CurveP256},
|
|
PreferServerCipherSuites: true,
|
|
InsecureSkipVerify: true,
|
|
CipherSuites: []uint16{
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
|
|
tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
|
|
tls.TLS_RSA_WITH_AES_256_CBC_SHA,
|
|
},
|
|
}
|
|
|
|
// Determine bind IP
|
|
bindIP := os.Getenv("BIND_IP")
|
|
if bindIP == "" {
|
|
bindIP = utils.GetOutboundIP().String()
|
|
}
|
|
// Determine bind port
|
|
bindPort := os.Getenv("BIND_PORT")
|
|
if bindPort == "" {
|
|
bindPort = "9443"
|
|
}
|
|
bindAddress := fmt.Sprint(bindIP, ":", bindPort)
|
|
slog.Info("Will listen on address", "ip", bindIP, "port", bindPort)
|
|
|
|
// Determine bind disable TLS
|
|
bindDisableTlsEnv := os.Getenv("BIND_DISABLE_TLS")
|
|
if bindDisableTlsEnv == "true" {
|
|
bindDisableTls = true
|
|
}
|
|
|
|
// Get file names for TLS cert/key
|
|
tlsCertFilename := os.Getenv("TLS_CERT_FILE")
|
|
if tlsCertFilename != "" {
|
|
tlsCertFilename = utils.GetFilePath(tlsCertFilename)
|
|
} else {
|
|
tlsCertFilename = "./cert.pem"
|
|
}
|
|
|
|
tlsKeyFilename := os.Getenv("TLS_KEY_FILE")
|
|
if tlsKeyFilename != "" {
|
|
tlsKeyFilename = utils.GetFilePath(tlsKeyFilename)
|
|
} else {
|
|
tlsKeyFilename = "./privkey.pem"
|
|
}
|
|
|
|
// Generate certificate if required
|
|
if !(utils.FileExists(tlsCertFilename) && utils.FileExists(tlsKeyFilename)) {
|
|
slog.Warn("Specified TLS certificate or private key do not exist", "certificate", tlsCertFilename, "tls-key", tlsKeyFilename)
|
|
utils.GenerateCerts(tlsCertFilename, tlsKeyFilename)
|
|
}
|
|
|
|
// Configure the http server
|
|
srv := &http.Server{
|
|
Addr: bindAddress,
|
|
// Good practice to set timeouts to avoid Slowloris attacks.
|
|
WriteTimeout: time.Second * 15,
|
|
ReadTimeout: time.Second * 15,
|
|
IdleTimeout: time.Second * 60,
|
|
Handler: loggedRouter, // Pass our instance of gorilla/mux in.
|
|
TLSConfig: tlsConfig,
|
|
TLSNextProto: make(map[string]func(*http.Server, *tls.Conn, http.Handler)),
|
|
}
|
|
slog.Info("Started listening on", "bindAddress", bindAddress)
|
|
|
|
// Run our server in a goroutine so that it doesn't block
|
|
// Also decide whether to start a TLS or plain http server
|
|
go func() {
|
|
if bindDisableTls {
|
|
if err := srv.ListenAndServe(); err != nil {
|
|
log.Println(err)
|
|
}
|
|
} else {
|
|
if err := srv.ListenAndServeTLS(tlsCertFilename, tlsKeyFilename); err != nil {
|
|
log.Println(err)
|
|
}
|
|
}
|
|
}()
|
|
|
|
c := make(chan os.Signal, 1)
|
|
// We'll accept graceful shutdowns when quit via SIGINT (Ctrl+C)
|
|
// SIGKILL, SIGQUIT or SIGTERM (Ctrl+/) will not be caught.
|
|
signal.Notify(c, os.Interrupt)
|
|
|
|
// Block until we receive our signal.
|
|
<-c
|
|
|
|
// Create a deadline to wait for.
|
|
ctx, cancel := context.WithTimeout(context.Background(), wait)
|
|
defer cancel()
|
|
// Doesn't block if no connections, but will otherwise wait
|
|
// until the timeout deadline.
|
|
srv.Shutdown(ctx)
|
|
// Optionally, you could run srv.Shutdown in a goroutine and block on
|
|
// <-ctx.Done() if your application should wait for other services
|
|
// to finalize based on context cancellation.
|
|
slog.Info("shutting down")
|
|
os.Exit(0)
|
|
}
|